STM32標準庫通用定時器輸入捕獲

T7H發表於2024-03-11

STM32標準庫定時器輸入捕獲

1.輸入捕獲介紹

輸入捕獲為STM32定時器的一個功能,可以用來測量輸入訊號的頻率和佔空比。

具體原理:當輸入訊號經過比較捕獲通道時,STM32會依據通道的極性設定決定是否觸發捕獲中斷TIM_IT_CCx。此時定時器會將當前計數值TIMx->CNT的值儲存在TIMx->CCRx中,透過計算兩次捕獲中斷的時間差便可計算出捕獲的電平時長,由此可計算出輸入訊號的頻率、週期、佔空比等資訊。

在本文中,使用野火指南者開發板,配置TIM2定時器的通道4為輸入通道,TIM3定時器的通道1為輸出通道。

2. 輸入捕獲通道與定時器初始化

需要引用標頭檔案

#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
void TIM2_Init()                                            // 定時器2初始化
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);     // 使能定時器2的時鐘
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);    // 使能GPIOA的時鐘
	
	GPIO_InitTypeDef GPIO_InitStructure;                    // 定義GPIO_InitTypeDef型別的結構體
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;      // 定義TIM_TimeBaseInitTypeDef型別的結構體
	TIM_ICInitTypeDef TIM_IC_nitStructure;                  // 定義TIM_ICInitTypeDef型別的結構體
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ;              // 選擇通道4的引腳
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   // 設定通道4為浮空輸入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       // 設定引腳速度為50MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);                   // 初始化GPIOA
	
	TIM_TimeBaseInitStructure.TIM_Period = 1000-1;          // 設定定時器2的自動重灌值,計數到1000-1
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720-1;	    // 設定定時器2的預分頻值,分頻720-1
	TIM_TimeBaseInitStructure.TIM_ClockDivision  = TIM_CKD_DIV1;    // 設定時鐘分割
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 設定計數器模式為向上計數
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);      // 初始化定時器2
	
	TIM_IC_nitStructure.TIM_Channel = TIM_Channel_4;        // 選擇通道4
	TIM_IC_nitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;     // 設定通道4的上升沿觸發
	TIM_IC_nitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;   // 設定通道4的輸入分頻器
	TIM_IC_nitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 設定通道4對映到TI4
	TIM_IC_nitStructure.TIM_ICFilter = 0x00;                // 設定通道4的濾波器
	TIM_ICInit(TIM2,&TIM_IC_nitStructure);                  // 初始化定時器2的通道4
	
	NVIC_InitTypeDef NVIC_InitStructure;                    // 定義NVIC_InitTypeDef結構體變數
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;         // 選擇定時器2的中斷通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;       // 設定中斷優先順序
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;      // 設定中斷子優先順序
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         // 使能中斷通道
	
	NVIC_Init(&NVIC_InitStructure);                         // 初始化NVIC_InitTypeDef結構體變數
	
	TIM_ITConfig(TIM2,TIM_IT_CC4 | TIM_IT_Update ,ENABLE);  // 使能定時器2的通道4的中斷和更新中斷
	
	TIM_Cmd(TIM2,ENABLE);                                   // 使能定時器2
}

需要注意輸入通道引腳為GPIO_Mode_IN_FLOATING模式,TIM_Period為定時器溢位值。

  • TIM_ICInitTypeDef:輸入捕獲通道配置結構體。

    • TIM_Channel:輸入通道,可選引數為TIM_Channel_x。

    • TIM_ICPolarity:輸入通道極性設定,可選引數為TIM_ICSelection_DirectTI、TIM_ICSelection_IndirectTI、TIM_ICSelection_TRC。

      • TIM_ICSelection_DirectTI:將定時器輸入通道1、2、3、4依次對映到IC1、IC2、IC3、IC4。

      • TIM_ICSelection_IndirectTI:將定時器輸入通道1、2、3、4依次對映到IC2、IC1、IC4、IC3。

      • TIM_ICSelection_TRC:將定時器輸入通道1、2、3、4連線至TRC我暫時也不知道這個TRC是啥

    • TIM_ICFilter:輸入通道濾波器設定,可選引數為0x0~0xF。決定了多少次邊沿變換會觸發一次輸入捕獲。

