日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stm32 输入捕获 测量脉宽

發布時間:2025/3/12 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32 输入捕获 测量脉宽 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

選用通用定時器TIM5的CH1。

PA0接一個按鍵,默認接GND,當按鍵按下時,IO口被拉高,此時,可利用定時器的輸入捕獲功能,測量按鍵按下的這段高電平的時間。

宏定義方便程序升級、移植,舉個例子:

輸入捕獲通道 GPIO 初始化,里面有一個GENERAL_TIM_CH1_GPIO_CLK,這個東西是個宏定義。使用不同GPIO的時候,只需要修改頭文件里面的宏定義,不需要修改這個函數。

// TIM 輸入捕獲通道GPIO相關宏定義 #define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA static void GENERAL_TIM_GPIO_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;// 輸入捕獲通道 GPIO 初始化RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure); }

下面是宏定義。

#ifndef __BSP_GENERALTIME_H #define __BSP_GENERALTIME_H#include "stm32f10x.h"/************通用定時器TIM參數定義,只限TIM2、3、4、5************/ // 當使用不同的定時器的時候,對應的GPIO是不一樣的,這點要注意 // 我們這里默認使用TIM5#define GENERAL_TIM TIM5 #define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define GENERAL_TIM_CLK RCC_APB1Periph_TIM5 #define GENERAL_TIM_PERIOD 0XFFFF #define GENERAL_TIM_PSC (72-1)// TIM 輸入捕獲通道GPIO相關宏定義 #define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA #define GENERAL_TIM_CH1_PORT GPIOA #define GENERAL_TIM_CH1_PIN GPIO_Pin_0 #define GENERAL_TIM_CHANNEL_x TIM_Channel_1// 中斷相關宏定義 #define GENERAL_TIM_IT_CCx TIM_IT_CC1 #define GENERAL_TIM_IRQ TIM5_IRQn #define GENERAL_TIM_INT_FUN TIM5_IRQHandler// 獲取捕獲寄存器值函數宏定義 #define GENERAL_TIM_GetCapturex_FUN TIM_GetCapture1 // 捕獲信號極性函數宏定義 #define GENERAL_TIM_OCxPolarityConfig_FUN TIM_OC1PolarityConfig// 測量的起始邊沿 #define GENERAL_TIM_STRAT_ICPolarity TIM_ICPolarity_Rising // 測量的結束邊沿 #define GENERAL_TIM_END_ICPolarity TIM_ICPolarity_Falling// 定時器輸入捕獲用戶自定義變量結構體聲明 typedef struct { uint8_t Capture_FinishFlag; // 捕獲結束標志位uint8_t Capture_StartFlag; // 捕獲開始標志位uint16_t Capture_CcrValue; // 捕獲寄存器的值uint16_t Capture_Period; // 自動重裝載寄存器更新標志 }TIM_ICUserValueTypeDef;extern TIM_ICUserValueTypeDef TIM_ICUserValueStructure;/**************************函數聲明********************************/ void GENERAL_TIM_Init(void);#endif /* __BSP_GENERALTIME_H */

下面GENERAL_TIM_Mode_Config是定時器模式配置。

里面初始化了兩個結構體。

對于時基結構體:

1.GENERAL_TIM_PERIOD配置ARR寄存器的值,決定計數器一個周期的計數時間,默認配置為0XFFFF(65535),即最大。


自動重裝載寄存器ARR:16位寄存器,這里面裝著計數器能計數的最大數值。當計數到這個值的時候,如果使能了中斷,定時器就產生溢出中斷。

CNT是16位的計數器,只能往上計數,最大計數值為65535。當計數達到自動重裝載寄存器的時候產生更新事件,并清零從頭開始計數


[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JOmOfWyS-1646185240167)(stm32 輸入捕獲 測量脈寬.assets/Snipaste_2022-02-27_10-49-11.png)]

時鐘源:定時器時鐘TIMxCLK,即內部時鐘CK_INT,經APB1預分頻器分頻提供。

APB1預分頻系數等于1,頻率不變。

庫函數中APB1預分頻的系數是2,即PCLK1=36M,所以定時器時鐘TIMxCLK=36*2=72M

計數器時鐘CK_CNT

定時器時鐘經過PSC預分頻器后,即CK_CNT,用來驅動計數器計數。

PSC是16位的預分頻器,可以對定時器時鐘TIMxCLK進行1~65536之間的任何一個數進行分頻。CK_CNT=TIMxCLK/(PSC+1)

2.GENERAL_TIM_PSC配置分頻因子,默認配置為72-1。可以計算出計數器的計數周期為(GENERAL_TIM_PSC+1)/72M=1us。


當捕獲通道TIx上出現上升沿時,發生第一次捕獲。

計數器CNT的值,被鎖存到捕獲寄存器CCR中,進入捕獲中斷。

輸入捕獲能捕獲的最小的時間為1us,最長的時間為1us *(0Xffff+1)=65536us=65.536ms。

超過這個計數周期的時候,產生中斷,然后在中斷里面做額外的處理。

需要記錄好產生了多少次更新中斷,最后把更新時間加入脈寬時間里面。


