找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 3256|回復(fù): 3
打印 上一主題 下一主題
收起左側(cè)

零基礎(chǔ)制作平衡小車【連載】3---STM32生成PWM控制電機(jī)旋轉(zhuǎn)

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:223481 發(fā)表于 2020-11-27 14:12 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式

制作平衡小車肯定會用到電機(jī),那么怎么控制呢?最簡單的就是直接加電壓,這樣電機(jī)就能轉(zhuǎn)動,但是至于轉(zhuǎn)多少圈,轉(zhuǎn)的快慢是不能控制的。這就不符合我們平衡小車的控制要求。這就需要用到PWM模式來控制電壓的大小,從而控制轉(zhuǎn)的快慢。至于轉(zhuǎn)了多少圈,就要用到編碼器了。今天先來看看怎么控制小車轉(zhuǎn)的快慢吧,一步一步來。

直流有刷電機(jī)的原理

簡單說下有刷電機(jī)的工作原理,其實(shí)很簡單,稍微懂點(diǎn)物理的就能看懂吧。

上面這張圖應(yīng)該都見過吧(因?yàn)槟悻F(xiàn)在就在看著它呢,手動滑稽)。小時(shí)候四驅(qū)賽車的那種黃色小馬達(dá)也都見過吧,是不是只要將電壓加載電刷的兩極上,電機(jī)就會轉(zhuǎn)了。那個(gè)電機(jī)的內(nèi)部結(jié)構(gòu)就是上圖這樣。這一種里面帶有電刷的電機(jī)叫有刷電機(jī),又因?yàn)槭怯玫闹绷麟,所以叫直流有刷電機(jī)。
我們只需要將電壓加到AB端,電機(jī)就能轉(zhuǎn),電機(jī)轉(zhuǎn)動的速度和電壓有關(guān),電壓越大,轉(zhuǎn)速越高。具體電機(jī)旋轉(zhuǎn)不懂的百度吧。我就不搬運(yùn)了,重點(diǎn)是都后面的PWM。

有一點(diǎn)需要注意的是,直流有刷電機(jī)的驅(qū)動頻率的問題,過高或者過低都不好,網(wǎng)上找了資料說是根據(jù)電機(jī)的不同頻率有所不同,一般10k-20k。我暫且信了吧。

PWM是什么?

既然知道了電壓和轉(zhuǎn)速成正比,那我們是不是只需要控制電壓就能控制轉(zhuǎn)速了。這就需要用到PWM技術(shù)了。PWM又名脈沖寬度調(diào)制,從名字上來看是調(diào)節(jié)脈沖寬度的。到底是怎么調(diào)節(jié)的呢?我是這樣理解的。
本來我們直接給電壓,電機(jī)就會轉(zhuǎn),現(xiàn)在我們通過控制電壓,讓他一會有輸出,一會不輸出。假設(shè)占空比為50%,也就是說,在一定時(shí)間內(nèi),電壓有一半的時(shí)間是閉合的,一半的時(shí)間是打開的,這樣求出來的平均電壓值應(yīng)該是原電壓值的一半。(不相信的同學(xué)可以一會試試,我后面把代碼傳上來)這樣我們就能控制電壓了。但是只控制電壓還不行呀,單片機(jī)引腳不能直接接到電機(jī)上呀,這就需要電機(jī)驅(qū)動器了。

TB6612直流有刷電機(jī)驅(qū)動


從淘寶截了一張圖,上面有引腳和使用說明,接線也很簡單。原理的話我沒細(xì)看,大致應(yīng)該是通過PWM來控制VM的電壓值,之后會有一個(gè)像H橋一樣的電路來控制正反轉(zhuǎn),當(dāng)然了都是集成到芯片內(nèi)部了。

