SysTick定时器是存在于ARM Cortex-M内核的一个滴答定时器,只要是ARM Cortex-M0/M3/M4/M7内核的MCU都包含这个定时器。
它是一个24位的递减定时器,当计数到 0 时,将从RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。
使用内核的SysTick定时器来实现延时,可以不占用系统定时器,由于和MCU外设无关,所以代码的移植,在不同厂家的Cortex-M内核MCU之间,可以很方便的实现。
Microsemi SmartFusion系列的FPGA芯片,内部就有一个ARM Cortex-M3内核的MCU,可以利用这个定时器,实现一个精确延时毫秒和微妙的函数。
在core_cm3.h文件中,有这样一个SysTick_Config函数:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) { if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); /* Reload value impossible */ } SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;/* Enable SysTick IRQ and SysTick Timer */ return (0UL); /* Function successful */ }通过后面的注释也可以看出,这是对SysTick定时器进行初始化,配置初始计数值,使能中断,使能定时器等。对应的中断函数为:
void SysTick_Handler(void) { }这个默认是空的,需要我们自己来实现。
如果SysTick初始化为:
SysTick_Config(SystemCoreClock / 1000); //定时1ms即SysTick定时器每1ms中断一次,如果我们定义全局变量,然后在中断函数中,让此变量递减,而在延时函数中,一直判断此变量是否减到了0,那么这样就实现了一个延时毫秒的函数。同理改变定时器的计数值为:
SysTick_Config(SystemCoreClock / 1000000); //定时1us那么就实现了每1us中断一次,所以延时微秒和延时毫秒函数的实现:
uint32_t fac_us=0; //us延时倍乘数 uint32_t fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数 void delay_init(void) { SystemCoreClockUpdate(); } void SysTick_Handler(void) { if(fac_us) fac_us--; if(fac_ms) fac_ms--; } void delay_us(uint32_t nus) { SysTick_Config(SystemCoreClock / 1000000); //定时1us fac_us = nus; while(fac_us != 0); } void delay_ms(uint32_t nms) { SysTick_Config(SystemCoreClock / 1000); //定时1ms fac_ms = nms; while(fac_ms != 0); }在使用延时函数之前,只需要进行系统时钟的更新即可。
从MSS_GPIO库函数中,可找到对单个GPIO进行控制的实现:
void MSS_GPIO_set_output ( mss_gpio_id_t port_id, uint8_t value ) { uint32_t gpio_idx = (uint32_t)port_id; ASSERT( gpio_idx < NB_OF_GPIO ); if ( gpio_idx < NB_OF_GPIO ) { GPIO_BITBAND->GPIO_OUT[gpio_idx] = (uint32_t)value; } }可以看到,最终是可以简化为GPIO_BITBAND->GPIO_OUT[gpio_idx] = value;
在a2fxxxm3.h头文件中有一个结构体的定义:
typedef struct { __IO uint32_t GPIO_0_CFG; ............ __IO uint32_t GPIO_31_CFG; __IO uint32_t GPIO_IRQ; __I uint32_t GPIO_IN; __IO uint32_t GPIO_OUT; } GPIO_TypeDef; typedef struct { __IO uint32_t GPIO_0_CFG[32]; ............ __IO uint32_t GPIO_31_CFG[32]; __IO uint32_t GPIO_IRQ[32]; __I uint32_t GPIO_IN[32]; __IO uint32_t GPIO_OUT[32]; } GPIO_BitBand_TypeDef;也就是说,最终的GPIO控制和读取,操作的其实是结构体成员GPIO_IN[32]和GPIO_OUT[32]这两个数组。
可以用一个带参数的宏来实现:
/* GPIO输出 */ #define MSS_IO_OUT(n) GPIO_BITBAND->GPIO_OUT[n] /* 读取输入 */ #define MSS_IO_IN(n) GPIO_BITBAND->GPIO_IN[n] /* 示例 */ MSS_IO_OUT(0) = 0; //GPIO_0输出0 MSS_IO_OUT(3) = 1; //GPIO_3输入1 uint8_t sw1_in = MSS_IO_IN(2);//读取GPIO_2输入 uint8_t sw2_in = MSS_IO_IN(3);//读取GPIO_3输入所以GPIO的控制和延时函数的实际调用:
#include "main.h" int main() { delay_init(); /* 更新系统时钟 */ MSS_WD_disable(); MSS_GPIO_init(); /* GPIO_0 & GPIO_1 配置成输出模式 */ MSS_GPIO_config(MSS_GPIO_0, MSS_GPIO_OUTPUT_MODE); MSS_GPIO_config(MSS_GPIO_1, MSS_GPIO_OUTPUT_MODE); /* GPIO_2配置成输入模式*/ MSS_GPIO_config(MSS_GPIO_2, MSS_GPIO_INPUT_MODE); while(1) { /* GPIO_0闪烁 */ MSS_IO_OUT(0) = 0; delay_ms(500); MSS_IO_OUT(0) = 1; delay_ms(500); /* 读取GPIO_2输入 */ if(MSS_IO_IN(2) == 0) MSS_IO_OUT(1) = 0; else MSS_IO_OUT(1) = 1; } }delay.c文件的内容:
#include "delay.h" uint32_t fac_us = 0; uint32_t fac_ms = 0; void delay_init(void) { SystemCoreClockUpdate(); } void SysTick_Handler(void) { if(fac_us) fac_us--; if(fac_ms) fac_ms--; } void delay_us(uint32_t nus) { SysTick_Config(SystemCoreClock / 1000000); fac_us = nus; while(fac_us != 0); } void delay_ms(uint32_t nms) { SysTick_Config(SystemCoreClock / 1000); fac_ms = nms; while(fac_ms != 0); }delay.h文件的内容:
#ifndef __DELAY_H__ #define __DELAY_H__ #include "a2fxxxm3.h" extern uint32_t fac_us; extern uint32_t fac_ms; void delay_init(void); void delay_ms(uint32_t nms); void delay_us(uint32_t nus); #endifsys.h文件的内容:
#ifndef __SYS_H__ #define __SYS_H__ #include "a2fxxxm3.h" /* GPIO输出 */ #define MSS_IO_OUT(n) GPIO_BITBAND->GPIO_OUT[n] /* 读取输入 */ #define MSS_IO_IN(n) GPIO_BITBAND->GPIO_IN[n] #endif