一、SysTick簡介
SysTick —系統定時器是屬于 CM3 內核中的一個外設,內嵌在 NVIC 中,系統定時器是一個 24bit 的向下遞減的計數器,計數器每計數一次的時間為 1/SYSCLK,一般我們設定系統時鐘 SYSCLK 等于 72M,當重裝載數值暫存器的值遞減到 0 的時候,系統定時器就產生一次中斷,以此回圈往復,
因為 SysTick 是屬于 CM3 內核的外設,所以所有基于 CM3 內核的單片機都具有這個系統定時器,使得軟體在 CM3 單片機中可以很容易的移植,系統定時器一般用于作業系統,用于產生時基,維持作業系統的心跳,
二、新建工程
1. 打開 STM32CubeMX 軟體,點擊“新建工程”

2. 選擇 MCU 和封裝

3. 配置時鐘
RCC 設定,選擇 HSE(外部高速時鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)

選擇 Clock Configuration,配置系統時鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 后,輸入回車,軟體會自動修改所有配置

4. 配置除錯模式
非常重要的一步,否則會造成第一次燒錄程式后續無法識別除錯器
SYS 設定,選擇 Debug 為 Serial Wire

5. 配置GPIO
GPIO 設定,在右邊圖中找到 LED 燈對應引腳,選擇 GPIO_Output,輸出低電平點亮,可以添加自定義標簽


6. 生成代碼
輸入專案名和專案路徑

選擇應用的 IDE 開發環境 MDK-ARM V5

點擊 GENERATE CODE 生成代碼

三、普通延時
3.1 修改main函式,實作流水燈
在 while() 回圈中加入引腳輸出置反函式 HAL_GPIO_TogglePin() 和延時函式 HAL_Delay(),
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
HAL_Delay(500);
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
HAL_GPIO_TogglePin(LED_B_GPIO_Port,LED_B_Pin);
HAL_Delay(500);
HAL_GPIO_TogglePin(LED_B_GPIO_Port,LED_B_Pin);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
3.2 HAL庫與標準庫代碼比較
STM32CubeMX 使用 HAL 庫生成的代碼:
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
HAL_Delay(500);
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
HAL_GPIO_TogglePin(LED_B_GPIO_Port,LED_B_Pin);
HAL_Delay(500);
HAL_GPIO_TogglePin(LED_B_GPIO_Port,LED_B_Pin);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
HAL_StatusTypeDef HAL_Init(void)
{
/* Configure Flash prefetch */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
defined(STM32F102x6) || defined(STM32F102xB) || \
defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
defined(STM32F105xC) || defined(STM32F107xC)
/* Prefetch buffer is not available on value line devices */
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
/* Init the low level hardware */
HAL_MspInit();
/* Return function status */
return HAL_OK;
}
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/* Configure the SysTick to have interrupt in 1ms time basis*/
if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
{
return HAL_ERROR;
}
/* Configure the SysTick IRQ priority */
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{
return HAL_ERROR;
}
/* Return function status */
return HAL_OK;
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
使用 STM32 標準庫的代碼:
/**
* @brief 主函式
* @param 無
* @retval 無
*/
int main(void)
{
/* LED 埠初始化 */
LED_GPIO_Config();
/* 配置SysTick 為10us中斷一次 */
SysTick_Init();
for(;;)
{
LED1( ON );
SysTick_Delay_Ms( 1000 );
LED1( OFF );
LED2( ON );
SysTick_Delay_Ms( 1000 );
LED2( OFF );
}
}
/**
* @brief 啟動系統滴答定時器 SysTick
* @param 無
* @retval 無
*/
void SysTick_Init(void)
{
/* SystemFrequency / 1000 1ms中斷一次
* SystemFrequency / 100000 10us中斷一次
* SystemFrequency / 1000000 1us中斷一次
*/
// if (SysTick_Config(SystemFrequency / 100000)) // ST3.0.0庫版本
if (SysTick_Config(SystemCoreClock / 100000)) // ST3.5.0庫版本
{
/* Capture error */
while (1);
}
}
// couter 減1的時間 等于 1/systick_clk
// 當counter 從 reload 的值減小到0的時候,為一個回圈,如果開啟了中斷則執行中斷服務程式,
// 同時 CTRL 的 countflag 位會置1
// 這一個回圈的時間為 reload * (1/systick_clk)
void SysTick_Delay_Us( __IO uint32_t us)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000000);
for(i=0;i<us;i++)
{
// 當計數器的值減小到0的時候,CRTL暫存器的位16會置1
while( !((SysTick->CTRL)&(1<<16)) );
}
// 關閉SysTick定時器
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}
void SysTick_Delay_Ms( __IO uint32_t ms)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000);
for(i=0;i<ms;i++)
{
// 當計數器的值減小到0的時候,CRTL暫存器的位16會置1
// 當置1時,讀取該位會清0
while( !((SysTick->CTRL)&(1<<16)) );
}
// 關閉SysTick定時器
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
HAL_InitTick(TICK_INT_PRIORITY); 對應 SysTick_Init();
HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) 對應 SysTick_Config(SystemCoreClock/1000000)
HAL_Delay(500); 對應 SysTick_Delay_Ms(500);
四、中斷中延時
在 ESP8266學習筆記(3)——GPIO介面使用 的 HAL_GPIO_EXTI_Callback 中加入按鍵消抖
/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==KEY1_Pin)
{
HAL_Delay(100);
if(HAL_GPIO_ReadPin(KEY1_Pin_Port,KEY1_Pin)==1)
{
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
}
}
}
/* USER CODE END 1 */
現在下載進單片機只會讓它在按鍵按下后沒有任何反應,這又是為什么呢,其實問題出在HAL_Delay() 上,
在進入回呼函式之后就一直在 HAL_Delay 陷入了死回圈中,原因是 Systick 優先級太低造成的,

Systick 中斷的搶占優先級和外部中斷的搶占優先級是一樣的,那么在外部中斷觸發時肯定不能接著觸發 Systick 中斷了,問題已經找到,只需要簡單地將外部中斷的搶占優先級改低即可,
- 搶占優先級,數字越小,優先級越高
- 若搶占優先級相同,判斷子優先級,同樣,數字越小,優先級越高

五、注意事項
用戶代碼要加在 USER CODE BEGIN N 和 USER CODE END N 之間,否則下次使用 STM32CubeMX 重新生成代碼后,會被洗掉,

? 由 Leung 寫于 2021 年 1 月 13 日
? 參考:《嵌入式-STM32開發指南》第二部分 基礎篇 - 第2章 Systick系統定時器(HAL庫)
STM32CubeMX實戰教程(三)——外部中斷(中斷及HAL_Delay函式避坑)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/248502.html
標籤:其他
上一篇:二、5 秒創建 k8s 集群
