一、初識定時器TIM
? ? ? ? 定時器就是計數器,定時器的作用就是設置一個時間,然后時間到后就會通過中斷等方式通知STM32執行某些程序。定時器除了可以實現普通的定時功能,還可以實現捕獲脈沖寬度,計算PWM占空比,輸出PWM波形,編碼器計數等。
STM32共11個定時器,2個高級控制定時器TIM1和TIM8,4個通用定時器TIM2~TIM5,兩個基本定時器TIM6和TIM7,兩個看門狗定時器和一個系統滴答定時器Systick.
高級定時器TIM1和TIM8的時鐘由APB1產生,其它六個通用定時器的時鐘由APB2產生。它們的最大頻率都可以配置成系統時鐘的頻率。
定時器種類 | 位數 | 計數模式 | 捕獲/比較通道 | 應用場景 |
---|---|---|---|---|
通用定時器 TIM2~TIM5 | 16 | 向上,向下,雙向 | 4 | 定時計數,PWM,輸入捕獲,輸出比較 |
高級定時器 TIM1和TIM8 | 16 | 向上,向下,雙向 | 4 | 在通用的基礎上,多了剎車信號輸入,死區時間互補輸出等工業電機功能 |
基本定時器 TIM6和TIM7 | 16 | 向上,向下,雙向 | 4 | 定時計數 |
二、基本定時器
(1)計數功能原理
? ? ? ? 在上一期文章提到,時鐘樹提供穩定頻率的方波信號,APB1上的時鐘線連接了基本定時器和通用寄存器,APB2上的時鐘線連接了高級定時器。
? ? ? ? 對于實現計數功能,只需要一個寄存器就可以滿足,寄存器只需要讀到時鐘信號的上升沿數值就加1。假如72MHZ的時鐘信號作為輸入,當該寄存器數值累加到7.2*10^7,就代表時間過去了1秒。但是寄存器通常只有16bit,最多能計數?65536個數。因此在該寄存器前面還要加一個類似的計數器,當計數滿足條件時才往后續電路發送高電平,預分頻器就可以充當這個角色,其本質也是一個16bit的計數器。當只需要將其設置為n-1,就可以進行n分頻,從0開始計數,一直計數到n-1才會向后續電路發送高電平。預分頻器最多可以進行65536分頻。因此一個由預分頻器和一個計數器組合成的定時器,最多可以計數65536^2次。m個定時器串聯,就可以計數65536^(2*m)次。?
(2) 自動重裝載寄存器
???????? 自動重裝載寄存器,它的作用就是實時監控計數器的值是否與自己的值相同。當計數器的值與自己的值相同時,便將計數器重置為0,并觸發定時器更新中斷。
(3)影子寄存器
? ? ? ? 所謂的影子寄存器就是某個寄存器的拷貝。在上圖中工作在一線的預分頻器和自動重裝載寄存器其實都是自己的影子寄存器。當定時器正在工作時,如果重新設置預分頻器值或者重新這是自動重裝載寄存器的值,那么只有當計數器和自動重裝載寄存器的值一樣時才會將新值更新到自身影子寄存器中。也就是給定時器設置的新參數值要等下個計數周期才生效。
? ? ? ? 自動重裝載寄存器可以根據程序員選擇是否開啟影子寄存器。如果不開啟,那么將自動重裝載寄存器數值調小時,可能會錯過計數器的值,使計數器一路上到65536才會歸0。
三、定時程序
(1)準備工作
為了發送數據進行模擬,需打開USART2用于設置模式為“異步”,在NVIC Settings中打開中斷,打上√,在DMA Settings,打開DMA傳輸功能,添加傳輸通道。
為提高計數精度,將外部時鐘源設置為"晶振",在時鐘設置界面中的HCLK的頻率設置為72MHZ,自動調整其它器件的時鐘頻率。
對于我使用的STM32F108T6芯片只有4個定時器,即1個高級,3個通用寄存器。雖然沒有基本定時器,但是這些定時器都包含了基本定時器功能。只需要對定時器的時鐘源選擇 Internal Clock (內部時鐘源)就算打開了定時器。因為本次模擬的時鐘頻率是72MHZ,設置預分頻器7200,自動重裝載寄存器為10000,那么完成一個周期的計數就是1s,也就是1s觸發一次定時器更新中斷。同第一步類似,TIM也可以開啟中斷和DMA通道。保存并生成代碼。
(2)實現定時任務和獲取計數器數值:
以下示例代碼為開啟TIM中斷和USART2中斷實現,以及開啟了自動重裝載寄存器的影子寄存器
1.開啟定時器
HAL_TIM_Base_Start(&htim);? ? ? ? //用阻塞的方式開始定時器
HAL_TIM_Base_Start_IT(&htim);? ? ? ? //用中斷(非阻塞)的方式開始定時器
HAL_TIM_Base_Start_DMA(&htim);? ? //用DMA(非阻塞)的方式開始定時器
2.中斷回調函數
在路徑 ~/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c中
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
當計數器數值和自動重裝載寄存器值一樣時調用
3.讀寫定時器中寄存器的宏定義操作
__HAL_TIM_GET_ANTORELOAD //獲取自動重裝載寄存器數值
__HAL_TIM_SET_ANTORELOAD?//設置自動重裝載寄存器數值
__HAL_TIM_GET_COUNTER????????//獲取計數器數值
__HAL_TIM_SET_COUNTER????????//設置計數器數值
__HAL_TIM_SET_PRESCALER? ??//設置預分頻器數值
4.示例代碼
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
/* 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 ---------------------------------------------------------*/
TIM_HandleTypeDef htim4;UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_rx;
DMA_HandleTypeDef hdma_usart2_tx;/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM4_Init(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
char message2[]="??";
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){if(htim == &htim4){HAL_UART_Transmit_IT(&huart2, (uint8_t*)message2,strlen(message2));}
}
/* 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_DMA_Init();MX_USART2_UART_Init();//TIM4的初始化MX_TIM4_Init();/* USER CODE BEGIN 2 *///用中斷的方式開啟定時器HAL_TIM_Base_Start_IT(&htim4);int counter = 0;char message[20];/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){counter = __HAL_TIM_GET_COUNTER(&htim4);sprintf(message,"counter:%d",counter);//每隔100ms發送 counter計數器數值//HAL_UART_Transmit_IT(&huart2, (uint8_t*)message,strlen(message));//延遲100msHAL_Delay(100-1);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}