定時器定時計數功能
1 定時器概述
- 定時器是對周期固定的脈沖信號進行計數,如MCU內部的外設時鐘(APB)。
- 計數器是對周期不確定的脈沖信號進行計數,如MCU的I/O引腳所引入的外部脈沖信號。
- 定時器和計數器本質上都是計數器,定時器是計數器的一種特例。
2 STM32定時器分類
對于STM32F103共存在6個外設定時器:高級定時器TIM1,通用定時器TIM2、TIM3、TIM4。
2.1 內核定時器
- 系統節拍定時器:Systick定時器是屬于內核中的一個外設,內嵌在NVIC中。
2.2 外設定時器
- 常規定時器> STM32F103xx增強型產品中,內置了多達3個可同步運行的標準定時器(TIM2、TIM3和TIM4)。每個定時器都有一個16位的自動加載遞加/遞減計數器、一個16位的預分頻器和4個獨立的通道,每個通道都可用于輸入捕獲、輸出比較、PWM和單脈沖模式輸出.
- 專用定時器
3 定時器的時鐘頻率
4 定時器的主要功能
4.1 定時計數
- 計數內部時鐘,即定時器模式
- 計數外部脈沖,即計數器模式
4.2 輸出比較
- PWM輸出
- 電平翻轉
- 單脈沖輸出
- 強制輸出
4.3 輸入捕獲
- 捕獲時保存定時器的當前計數值
- 捕獲時,可選擇觸發捕獲中斷
- 觸發捕獲的信號邊沿類型可選擇(上升沿,下降沿,雙邊沿)
5 HAL庫的定時器設計
5.1 定時器句柄結構的組成
5.2 三種外設編程模型
- 以后綴區分編程模型
- 入口參數均為外設句柄的指針
方式 | 函數 |
---|---|
輪詢方式 | HAL_TIM_Base_Start(TIM_HandleTypeDef *htim );HAL_TIM_Base_Stop TIM_HandleTypeDef *htim ); |
中斷方式 | HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim); |
DMA方式 | HAL_TIM_Base_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData, uint16_t Length); |
6 定時器的定時/計數功能
6.1 時基單元
①預分頻模塊
- 預分頻計數器:對預分頻時鐘CK_PSC進行分頻
- 預分頻寄存器:TIMx_PSC:設置預分頻系數PSC
- 作用1:擴大定時器的定時范圍
- 作用2:獲取精確的計數時鐘
預分頻模塊工作原理: 定時器啟動后,預分頻計數器的初值為0,預分頻時鐘CK_PSC每來一個時鐘,預分頻計數器的值就加1。當計數值等于預分頻寄存器所設定的預分頻系數PSC時,預分頻計數器的值將清零,開始下一輪計數。
預分頻時序圖:
假設預分頻系數PSC=3,預分頻計數器從0計數到PSC實際計數值為PSC+1,也就是進行了四分頻。
②計數模塊
- 核心計數器:對計數時鐘CK_CNT進行二次計數
- 計數器寄存器:TIMx_CNT:存放核心計數器運行時的當前計數值
③自動重載模塊
- 自動重載模塊由自動重載寄存器TIMx_ARR組成
- 遞增計數模式:TIMx_ARR的值作為核心計數器的計數終值
- 遞減計數模式:TIMx_ARR的值作為核心計數器的計數初值
④計數模式
計數模式 | 計數器溢出值 | 計數器重載值 |
---|---|---|
遞增計數 | CNT=ARR | CNT=0 |
遞減計數 | CNT=0 | CNT=ARR |
中心對齊計數 | CNT=ARR-1CNT=1 | CNT=ARRCNT=0 |
6.2 定時器時序圖
①定時時間公式
②相關寄存器
- 預分頻寄存器TIMx_PSC: 設置預分頻系數,將預分頻時鐘(CK_PSC)進行1~65536之間的任意值分頻,得到計數時鐘(CK_CNT)。
- 計數器寄存器TIMx_CNT : 存放核心計數器運行時的當前計數值,便于用戶實時掌握核心計數器的當前計數值。芯片復位后,默認值為0。
- 自動重載寄存器TIMx_ARR: 為計數器設置計數邊界或重載值。比如計數器遞增計數時,記到多少發生溢出;遞減計數時,從多少開始往下計數。
6.3 外部脈沖計數
6.4 數據類型和接口函數
成員變量:
成員變量ClockDivision的取值范圍:
TIM_CLOCKDIVISION_DIV1 | 對定時器時鐘TIM_CLK進行1分頻 |
---|---|
TIM_CLOCKDIVISION_DIV2 | 對定時器時鐘TIM_CLK進行2分頻 |
TIM_CLOCKDIVISION_DIV4 | 對定時器時鐘TIM_CLK進行4分頻 |
成員變量CounterMode的取值范圍
TIM_COUNTERMODE_UP | 遞增計數模式 |
---|---|
TIM_COUNTERMODE_DOWN | 遞減計數模式 |
TIM_COUNTERMODE_CENTERALIGNED1 | 中心對齊計數模式1 |
TIM_COUNTERMODE_CENTERALIGNED2 | 中心對齊計數模式2 |
TIM_COUNTERMODE_CENTERALIGNED3 | 中心對齊計數模式3 |
成員變量AutoReloadPreload的取值范圍
TIM_AUTORELOAD_PRELOAD_DISABLE | 預裝載功能關閉 |
---|---|
TIM_AUTORELOAD_PRELOAD_ENABLE | 預裝載功能開啟 |
- 用于設置自動重載寄存器TIMx_ARR的預裝載功能,即自動重裝寄存器的內容是更新事件產生時寫入有效,還是立即寫入有效;
- 預裝載功能在多個定時器同時輸出信號時比較有用,可以確保多個定時器的輸出信號在同一個時刻變化,實現同步輸出;
- 單個定時器輸出時,一般不開啟預裝載功能。
接口函數:
時基單元初始化函數:
HAL_TIM_Base_Init
函數原型 HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim) 功能描述 按照定時器句柄中指定的參數初始化定時器時基單元 入口參數 htim:定時器句柄的地址 返回值 HAL狀態值 注意事項 1. 該函數將調用MCU底層初始化函數HAL_TIM_Base_MspInit完成引腳、時鐘和中斷的設置2. 該函數由CubeMX自動生成 輪詢模式啟動函數:
HAL_TIM_Base_Start
函數原型 HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim) 功能描述 在輪詢方式下啟動定時器運行 入口參數 htim:定時器句柄的地址 返回值 HAL狀態值 注意事項 1. 該函數在定時器初始化完成之后調用2. 函數需要由用戶調用,用于輪詢方式下啟動定時器運行 中斷模式啟動函數:
HAL_TIM_Base_Start_IT
函數原型 HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim) 功能描述 使能定時器的更新中斷,并啟動定時器運行 入口參數 htim:定時器句柄的地址 返回值 HAL狀態值 注意事項 1. 該函數在定時器初始化完成之后調用2. 函數需要由用戶調用,用于使能定時器的更新中斷,并啟動定時器運行3. 啟動前需要調用宏函數 __HAL_TIM_CLEAR_IT 來清除更新中斷標志 定時器中斷通用處理函數
HAL_TIM_IRQHandler
函數原型 void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim) 功能描述 作為所有定時器中斷發生后的通用處理函數 入口參數 htim:定時器句柄的地址 返回值 無 注意事項 1. 函數內部先判斷中斷類型,并清除對應的中斷標志,最后調用回調函數完成中斷處理2. 該函數由CubeMX自動生成 定時器更新中斷回調函數
HAL_TIM_PeriodElapsedCallback
函數原型 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 功能描述 回調函數,用于處理所有定時器的更新中斷,用戶在該函數內編寫實際的任務處理程序 入口參數 htim:定時器句柄的地址 返回值 無 注意事項 1.該函數由定時器中斷通用處理函數HAL_TIM_IRQHandler調用,完成所有定時器的更新中斷的任務處理2.函數內部需要根據定時器句柄的實例來判斷是哪一個定時器產生的本次更新中斷3.函數由用戶根據具體的處理任務編寫 計數值讀取函數
__HAL_TIM_GET_COUNTER
#define __HAL_TIM_GET_COUNTER(__HANDLE__) ((__HANDLE__)- >Instance- >CNT)
__HANDLE__
:定時器句柄的地址該函數通過直接訪問計數器寄存器TIMx_CNT來獲取計數器的當前計數值。
定時器中斷標志清除函數
__HAL_TIM_CLEAR_IT
#define __HAL_TIM_CLEAR_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)- >Instance- >SR = ~(__INTERRUPT__))
__HANDLE__
:定時器句柄的地址__INTERRUPT__
:定時器中斷標志
任務實踐1
基于STM32F103C8T6,開發板原理圖
利用開發板上的按鍵KEY2來觸發外部脈沖,按鍵每按下一次,就利用PA1引腳發送一個周期2ms左右的脈沖,送到定時器2的外部觸發引腳ETR(PA0)進行計數,并將計數結果通過串口發送到PC上顯示。
注:本任務例程使用的開發板,KEY2與PA5相連接。KEY2原理圖如下:
使用按鍵時,需要設置PA5為輸入上拉模式,這樣在KEY2沒有按下時,PA5可以讀取到高電平,KEY2按下時PA5可以讀取到低電平。
配置PA1為GPIO_Output(User Label:PULSE),PA5為GPIO_Input(User Label:KEY2),上拉模式。
上述操作在stm32f1xx_hal_gpio.c
中生成GPIO引腳初始化函數MX_GPIO_Init
,并在main.c中調用void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); /*Configure GPIO pin : PA1 */ GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /*Configure GPIO pin : PA5 */ GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); }
配置定時器2時鐘源為外部觸發引腳ETR2,自動重載寄存器ARR配置為一個相對較大的計數,防止溢出。外部脈沖信號采用默認配置:不使用濾波,不進行脈沖信號反相,不進行脈沖信號分頻。
上述操作在tim.c
生成引腳初始化函數:void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(tim_baseHandle- >Instance==TIM2) { /* USER CODE BEGIN TIM2_MspInit 0 */ /* USER CODE END TIM2_MspInit 0 */ /* TIM2 clock enable */ __HAL_RCC_TIM2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**TIM2 GPIO Configuration PA0-WKUP ------ > TIM2_ETR */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN TIM2_MspInit 1 */ /* USER CODE END TIM2_MspInit 1 */ } } void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle) { if(tim_baseHandle- >Instance==TIM2) { /* USER CODE BEGIN TIM2_MspDeInit 0 */ /* USER CODE END TIM2_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_TIM2_CLK_DISABLE(); /**TIM2 GPIO Configuration PA0-WKUP ------ > TIM2_ETR */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0); /* USER CODE BEGIN TIM2_MspDeInit 1 */ /* USER CODE END TIM2_MspDeInit 1 */ } }
上述操作在
tim.c
生成如下代碼完成初始化定時器,并在main.c
中調用/* TIM2 init function */ void MX_TIM2_Init(void) { /* USER CODE BEGIN TIM2_Init 0 */ /* USER CODE END TIM2_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; /* USER CODE BEGIN TIM2_Init 1 */ /* USER CODE END TIM2_Init 1 */ htim2.Instance = TIM2; htim2.Init.Prescaler = 0; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 65535; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_ETRMODE2; sClockSourceConfig.ClockPolarity = TIM_CLOCKPOLARITY_NONINVERTED; sClockSourceConfig.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1; sClockSourceConfig.ClockFilter = 0; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM2_Init 2 */ /* USER CODE END TIM2_Init 2 */ }
其中調用的
HAL_TIM_Base_Init
函數將調用HAL_TIM_Base_MspInit
完成引腳初始化。配置串口外設USART1,選擇異步模式,無硬件流控。
上述操作在usart.c中生成串口配置相關函數,并在main.c中調用。usart的封裝邏輯與tim相同,不再贅述。void MX_USART1_UART_Init(void) { /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART1_Init 2 */ /* USER CODE END USART1_Init 2 */ } void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(uartHandle- >Instance==USART1) { /* USER CODE BEGIN USART1_MspInit 0 */ /* USER CODE END USART1_MspInit 0 */ /* USART1 clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------ > USART1_TX PA10 ------ > USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE END USART1_MspInit 1 */ } } void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) { if(uartHandle- >Instance==USART1) { /* USER CODE BEGIN USART1_MspDeInit 0 */ /* USER CODE END USART1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_USART1_CLK_DISABLE(); /**USART1 GPIO Configuration PA9 ------ > USART1_TX PA10 ------ > USART1_RX */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10); /* USER CODE BEGIN USART1_MspDeInit 1 */ /* USER CODE END USART1_MspDeInit 1 */ } }
程序編寫
使用串口輸出,需要在Keil的Options中勾選Use MicroLIB.
在main.c中重定義printf
和scanf
函數/* USER CODE BEGIN Includes */ #include < stdio.h > /* USER CODE END Includes */ /* USER CODE BEGIN 4 */ int fputc (int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } /* USER CODE END 4 */
用戶變量定義代碼
/* USER CODE BEGIN PV */ uint8_t Result = 0; /* USER CODE END PV */
用戶變量初始化代碼
/* USER CODE BEGIN 2 */ HAL_TIM_Base_Start_IT(&htim2); printf("Timer count function test: n"); /* USER CODE END 2 */
用戶應用代碼
/* USER CODE BEGIN 3 */ if (HAL_GPIO_ReadPin(GPIOA, KEY2_Pin) == GPIO_PIN_RESET) { HAL_Delay(10); if (HAL_GPIO_ReadPin(GPIOA, KEY2_Pin) == GPIO_PIN_RESET) { HAL_GPIO_WritePin(GPIOA, PULSE_Pin, GPIO_PIN_SET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOA, PULSE_Pin, GPIO_PIN_RESET); HAL_Delay(1); Result = __HAL_TIM_GET_COUNTER(&htim2); printf("Count = %d", Result); } while (HAL_GPIO_ReadPin(GPIOA, KEY2_Pin) == GPIO_PIN_RESET); } } /* USER CODE END 3 */
實驗現象
任務實踐2
設計電子時鐘,從00:00:00開始計時,并將計時信息通過串口UART1發送到PC進行顯示。
配置定時器1產生1s的更新中斷。計算PSC和ARR的值,將T=1s,TIM_CLK=8000000Hz帶入公式
得可取值PSC=1999,ARR=3999.
在CubeMX中使能TIM1,采用內部時鐘 (8MHz) ,設置PSC和ARR值,并在嵌套向量中斷控制器NVIC設置中使能TIM1的更新中斷。
上述操作在tim.c
中生成TIM1初始化函數,并在main.c
中調用配置USART1串口輸出,生成代碼后應在工程中重定向
printf
和scanf
函數,方法同任務實踐1,這部分不再贅述。編寫代碼
在main.c
中進行用戶數據類型定義/* USER CODE BEGIN PTD */ typedef struct { uint8_t hour; uint8_t minutes; uint8_t second; }CLOCK_Typedef; /* USER CODE END PTD */
用戶變量定義
/* USER CODE BEGIN PV */ CLOCK_Typedef clock = {0}; /* USER CODE END PV */
用戶初始化代碼
/* USER CODE BEGIN 2 */ // 清除更新中斷標志,避免定時器一啟動就進入中斷 __HAL_TIM_CLEAR_IT(&htim1, TIM_IT_UPDATE); HAL_TIM_Base_Start_IT(&htim1); /* USER CODE END 2 */
用戶應用代碼
/* USER CODE BEGIN 3 */ printf("Time:%02d:%02d:%02d.rn", clock.hour, clock.minutes, clock.second); HAL_Delay(1000); } /* USER CODE END 3 */
編寫定時器更新中斷回調函數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim- >Instance == TIM1) // 判斷時鐘源 { clock.second++; if (clock.second == 60) { clock.second = 0; clock.minutes++; if (clock.minutes == 60) { clock.minutes = 0; clock.hour++; if (clock.hour == 24) { clock.hour = 0; } } } } }
-
mcu
+關注
關注
146文章
17357瀏覽量
352791 -
STM32
+關注
關注
2273文章
10926瀏覽量
357772 -
計數器
+關注
關注
32文章
2276瀏覽量
95050 -
定時器
+關注
關注
23文章
3256瀏覽量
115445 -
脈沖信號
+關注
關注
6文章
400瀏覽量
37094
發布評論請先 登錄
相關推薦
STM32使用定時器計數
定時器基礎知識與PWM輸出原理是什么
定時器與計數器的基礎知識和使用方法及C程序實現1s鐘定時的程序
![<b class='flag-5'>定時器</b>與<b class='flag-5'>計數器</b>的<b class='flag-5'>基礎知識</b>和使用方法及C程序實現1s鐘<b class='flag-5'>定時</b>的程序](https://file.elecfans.com/web1/M00/8E/92/o4YBAFy3y9aATMXoAAQ7JAGfYTQ120.png)
電機控制基礎——定時器基礎知識與PWM輸出原理
![電機控制基礎——<b class='flag-5'>定時器</b><b class='flag-5'>基礎知識</b>與PWM輸出原理](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
stm32中斷源有哪些_【話說定時器系列】之八:STM32定時器計數定時基本功能實驗...
![<b class='flag-5'>stm32</b>中斷源有哪些_【話說<b class='flag-5'>定時器</b>系列】之八:<b class='flag-5'>STM32</b><b class='flag-5'>定時器</b><b class='flag-5'>計數</b><b class='flag-5'>定時</b>基本<b class='flag-5'>功能</b>實驗...](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
STM32 使用定時器計數 寄存器版本
![<b class='flag-5'>STM32</b> 使用<b class='flag-5'>定時器</b><b class='flag-5'>計數</b> 寄存<b class='flag-5'>器</b>版本](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論