SysTick 定时器被集成在NVIC中。因此,只要是Cortex-M3 内核的单片机,就都有它。这个学习笔记就用SysTick 定时器来实现走马灯的功能。
SysTick 定时器非常简答,只有四个寄存器。这四个寄存器的含义在《Cortex-M3权威指南》那本书中讲的非常的清楚,这里不复述了,下面只讲讲在STM32上SysTick有什么特殊之处。按照CMSIS 标准,用C语言访问这四个寄存器时使用的寄存器名称分别如下:
SysTick->CTRL
SysTick->LOAD
SysTick->VAL
SysTick->CALIB
SysTick->CALIB 的值固定为9000,因此,只有当系统嘀嗒时钟设定为9MHz(HCLK/8的最大值) ,产生1ms 时间基准。
STM32提供了2个时钟源:
0: AHB/8
1: Processor clock (AHB)
因此,SysTick->CTRL = 7 表示使用处理器时钟作为时钟源,使能SysTick,并且使能SysTick中断。SysTick->CTRL = 3 时频率降为原来的1/8。
我的开发板上有四个LED,分别对应的GPIO端口D 的 PD2、PD3、PD4和PD7。
下面是例子程序,仍然先是直接设置寄存器。
#include "stm32f10x.h" #define RCC_GPIO_LED RCC_APB2Periph_GPIOD #define GPIO_LED_PORT GPIOD #define GPIO_LED1 GPIO_Pin_2 #define GPIO_LED2 GPIO_Pin_3 #define GPIO_LED3 GPIO_Pin_4 #define GPIO_LED4 GPIO_Pin_7 #define GPIO_LED_ALL GPIO_LED1 |GPIO_LED2 |GPIO_LED3 |GPIO_LED4 void LED_Spark(void) { static int state = 0; switch (state) { case 0: GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL); GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED1); state ++; break; case 1: GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL); GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED2); state ++; break; case 2: GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL); GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED3); state ++; break; case 3: GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL); GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED4); state = 0; break; default: state = 0; break; } } int main(void) { SystemInit(); RCC->APB2ENR |= 0x00000020; GPIOD->CRL = 0x24422244; //PD2 PD3 PD4 PD7 Set to Output mode SysTick->LOAD = 24000000/200; SysTick->CTRL = 3; for(;;) { } } /** * @brief This function handles SysTick Handler. * @param None * @retval None */ void SysTick_Handler(void) { static int count = 0; count ++; if (count == 100) { LED_Spark(); count = 0; } }
然后是利用STM32 固件函数库提供的函数的例子。
#include "stm32f10x.h" #define RCC_GPIO_LED RCC_APB2Periph_GPIOD #define GPIO_LED_PORT GPIOD #define GPIO_LED1 GPIO_Pin_2 #define GPIO_LED2 GPIO_Pin_3 #define GPIO_LED3 GPIO_Pin_4 #define GPIO_LED4 GPIO_Pin_7 #define GPIO_LED_ALL GPIO_LED1 |GPIO_LED2 |GPIO_LED3 |GPIO_LED4 void LED_Spark(void) { static int state = 0; switch (state) { case 0: GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL); GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED1); state ++; break; case 1: GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL); GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED2); state ++; break; case 2: GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL); GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED3); state ++; break; case 3: GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL); GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED4); state = 0; break; default: state = 0; break; } } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; SystemInit(); SysTick_Config(SystemCoreClock/100); /* Enable GPIOB, GPIOC and AFIO clock */ RCC_APB2PeriphClockCmd(RCC_GPIO_LED, ENABLE); //RCC_APB2Periph_AFIO /* LEDs pins configuration */ GPIO_InitStructure.GPIO_Pin = GPIO_LED_ALL; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIO_LED_PORT, &GPIO_InitStructure); for(;;) { } } /** * @brief This function handles SysTick Handler. * @param None * @retval None */ void SysTick_Handler(void) { static int count = 0; count ++; if (count == 100) { LED_Spark(); count = 0; } }
需要说明的是,若是用 SysTick_Config 函数来设置SysTick的中断频率,时钟源就不能人为的指定了,这时使用的时钟源就是内核的频率。
SystemCoreClock 是个全局变量,它的值就是内核的运行频率,不过前提要调用 SystemInit() 函数来设置内核的频率。如果内核的频率是字节写寄存器来设置的,SystemCoreClock 的值就不一定对了。