STM32F4XX 學習日志:定時器輸入捕獲
- 前言
- 任務目標
- 設計程序
- TIM1初始化代碼
- TIM5初始化代碼
- 中斷服務函式
- 薛定諤的代碼
- ·以上代碼親測有效
前言
使用反客科技STM32F407VET6 M1的核心板,板載8M主時鐘晶振(HSE),32.768kHz低速外部晶振(LSE),含有一個用戶LED以及一個用戶按鍵,
任務目標
使用定時器輸入捕獲測量方波高電平時間,
學生黨放假在家沒有信號發生器,只能用定時器產生一個方波用于測量,
沒錯,我測我自己,
設計程序
首先生成一個PWM波,這里我選用高級定時器1也就是TIM1進行配置,
TIM1掛載在APB2高速總線上,我使用標準庫將系統時鐘配置為168MHZ,則該總線頻率也為168MHZ,
(標準庫默認的是配置HSE為時鐘源,而在我這塊板子上不兼容標準庫的默認配置,所以我就自己配置了HSI為系統時鐘,具體代碼在我上一篇博客中,這里是傳送門:鏈接: STM32F4XX 學習日志:定時器中斷模擬PWM波實作呼吸燈.)

TIM1初始化代碼
復用引腳PA8為通道1引腳

映射引腳代碼,
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
這里注意一下stm32f4的引腳時鐘是AHB1總線的
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
我看網上的其他代碼好像F1的芯片引腳時鐘不是在這條總線上的,敲代碼的時候如果不注意很容易忽略(別問我怎么知道的,我之前就是只改了引數,系統也不會報錯)
定時器初始化為輸出比較,PWM波模式1,該模式效果可以由資料手冊查到,
·
·
·
·
·
算了我覺得你可能不會去查,我還是貼出來吧【狗頭】

定時器周期設定為100ms也就是10HZ
計算 168 000 000 / 16800 / 1000 = 10HZ,
168M先經過16800倍數分頻率得到1 000 0HZ的頻率
然后計數器每1000次計數會產生溢位,
以上
是我之前初學的時候所了解的知識,但是對于怎么計算出周期還是有點懵懵的,后來經過多次練習才了解了,
定時器需要作業肯定需要一個時鐘,而這個時鐘則是由系統時鐘分出來的,
那么定時器的時鐘有什么用呢,這可能需要一點數電的知識,
那么最簡單的理解方法就是,既然定時器計數值會自動變化,那么每一次變化期間的間隔時間是多少呢????????????
那自然就是定時器的時鐘周期,也就是由系統時鐘分頻得出的頻率,
這樣理解就顯得容易計算了,
主時鐘分過來來的頻率是1 000 0HZ換算成周期也就是100us,換句話說也就是每隔100us,定時器的值變化1,
而后定時器是每計數1000次產生一個溢位,
也就有1000個100us 1000 00us=100ms=0.1s
這樣理解就方便多了,
而后就是正常的配置程序,這里不做贅述,
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 1. 使能時鐘 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType= GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/* 2. 配置定時器引數 */
TIM_TimeBaseStructure.TIM_Prescaler = 16800 - 1; /* 定時器時鐘分頻系數 */
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; /* 定時器重裝載值 */
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; /* 計數器模式 */
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; /* 重復計數值 */
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //設定每次進入中斷為電平翻轉模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //輸出開
TIM_OCInitStructure.TIM_Pulse = 550; //設定最初CCR為0,這樣一配置完就進去中斷服務程式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //設定最開始的電平為高電平
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //載入暫存器
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1預裝載使能
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的預裝載暫存器
TIM_Cmd(TIM1, ENABLE); //使能TIM1
TIM5初始化代碼
接下來我是用通用定時器TIM5來進行輸入捕獲
根據資料手冊TIM5掛載在APB1總線上,當系統頻率配置為168MHZ時,該總線頻率為84MHZ,
也就是該定時器,可以從系統分得84MHZ的時鐘,但是定時器肯定跑不了這么高的時鐘,所以要先進行分頻,為了方便計算,我選擇84分頻,所以定時器計數頻率為1 000 000MHZ,
同時再給計數器設定1000的計數值,也就是計數值變化1000次產生一次上溢事件,
所以上溢時間發生的頻率為1 000 HZ 也就是1ms一次,
接下來就是輸入捕獲的模式配置,這里我選擇通道1為注入通道,
重映射引腳PA0為定時器5通道1,
- 這里注意一下,我看過許多STM32F1的代碼對引腳的初始化都是配置為輸入模式,而F4的芯片應該是要配置為復用模式,這里是一點不同的地方,如果配置不對有的地方可能會不能用,我之前就遇到過,但凡是重映射引腳,我現在一律配置為復用模式(F4代碼中沒有復用時鐘開啟好像),
- 然后這一段代碼是配置數字濾波器時鐘的的
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM5_ICInitStructure.TIM_ICFilter = 0x02;
這里TIM_ICPSC_DIV1意思是:配置捕獲1預分頻器,每次在捕獲輸入上檢測到邊緣時執行的捕獲,換句話說,檢測到上升沿就判斷是上升沿,這句話可能有點奇怪,如果換一種說法
檢測到1次上升沿就捕獲,也就說還能設定檢測到兩次上升沿,四次,八次才捕獲配置,
接下來一個成員變數就是給濾波器配置時鐘周期,濾波器的時鐘周期是由定時器直接分得的,這直接決定了濾波器的采樣周期,這里貼出資料手冊上的介紹,

