制作平衡小車肯定會用到電機,那么怎么控制呢?最簡單的就是直接加電壓,這樣電機就能轉動,但是至于轉多少圈,轉的快慢是不能控制的。這就不符合我們平衡小車的控制要求。這就需要用到PWM模式來控制電壓的大小,從而控制轉的快慢。至于轉了多少圈,就要用到編碼器了。今天先來看看怎么控制小車轉的快慢吧,一步一步來。 直流有刷電機的原理簡單說下有刷電機的工作原理,其實很簡單,稍微懂點物理的就能看懂吧。

上面這張圖應該都見過吧(因為你現在就在看著它呢,手動滑稽)。小時候四驅賽車的那種黃色小馬達也都見過吧,是不是只要將電壓加載電刷的兩極上,電機就會轉了。那個電機的內部結構就是上圖這樣。這一種里面帶有電刷的電機叫有刷電機,又因為是用的直流電,所以叫直流有刷電機。
我們只需要將電壓加到AB端,電機就能轉,電機轉動的速度和電壓有關,電壓越大,轉速越高。具體電機旋轉不懂的百度吧。我就不搬運了,重點是都后面的PWM。 有一點需要注意的是,直流有刷電機的驅動頻率的問題,過高或者過低都不好,網上找了資料說是根據電機的不同頻率有所不同,一般10k-20k。我暫且信了吧。 PWM是什么?既然知道了電壓和轉速成正比,那我們是不是只需要控制電壓就能控制轉速了。這就需要用到PWM技術了。PWM又名脈沖寬度調制,從名字上來看是調節脈沖寬度的。到底是怎么調節的呢?我是這樣理解的。
本來我們直接給電壓,電機就會轉,現在我們通過控制電壓,讓他一會有輸出,一會不輸出。假設占空比為50%,也就是說,在一定時間內,電壓有一半的時間是閉合的,一半的時間是打開的,這樣求出來的平均電壓值應該是原電壓值的一半。(不相信的同學可以一會試試,我后面把代碼傳上來)這樣我們就能控制電壓了。但是只控制電壓還不行呀,單片機引腳不能直接接到電機上呀,這就需要電機驅動器了。 TB6612直流有刷電機驅動
從淘寶截了一張圖,上面有引腳和使用說明,接線也很簡單。原理的話我沒細看,大致應該是通過PWM來控制VM的電壓值,之后會有一個像H橋一樣的電路來控制正反轉,當然了都是集成到芯片內部了。 STM32F103ZET6生成PWM代碼/*使用通用定時器2 通道1 PA0引腳輸出PWM------------ 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) 時鐘分頻71,系統總線時鐘72M,可以算出定時器時鐘為72/(71+1)=1M在這里我們需要設定PWM頻率為10Khz=0.1ms,因此自動重裝載的值ARR=100.因為定時器時鐘為1M=0.001ms,因此只需要累加100次,就等于0.1ms=10Khz了。占空比我們設定50%因此我們配置如下TIM_TimeBaseInitTypeStructure.TIM_Prescaler = 71; //定時器時鐘分頻TIM_TimeBaseInitTypeStructure.TIM_Period = (100 - 1); //自動重裝載的值 TIM_OCInitTypeStructure.TIM_Pulse = 50; //設置占空比大小*/void PWM_Init(void){ //定義結構體變量 GPIO_InitTypeDef GPIO_InitTypeStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeStructure; TIM_OCInitTypeDef TIM_OCInitTypeStructure; //使能引腳時鐘 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能引腳時鐘 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //引腳配置 GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出模式 GPIO_InitTypeStructure.GPIO_Pin = GPIO_Pin_0; //PA0 GPIO_InitTypeStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度為50M GPIO_Init(GPIOA, &GPIO_InitTypeStructure); //寫入到寄存器中 //定時器基本配置 TIM_TimeBaseStructInit(&TIM_TimeBaseInitTypeStructure); //恢復默認值 //自動重裝載寄存器的值,當TIM_Period+1個頻率后產生一個更新或者中斷 TIM_TimeBaseInitTypeStructure.TIM_Period = (100 - 1); //自動重裝載的值 //驅動CNT計數器的時鐘 = Fck_int/(psc+1) TIM_TimeBaseInitTypeStructure.TIM_Prescaler = 71; //定時器時鐘分頻 //時鐘分頻因子 ,配置死區時間時需要用到 TIM_TimeBaseInitTypeStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分割 //向上計數模式 TIM_TimeBaseInitTypeStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數器模式 //重復計數器的值,沒用到不用管 TIM_TimeBaseInitTypeStructure.TIM_RepetitionCounter = 0; //重復計數器的值. //寫入到寄存器 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitTypeStructure); //PWM模式配置 TIM_OCStructInit(&TIM_OCInitTypeStructure); //恢復默認值 TIM_OCInitTypeStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇PWM1模式 TIM_OCInitTypeStructure.TIM_OCIdleState = TIM_OCIdleState_Set; //空閑時間的引腳電平 TIM_OCInitTypeStructure.TIM_OCPolarity = TIM_OCPolarity_High; //指定輸出極性 TIM_OCInitTypeStructure.TIM_OutputState = TIM_OutputState_Enable; //輸出比較使能 TIM_OCInitTypeStructure.TIM_Pulse = 80; //設置占空比大小 //初始化通道1 TIM_OC1Init(TIM2, &TIM_OCInitTypeStructure); //啟用CCR1上的TIMx外設預加載寄存器。 TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable); //使能定時器 TIM_Cmd(TIM2,ENABLE);}- 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
示波器測量1.當占空比為50%時

測量電壓值

2.占空比為80%時

測量值
 總結1.PWM配置過程
①配置GPIO引腳,用的哪個引腳就配置哪個引腳,注意別忘了開啟時鐘使能
②配置完引腳,配置定時器基本參數
注意:
在配置TIM_Period的值的時候一定不要忘記最終計算的值是TIM_Period+1;
TIM_Prescaler在最終計算的值也是TIM_Prescaler+1。 TIM_TimeBaseStructInit(&TIM_TimeBaseInitTypeStructure); //恢復默認值
上面這個函數可要可不要,規范化代碼最好是寫上去。
③配置PWM輸出通道
④啟用CCR1上的TIMx外設預加載寄存器
⑤使能定時器 特別特別注意下面這個函數 //啟用CCR1上的TIMx外設預加載寄存器。
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable); 進入到函數主題 void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload){ uint16_t tmpccmr1 = 0; /* Check the parameters */ assert_param(IS_TIM_LIST8_PERIPH(TIMx)); assert_param(IS_TIM_OCPRELOAD_STATE(TIM_OCPreload)); tmpccmr1 = TIMx->CCMR1; /* Reset the OC1PE Bit */ tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC1PE); /* Enable or Disable the Output Compare Preload feature */ tmpccmr1 |= TIM_OCPreload; /* Write to TIMx CCMR1 register */ TIMx->CCMR1 = tmpccmr1;}中文手冊中的寄存器說明


函數進來首先復位OC1PE位,之后再根據形參來決定該位置高還是置低。如果我們使能該位,則當我們在改變CCR1寄存器中的值的時候,他不會立即改變,而是將這個周期走完,下個周期才會更新CCR1 的值。 比如我們設定ARR=100,CCR1=50,累加器從0開始往上加,當OC1PE=0,我們在累加器加到30的時候突然改變CCR1的值,讓CCR1=70,此時當累加器加到50的時候,電平是不會跳變的,因為OC1PE=0時,改變CCR1則會立即生效,此時已經將CCR1=70了,因此達到70才會跳變。 當OC1PE=1時,他不會在這個周期生效,累加器還會在50的時候跳變,在下一個周期才會從70跳變。 因為我寫的這個程序中沒有改變CCR1的值,所以該行代碼寫不寫都沒有什么影響。只不過我寫的時候不明白這個函數是什么意思,就去搜索了一下,順便把他記錄下來。
|