|
回顧 上一章節(jié)學(xué)習(xí)了PWM生成,剛好買的元器件也都到了。測(cè)試下代碼,完美運(yùn)行。這不又趁著周末,進(jìn)行下一個(gè)環(huán)節(jié)—定時(shí)器編碼器模式。
目的是為下一步PID控制做準(zhǔn)備。 遇到的問(wèn)題周末學(xué)習(xí)編碼器模式也是一波三折呀,沒人指點(diǎn)真的是寸步難行呀。氣的直咬牙。
說(shuō)說(shuō)啥情況:
我用上節(jié)的PWM輸出控制電機(jī)旋轉(zhuǎn),用編碼器采集脈沖,想把這個(gè)脈沖顯示在LCD上,就這個(gè)顯示功能搗鼓了一天多,最后還是沒成功,最后沒辦法了,只能串口打印出來(lái)。就在昨天晚上準(zhǔn)備更新博客的時(shí)候想想不能就這么算了,又去調(diào)試了幾下,奇跡出現(xiàn)了,LCD竟然可以顯示了,弄完之后十點(diǎn)多了,就去睡覺了。目前問(wèn)題還是不確定在哪,之前的代碼修改的也找不到了,晚上回家再試試改成之前的代碼,找找問(wèn)題的根本原因。
改天單獨(dú)開個(gè)帖子說(shuō)說(shuō)吧,今天先學(xué)習(xí)編碼器。 編碼器的作用首先需要明白的一點(diǎn)就是為什么要用編碼器模式,而不是直接用輸入捕獲。
最主要的原因就是編碼器采集的是兩個(gè)信號(hào),根據(jù)兩個(gè)信號(hào)的高低電平來(lái)判斷是正轉(zhuǎn)還是反轉(zhuǎn),比輸入捕獲用一個(gè)信號(hào)的抗干擾能力強(qiáng)。如果一個(gè)信號(hào)有干擾,而另一個(gè)信號(hào)沒有干擾,則計(jì)數(shù)器不會(huì)計(jì)數(shù),
就像下圖這樣,圖片在中文參考手冊(cè)中找的

圖中紅線標(biāo)注的是毛刺,當(dāng)TI1有干擾的情況下,計(jì)數(shù)器是不會(huì)繼續(xù)向上累加的。如果用輸入捕獲的話,左邊紅色框中有兩個(gè)脈沖,也就是說(shuō)累加器會(huì)加兩次。這就出現(xiàn)了誤差,本來(lái)不該加,他卻加了。
這就是首選編碼器模式的主要原因。知道了他的作用還需要知道原理。 編碼器采集原理1.理解框圖

上圖是定時(shí)器的框圖,首先我們會(huì)從電機(jī)屁股后面的編碼器引出兩根信號(hào)線接到單片機(jī)的編碼器引腳,如上圖第一個(gè)框內(nèi),接到某定時(shí)器的TIMx_CH1和TIMx_CH2兩個(gè)通道的引腳上。進(jìn)來(lái)之后進(jìn)行濾波和邊沿檢測(cè)之后輸出TI1FP1和TI1FP2,之后就直接到2號(hào)框了。2號(hào)框是將TI1FP1和TI1FP2接入編碼器接口,在這里判斷是該向上還是向下計(jì)數(shù),之后來(lái)到3號(hào)框,進(jìn)行分頻,最后來(lái)到CNT計(jì)數(shù)器進(jìn)行計(jì)數(shù)。我們可以定時(shí)采集CNT里面的值,這樣就能算出電機(jī)的轉(zhuǎn)速和其他一些參數(shù)了。
具體配置一會(huì)看代碼吧。大致了解一下定時(shí)器框圖。 2.理解計(jì)數(shù)方式
框圖明白了,腦海里就明白了編碼器工作的大致原理了。但還需要明白一些細(xì)節(jié)方面的東西。比如它是怎么通過(guò)兩路信號(hào)進(jìn)行計(jì)數(shù)的,到底是向上還是向下計(jì)數(shù)等等。 

