參考資料《 ARM Cortex™-M4F 技術參考手冊》-4.5 章節 SysTick Timer(STK)
SysTick(系統滴答定時器),本質上就是一個內嵌在NVIC中的一個定時器,屬於內核中的一個外設,是一個24位的向下遞減的計數器,計數器每1/SYSCLK就減1,當寄存器的值減到0的時就會產生一次(硬件上的)中斷,也有叫做滴答中斷。主要的目的1、用來產生精確的延時。2、一般用於操作系統,用於產生時基,維持操作系統的心跳給操作系統提供一個單獨的心跳(時鐘)節拍。關於對於SyeTick的介紹我推薦看一下這位大神博客 https://blog.csdn.net/yx_l128125/article/details/7884423。
SysTick的寄存器總共有四個,在使用SysTick產生定時的時候,只需配置前三個寄存器,最後一個校準值寄存器不需要使用。
接下來對各個寄存器的描述
SysTick控制及狀態寄存器CTRL主要用來讀取當前數值寄存器裏的值、選擇時鐘源、使能異常請求、使能定時器。
SysTick重裝載數值寄存器LOAD主要是用來存儲計數的值,最大爲
SysTick當前數值寄存器VAL,每一個計數器每1/SYSCLK就減1,當減到0時,計數標誌位置1,然後把重裝載數值寄存器裏的值寫進當前數值寄存器,並清除計數標誌位。
Sys校準數值寄存器CALIB
以上就是關於SysTick的介紹,接下來就要操作一下了,
我們要用SysTick來定時1S使LED燈狀態變化一次,所以
1、首先,應先線配置SysTick相關寄存器,來產生1s的延時
2、初始化LED燈
3、編寫主函數
由於Systick是嵌入在內核中的所以在硬件電路中只需要有LED模塊即可,想要用SysTick實現流水燈1s切換一次狀態的操作,首先就是要配置SysTick,這裏我們直接用STM32官方額庫函數SysTick_Config(uint32_t ticks),在內核頭文件core_cm4.h中,這個函數的功能是配置系統定時器和中斷,並且打開SysTick,使計數器處於自由計數模式來產生一個週期性的中斷。在這個函數中形參ticks是用來設置重裝載寄存器的值,最大不能超過重裝載寄存器的值,當重裝載寄存器的值減到0時,就會產生一箇中斷,然後重裝載寄存器的值又寫入當前計數器中重新往下遞減計數,以此循環。接下來這個庫函數是給該中斷配置中斷優先級(關於中斷優先級可以查看STM32中斷及NVIC概述),然後給當前數值寄存器賦初值,接下來配置時鐘源,使能中斷,開啓定時器。當返回0時,說明函數功能已經實現,返回1則表示失敗。這個初始化函數主要是配置了SysTick中的三個寄存器:LOAD、VAL、CTRL,還調用了NVIC_SetPriority()配置中斷優先級函數。
因爲SysTick是內核中的外設,所以時鐘選擇爲HCLK180MHz時鐘
由於官方沒有 SysTick的初始化函數,只有配置函數Config所以,我們自己簡單的編寫一個SysTick_Init()函數
因爲HCLK爲180MHz,所以當ticks=SystemFrequency/1000時(180 000 000/1000),每經過1/SystemFrequency一次,ticks的值就減1,所以計數時間爲ticks/(1/SystemFrequency)=1ms,,所以我們配置的SysTick爲10us進一次中斷。
配置好SysTick後,我們只需要再定義一個變量來記錄進入中斷的次數N,N*SysTick就等於我們所要定時的時間。
nTime就是我進入SysTick中斷的次數,每進入一次中斷服務函數,TimingDelay就減1,當TimingDelay不等於0時,延時結束。
中斷服務函數如下
關於SysTick配置已經描述完,接下來初始化LED燈。
關於LED的初始化我就不再做過多的描述,可以參考STM32固件庫函數點亮LED燈,代碼如下
#include "stm32f4xx_gpio.h"
#define GPIO_R_Pin GPIO_Pin_10
#define GPIO_R_Port GPIOH
#define GPIO_G_Pin GPIO_Pin_11
#define GPIO_G_Port GPIOH
#define GPIO_B_Pin GPIO_Pin_12
#define GPIO_B_Port GPIOH
/************************控制LED燈亮滅的宏***************/
/*直接操作寄存器的方法控制IO*/
#define LED_PORT_OUT_HI(p,i) { p->BSRRL = i ;} //輸出爲高電平
#define LED_PORT_OUT_LO(p,i) { p->BSRRH = i ;} //輸出爲低電平
#define LED_PORT_OUT_Toggle(p,i) { p->BSRRL ^= i ;} //輸出爲反狀態
/*定義控制IO的宏*/
#define LED_R_Toggle LED_PORT_OUT_Toggle(GPIO_R_Port,GPIO_R_Pin)
#define LED_R_ON LED_PORT_OUT_LO(GPIO_R_Port,GPIO_R_Pin)
#define LED_R_OFF LED_PORT_OUT_HI(GPIO_R_Port,GPIO_R_Pin)
#define LED_G_Toggle LED_PORT_OUT_Toggle(GPIO_G_Port,GPIO_G_Pin)
#define LED_G_ON LED_PORT_OUT_LO(GPIO_G_Port,GPIO_G_Pin)
#define LED_G_OFF LED_PORT_OUT_HI(GPIO_G_Port,GPIO_G_Pin)
#define LED_B_Toggle LED_PORT_OUT_Toggle(GPIO_B_Port,GPIO_B_Pin)
#define LED_B_ON LED_PORT_OUT_LO(GPIO_B_Port,GPIO_B_Pin)
#define LED_B_OFF LED_PORT_OUT_HI(GPIO_B_Port,GPIO_B_Pin)
#define LED_RGBOFF LED_R_OFF;\
LED_G_OFF;\
LED_B_OFF
void LED_GPIO_Config(void);
#include "bsp_led.h"
/**
* @brief 初始化控制LED的IO
* @param 無
* @retval 無
*/
void LED_GPIO_Config(void)
{
/*定義一個GPIO_InitTypeDef類型的結構體*/
GPIO_InitTypeDef GPIO_InitStructure;
/*開啓LED相關的GPIO外設時鐘*/
RCC_AHB1PeriphClockCmd ( LED1_GPIO_CLK|
LED2_GPIO_CLK|
LED3_GPIO_CLK, ENABLE);
/*選擇要控制的GPIO引腳*/
GPIO_InitStructure.GPIO_Pin = LED1_PIN;
/*設置引腳模式爲輸出模式*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
/*設置引腳的輸出類型爲推輓輸出*/
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
/*設置引腳爲上拉模式*/
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
/*設置引腳速率爲2MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
/*調用庫函數,使用上面配置的GPIO_InitStructure初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*選擇要控制的GPIO引腳*/
GPIO_InitStructure.GPIO_Pin = LED2_PIN;
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/*選擇要控制的GPIO引腳*/
GPIO_InitStructure.GPIO_Pin = LED3_PIN;
GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);
/*關閉RGB燈*/
LED_RGBOFF;
}
配置好SysTick和LED後,接下來就開始寫主函數
主函數如下
int main(void)
{
/* LED 端口初始化 */
LED_GPIO_Config();
/* 配置SysTick 爲10us中斷一次,時間到後觸發定時中斷,
*進入stm32fxx_it.c文件的SysTick_Handler處理,通過數中斷次數計時
*/
SysTick_Init();
while(1)
{
LED_RED;
Delay_us(100000); // 10000 * 10us = 1000ms
//延時1s
LED_GREEN;
Delay_us(100000);
//延時1s
LED_BLUE;
Delay_us(100000);
//延時1s
}
}