声明:以下都是自己学习的一些心得。
一、Systick滴答定时器基础知识
由官方提供的用户手册可知,Systick滴答定时器的时钟源来自内部低速振荡器(LRC时钟),频率为32.768KHz。
二、代码实现
打开相关寄存器的宏,在文件hc32f4xx_conf.h
#define LL_CLK_ENABLE (DDL_ON)
#define LL_EFM_ENABLE (DDL_ON)
#define LL_GPIO_ENABLE (DDL_ON)
#define LL_INTERRUPTS_ENABLE (DDL_ON)
#define LL_PWC_ENABLE (DDL_ON)
#define LL_FCG_ENABLE (DDL_ON)
main.c头文件中,定义相关寄存器写保护的宏。
/* unlock/lock peripheral */
#define EXAMPLE_PERIPH_WE (LL_PERIPH_GPIO | LL_PERIPH_EFM | LL_PERIPH_FCG | \
LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_SRAM)
#define EXAMPLE_PERIPH_WP (LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_SRAM)
解释上面的宏内容:
LL_PERIPH_EFM 是嵌入式FLASH寄存器的写保护,FLASH是用来存储程序的。
LL_PERIPH_FCG 是控制器,控制相关寄存器(自己用到的)的使能。
LL_PERIPH_PWC_CLK_RMU 是 PWC、CLK、RMU寄存器的写保护,如何实现呢?根据以下代码:
__STATIC_INLINE void PWC_REG_Unlock(uint16_t u16Module)
{
SET_REG16_BIT(CM_PWC->FPRC, u16Module);
}
也就是通过对PWC(电源控制)中FPRC(功能保护控制寄存器)操作最低两位b0、b1实现,这两位保护对象有很多,具体看用户手册 参考表5-9。
LL_PERIPH_SRAM 是静态存储器写保护,用来存储变量的。
定时器触发中断函数和主函数
void SysTick_Handler(void)
{
SysTick_IncTick();
}
/**
* @brief Main function of SysTick.
* @param None
* @retval int32_t return value, if needed
*/
int32_t main(void)
{
/* Peripheral registers write unprotected */
LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
/* Configure BSP */
BSP_LED_Init();
/* SysTick configuration */
(void)SysTick_Init(1000U);
/* Peripheral registers write protected */
LL_PERIPH_WP(EXAMPLE_PERIPH_WP);
for (;;) {
BSP_LED_Toggle(LED_RED);
SysTick_Delay(500U);
BSP_LED_Toggle(LED_BLUE);
SysTick_Delay(500U);
}
}
先看主函数中,先关闭所需寄存器的写保护,LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
再初始化LED灯,BSP_LED_Init();通过for循环实现IO口初始化。
void BSP_LED_Init(void)
{
uint8_t i;
stc_gpio_init_t stcGpioInit;
/* configuration structure initialization */
(void)GPIO_StructInit(&stcGpioInit);
stcGpioInit.u16PinDir = PIN_DIR_OUT;
/* Initialize LED pin */
for (i = 0U; i < BSP_LED_NUM; i++) { //BSP_LEN_NUM = 4UL
(void)GPIO_Init(BSP_LED_PORT_PIN[i].port, BSP_LED_PORT_PIN[i].pin, &stcGpioInit);
}
}
初始化系统滴答定时器,(void)SysTick_Init(1000U); 注意这里的1000U是频率。
这里1000U的作用是 设置定时器的步长为 1。返回值为i32Ret ,当i32Ret为LL_OK的时候,即定时器通过SysTick_Config(HCLK_VALUE / u32Freq)开启,就会触发定时器中断。
__WEAKDEF int32_t SysTick_Init(uint32_t u32Freq)
{
int32_t i32Ret = LL_ERR;
if ((0UL != u32Freq) && (u32Freq <= 1000UL)) {
m_u32TickStep = 1000UL / u32Freq;
/* Configure the SysTick interrupt */
if (0UL == SysTick_Config(HCLK_VALUE / u32Freq)) {
i32Ret = LL_OK;
}
}
return i32Ret;
}
__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_LOAD_RELOAD_Msk 重装置最大为0xFFFFFFUL
SysTick->VAL 清空当前计数值寄存器VAL
SysTick->CTRL 为使能标志位, 这里使能了SysTick_CTRL_CLKSOURCE_Msk和SysTick_CTRL_TICKINT_Msk 。
最后LL_PERIPH_WP(EXAMPLE_PERIPH_WP)来重新开启写保护。
main函数里面的for循环就不多详细解读,具体在GPIO口代码解读里有。
接下来我们分析一下 滴答定时器触发中断函数
void SysTick_Handler(void)
{
SysTick_IncTick();
}
SysTick_Handler()函数不需要定义,当发生中断的时候就会自动跳到这里面,因为在启动文件startup_hc32f460.s中就使用汇编声明了,不懂没关系,知道有这回事就行了。
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
SysTick_IncTick()是一个计数函数。
__WEAKDEF void SysTick_IncTick(void)
{
m_u32TickCount += m_u32TickStep;
}
而这个m_u32TickCount初始为0,m_u32TickStep在主函数中通过函数(void)SysTick_Init(1000U) 设置为步长为1。
这样例程就提供了一个 使用Systick系统滴答定时器完成ms的定时,具体使用:
SysTick_Delay(500U); //500ms定时