五一之際,先祝大家五一快樂、其實快樂很簡單,工作的人有假放,學習的人也有假放,像我,有假放才有更多的時間學自己想學的東西、51假期學51,可惜沒有32假期呀、好了、、言歸正傳,大家聽過吸星大法吧、、在這里、智商和情商比我高的人估計又知道我要說什么了、、沒錯了、、今天我們來了解“葵花寶典”第STM32篇之輸入捕獲,也就是上文所講的“吸星大法”,
那輸入捕獲可以用來干嘛呢??這個問題問的好,輸入捕獲可以用來測量脈沖寬度或者測量頻率,假如要捕獲一個脈沖的高電平脈寬,我們要怎么做呢??別急哈、、接下來我們從頭慢慢的分析到腳、、
據老夫所知:STM32的輸入捕獲,就是通過檢測通道上的邊沿信號,在邊沿信號發生跳變(比如說突然來個上升沿或者下降沿),計數器就把此刻的計數值存放到對應通道的捕獲比較寄存器,就這樣、、就捕捉到了“美女”、話是這么說、、可操作起來不僅僅是幾句話、因為初始化和對捕獲的處理是不一樣的、所以,為了做好迎接捕獲的准備,我們來介紹下幾個比較陌生的位:
對於定時器的一些寄存器,在之前的博客都有涉及到,如
TIMx_CR1,
捕獲/比較模式寄存器1(TIMx_CCMR1),
捕獲/比較使能寄存器(TIMx_CCER),
計數器(TIMx_CNT)
預分頻器(TIMx_PSC)
自動重裝載寄存器(TIMx_ARR)
捕獲/比較寄存器1(TIMx_CCR1)
我們再來看看捕獲/比較模式寄存器1(TIMx_CCMR1),由於我們是用TIM5_CH1,所以該寄存器中
CC1S[1:0]:捕獲/比較1選擇 (Capture/Compare 1 selection)我們選擇01:CC1通道被配置為輸入,IC1映射在TI1上;這個知道為啥是TI1嗎??請看我那銷魂美麗的塗鴉:
這里我們檢測高電平的寬度,所以我們檢測的時候只要遇到上升沿就觸發捕獲一次,但是我們要怎么設置呢,請看這幾位:
IC1PSC[1:0]:輸入/捕獲1預分頻器 (Input capture 1 prescaler)00:無預分頻器,捕獲輸入口上檢測到的每一個邊沿都觸發一次捕獲;
神奇吧,好了,IC1F[3:0]:輸入捕獲1濾波器 (Input capture 1 filter)(這個就是上圖中的輸入濾波器,在這里我們不做濾波處理,為什么,請看以下解釋)
在這里解釋下:數字濾波器由一個事件計數器組成,它記錄到N個事件后會產生一個輸出的跳變:這個N可以取值具體參考中文手冊,意思是說:我采樣高電平,只有連續采樣到N個電平是高電平的話我才認為是有效的高電平,低於N個我就認為是無效的、在這篇博客里,我們只要是采樣到高電平就行,所以這里就不采用數字濾波。
我們來看看這個寄存器 捕獲/比較使能寄存器(TIMx_CCER),要使捕獲使能,我們就需要設置使能位
CC1E:輸入/捕獲1輸出使能 (Capture/Compare 1 output enable)為0;
對於我們輸入捕獲后要處理的我們交給我們的中斷,所以在這里我們要開啟中斷使能位
DMA/中斷使能寄存器(TIMx_DIER) CC1IE:允許捕獲/比較1中斷 (Capture/Compare 1 interrupt enable)為1;
介紹了以上幾位大神,接下來,我們要怎么個思路呢??==當我們捕獲到上升沿時,我們把此時的CNT中的值讀出來,然后等待下降沿的到來,這時候要分為兩種情況:
第一:下降沿來了,我們就記錄此刻CNT的值,(捕獲值)然后重復以上動作
第二:下降沿沒來,可是這時候定時器的計數值已經到了,也就是要溢出了,這時候要特殊處理下,也就是直接把計數值返回
(注:在這里,要注意捕獲值跟計數值的差別,他們是不一樣的、)至於為什么不一樣,大家可以思考思考、、
所以我們將兩次捕獲的值相減,(下降沿的值減去上升沿的值)就可以得到高電平的脈寬了、、而這些事,我們都在中斷服務函數里處理(中斷喔、、想起沒??要做什么知道吧、、注意,這時候有兩個中斷觸發:更新中斷和捕獲中斷,更新中斷用來處理定時器計數溢出,捕獲中斷用來處理捕獲事件)
接下來,我們看看我們具體的實現步驟
1:開啟掛載在ABP1的TIM5時鍾,開啟掛載在ABP2的GPIOA的時鍾,並初始化TIM5和GPIOA,由於這兩個初始化前幾篇博客有涉及到,故直接貼出代碼:
1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); 2 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE); 3 4 KEY_Init(); //IO我已在按鍵的函數里初始化了 5 6 TIM_TimeBaseStructure.TIM_Period = arr; 7 TIM_TimeBaseStructure.TIM_Prescaler = psc; 8 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 9 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 10 TIM_TimeBaseInit(TIM5, & TIM_TimeBaseStructure);
2、設置TIM_CH1的輸入捕獲功能,打開“stm3210x.tim.h”我們可以看到
1 typedef struct 2 { 3 4 uint16_t TIM_Channel; /*!< Specifies the TIM channel.設置通道 5 This parameter can be a value of @ref TIM_Channel */ 6 7 uint16_t TIM_ICPolarity; /*!< Specifies the active edge of the input signal.設置輸入信號的有效捕獲極性 8 This parameter can be a value of @ref TIM_Input_Capture_Polarity */ 9 10 uint16_t TIM_ICSelection; /*!< Specifies the input. 設置映射關系 11 This parameter can be a value of @ref TIM_Input_Capture_Selection */ 12 13 uint16_t TIM_ICPrescaler; /*!< Specifies the Input Capture Prescaler. 設置捕獲的分配系數 14 This parameter can be a value of @ref TIM_Input_Capture_Prescaler */ 15 16 uint16_t TIM_ICFilter; /*!< Specifies the input capture filter. 設置數字濾波器的長度 17 This parameter can be a number between 0x0 and 0xF */ 18 } TIM_ICInitTypeDef;
根據我們以上的了解,我們設置,請看以下代碼:
1 TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //通道1 2 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲 3 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1 4 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //不分頻 5 TIM_ICInitStructure.TIM_ICFilter = 0x0; //不濾波 6 TIM_ICInit(TIM5, &TIM_ICInitStructure);
3、設置中斷優先級、在這里比較簡單,直接看代碼:
1 NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; 2 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; 3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; 4 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 5 NVIC_Init(&NVIC_InitStructure);
4.使能中斷並開啟定時器
1 2 TIM_ITConfig(TIM5, TIM_IT_Update | TIM_IT_CC1, ENABLE ); 3 4 TIM_Cmd(TIM5, ENABLE);
5、編寫中斷服務函數
1 u8 TM5_CH1_CAPTURE_STA = 0;//8位0x00~0x80 2 u16 TM5_CH1_CAPTURE_VAL; //捕獲高電平后定時器溢出的次數 3 4 void TIM5_IRQHandler(void) 5 { 6 if((TM5_CH1_CAPTURE_STA & 0x80) == 0 ) //未成功捕獲,0x80捕獲完成 7 { 8 if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET) //數據更新中斷產生 9 { 10 if(TM5_CH1_CAPTURE_STA & 0x40) //已經捕獲到高電平 11 { 12 if((TM5_CH1_CAPTURE_STA & 0x3f)==0x3f) //溢出 13 { 14 TM5_CH1_CAPTURE_STA |= 0x80; //強制捕獲成功 15 TM5_CH1_CAPTURE_VAL = 0xffff; //此時的計數值 16 } 17 else 18 { 19 TM5_CH1_CAPTURE_STA++; 20 } 21 22 } 23 } 24 if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)//發生捕獲 25 { 26 if(TM5_CH1_CAPTURE_STA & 0x40) //成功捕獲到一次下降沿,但不是第一次捕獲 27 { 28 TM5_CH1_CAPTURE_STA |= 0x80; //捕獲成功 29 TM5_CH1_CAPTURE_VAL = TIM_GetCapture1(TIM5);//獲取捕獲值 30 TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Rising);//要設置為上升沿,等待下降沿的來臨 31 } 32 else //第一次捕獲 33 { 34 TM5_CH1_CAPTURE_STA = 0; 35 TM5_CH1_CAPTURE_VAL = 0; 36 TIM_SetCounter(TIM5, 0); //還沒等到下降沿來時把所有的都清零 37 TM5_CH1_CAPTURE_STA |= 0x40; 38 TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Falling);//要設置成下降沿,等到上升沿的來臨 39 } 40 41 } 42 } 43 TIM_ClearITPendingBit(TIM5, TIM_IT_CC1 | TIM_IT_Update); 44 45 }
6、注意紅色標注部分,好好理解剛開始說的計數值和捕獲值的區別、還有
TIM_SetCounter(TIM5, 0); //設置計數器寄存器值
TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Falling);//設置通道1輸入捕獲極性
這兩個函數是庫函數為我們提高的可以單獨對通道進行操作的函數,非常方便
7、到這里,我們需要在主函數里稍微寫下:
1 if(TM5_CH1_CAPTURE_STA & 0x80) 2 { 3 temp = (TM5_CH1_CAPTURE_VAL & 0x3f); 4 temp *= 65536; //計數器為0~65535,也就是65536一次 5 temp += TM5_CH1_CAPTURE_VAL; 6 printf("HIGH is %d\r\n",temp); 7 TM5_CH1_CAPTURE_STA = 0;//這里因為我們在之前的捕獲時,若捕獲成功則為1,並沒有清0,所以要進行下一次捕獲的話,要在這里進行清零 8 }
8、好了,至於我標題說的小應用,也就是把我們上次PWM的輸出給這次的輸入捕獲,大家通過串口就可以看到高電平的脈寬了。當然,在這篇博客的程序里也需要保留上次PWM輸出的程序方可、、
9、大家肚子餓了吧、、吃飯去吧、、
今天五一,還是宅在宿舍學32,也很開心,因為學到了東西、、有假放開心、、有學到知識也開心,即使在學的過程中可能會被煩惱到、、其實快樂也很簡單、只是看你怎么把握、、五月的開始、、將繼續學stm32、、還有英語六級、、加油、、在這里、有錯的地方希望能指教、、我也虛心的向您學習、、