本文例子參考《STM32單片機開發實體——基于Proteus虛擬仿真與HAL/LL庫》
源代碼:https://github.com/LanLinnet/STM33F103R6
專案要求
通過定時器延時(阻塞)的方式,實作LED燈以1秒為周期閃爍,
硬體設計
-
在第一節的基礎上,在Proteus中添加電路如下圖所示,

-
要對芯片進行設定,我們首先要了解定時器的作業機制,
(1)定時器概述
STM32F103系列單片機最多支持8個定時器,其中STM32F103R6單片機內部僅保留TIM1、TIM2和TIM3這3個定時器,其中TIM1是高級定時器,TIM2和TIM3是普通定時器,- 普通定時器除具備基本的定時功能外,還可以為DAC提供一個觸發通道,增加了輸入捕獲、輸出比較、單脈沖輸出、PWM信號輸出、正交編碼器等功能,
- 高級定時器在具備普通定時器的功能的基礎上,還增加了可輸出帶死區控制的互補PWM信號、緊急制動、定時器同步等功能,最多可以輸出6路PWM信號,
在本專案中我們使用普通定時器TIM3,
(2)基本定時功能
本次專案中我們只需要采用定時器的基本定時功能即可,其本質上就是對周期性脈沖信號進行計數,STM32芯片的時鐘源有很多,我們簡單列舉一下:- 低速內部時鐘LSI:一般由內部RC振蕩器提供,多用于RTC和看門狗電路,
- 低速外部時鐘LSE:一般由外部晶振提供,多用于實時時鐘,頻率一般為32.768kHz,
- 高速內部時鐘HSI:一般由內部RC振蕩器提供,多用于系統時鐘和PLL輸入,頻率一般為8MHz,
- 高速外部時鐘HSE:一般由外部晶振提供(4~16MHz,多為8MHz),用于系統時鐘和PLL輸入,
(3)計數模式
定時器有3種不同的計數模式,即向上計數、向下計數和中央對齊(向上/向下)計數模式,這里我們采用向上計數模式,從默認初始值0開始做加法計數,加到預設值之后產生一次溢位事件,自動復位至初始值0,之后開始新一輪的計數,下面是STM32F103R6芯片的時鐘樹,由圖可知TIM3的時鐘源來自APB1(Advanced Peripheral Bus1, 高級外設總線1),其時鐘頻率我們后面會在CubeMX中進行配置,

(4)時間計算
定時器有3種不同的計數模式,即向上計數、向下計數和中央對齊(向上/向下)計數模式,這里我們采用向上計數模式,從默認初始值0開始做加法計數,加到預設值之后產生一次溢位事件,自動復位至初始值0,之后開始新一輪的計數,
APB1 Timer clocks脈沖經過1個TIM3的專屬預分頻器分頻之后就會成為TIM3的計數脈沖,預分頻引數保存在一個16位的暫存器TIM3_PSC(簡稱PSC)中,
已知APB1 Timer clocks脈沖的頻率為\(f_{CLK}\),TIM3的計數脈沖周期為\(T_{CNT}\),預分頻引數為PSC,那么有公式如下:\(T_{CNT}=\frac{PSC+1}{f_{CLK}}\)
又因為STM32單片機所有的定時器都是16位定時器,其計數范圍為0~65535,那么我們就可以根據實際需要設計定時器的預設值,也就是自動多載暫存器TIM3_ARR(簡稱ARR)的值,此時TIM3采用向上計數的模式,那么其一次溢位時間(或者說一個計數周期)\(T_{OUT}\)就可以由上面的公式變為:
\(T_{OUT}=T_{CNT}\left(ARR+1\right)=\frac{\left(PSC+1\right)\left(ARR+1\right)}{f_{CLK}}\)
如果時鐘頻率采用默認的8MHz,我們不妨設定PSC為
7999,那么此時可以計算出TIM3的計數脈沖周期為\(T_{CNT}\)恰好為1ms\(T_{CNT}=\frac{PSC+1}{f_{CLK}}=\frac{7999+1}8=1000\mu s=1ms\)
由于我們設定的LED燈閃爍周期為1秒,那么亮或滅一次狀態的持續時間就是500ms,TIM3的計數脈沖周期為1ms,所以一個狀態需要計數500次,又因為本專案我們采用的是定時器阻塞的編程方式,我們只需要通過回圈不斷檢測當前計數值是否到了500即可,正因為如此,定時器一次溢位時間就需要大于500ms(不然計數永遠達不到500),我們不妨設定ARR為
999,這時套用上面的公式有\(T_{OUT}=T_{CNT}\left(ARR+1\right)=1000\times\left(999+1\right)=10^6\mu s=1s\)
滿足條件,
-
打開CubeMX,建立工程,我們首先將PC0管腳設定為GPIO_Output,
隨后對定時器進行設定:點擊“Categories”中的“Timer”串列,選中“TIM3”,在“TIM3 Mode and Configuration”視窗中設定“Clock Source”為Internal Clock,設定“PSC”為7999,“Counter Period”為999,

接下來點擊“Clock Configuration”進入時鐘配置界面,這里我們采用默認設定的8MHz,

-
點擊“Generator Code”生成Keil工程,
軟體撰寫
-
本次我們需要實作定時器阻塞延時使得LED燈閃爍,需要用到定時器運行和定時器停止函式,其API檔案如下:
HAL_TIM_Base_Start 定時器運行

HAL_TIM_Base_Stop 定時器停止

此外,還需要使用宏定義為定時器設定初始計數值__HAL_TIM_SET_COUNTER和獲取定時器當前計數值__HAL_TIM_GET_COUNTER,我們可以在“stm32f1xx_hal_tim.h”檔案中找到這兩個宏定義,

-
點擊“Open Project”在Keil中打開工程,雙擊“main.c”檔案,
-
我們需要設定一個自定義延時函式,輸入數值n,可以以定時器阻塞的方式延時n ms,首先在main.c檔案的最上面宣告這個函式,
/* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ void My_Delay_ms(uint16_t nms); //宣告自定義函式 /* USER CODE END 0 */在
/* USER CODE BEGIN 4 */和/* USER CODE END 4 */之間插入該自定義函式,代碼如下/* USER CODE BEGIN 4 */ //自定義函式 void My_Delay_ms(uint16_t nms) { uint16_t counter = 0; __HAL_TIM_SET_COUNTER(&htim3, 0); //為定時器TIM3設定初始計數值0 HAL_TIM_Base_Start(&htim3); //運行定時器 do { counter = __HAL_TIM_GET_COUNTER(&htim3); //當計數值小于nms時,一直回圈獲取定時器當前計數值 } while(counter < nms); HAL_TIM_Base_Stop(&htim3); //到nms時停止定時器TIM3 } /* USER CODE END 4 */最后,我們在while回圈中添加下面的代碼
/* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0); //PC0引腳電平翻轉 My_Delay_ms(500); //延時500ms /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
聯合除錯
- 點擊運行,生成HEX檔案,
- 在Proteus中加載相應HEX檔案,點擊運行,LED燈以1秒為周期閃爍,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/471721.html
標籤:嵌入式
上一篇:慢動作時間限制