- 從圖中可以得知定時器將一次邊沿信號變化視為真實變化(而不是噪聲)的真正因素是濾波器在周期內連續捕獲了N次都為上升沿才認定為是上升沿,
這里的捕獲和前一個預分頻的捕獲可能有點混淆,這里做這樣的理解
1.濾波器的捕獲是用于判斷信號是否是變化而不是由于噪聲造成抖動,當在周期內連續檢測到N次的事件,才認為該事件是一個有效邊沿,反之則為噪聲雜波,
2.預分頻的配置是用于檢測何時該觸發中斷事件,也就是當來N次上升沿時候才觸發中斷,
以下貼出初始化代碼, 使能更新中斷與捕獲中斷,
TIM_ICInitTypeDef TIM5_ICInitStructure;
void TIM5_Cap_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能TIM5時鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 清除之前設定
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //PA0 輸入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0); //PA0 下拉
//初始化定時器5 TIM5
TIM_TimeBaseStructure.TIM_Period = 84-1; //設定計數器自動重裝值
TIM_TimeBaseStructure.TIM_Prescaler =1000-1; //預分頻器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設定時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的引數初始化TIMx的時間基數單位
//初始化TIM5輸入捕獲引數
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 選擇輸入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻
TIM5_ICInitStructure.TIM_ICFilter = 0x02;//IC1F=0000 配置輸入濾波器 不濾波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
//中斷分組初始化
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM3中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占優先級2級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //從優先級0級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的引數初始化外設NVIC暫存器
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允許更新中斷 ,允許CC1IE捕獲中斷
TIM_Cmd(TIM5,ENABLE ); //使能定時器5
}
中斷服務函式
這里遇到了問題
首先講一下我的思路,希望有大佬看出來哪里出問題了幫我做個解答,
- 這里我原本是打算,觸發捕獲事件中斷的時候,記錄當前定時器的計數值,也就是CNT中的值,然后通過計算得出更精確的數值,
TIM5->CNT //該暫存器中即為定時器當前計數值
這里先講方法:在檢測當上升沿之后,先將捕獲設定為下降沿捕獲,記錄進入定時器更新事件中斷的次數,按照剛剛的配置方法也就是記錄一次中斷為1ms,當捕獲到下降沿的時候再記錄當前的計數器的值,而后通過計算得出該信號處于上升沿的時間,
以下貼出代碼(問題往下翻)
u8 TIM5CH1_CAPTURE_STA=0; //輸入捕獲狀態
float TIM5CH1_CAPTURE_VAL; //輸入捕獲值
float TIM5CH1_CAPTURE_VAL_1,TIM5CH1_CAPTURE_VAL_2;
float TIM5CH1_CAPTURE_VAL_N=0;
void TIM5_IRQHandler(void)
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
// if( TIM5CH1_CAPTURE_STA == 0 )//未捕獲到上升沿
// {
// TIM5CH1_CAPTURE_VAL_N = 0;
// }
if(TIM5CH1_CAPTURE_STA==1)//未捕獲到上升沿
{
TIM5CH1_CAPTURE_VAL_N++;
// TIM5->CNT = 0 ;
}
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕獲1發生捕獲事件
{
if(TIM5CH1_CAPTURE_STA==0)
{
// TIM5CH1_CAPTURE_VAL_1=TIM5->CNT;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);
TIM5->CNT = 0 ;
TIM5CH1_CAPTURE_STA=1;
}
else
{
// TIM5CH1_CAPTURE_VAL_2=TIM5->CNT;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
TIM5->CNT = 0 ;
TIM5CH1_CAPTURE_VAL=TIM5CH1_CAPTURE_VAL_N;
/*+(((float)TIM5CH1_CAPTURE_VAL_2+1000-(float)TIM5CH1_CAPTURE_VAL_1)/1000)*/
TIM5CH1_CAPTURE_VAL_N=0;
TIM5CH1_CAPTURE_STA=0;
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中斷標志位
}
薛定諤的代碼
好吧,這也是我自己學藝不精,講一下問題,
可以看到,我在以上代碼中并沒有將我自己所講的思路完全實作,而是只保留了用更新中斷計算時間的部分,
關于記錄定時器計數值以及相關的計算代碼以及被我注釋掉了,
原因是實際運行時,這部分出現奇怪的問題,
當我在代碼中賦值部分打上斷點時,通過除錯可以看到其變數的值在不斷的變化,
/*就是這倆行代碼,打斷點和不打斷點是兩種狀態,*/
TIM5CH1_CAPTURE_VAL_1=TIM5->CNT;
TIM5CH1_CAPTURE_VAL_2=TIM5->CNT;
但是!當我不在這一句上打斷點的時候,除錯時直接運行到中斷服務函式最后一句,雖然該計數器的值不斷在變化,但是記錄計數器的值的變數值卻不會改變一直為42(十進制),
當我重新打上斷點,發現,這個變數又開始愉快的變化了(微笑),
希望有大佬可以看出是什么問題,
需要工程檔案的可以評論,
·以上代碼親測有效
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/253547.html
標籤:其他
