1.stm32定時器介紹
1.1.stm32f103定時器介紹
- 定時器可以對輸入的時鐘進行計數(shù),并在計數(shù)值達到設(shè)定值時觸發(fā)中斷。
- 16位計數(shù)器、預(yù)分頻器、自動重裝載寄存器的時基單元。
- 不僅具備基本的定時中斷功能,而且還包含內(nèi)外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發(fā)模式等多種功能。
- 根據(jù)復(fù)雜度和引用場景分為了高級定時器、通用定時器、基本定時器三種類型。
以上是各類定時器的主要功能
1.2.定時器計數(shù)模式
- 向上計數(shù)模式
計數(shù)器從0計數(shù)到自動加載值(TIMx_ARR),然后重新從0開始技術(shù)并且產(chǎn)生一個計數(shù)器溢出事件。 - 向下計數(shù)模式
計數(shù)器從自動裝入的值(TIMx_ARR)開始向下計數(shù)到0,然后從自動裝入的值重新開始,并產(chǎn)生一個計數(shù)器向下溢出事件。 - 中央對齊模式(向上/向下計數(shù))
計數(shù)器從0開始計數(shù)到自動裝入的值-1,產(chǎn)生一個計數(shù)器溢出事件,然后向下計數(shù)到1并且產(chǎn)生一個計數(shù)器溢出四件;然后再從0開始重新計數(shù)。
1.3.定時器的時鐘
上圖是stm32的系統(tǒng)結(jié)構(gòu),我們可以從上圖中看到,TIM1和TIM8是掛載到APB2總線上的,而TIM2-TIM7是掛載到APB1總線上的。
在stm32f103系列的MCU中,雖然定時器掛載的總線是不同的,但是每個定時器的時鐘頻率都是相同的。而其他系列的MCU,比如說stm32f407系列的MCU,如果掛載到不同的總線上,那么定時器的時鐘頻率可能是不同的。從下圖配置的cubemx圖中我們可以看到每個定時器上具體的時鐘。
我們從上面這個圖中可以看到,掛載到APB1和APB2總線上的定時器的時鐘都是72MHZ。
2.stm32時鐘的工作方式
我們這個地方主要就是講一下定時器基本的定時計數(shù)的功能,定時器基本的定時計數(shù)功能主要是通過以下的框圖來進行的。
- 時鐘源:定時器時鐘TIMxCLK,即內(nèi)部時鐘CK_INT,經(jīng)過APB預(yù)分頻器分頻后提供,就是從APB總線上引出的時鐘來源。
- 計數(shù)器時鐘:計數(shù)器時鐘是經(jīng)過②PSC預(yù)分頻器后產(chǎn)生的,就是由于不需要APB總線上這么高頻率的時鐘,所以在時鐘輸入以后,還需要進行預(yù)分頻以后,才能產(chǎn)生真正驅(qū)動CNT計數(shù)器的時鐘,每個定時器的時鐘的預(yù)分頻器PSC都是最高16位的,所以不能超過預(yù)分頻器的范圍。
- 計數(shù)器CNT:計數(shù)器CNT是進行計數(shù)的單元,當(dāng)接收到CK_CNT來的時鐘信號以后,每經(jīng)過一個時鐘頻率,CNT就會進行向上計數(shù)或者向下計數(shù),當(dāng)向上計數(shù)到自動重裝載寄存器的值(ARR)或者從ARR減到0以后,就會產(chǎn)生中斷或者事件更新。計數(shù)器CNT是一個16位/32位的計數(shù)器,每個定時器都是不同的,所以我們需要根據(jù)實際的情況來進行設(shè)置。
- 自動重裝載寄存器(ARR):這里面裝著計數(shù)器能計數(shù)的最大數(shù)值,當(dāng)計數(shù)到這個值的時候,如果使能了中斷的話,定時器就會產(chǎn)生溢出中斷。
- 計時中斷時間:1/(TIMxCLK/(PSC+1)*(ARR+1))
3.定時器中斷具體實現(xiàn)
我們需要實現(xiàn)的目標(biāo)是利用基本定時器實現(xiàn)定時1s中斷,并在中斷處理函數(shù)中控制LED燈的亮滅。
3.1.cubemx的具體配置
首先配置TIM2的相關(guān)參數(shù)
其次配置TIM2的相關(guān)中斷
3.2.具體代碼的實現(xiàn)
我們設(shè)置了定時器中斷,同樣需要在這個中斷C文件中去尋找相關(guān)的TIM2的中斷,我們找到這個函數(shù)以后追進去。
我們找到定時器更新事件,不出意外需要重寫回調(diào)函數(shù)。
進行回調(diào)函數(shù)的重寫,寫的規(guī)則是if(htim->Instance == TIM2) {HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);},這樣就可以在每一秒鐘,使得PA1口的LED燈進行亮滅的操作。
其中htim->Instance 是指htim是一個結(jié)構(gòu)體指針,然后指向他的成員變量intance(這個是寄存器基地址),如果這個寄存器的基地址等于TIM2的基地址,則證明這兩者相同,也就是說是TIM2產(chǎn)生的事件更新中斷,然后再進行下一步的操作。
寫到這個地方以后,我們還需要進行定時器中斷時基單元的開啟,這樣,定時器才能開始計數(shù)。
我們在main函數(shù)中開啟TIM2的時基單元,這樣就能實現(xiàn)最終的效果了。
4.通用定時器功能分析
通用定時器除了最基本的定時功能以外,還有輸入捕獲和輸出比較的功能,具體的功能我們可以通過下面的結(jié)構(gòu)框圖來進行了解。
我們一塊一塊來分析
-
第1塊是整個定時器的時鐘源,用來進行時鐘源的選擇,時鐘源有以下幾個選擇:
- 內(nèi)部時鐘(CK_INT):內(nèi)部時鐘源就是通過stm32的內(nèi)部時鐘來進行時鐘的輸入的,通過內(nèi)部時鐘總來作為定時器的時鐘來源
- 外部時鐘模式1:外部輸入引腳Tlx(x=1,2,3,4),采用定時器的外部通道引腳的輸入信號(TI1,TI2)作為定時器的時鐘源。TI1(TI2)通過濾波和邊緣檢測,得到信號TI1FP2(TI2FP2)作為計數(shù)器的觸發(fā)信號。值得注意的是,只有TI1FP2和TI2FP2可以作為時鐘源,也就是說想要使用外部時鐘模式1,時鐘信號只能通過通道1或者通道2輸入。
- 外部時鐘模式2:外部觸發(fā)輸入ETR,采用定時器的ETR引腳作為外部時鐘信號源,通過邊沿檢測和濾波來作為定時器的時鐘源。
- 外部觸發(fā)輸入(ITRx):使用一個定時器作為另一定時器的預(yù)分頻器,將主定時器的TRGO信號作為從定時器的ITRx輸入作為時鐘源。
一般而言,我們都是使用內(nèi)部時鐘作為定時器的時鐘來源
-
第2塊是通用定時器的控制器,主要包括觸發(fā)控制器、從模式控制器以及編碼器接口。觸發(fā)控制器用來針對片內(nèi)外設(shè)輸出觸發(fā)信號,比如為其他定時器提供時鐘和觸發(fā)DAC/ADC轉(zhuǎn)換。編碼器接口專門針對編碼器計數(shù)來使用;從模式控制器可以控制計數(shù)器復(fù)位、啟動、遞增/遞減、計數(shù)等功能??刂破髌鋵嵳f白了就是時鐘系統(tǒng)的配置單元,一個外設(shè)中需要使能相應(yīng)的功能那么就需要進行控制器的配置,比如說我們需要配置定時器的中斷、定時器主從級聯(lián)、定時器發(fā)送信號等功能就需要進行控制器的配置,由控制器來控制整個定時器的功能。
-
第3塊是時基單元,定時器產(chǎn)生的定時計數(shù)就是從這個地方產(chǎn)生的。通用定時器的時基單元主要由預(yù)分頻器PSC、計數(shù)器CNT、自動沖轉(zhuǎn)載寄存器ARR組成的。時鐘單元我們在第二章stm32時鐘工作方式講過,這個地方就不再贅述。
-
第4塊是輸入捕獲的部分,輸入捕獲部分我們主要就是用來捕獲外部輸入信號的頻率或者脈寬,我們肯定是需要去將外部的信號去連接我們的定時器通道。
- 當(dāng)一個信號輸入進來以后,首先我們需要對輸入進來的信號進行一個濾波,因為輸入進來的信號可能不是很穩(wěn)定的信號,所以我們首先需要進行濾波的操作;然后我們還需要進行一個邊沿的檢測,當(dāng)我們需要去檢測一個外部信號的脈寬或者頻率的時候,我們首先要想的就是如何去檢測這個信號,我們肯定是要用邊沿來檢測這樣一個信號,如果要去檢測脈寬,那么就需要測量一個信號的上升沿/下降沿和下降沿/上升沿,如果需要去檢測一個信號的頻率,那么我們就需要去檢測一個信號的兩次邊沿,這樣就能測量出來這個信號的頻率,這就是我們需要使用一個輸入濾波和邊沿檢測器的原因了。
- 采集到信號以后,我們需要進行預(yù)分頻器的設(shè)置,我們可以去設(shè)置這個預(yù)分頻器,也可以不去設(shè)置這個預(yù)分頻器的值,如果設(shè)置這個預(yù)分頻器,比如說我們將預(yù)分頻器的值設(shè)置為2,那么這個意思就是,我們在采集信號頻率的時候,并不是采集到兩次上升沿或者兩次下降沿就去報告我們采集到這個信號的頻率,我們需要去采集四次上升沿和下降沿再去匯報這個信號的頻率,這樣做的目的就是我們可以進行信號的一次采集濾波,更好地去測試信號的頻率。
-
第5塊是捕獲/比較寄存器,輸入捕獲和輸出比較的時候都需要用到這個寄存器,在我們使用輸入捕獲的功能的時候,我們可以用這個寄存器來存放我們輸入捕獲到的值,這就是我們在輸入捕獲過程中需要使用這個寄存器的方法;而輸出比較的時候,我們需要通過這個寄存器來設(shè)置CCR的值,通過這個CCR的值和ARR的值的比較,我們就可以輸出一個PWM的波形,這樣就可以用作電機PWM調(diào)速或者產(chǎn)生呼吸燈的效果,具體的輸出比較功能我們放在第6塊來講解。
-
輸出比較的功能就是用來產(chǎn)生PWM波形,下面這個圖就是顯示PWM的原理。
我們可以看到在0-t1的過程中,CCR的值小于ARR的值,這樣就可以產(chǎn)生一個低電平;在t1-t2的過程中,ARR的值大于CCR,這樣就可以產(chǎn)生一個高電平;我們知道,PWM的全稱是脈沖寬度調(diào)制,當(dāng)高低電平在一個周期中有不同的時候,這樣就可以產(chǎn)生不同的電壓。比如說我們知道高電平是3.3V,當(dāng)?shù)碗娖綍r間在一個周期里面占40%,那么高電平時間占60%的時間,那么這樣在一個周期里面的平均電平就是1.98V,這樣就是PWM的原理,通過輸出比較的功能,我們就可以看到這樣就可以產(chǎn)生一個PWM的信號用來驅(qū)動電機或者產(chǎn)生呼吸燈的效果。
5.高級定時器功能分析
高級定時器和通用定時器的主要區(qū)別就是在第3塊和第5塊的地方
第3塊主要的區(qū)別就是,高級定時器比通用定時器多了一個重復(fù)次數(shù)計數(shù)器,重復(fù)次數(shù)計數(shù)器的作用就是,當(dāng)CNT計數(shù)器產(chǎn)生溢出的時候,并不會直接產(chǎn)生中斷,而是會將溢出傳遞到重復(fù)次數(shù)計數(shù)器,重復(fù)次數(shù)計數(shù)器也會存一個數(shù)值,每次CNT計數(shù)器產(chǎn)生溢出以后,重復(fù)次數(shù)計數(shù)器數(shù)值就會減一,當(dāng)重復(fù)次數(shù)計數(shù)器減到0以后,才會產(chǎn)生溢出。
第5塊主要區(qū)別就是高級定時器增加了可編程的死區(qū)互補輸出功能,主要應(yīng)用在工業(yè)電機控制方面。但是目前來講,我沒有用到過這個功能,因此我也就不在這里班門弄斧了。
6.輸入捕獲實驗
我們上面介紹過,定時器有一個非常好用的功能就是輸入捕獲,很多的示波器的原理就是利用時鐘的輸入捕獲的功能,本章節(jié)就來具體講一個輸入捕獲的實驗。
實驗?zāi)繕?biāo):利用定時器4的輸入捕獲功能測量按鍵按下后低電平持續(xù)的時間,并用LED燈亮的時間來顯示捕獲的時間。
6.1.理論知識
輸入捕獲功能框圖如下圖所示
我們可以從每個定時器的通道中來進行輸入捕獲,具體的結(jié)構(gòu)分析我們在上面的理論知識中已經(jīng)講過了,這里就不再贅述了。
6.2.cubemx配置
其他配置都和原來的配置相同,將調(diào)試端口、時鐘配置、文件名配置完成后就可以生成對應(yīng)的文件了。
6.3.具體代碼實現(xiàn)
實驗?zāi)繕?biāo):利用定時器4的輸入捕獲功能測量按下按鍵后低電平持續(xù)的時間,并通過PA3口LED燈亮的時間來顯示。
- 首先輸入捕獲模式,可以用來測量頻率或者電平保持的時間,如下圖所示:
我們?nèi)绻枰东@低電平的時間,就需要從value2處觸發(fā)一次捕獲中斷,然后再從value3處觸發(fā)一次捕獲中斷,通過這兩次捕獲中斷的差值就可以來計算出低電平持續(xù)的時間了。但是我們一開始設(shè)置的是下降沿捕獲,而第二次捕獲需要進行上升沿的捕獲,所以中間就會涉及到一次邊沿捕獲方式的轉(zhuǎn)變。 - 然后我們需要考慮捕獲的時間,由于我們低電平持續(xù)的時間可能會大于定時器ARR的值,導(dǎo)致中途溢出,如下圖所示:
所以我們還需要考慮每次中途溢出的時間,我們可以用一個標(biāo)志位來記錄每次按鍵按下以后,低電平持續(xù)時間中,定時器中斷溢出了幾次,這樣我就可以使用標(biāo)志位*ARR的值來計算溢出的時間,然后通過最后捕獲的時間減去初始時間就能得到最后精確的值了。
以下是我的代碼的具體實現(xiàn)方式:
- 我們首先找到TIM4的中斷函數(shù)
- 追進去以后需要找到兩個回調(diào)函數(shù),第一個就是定時器中斷溢出的回調(diào)函數(shù),第二個就是輸入捕獲的回調(diào)函數(shù),我們找到這兩個回調(diào)函數(shù)以后,需要對這兩個回調(diào)函數(shù)進行重寫,來實現(xiàn)我們的功能。
3. 兩個回調(diào)函數(shù)的重寫
我的兩個回調(diào)函數(shù)是在time.c中重寫并實現(xiàn)具體的功能的,我的回調(diào)函數(shù)都有寫注釋,然后具體可以看我寫的注釋。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3); //當(dāng)按鍵按下以后,每過1s鐘,PA3上的LED燈就翻轉(zhuǎn)一次
tim_value+=10000; //按鍵按下后,當(dāng)?shù)竭_溢出以后,tim_value的值就加上ARR設(shè)置的值,ARR設(shè)置的值為10000
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) //輸入捕獲中斷的回調(diào)函數(shù)
{
if(htim->Instance==TIM4) //檢查是否為TIM4觸發(fā)的中斷
{
if(toggle_flag==0) //檢查標(biāo)志位
{
HAL_Delay(20); //消抖
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)==GPIO_PIN_RESET)
{
toggle_flag=1; //進入中斷后首先翻轉(zhuǎn)標(biāo)志位
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); //PA2的LED燈亮200ms,顯示進入輸入捕獲函數(shù)
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_TIM_Base_Start_IT(htim); //開啟溢出中斷,開始進行計時
__HAL_TIM_DISABLE(htim); //關(guān)閉TIM4
__HAL_TIM_SET_COUNTER(htim,0); //設(shè)置時鐘計數(shù)值為0
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); //清除TIM4通道1的原始設(shè)置
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);//將觸發(fā)輸入捕獲中斷源設(shè)置為上升沿捕獲
__HAL_TIM_ENABLE(htim);//使能TIM4
}
}
else if(toggle_flag==1)//檢查標(biāo)志位
{
HAL_Delay(20);//消抖
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)==GPIO_PIN_SET)//檢查是否為上升沿
{
toggle_flag=0;
HAL_TIM_Base_Stop_IT(htim);//關(guān)閉溢出中斷
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);//若是上升沿觸發(fā)輸入捕獲中斷,LED燈亮200ms
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
tim_value=(tim_value+HAL_TIM_ReadCapturedValue(&htim4,TIM_CHANNEL_1 ))/10+20;//計算捕獲的總時間
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); //計算出總時間后,使得PA2口的LED燈亮的時間和總時間相同,以顯示輸入捕獲效果
HAL_Delay(tim_value);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
__HAL_TIM_DISABLE(htim);
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);
__HAL_TIM_ENABLE(htim);
}
}
}
}
- 其他工作
我們用到了定時器的相關(guān)內(nèi)容,就需要使能這些內(nèi)容。
- 首先我們需要開啟定時器的輸入捕獲功能。
- 當(dāng)然我們還需要開啟定時器溢出中斷的功能,但是這部分功能我在重寫輸入捕獲的回調(diào)函數(shù)的時候才進行操作,具體可以看我上面的代碼
- 定義相關(guān)變量
我們在重寫定時器溢出回調(diào)函數(shù)和定時器輸入捕獲回調(diào)函數(shù)的時候用到了一些變量,我們需要在time.c中定義這些變量
下面是我的time.c和main.c中的具體代碼
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @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();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_1);
// extern uint32_t tim_value;
// extern uint8_t send_flag;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// if(send_flag==1)
// {
// tim_value/=1000;
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
// HAL_Delay(tim_value);
// tim_value=0;
// }
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
time.c文章來源:http://www.zghlxwxcb.cn/news/detail-775430.html
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file tim.c
* @brief This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "tim.h"
/* USER CODE BEGIN 0 */
uint32_t tim_value=0;
uint8_t toggle_flag=0;
//uint8_t send_flag=0;
/* USER CODE END 0 */
TIM_HandleTypeDef htim4;
/* TIM4 init function */
void MX_TIM4_Init(void)
{
/* USER CODE BEGIN TIM4_Init 0 */
/* USER CODE END TIM4_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM4_Init 1 */
/* USER CODE END TIM4_Init 1 */
htim4.Instance = TIM4;
htim4.Init.Prescaler = 7200;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 10000;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */
/* USER CODE END TIM4_Init 2 */
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(tim_baseHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspInit 0 */
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**TIM4 GPIO Configuration
PB6 ------> TIM4_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* TIM4 interrupt Init */
HAL_NVIC_SetPriority(TIM4_IRQn, 1, 1);
HAL_NVIC_EnableIRQ(TIM4_IRQn);
/* USER CODE BEGIN TIM4_MspInit 1 */
/* USER CODE END TIM4_MspInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspDeInit 0 */
/* USER CODE END TIM4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM4_CLK_DISABLE();
/**TIM4 GPIO Configuration
PB6 ------> TIM4_CH1
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);
/* TIM4 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM4_IRQn);
/* USER CODE BEGIN TIM4_MspDeInit 1 */
/* USER CODE END TIM4_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3); //當(dāng)按鍵按下以后,每過1s鐘,PA3上的LED燈就翻轉(zhuǎn)一次
tim_value+=10000; //按鍵按下后,當(dāng)?shù)竭_溢出以后,tim_value的值就加上ARR設(shè)置的值,ARR設(shè)置的值為10000
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) //輸入捕獲中斷的回調(diào)函數(shù)
{
if(htim->Instance==TIM4) //檢查是否為TIM4觸發(fā)的中斷
{
if(toggle_flag==0) //檢查標(biāo)志位
{
HAL_Delay(20); //消抖
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)==GPIO_PIN_RESET)
{
toggle_flag=1; //進入中斷后首先翻轉(zhuǎn)標(biāo)志位
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); //PA2的LED燈亮200ms,顯示進入輸入捕獲函數(shù)
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_TIM_Base_Start_IT(htim); //開啟溢出中斷,開始進行計時
__HAL_TIM_DISABLE(htim); //關(guān)閉TIM4
__HAL_TIM_SET_COUNTER(htim,0); //設(shè)置時鐘計數(shù)值為0
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); //清除TIM4通道1的原始設(shè)置
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);//將觸發(fā)輸入捕獲中斷源設(shè)置為上升沿捕獲
__HAL_TIM_ENABLE(htim);//使能TIM4
}
}
else if(toggle_flag==1)//檢查標(biāo)志位
{
HAL_Delay(20);//消抖
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)==GPIO_PIN_SET)//檢查是否為上升沿
{
toggle_flag=0;
HAL_TIM_Base_Stop_IT(htim);//關(guān)閉溢出中斷
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);//若是上升沿觸發(fā)輸入捕獲中斷,LED燈亮200ms
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
tim_value=(tim_value+HAL_TIM_ReadCapturedValue(&htim4,TIM_CHANNEL_1 ))/10+20;//計算捕獲的總時間
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); //計算出總時間后,使得PA2口的LED燈亮的時間和總時間相同,以顯示輸入捕獲效果
HAL_Delay(tim_value);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
__HAL_TIM_DISABLE(htim);
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);
__HAL_TIM_ENABLE(htim);
}
}
}
}
/* USER CODE END 1 */
以上就是定時器(上)的相關(guān)內(nèi)容,我們了解了定時器的相關(guān)結(jié)構(gòu)、定時器的工作原理、定時器中斷實驗和輸入捕獲實驗,歡迎各位大佬進行批評指正!文章來源地址http://www.zghlxwxcb.cn/news/detail-775430.html
到了這里,關(guān)于手把手教你開發(fā)stm32——定時器(上)(基于hal庫)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!