其實(shí)也很簡(jiǎn)單,結(jié)合上面兩張圖片很好理解。簡(jiǎn)單說(shuō)一下,明白一個(gè)其他都明白了。
首先假定此時(shí)是電機(jī)正轉(zhuǎn),還有一點(diǎn)要知道,編碼器兩個(gè)信號(hào)相差90°的相位角。一個(gè)周期的信號(hào)可以分為0 π/2 π 3/2*π 2π,如上圖,剛好差了π/2。上圖是兩個(gè)通道上下邊沿都檢測(cè)(SMS=’011’) 。
當(dāng)TI1為上升沿時(shí)時(shí),此時(shí)TI2為低電平,對(duì)應(yīng)下面的圖,先看【有效邊沿】,因?yàn)槭强吹腡I1上升沿,所以第一項(xiàng)選擇【僅在TI1計(jì)數(shù)】,之后找對(duì)應(yīng)的【TI1FP1 信號(hào)項(xiàng)】,選擇上升。因?yàn)閳D中TI1是上升沿,對(duì)應(yīng)TI2是低電平,因此在【相對(duì)信號(hào)的電平】那一項(xiàng)應(yīng)該選擇低,對(duì)應(yīng)起來(lái)就是向上計(jì)數(shù)。如下圖紅色部分。
理解了TI1上升沿計(jì)數(shù)之后,其他的都一樣了。