STM32F103ZET6生成PWM代碼/*使用通用定時(shí)器2  通道1  PA0引腳輸出PWM------------ PWM信號 周期和占空比的計(jì)算 --------------- ARR :自動重裝載寄存器的值 CLK_cnt:計(jì)數(shù)器的時(shí)鐘,等于 Fck_int / (psc+1) = 72M/(psc+1) PWM 信號的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M 占空比P=CCR/(ARR+1) 時(shí)鐘分頻71,系統(tǒng)總線時(shí)鐘72M,可以算出定時(shí)器時(shí)鐘為72/(71+1)=1M在這里我們需要設(shè)定PWM頻率為10Khz=0.1ms,因此自動重裝載的值A(chǔ)RR=100.因?yàn)槎〞r(shí)器時(shí)鐘為1M=0.001ms,因此只需要累加100次,就等于0.1ms=10Khz了。占空比我們設(shè)定50%因此我們配置如下TIM_TimeBaseInitTypeStructure.TIM_Prescaler = 71;                   //定時(shí)器時(shí)鐘分頻TIM_TimeBaseInitTypeStructure.TIM_Period = (100 - 1);                //自動重裝載的值 TIM_OCInitTypeStructure.TIM_Pulse = 50;                              //設(shè)置占空比大小*/void PWM_Init(void){    //定義結(jié)構(gòu)體變量    GPIO_InitTypeDef GPIO_InitTypeStructure;    TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitTypeStructure;    TIM_OCInitTypeDef TIM_OCInitTypeStructure;        //使能引腳時(shí)鐘    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);    //使能引腳時(shí)鐘    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);            //引腳配置    GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //復(fù)用推挽輸出模式    GPIO_InitTypeStructure.GPIO_Pin = GPIO_Pin_0;           //PA0    GPIO_InitTypeStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度為50M    GPIO_Init(GPIOA, &GPIO_InitTypeStructure);              //寫入到寄存器中            //定時(shí)器基本配置    TIM_TimeBaseStructInit(&TIM_TimeBaseInitTypeStructure);                             //恢復(fù)默認(rèn)值    //自動重裝載寄存器的值,當(dāng)TIM_Period+1個(gè)頻率后產(chǎn)生一個(gè)更新或者中斷    TIM_TimeBaseInitTypeStructure.TIM_Period = (100 - 1);                               //自動重裝載的值    //驅(qū)動CNT計(jì)數(shù)器的時(shí)鐘 = Fck_int/(psc+1)    TIM_TimeBaseInitTypeStructure.TIM_Prescaler = 71;                                   //定時(shí)器時(shí)鐘分頻    //時(shí)鐘分頻因子 ,配置死區(qū)時(shí)間時(shí)需要用到    TIM_TimeBaseInitTypeStructure.TIM_ClockDivision = TIM_CKD_DIV1;                     //時(shí)鐘分割    //向上計(jì)數(shù)模式    TIM_TimeBaseInitTypeStructure.TIM_CounterMode = TIM_CounterMode_Up;                 //計(jì)數(shù)器模式    //重復(fù)計(jì)數(shù)器的值,沒用到不用管    TIM_TimeBaseInitTypeStructure.TIM_RepetitionCounter = 0;                            //重復(fù)計(jì)數(shù)器的值.    //寫入到寄存器    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitTypeStructure);            //PWM模式配置    TIM_OCStructInit(&TIM_OCInitTypeStructure);                         //恢復(fù)默認(rèn)值    TIM_OCInitTypeStructure.TIM_OCMode = TIM_OCMode_PWM1;               //選擇PWM1模式    TIM_OCInitTypeStructure.TIM_OCIdleState = TIM_OCIdleState_Set;      //空閑時(shí)間的引腳電平    TIM_OCInitTypeStructure.TIM_OCPolarity = TIM_OCPolarity_High;       //指定輸出極性    TIM_OCInitTypeStructure.TIM_OutputState = TIM_OutputState_Enable;   //輸出比較使能    TIM_OCInitTypeStructure.TIM_Pulse = 80;                             //設(shè)置占空比大小        //初始化通道1    TIM_OC1Init(TIM2, &TIM_OCInitTypeStructure);        //啟用CCR1上的TIMx外設(shè)預(yù)加載寄存器。    TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);        //使能定時(shí)器    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.當(dāng)占空比為50%時(shí)

測量電壓值

2.占空比為80%時(shí)

測量值

總結(jié)

1.PWM配置過程
①配置GPIO引腳,用的哪個(gè)引腳就配置哪個(gè)引腳,注意別忘了開啟時(shí)鐘使能
②配置完引腳,配置定時(shí)器基本參數(shù)
注意:
在配置TIM_Period的值的時(shí)候一定不要忘記最終計(jì)算的值是TIM_Period+1;
TIM_Prescaler在最終計(jì)算的值也是TIM_Prescaler+1。

TIM_TimeBaseStructInit(&TIM_TimeBaseInitTypeStructure); //恢復(fù)默認(rèn)值
上面這個(gè)函數(shù)可要可不要,規(guī)范化代碼最好是寫上去。
③配置PWM輸出通道
④啟用CCR1上的TIMx外設(shè)預(yù)加載寄存器
⑤使能定時(shí)器

特別特別注意

下面這個(gè)函數(shù)

//啟用CCR1上的TIMx外設(shè)預(yù)加載寄存器。
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);

進(jìn)入到函數(shù)主題

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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

中文手冊中的寄存器說明


函數(shù)進(jìn)來首先復(fù)位OC1PE位,之后再根據(jù)形參來決定該位置高還是置低。如果我們使能該位,則當(dāng)我們在改變CCR1寄存器中的值的時(shí)候,他不會立即改變,而是將這個(gè)周期走完,下個(gè)周期才會更新CCR1 的值。

比如我們設(shè)定ARR=100,CCR1=50,累加器從0開始往上加,當(dāng)OC1PE=0,我們在累加器加到30的時(shí)候突然改變CCR1的值,讓CCR1=70,此時(shí)當(dāng)累加器加到50的時(shí)候,電平是不會跳變的,因?yàn)镺C1PE=0時(shí),改變CCR1則會立即生效,此時(shí)已經(jīng)將CCR1=70了,因此達(dá)到70才會跳變。

當(dāng)OC1PE=1時(shí),他不會在這個(gè)周期生效,累加器還會在50的時(shí)候跳變,在下一個(gè)周期才會從70跳變。

因?yàn)槲覍懙倪@個(gè)程序中沒有改變CCR1的值,所以該行代碼寫不寫都沒有什么影響。只不過我寫的時(shí)候不明白這個(gè)函數(shù)是什么意思,就去搜索了一下,順便把他記錄下來。


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏2 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:223481 發(fā)表于 2020-11-27 14:13 | 只看該作者
原文鏈接:
https://blog.csdn.net/D_SEngineer?spm=1000.2115.3001.5113
去掉表情
回復(fù)

使用道具 舉報(bào)

板凳
ID:666561 發(fā)表于 2020-11-27 18:17 | 只看該作者
樓主加油,正好跟著學(xué)習(xí)
回復(fù)

使用道具 舉報(bào)

地板
ID:223481 發(fā)表于 2020-12-1 15:28 | 只看該作者
聽檐雨 發(fā)表于 2020-11-27 18:17
樓主加油,正好跟著學(xué)習(xí)

在這里發(fā)表代碼全亂了。
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表