資料來著江協科技
? ? ? ?這篇是編碼器測速,江科大的源碼在測速的時候,定時器TIM2是一直在跑的,不受其它控的,它就一直隔1S讀一次CNT的值。它也不管是否有輸入信號。源碼程序修改一下是可以實現對PWM信號以測頻法的方式讀取。
? ? ? ?筆者稍微改了一下這源碼程序,讓TIM3有信號輸入時,TIM2才開始工作計數。源碼在讀連續信號的時候還是好用的,在讀離散信號的時候可能就不怎么好用了。
比如我希望在某個IO口檢測到一段1KHZ的頻率的方波(或幾次計數),方波的持續時間達到300ms,就開啟某個功能,源碼這個方案就不太好用,因此稍微修改了下程序。讓其也滿足這個條件
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"
#include "LED.h"int16_t Num; //定義在定時器中斷里自增的變量
uint16_t i = 0; //中斷次數指示,TIM2是1S中斷int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化Timer_Init(); //定時中斷初始化Encoder_Init();// LED_Init();/*顯示靜態字符串*/OLED_ShowString(1, 1, "CNT:"); //1行1列顯示字符串Num:while (1){if(Num != 0)OLED_ShowSignedNum(1, 5, Num, 5); //不斷刷新顯示Num變量}
}/*** 函 數:TIM2中斷函數* 參 數:無* 返 回 值:無* 注意事項:此函數為中斷函數,無需調用,中斷觸發后自動執行* 函數名為預留的指定名稱,可以從啟動文件復制* 請確保函數名正確,不能有任何差異,否則中斷函數將不能進入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判斷是否是TIM2的更新事件觸發的中斷{Num = TIM3_GetNumber(); //TIM3的CNT值賦值給Num,該值在1s中斷中 TIM_SetCounter( TIM3, 0); //TIM3的CNT清零 TIM_Cmd(TIM2, DISABLE); //關閉TIM2定時器TIM_SetCounter( TIM2, 0); //TIM2 CNT計數清0 TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //使能TM3捕獲中斷 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中斷標志位}
}
Encoder.c
#include "stm32f10x.h" // Device headervoid Encoder_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //開啟TIM3外設時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //開啟GPIOA外設時鐘GPIO_InitTypeDef GPIO_InitStruct; //GPIO功能設置GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU ;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz ;GPIO_Init( GPIOA, &GPIO_InitStruct);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; //時基單元設置TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1 ;//該處設置對于目前的程序信號好像不起作用,最起碼結果上不起作用因此默認為不分頻TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up ;TIM_TimeBaseInitStruct.TIM_Period = 65536-1 ; //ARRTIM_TimeBaseInitStruct.TIM_Prescaler = 1-1 ; //Psc,設置為不分頻即1個觸發信號觸發一次TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);TIM_ICInitTypeDef TIM_ICInitStruct; //信號輸入捕獲設置,設置了兩個通道TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel = TIM_Channel_1 ;TIM_ICInitStruct.TIM_ICFilter = 0x0F ;TIM_ICInit(TIM3, &TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel = TIM_Channel_2 ;TIM_ICInitStruct.TIM_ICFilter = 0x0F ;TIM_ICInit(TIM3, &TIM_ICInitStruct);TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, //編碼器設置TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //TM3捕獲1中斷使能// 配置TIM3中斷NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //響應優先級NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);TIM_Cmd( TIM3, ENABLE); //時鐘使能}uint16_t TIM3_GetNumber(void)
{return TIM_GetCounter(TIM3);}void TIM3_IRQHandler(void)
{if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET){ TIM_Cmd(TIM2, ENABLE); // 使能TIM2定時器 TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); // 清除中斷標志TIM_ITConfig(TIM3, TIM_IT_CC1, DISABLE);//TIM3關閉捕獲中斷}
}
修改后的代碼功能:
? ? ? ?TIM3通道1檢測到輸入捕獲的時候會進入中斷使能定時器2,定時器2開始計時,當定時器2溢出時進入定時器2的中斷讀取TIM3 CNT里的值并且清0,(顯然如果TIM3的輸入信號頻率過快的話會產生一點延時)并關閉定時器2,使能定時器3捕獲中斷,退出TIM2中斷如果編碼器還在轉動又會馬上進入TIM3捕獲中斷開啟定時器2,讓其再開啟定時功能。
? ? ? 以此為基礎重寫了之前的測頻法程序。
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "ICfeq.h"uint16_t Num; //定義在定時器中斷里自增的變量
uint16_t i = 0; //中斷次數指示,TIM2是1S中斷int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化Timer_Init(); //定時中斷初始化IC_Init();/*顯示靜態字符串*/OLED_ShowString(1, 1, "CNT:"); //1行1列顯示字符串Num:while (1){if(Num != 0)OLED_ShowNum(1, 5, Num, 5); //不斷刷新顯示Num變量}
}/*** 函 數:TIM2中斷函數* 參 數:無* 返 回 值:無* 注意事項:此函數為中斷函數,無需調用,中斷觸發后自動執行* 函數名為預留的指定名稱,可以從啟動文件復制* 請確保函數名正確,不能有任何差異,否則中斷函數將不能進入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判斷是否是TIM2的更新事件觸發的中斷{//Delay_ms( 10);Num = TIM3_GetNumber(); //TIM3的CNT值賦值給Num,該值在1s中斷中 // Delay_ms( 100);TIM_SetCounter( TIM3, 0); //TIM3的CNT清零 TIM_Cmd(TIM2, DISABLE); //關閉TIM2定時器TIM_SetCounter( TIM2, 0); //TIM2 CNT計數清0 TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //使能TM3捕獲中斷 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中斷標志位}
}
ICfeq.c
#include "stm32f10x.h" // Device headervoid IC_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //開啟TIM3外設時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //開啟GPIOA外設時鐘GPIO_InitTypeDef GPIO_InitStruct; //GPIO功能設置GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU ;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 ;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz ;GPIO_Init( GPIOA, &GPIO_InitStruct);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; //時基單元設置TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1 ;//該處設置對于目前的程序信號好像不起作用,最起碼結果上不起作用因此默認為不分頻TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up ;TIM_TimeBaseInitStruct.TIM_Period = 65536-1 ; //ARRTIM_TimeBaseInitStruct.TIM_Prescaler = 1-1 ; //Psc,設置為不分頻即1個觸發信號觸發一次TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);TIM_ICInitTypeDef TIM_ICInitStruct; //信號輸入捕獲設置TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel = TIM_Channel_1 ;TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising ;TIM_ICInitStruct.TIM_ICFilter = 0x0F ;TIM_ICInit(TIM3, &TIM_ICInitStruct);
// TIM_SelectInputTrigger( TIM3, TIM_TS_TI1FP1);TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //TM3捕獲1中斷使能// 配置TIM3中斷NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //響應優先級NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);TIM_Cmd( TIM3, ENABLE); //時鐘使能}uint16_t TIM3_GetNumber(void)
{return TIM_GetCounter(TIM3);}void TIM3_IRQHandler(void)
{if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET){ TIM_Cmd(TIM2, ENABLE); // 使能TIM2定時器 TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); // 清除中斷標志TIM_ITConfig(TIM3, TIM_IT_CC1, DISABLE);//TIM3關閉捕獲中斷}
}
? ? ?程序的其它組件部分參考江科大的文件,TIM2的定時是1S,輸入信號是1KHZ的PWM,輸入端口是PA6,現在的程序與筆者之前的測頻法比較。現在的程序更合理點,如果在主函數中斷賦值語句前插入延時語句
Delay_ms( 10);Num = TIM3_GetNumber(); //TIM3的CNT
,那么最終得到的CNT值是會變大,前一個測頻法程序是不會的,前一個測頻法只能測試連續輸入PWM。如果信號比較離散的話,它測試結果會變的不準確。
? ? ? 分享一下學習過程中發生的錯誤,之前這個代碼一直有個BUG,編碼器旋鈕你隨便轉的話可能會導致程序死機卡死。后面想了不少時間,找的問題是中斷優先級照成的。
TIM3的中斷控制著TIM2的中斷,并且進入中斷會使自身中斷關閉。TIM2中斷會關閉自身的定時器并使能TIM3中斷,如過有中斷嵌套的話,位置不對就會死機。筆者之前的中斷優先級設置的不合理,就出現死機了。