到這里,基本上編碼器模式已經(jīng)掌握的差不多了,下面就開始代碼了。 STM32F103ZET6生成編碼器代碼#include "PWM.h"//通用定時(shí)器TIM3 PA6void PWM_Init(void){ //基本定時(shí)器初始化部分 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; //初始化定時(shí)器 TIM_DeInit(TIM3); //GPIO初始化部分 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); //使能定時(shí)器時(shí)鐘 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //死區(qū)時(shí)間時(shí)鐘源,該例程只進(jìn)行PWM輸出,沒有用到死區(qū),因此不設(shè)定 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //向上計(jì)數(shù)模式 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //自動(dòng)重裝載的值設(shè)定為1000 TIM_TimeBaseInitStructure.TIM_Period = (1000 - 1); //定時(shí)器周期分頻數(shù) TIM_TimeBaseInitStructure.TIM_Prescaler = (72 - 1); //重復(fù)計(jì)數(shù)器的值,只有高級(jí)定時(shí)器1和8有重復(fù)計(jì)數(shù)功能,其他都沒有,因此設(shè)定為0 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //初始化定時(shí)器3 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); //PWM初始化部分 //pwm1模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //初始化電平 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //使能輸出比較 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //初始化PWM占空比為0 TIM_OCInitStructure.TIM_Pulse = 1000; //初始化pwm TIM_OC1Init(TIM3,&TIM_OCInitStructure); //使能TIM3在CCR2上的預(yù)裝載寄存器 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能定時(shí)器3 TIM_Cmd(TIM3,ENABLE); }//編碼器模式 通用定時(shí)器2 PA0 PA1void Advance_TIM_Init(void){ //GPIO初始化 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 設(shè)置中斷組為0 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 設(shè)置中斷來(lái)源 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 設(shè)置搶占優(yōu)先級(jí) NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 設(shè)置子優(yōu)先級(jí) NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); //基本定時(shí)器初始化部分 //使能定時(shí)器時(shí)鐘 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);//設(shè)置缺省值 // 自動(dòng)重裝載寄存器的值,累計(jì)TIM_Period+1個(gè)頻率后產(chǎn)生一個(gè)更新或者中斷 TIM_TimeBaseStructure.TIM_Period = (359*4); // 驅(qū)動(dòng)CNT計(jì)數(shù)器的時(shí)鐘 = Fck_int/(psc+1) TIM_TimeBaseStructure.TIM_Prescaler = (72 - 1); // 時(shí)鐘分頻因子 ,配置死區(qū)時(shí)間時(shí)需要用到 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 計(jì)數(shù)器計(jì)數(shù)模式,設(shè)置為向上計(jì)數(shù) TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重復(fù)計(jì)數(shù)器的值,沒用到不用管 TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定時(shí)器 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //編碼器模式 TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising); TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 10; TIM_ICInit(TIM2, &TIM_ICInitStructure); /* //輸入捕獲初始化部分 //選擇捕獲通道 TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //濾波設(shè)置 TIM_ICInitStructure.TIM_ICFilter = 0x0; //設(shè)置捕獲的邊沿 上升沿 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising ; // 1分頻,即捕獲信號(hào)的每個(gè)有效邊沿都捕獲 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1 ; // 設(shè)置捕獲通道的信號(hào)來(lái)自于哪個(gè)輸入通道,有直連和非直連兩種 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 初始化PWM輸入模式,不能用下面這個(gè)函數(shù)進(jìn)行初始化輸入捕獲模式,會(huì)出錯(cuò)。 //普通的輸入捕獲模式用TIM_ICInit函數(shù),特殊的PWM捕獲用TIM_PWMIConfig函數(shù) //TIM_ICInit(TIM2,&TIM_ICInitStructure); TIM_PWMIConfig(TIM2, &TIM_ICInitStructure); // 選擇輸入捕獲的觸發(fā)信號(hào) TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1); // PWM輸入模式時(shí),從模式必須工作在復(fù)位模式,當(dāng)捕獲開始時(shí),計(jì)數(shù)器CNT會(huì)被復(fù)位 TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable); */ //使能中斷 TIM_ITConfig(TIM2,TIM_IT_Update, ENABLE); // 清除中斷標(biāo)志位 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 使能高級(jí)控制定時(shí)器,計(jì)數(shù)器開始計(jì)數(shù) TIM_Cmd(TIM2, ENABLE);}/*單位時(shí)間編碼器計(jì)數(shù) 輸入定時(shí)器 輸出速度值*/int Read_Encoder(u8 TIMX){ int Encoder_TIM; switch(TIMX) { case 2: Encoder_TIM= (short)TIM2 -> CNT; TIM2 -> CNT=0;break; case 3: Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=0;break; case 4: Encoder_TIM= (short)TIM4 -> CNT; TIM4 -> CNT=0;break; default: Encoder_TIM=0; } return Encoder_TIM;}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
1.PWM輸出
較上一節(jié)講得做了引腳變動(dòng),這次使用定時(shí)器3,PA6引腳作為PWM輸出
具體配置和上一節(jié)差不多,不在啰嗦。
2.編碼器采集
編碼器采集用的是通用定時(shí)器2的PA0,PA1引腳,程序已經(jīng)詳細(xì)的注釋了。講幾個(gè)重要的參數(shù)配置、其中用 /* */ 注釋的是普通的輸入捕獲模式、
3.編碼器配置中的主要參數(shù)講解
①中斷優(yōu)先級(jí)
不用問(wèn),問(wèn)就是最高優(yōu)先級(jí),再平衡小車系統(tǒng)中,電機(jī)的速度和位置是至關(guān)重要的,而采集編碼器數(shù)據(jù)就是作為計(jì)算速度和位置的重要參數(shù)、
②TIM_TimeBaseStructure.TIM_Period = (359 * 4);
這行代碼是配置自動(dòng)重裝載的值,在這里設(shè)置359 * 4其實(shí)意義不大,因?yàn)槲覀冏龅氖菍?shí)驗(yàn),而且轉(zhuǎn)速不是很快,編碼器一圈才11個(gè)脈沖,因此采樣值很小,滿占空比才到達(dá)50左右,因此該數(shù)值只要大于60就行。網(wǎng)上很多都是一圈300脈沖的那種高精度編碼器,因此該值需要大于在采樣周期能采樣的最大值,再留一些余量就行了。假設(shè)你編碼器1s采樣一次,一次采樣500個(gè)脈沖,而你設(shè)定ARR=400,這肯定是不行的,因此該值應(yīng)該根據(jù)自己的實(shí)際情況進(jìn)行設(shè)定。
③TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
在編碼器模式中,該配置不起作用,正反項(xiàng)計(jì)數(shù)是根據(jù)兩個(gè)信號(hào)的前后順序來(lái)決定的,親測(cè)該配置對(duì)計(jì)數(shù)無(wú)作用。
④TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
重中之重
這個(gè)函數(shù)明白了就好理解,不要被表象給迷惑到就行。
1.這個(gè)函數(shù)是開啟編碼器模式的,第一個(gè)參數(shù)是選擇定時(shí)器,在這里我們選擇定時(shí)器2;
2.第二個(gè)參數(shù)是選擇計(jì)數(shù)器模式的,進(jìn)入到函數(shù)體中可以找到具體配置的是TIMx_SMCR寄存器中的SMS【2-0】位。具體說(shuō)明如下:

因此選擇的是編碼器模式3,根據(jù)另一個(gè)信號(hào)的電平來(lái)決定怎么計(jì)數(shù),上面在將編碼器計(jì)數(shù)原理的時(shí)候也說(shuō)過(guò)了,向上向下計(jì)數(shù)是要看另一路信號(hào)的電平的。
3.最后兩個(gè)參數(shù)也是最不容易理解的,主要是好多都是根據(jù)字面意思講解,沒有到函數(shù)體中看到底是配置的哪一個(gè)寄存器。當(dāng)你真正的進(jìn)去看函數(shù)體的時(shí)候你就明白了。
這兩個(gè)參數(shù)從下面這張圖中還以為是配置上升沿、下降沿還是雙邊沿檢測(cè)的,其實(shí)不是,