3. 中斷函式編寫

輸入捕獲中斷與定時器中斷共用一箇中斷NVIC。

uint16_t Up_Capture_Cnt,Down_Capture_Cnt,Up_Capture,Up_Capture_Cnt_Temp,Down_Capture;
uint16_t timer_cnt2,timer_cnt1 = 0;
uint16_t Get_State = 0,Get_State1 = 0;

void TIM2_IRQHandler()                              // 定時器2中斷函式
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)  // 定時器2更新中斷
	{
		timer_cnt1++;                               // 定時器計數標誌量1每溢位一次加一
		timer_cnt2++;                               // 定時器計數標誌量2每溢位一次加一
		if(timer_cnt1 == 10000)                     // 定時器計數標誌量1溢位時清零
		{
			timer_cnt1 = 0;                         // 定時器計數標誌量1清零
		}
		if(timer_cnt2 == 10000)                     // 定時器計數標誌量2溢位時清零
		{
			timer_cnt2 = 0;                         // 定時器計數標誌量2清零
		}
	}
	if(TIM_GetITStatus(TIM2,TIM_IT_CC4) == SET)     // 定時器2輸入捕獲中斷
	{
		switch(Get_State)                           // 判斷輸入捕獲狀態
		{
			case 0 :            
				Up_Capture_Cnt_Temp = Up_Capture_Cnt;       // 儲存上一次輸入捕獲通道的值
				Down_Capture_Cnt =  TIM_GetCapture4(TIM2);  // 獲取當前輸入捕獲通道的值
				Down_Capture = Down_Capture_Cnt + (timer_cnt2 * 1000) - Up_Capture_Cnt_Temp;    // 計算脈衝寬度
				timer_cnt1 = 0;                             // 定時器計數標誌量1清零
				timer_cnt2 = 0;                             // 定時器計數標誌量2清零
				TIM_ClearITPendingBit(TIM2,TIM_IT_CC4);     // 清除輸入捕獲通道的中斷標誌位
				TIM_OC4PolarityConfig(TIM2,TIM_ICPolarity_Falling); // 設定輸入捕獲通道的極性為下降沿
				Get_State = 1;                              // 設定輸入捕獲通道的狀態為1
				break;                                      // 跳出switch語句
			case 1:         
				Up_Capture_Cnt =  TIM_GetCapture4(TIM2);    // 獲取當前輸入捕獲通道的值
				Up_Capture = Up_Capture_Cnt + (timer_cnt1 * 1000) - Down_Capture_Cnt;           // 計算脈衝寬度
				timer_cnt1 = 0;                             // 定時器計數標誌量1清零
				timer_cnt2 = 0;                             // 定時器計數標誌量2清零
				TIM_ClearITPendingBit(TIM2,TIM_IT_CC4);     // 清除輸入捕獲通道的中斷標誌位
				TIM_OC4PolarityConfig(TIM2,TIM_ICPolarity_Rising);  // 設定輸入捕獲通道的極性為上升沿
				Get_State = 0;                              // 設定輸入捕獲通道的狀態為0
				break;                                      // 跳出switch語句
		}
	}
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);              // 清除定時器溢位中斷標誌位
}

4. 中斷函式程式碼具體邏輯解釋

光看程式碼可能捋不清先後關係,來看下圖就知道了,如圖1所示:

alt text

圖1.採集輸入捕獲訊號向量圖
在圖中可以看到,當輸入捕獲通道的訊號週期要長於輸入捕獲的通道時鐘週期時,會導致第二次讀取的值比第一次讀取的值小,如果不使用定時器溢位次數進行輔助運算會導致算出來的是負數。之後第一次讀取的值+溢位時間-第二次讀取的值,得到的結果就是脈衝寬度,第二次讀取的值+溢位時間-第一次讀取的值,得到的就是週期中另一部分的寬度。有了這些資訊,就可以得到頻率、週期和佔空比了。

相關文章