MM32F3273G8P火龙果开发板MindSDK开发教程4 - 滴嗒定时器Systick的配置
1、Systick寄存器
Systick是ARM内核的一个外设,所以在不同芯片的代码上移植比较方便,他总共有4个寄存器,
从Systick定义中可以看到:
typedef struct
{
__IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
__IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
__IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
__IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
2、轮询方式延时
一般,systick ms级别的延时,采用中断方式,load值为sysclk/1000,这样每ms产生中断,可以用来ms级别的延时,或者为freeRtos提供节拍。
同样,采用轮询方式延时,也可以实现ms级的延时,并且可以实现us级的延时。
systick的时钟源分两种,ctrl寄存器第二位为0时采用sysclk/8的时钟频率,第二位为1时采用系统时钟。
轮询方式延时采用sysclk/8分频方式,也可以采用不分频的方式。
a、 采用8分频的方式
假如系统时钟为120M,8分频后为15M,即1/15000000计一次数,1us需要15次。所以代码中倍频因子为了代码的可移植性,使其count_1us = sysclk/8000000 = 15。
相关代码:
volatile static float count_1us = 0;
volatile static float count_1ms = 0;
/**
* 初始化滴答定时器函数
* 轮询方式延时
*/
void BOARD_Systick_Init()
{
// Systick CTRL 寄存器 第二位为0时,Systick时钟==Sysclk/8 ,
// 第二位为1时,Systick = Sysclk
SysTick->CTRL &= ~(1<<2);// 将systick使用内核时钟,根据时钟树,即120/8=15M;
count_1us = (float)(CLOCK_GetBootHSEValue()/8000000);
count_1ms = (float)1000*count_1us;
}
void BOARD_Delay1Us(uint32_t count)
{
uint32_t ctl;
/* reload the count value */
SysTick->LOAD = (uint32_t)(count * count_1us);
/* clear the current count value */
SysTick->VAL = 0x0000U;
/* enable the systick timer */
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
/* wait for the COUNTFLAG flag set */
do{
ctl = SysTick->CTRL;
}while((ctl&SysTick_CTRL_ENABLE_Msk)&&!(ctl & SysTick_CTRL_COUNTFLAG_Msk));
/* disable the systick timer */
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
/* clear the current count value */
SysTick->VAL = 0x0000U;
}
void BOARD_Delay1Ms(uint32_t count)
{
uint32_t ctl;
/* reload the count value */
SysTick->LOAD = (uint32_t)(count * count_1ms);
/* clear the current count value */
SysTick->VAL = 0x0000U;
/* enable the systick timer */
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
/* wait for the COUNTFLAG flag set */
do{
ctl = SysTick->CTRL;
}while((ctl&SysTick_CTRL_ENABLE_Msk)&&!(ctl & SysTick_CTRL_COUNTFLAG_Msk));
/* disable the systick timer */
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
/* clear the current count value */
SysTick->VAL = 0x0000U;
}
实验现象
BOARD_Delay1Ms(1000)
BOARD_Delay1Us(1000)
产生的对应波形为
b、 采用不分频的方式
设置systick的时钟=sysclk=120M,所以1us需要计数120次,1ms需要计数120*1000次。
us延时设置LOAD寄存器=sysclk/1000000;
ms延时设置LOAD寄存器=sysclk/1000;
当每次计数结束时,就是LOAD减为0的时候,CTRL寄存器的16位会置为1,我们监测这位的数值即可实现延时。这里同上面一样
实现代码
uint32_t BOARD_Systick_Init(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
return (0); /* Function successful */
}
void BOARD_Delay1Us( __IO uint32_t us)
{
uint32_t i;
BOARD_Systick_Init(CLOCK_GetBootHSEValue() / 1000000);
for(i=0;i<us;i++)
{
/* When the counter value decreases to 0, bit 16 of the CRTL register will be set to 1 */
/* When set to 1, reading this bit will clear it to 0 */
while( !((SysTick->CTRL)&(1<<16)) );
}
/* Turn off the SysTick timer */
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
void BOARD_Delay1Ms( __IO uint32_t ms)
{
uint32_t i;
BOARD_Systick_Init(CLOCK_GetBootHSEValue() / 1000);
for(i=0;i<ms;i++)
{
/* When the counter value decreases to 0, bit 16 of the CRTL register will be set to 1 */
/* When set to 1, reading this bit will clear it to 0 */
while( !((SysTick->CTRL)&(1<<16)) );
}
/* Turn off the SysTick timer */
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
中断方式延时
设置每1ms触发中断,在中断里记录中断次数,亦可实现ms级的延时。
volatile static uint32_t delay;
uint32_t BOARD_Systick_Init(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
return (0); /* Function successful */
}
void BOARD_Delay1Ms(uint32_t count)
{
delay = count;
while(0U != delay){
}
}
void BOARD_Delay_Decrement(void)
{
if (0U != delay){
delay--;
}
}
void SysTick_Handler(void)
{
BOARD_Delay_Decrement();
}
仿Hal库延时
static uint32_t uwTick = 0;
int Drv_SysTick_Config(void)
{
volatile uint32_t prioritygroup = 0x00U;
volatile uint32_t SystemCoreClock = CLOCK_GetBootHSEValue();
/* Configure the SysTick to have interrupt in 1ms time basis*/
if(SysTick_Config(SystemCoreClock/1000) > 0)
{
return -1;
}
/* Configure the SysTick IRQ priority */
prioritygroup = NVIC_GetPriorityGrouping();
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(prioritygroup, 15, 0));
return 0;
}
/**
* @brief 内核滴答定时器的中断服务函数
*
*/
void SysTick_Handler(void)
{
uwTick++;
}
/**
* @brief 获取系统运行的tick计数值
*
* @return unsigned int 滴答定时器以1kHz计数的值
*/
unsigned int HAL_GetTick(void)
{
return uwTick;
}
/**
* @brief 利用滴答定时器实现的延时函数,默认单位是ms
*
* @param Delay
*/
void HAL_Delay(unsigned int Delay)
{
volatile uint32_t tickstart = HAL_GetTick();
volatile uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < 0xFFFFFFFFU)
{
wait += (uint32_t)(1);
}
while ((HAL_GetTick() - tickstart) < wait)
{
}
}
4、代码
https://gitee.com/xiaoguo-tec_0/mm32-f3273.git