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);
#endif
sys.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