GENERAL_TIM_NVIC_Config是中斷優先級配置。只有一個中斷源,優先級可以隨便配置。

#include "bsp_GeneralTim.h"// 定時器輸入捕獲用戶自定義變量結構體定義 TIM_ICUserValueTypeDef TIM_ICUserValueStructure = {0,0,0,0};// 中斷優先級配置 static void GENERAL_TIM_NVIC_Config(void) {NVIC_InitTypeDef NVIC_InitStructure; // 設置中斷組為0NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 設置中斷來源NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ ; // 設置主優先級為 0NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 設置搶占優先級為3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure); }static void GENERAL_TIM_GPIO_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;// 輸入捕獲通道 GPIO 初始化RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure); }///* // * 注意:TIM_TimeBaseInitTypeDef結構體里面有5個成員,TIM6和TIM7的寄存器里面只有 // * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的時候只需初始化這兩個成員即可, // * 另外三個成員是通用定時器和高級定時器才有. // *----------------------------------------------------------------------------- // *typedef struct // *{ TIM_Prescaler 都有 // * TIM_CounterMode TIMx,x[6,7]沒有,其他都有 // * TIM_Period 都有 // * TIM_ClockDivision TIMx,x[6,7]沒有,其他都有 // * TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有 // *}TIM_TimeBaseInitTypeDef; // *----------------------------------------------------------------------------- // *//* ---------------- PWM信號 周期和占空比的計算--------------- */ // ARR :自動重裝載寄存器的值 // CLK_cnt:計數器的時鐘,等于 Fck_int / (psc+1) = 72M/(psc+1) // PWM 信號的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M // 占空比P=CCR/(ARR+1)static void GENERAL_TIM_Mode_Config(void) {// 開啟定時器時鐘,即內部時鐘CK_INT=72MGENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);/*--------------------時基結構體初始化-------------------------*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 自動重裝載寄存器的值,累計TIM_Period+1個頻率后產生一個更新或者中斷TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD; // 驅動CNT計數器的時鐘 = Fck_int/(psc+1)TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_PSC; // 時鐘分頻因子 ,配置死區時間時需要用到TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 計數器計數模式,設置為向上計數TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重復計數器的值,沒用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定時器TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);/*--------------------輸入捕獲結構體初始化-------------------*/ TIM_ICInitTypeDef TIM_ICInitStructure;// 配置輸入捕獲的通道,需要根據具體的GPIO來配置TIM_ICInitStructure.TIM_Channel = GENERAL_TIM_CHANNEL_x;// 輸入捕獲信號的極性配置TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;// 輸入通道和捕獲通道的映射關系,有直連和非直連兩種TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;// 輸入的需要被捕獲的信號的分頻系數TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;// 輸入的需要被捕獲的信號的濾波系數TIM_ICInitStructure.TIM_ICFilter = 0;// 定時器輸入捕獲初始化TIM_ICInit(GENERAL_TIM, &TIM_ICInitStructure);// 清除更新和捕獲中斷標志位TIM_ClearFlag(GENERAL_TIM, TIM_FLAG_Update|GENERAL_TIM_IT_CCx); // 開啟更新和捕獲中斷 TIM_ITConfig (GENERAL_TIM, TIM_IT_Update | GENERAL_TIM_IT_CCx, ENABLE );// 使能計數器TIM_Cmd(GENERAL_TIM, ENABLE); }void GENERAL_TIM_Init(void) {GENERAL_TIM_GPIO_Config();GENERAL_TIM_NVIC_Config();GENERAL_TIM_Mode_Config(); }

中斷服務函數

輸入捕獲的起始邊沿為GENERAL_TIM_STRAT_ICPolarity,這是一個宏,默認配置為上升沿。

// 測量的起始邊沿 #define GENERAL_TIM_STRAT_ICPolarity TIM_ICPolarity_Rising

按鍵默認接GND,按鍵按下的時候被拉高,這個時候這個由低到高的上升沿被捕獲到,這是第一次捕獲。

此時把計數器清0,開始計數,同時把捕獲邊沿改成下降沿捕獲。

第二次進入中斷服務函數的時候,說明捕獲到下降沿,這個時候表示脈寬捕獲完畢。

讀取捕獲寄存器的值,然后可以通過這個值算出脈寬的時間。

最后,把捕獲編譯配置為上升沿,為的是下一次捕獲。

脈寬的時間超過了計數器的最大計數時間,就會產生更新中斷,需要記錄產生了多少次更新中斷。

最后算脈寬的時間的時候加上更新的時間。

中斷服務函數里面,捕獲結束標志位、捕獲開始標志位、捕獲寄存器的值、自動重裝載更新標志,在一個結構體里面定義。