其實(shí)不是這樣,從函數(shù)體中找到這兩個(gè)參數(shù),其實(shí)是配置CCER寄存器中的CC1E、CC1P、CC2E以及CC2P,CC1E和CC2E是使能的,CC1P、CC2P是配置極性的,具體看中文手冊(cè),我也給你截好圖了

反向和不反向的意思就是從0到ARR計(jì)數(shù)還是從ARR到0 計(jì)數(shù)、而該位正是配置這一點(diǎn)的,在一個(gè)TIM_ICPolarity_BothEdge這個(gè)定義并不能在這個(gè)函數(shù)的形參中使用。該函數(shù)只能配置反向和不反向,下圖紅色框中需要注意下。

4.TIM_ICInitStructure.TIM_ICFilter = 10;
這個(gè)是設(shè)定濾波的,設(shè)定0的時(shí)候不是很穩(wěn)。具體參考中文手冊(cè)如下圖:
 寫到這里,輸入捕獲的配置基本完后了。下面就是采樣了,我采用系統(tǒng)滴答定時(shí)器進(jìn)行1s采樣一次,這個(gè)值是輸入捕獲1s中采樣的編碼器的值。并把它顯示到屏幕上或者串口打印出來(lái)。 int Read_Encoder(u8 TIMX);
這個(gè)函數(shù)參考網(wǎng)上的代碼寫的,把這放到滴答定時(shí)器中斷里。如下圖 void SysTick_Handler(void){ flag ++; if(flag >= 1000) { flag = 0; Frequency1 = Read_Encoder(2); }}之后主函數(shù)一直循環(huán)顯示即可。這個(gè)是顯示在屏幕上的。 int main(void) { char aa[10; delay_init(); //延時(shí)函數(shù)初始化 LCD_Fill(30,130,239,130+16,WHITE); LCD_Init(); //uart_init(115200); //串口初始化為115200 SysTick_Config(SystemCoreClock / 1000);//1ms Advance_TIM_Init(); PWM_Init(); LCD_ShowString(30,50,16,"Show Frequency1",0); while(1) { sprintf(aa,"%d",Frequency1); LCD_ShowString(30,70,16,aa,0); //printf("count = %d\r\n",Frequency1); //delay_ms(10);//每隔1s打印一次編碼器角度,用手去撥動(dòng)編碼器 使其慢速旋轉(zhuǎn) } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
總結(jié)基本都是老套路了、
①定義函數(shù)結(jié)構(gòu)體
②使能GPIO時(shí)鐘
③配置GPIO
④配置中斷優(yōu)先級(jí)
⑤使能定時(shí)器時(shí)鐘
⑥配置基本定時(shí)器
⑦配置編碼器模式
⑧初始化輸入捕獲
⑨使能定時(shí)器 最主要的還是理解其中的一些重要的參數(shù),其實(shí)也不難(不過(guò)對(duì)于新手確實(shí)難,弄過(guò)一遍回過(guò)頭才覺得簡(jiǎn)單),進(jìn)入到函數(shù)體中仔細(xì)看看函數(shù),在對(duì)著手冊(cè)看寄存器內(nèi)容說(shuō)明也就明白啦。 感悟這個(gè)帖子已經(jīng)寫了好幾天了,7月3號(hào)就開始寫,寫到現(xiàn)在才弄完。中間過(guò)程很艱苦,不過(guò)一旦走出來(lái),滿滿的成就感。 LCD相關(guān)的函數(shù)操作直接找的別人的代碼,我還沒有仔細(xì)研究,先拿來(lái)用吧。還是先把項(xiàng)目完成吧,拖得時(shí)間長(zhǎng)了就沒信心了。 一個(gè)人搞軟件有時(shí)候真的是累,不是那種身體勞累,是心累。搞了幾天還沒把一個(gè)問(wèn)題解決的那種心情,真的是煩躁,恨不得把電腦砸了。他不像干體力活,加把勁就干完了,這種加把勁都不到往哪加。就像這個(gè)lcd顯示采集到的編碼器的值,就這一個(gè)問(wèn)題弄了兩三天。各種百度,同樣代碼別人就行,到我這就不行了。到目前為止還沒有真正找到答案、-_-|| 源碼代碼放到公眾號(hào)了,后續(xù)我會(huì)將之前的代碼也都放到公眾號(hào)上,歡迎小伙伴們交流學(xué)習(xí)哈。關(guān)注公眾號(hào)回復(fù)
“編碼器模式”
獲取源碼。

|