// 定時器輸入捕獲用戶自定義變量結構體聲明 typedef struct { uint8_t Capture_FinishFlag; // 捕獲結束標志位uint8_t Capture_StartFlag; // 捕獲開始標志位uint16_t Capture_CcrValue; // 捕獲寄存器的值uint16_t Capture_Period; // 自動重裝載寄存器更新標志 }TIM_ICUserValueTypeDef; void GENERAL_TIM_INT_FUN(void) {// 當要被捕獲的信號的周期大于定時器的最長定時時,定時器就會溢出,產生更新中斷// 這個時候我們需要把這個最長的定時周期加到捕獲信號的時間里面去if ( TIM_GetITStatus ( GENERAL_TIM, TIM_IT_Update) != RESET ) { TIM_ICUserValueStructure.Capture_Period ++; TIM_ClearITPendingBit ( GENERAL_TIM, TIM_FLAG_Update ); }// 上升沿捕獲中斷if ( TIM_GetITStatus (GENERAL_TIM, GENERAL_TIM_IT_CCx ) != RESET){// 第一次捕獲if ( TIM_ICUserValueStructure.Capture_StartFlag == 0 ){// 計數器清0TIM_SetCounter(GENERAL_TIM, 0 );// 自動重裝載寄存器更新標志清0TIM_ICUserValueStructure.Capture_Period = 0;// 存捕獲比較寄存器的值的變量的值清0 TIM_ICUserValueStructure.Capture_CcrValue = 0;// 當第一次捕獲到上升沿之后,就把捕獲邊沿配置為下降沿GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Falling);// 開始捕獲標準置1 TIM_ICUserValueStructure.Capture_StartFlag = 1; }// 下降沿捕獲中斷else // 第二次捕獲{// 獲取捕獲比較寄存器的值,這個值就是捕獲到的高電平的時間的值TIM_ICUserValueStructure.Capture_CcrValue = GENERAL_TIM_GetCapturex_FUN (GENERAL_TIM);// 當第二次捕獲到下降沿之后,就把捕獲邊沿配置為上升沿,好開啟新的一輪捕獲GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Rising);// 開始捕獲標志清0 TIM_ICUserValueStructure.Capture_StartFlag = 0;// 捕獲完成標志置1 TIM_ICUserValueStructure.Capture_FinishFlag = 1; }TIM_ClearITPendingBit (GENERAL_TIM,GENERAL_TIM_IT_CCx); } }

main函數里面,一些初始化,在一個while循環中打印測量的脈寬時間。

計算的時候,把周期GENERAL_TIMPERIOD和Capture_CcrValue的值都加1后再運算

#define GENERAL_TIM_PSC (72-1) //CK_CNT=TIMxCLK/(PSC+1) // TIM 計數器的驅動時鐘,定時器時鐘經過PSC預分頻器后,即CK_CNT,用來驅動計數器計數。uint32_t TIM_PscCLK = 72000000 / (GENERAL_TIM_PSC+1);// 自動重裝載寄存器的值,累計TIM_Period+1個頻率后產生一個更新或者中斷TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD; #define GENERAL_TIM_PERIOD 0XFFFFuint16_t Capture_CcrValue; // 捕獲寄存器的值uint16_t Capture_Period; // 自動重裝載寄存器更新標志 /*當要被捕獲的信號的周期大于定時器的最長定時時,定時器就會溢出,產生更新中斷 Capture_Period記錄進入更新中斷的次數*/

下面這個計算高電平時間的計數器的值,進入更新中斷的次數*(自動重裝載寄存器的值+1)+第二次捕獲時,捕獲比較寄存器的值+1。

// 計算高電平時間的計數器的值time = TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM_PERIOD+1) + (TIM_ICUserValueStructure.Capture_CcrValue+1);

為什么都要加一呢,是因為:

計數器CNT:CNT是16位的計數器,只能往上計數,最大計數值為65535。當計數達到自動重裝載寄存器的時候產生更新事件,并清零從頭開始計數。

計數器是從0開始計數的,0計數到65536,就是65537個數了。

計算高電平脈寬時間:TIM_PscCLK是計數頻率,倒數就是計一個數的周期。

計數器的計數周期 * 高電平時間的計數器的值=(1/TIM_PscCLK) * time=time/TIM_PscCLK。

printf ( "\r\n測得高電平脈寬時間:%d.%d s\r\n",time/TIM_PscCLK,time%TIM_PscCLK );

#include "stm32f10x.h" #include "bsp_led.h" #include "bsp_usart.h" #include "bsp_GeneralTim.h" /*** @brief 主函數* @param 無 * @retval 無*/ int main(void) {uint32_t time;// TIM 計數器的驅動時鐘uint32_t TIM_PscCLK = 72000000 / (GENERAL_TIM_PSC+1);/* 串口初始化 */USART_Config();/* 定時器初始化 */GENERAL_TIM_Init();printf ( "\r\nSTM32 輸入捕獲實驗\r\n" );printf ( "\r\n按下K1,測試K1按下的時間\r\n" );while ( 1 ){if(TIM_ICUserValueStructure.Capture_FinishFlag == 1){// 計算高電平時間的計數器的值time = TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM_PERIOD+1) + (TIM_ICUserValueStructure.Capture_CcrValue+1);// 打印printf ( "\r\n測得高電平脈寬時間:%d.%d s\r\n",time/TIM_PscCLK,time%TIM_PscCLK );TIM_ICUserValueStructure.Capture_FinishFlag = 0; } } }

總結

以上是生活随笔為你收集整理的stm32 输入捕获 测量脉宽的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。