本內容基于江協科技STM32視頻內容,整理而得。
文章目錄
- 1. OC輸出比較和PWM
- 1.1 OC輸出比較
- 1.2 PWM(脈沖寬度調制)
- 1.3 輸出比較通道(高級)
- 1.4 輸出比較通道(通用)
- 1.5 輸出比較模式
- 1.6 PWM基本結構
- 1.7 參數計算
- 2. 舵機和直流電機
- 2.1 舵機簡介
- 2.1.2 硬件電路
- 2.2 直流電機及驅動簡介
- 2.3 電機驅動硬件電路
- 3. 輸出比較庫函數及代碼
- 3.1 輸出比較庫函數
- 3.2 PWM驅動LED呼吸燈
- 3.2.1 硬件連接
- 3.2.2 代碼流程
- 3.2.3 代碼
- 3.3 PWM驅動舵機
- 3.3.1 硬件連接
- 3.3.2 代碼流程
- 3.3.3 代碼
- 3.4 PWM驅動直流電機
- 3.4.1 硬件連接
- 3.4.2 代碼流程
- 3.4.3 代碼
1. OC輸出比較和PWM
1.1 OC輸出比較
- 輸出比較可以通過比較CNT與CCR(捕獲/比較寄存器)寄存器值的關系,來對輸出電平進行置1、置0或翻轉的操作,用于輸出一定頻率和占空比的PWM波形
- 每個高級定時器和通用定時器都擁有4個輸出比較通道
- 高級定時器的前3個通道額外擁有死區生成和互補輸出的功能。
1.2 PWM(脈沖寬度調制)
-
在具有慣性的系統中,可以通過對一系列脈沖的寬度進行調制,來有效地獲得所需要的模擬參量,常應用于電機控速等領域。
-
PWM參數:
頻率 = 1 / TS
占空比 = TON / TS
分辨率 = 占空比變化步距
高低電平跳變的數字信號可以等效為中間這個虛線所表示的模擬量,當上面電平時間長一點,下面電平短一點的時候,等效的模擬量就偏向于上面;當下面電平時間長一點,上面電平時間短一點的時候,等效的模擬量就偏向于下面。 -
頻率
TS代表一個高低電平變換周期的時間,周期的倒數就是頻率,PWM頻率越快,那它等效模擬的信號就越平穩,不過同時性能開銷就越大,一般來說PWM的頻率都在幾K到幾十KHz,這個頻率就已經足夠快了。 -
占空比
占空比決定了PWM等效出來的模擬電壓的大小,占空比越大,等效的模擬電壓就越趨近于高電平;占空比越小,等效的模擬電壓就越趨近于低電平,等效關系一般來說是線性的。 -
分辨率
分辨率:比如有的占空比只能是1%、2%、3%等等這樣以1%的步距跳變,那分辨率就是1%。如果可以是1.1%、1.2%、1.3%等等這樣以0.1%的步距跳變,那分辨率就是0.1%。分辨率就是占空比變化的精細程度。這個分辨率需要多高,得看實際項目的需求,如果即要高頻率,又要高分辨率,這就對硬件電路要求比較高。如果要求不高的話,1%的分辨率也就足夠使用了。
使用PWM波形,可以在數字系統等效輸出模擬量,就能實現LED控制亮度、電機控速等功能。
1.3 輸出比較通道(高級)
1.4 輸出比較通道(通用)
- ETRF輸入:是定時器的一個小功能,一般不用。
- CCR1:捕獲/比較寄存器1;
- CCER:捕獲/比較使能寄存器;
- CCMR1:捕獲/比較模式寄存器1;
- 左邊是CNT計數器和CCR1(第一路捕獲/比較寄存器),當CNT>CCR1或CNT=CCR1時,就會給輸出模式控制器傳一個信號,輸出模式控制器就會改變它輸出OC1 REF(ref是指參考信號)的高低電平。
- REF信號可以往上前往主模式控制器,可以把REF映射到主模式的TRGO輸出上;
- REF信號往下走就到達極性選擇,主要走下面一條路:給寄存器CCER的CC1P位寫0,信號往上走,就是信號電平不翻轉;寄存器寫1,信號往下走,就是信號通過一個非門取反,則輸出信號就是輸入信號高低電平反轉的信號。這就是極性選擇:就是選擇是不是要把高低電平反轉一下。
- 輸出使能電路:選擇要不要輸出;
- OC1引腳:是CH1通道的引腳。
1.5 輸出比較模式
就是輸出模式控制器里面執行的邏輯。模式控制器的輸入是CNT和CCR的大小關系,輸出是REF的高低電平,
模式 | 描述 |
---|---|
凍結 | CNT=CCR時,REF保持為原狀態 |
匹配時置有效電平 | CNT=CCR時,REF置有效電平 |
匹配時置無效電平 | CNT=CCR時,REF置無效電平 |
匹配時電平翻轉 | CNT=CCR時,REF電平翻轉 |
強制為無效電平 | CNT與CCR無效,REF強制為無效電平 |
強制為有效電平 | CNT與CCR無效,REF強制為有效電平 |
PWM模式1 | 向上計數: CNT < CCR時,REF置有效電平,CNT ≥ CCR時,REF置無效電平 向下計數:CNT > CCR時,REF置無效電平,CNT ≤ CCR時,REF置有效電平 |
PWM模式2 | 向上計數: CNT < CCR時,REF置無效電平,CNT ≥ CCR時,REF置有效電平 向下計數:CNT > CCR時,REF置有效電平,CNT ≤ CCR時,REF置無效電平 |
-
凍結
當正在輸出PWM波,突然想暫停一會輸出,可以設置為該模式。當切換為凍結模式后,輸出就暫停了,并且高低電平也維持為暫停時刻的狀態,保持不變。 -
匹配時模式
- 有效電平和無效電平是高級定時器里的說法,是和關斷、剎車這些功能配合的。置有效電平就是置高電平,置無效電平就是置低電平。這三個模式都是當CNT和CCR值相等時,執行操作。這些模式就是可以用做波形輸出。
- 匹配時電平翻轉:可以輸出一個頻率可調,占空比始終為50%的PWM波形。比如設置CCR為0,那CNT每次更新清0時,就會產生一次CNT=CCR的事件,這就會導致輸出電平翻轉一次,每更新兩次,輸出為一個周期。并且高電平和低電平的時間始終是相等的,也就是占空比始終為50%。當改變定時器更新頻率時,輸出波形的頻率也會隨之改變,輸出波形的頻率=更新頻率/2,因為更新兩次才為一個周期。
-
強制模式
如果想暫停波形輸出,并且在暫停期間保持低電平或高電平,則可以設置為強制模式。 -
PWM模式1和PWM模式2
PWM1和PWM2可以用于輸出頻率和占空比都可調的PWM波形。
PWM模式2實際上就是PWM模式1輸出的取反。
1.6 PWM基本結構
不需要更新事件的中斷申請。在配置好時基單元后,CNT就可以開始不斷地自增運行了,CCR設置的高,則占空比大;設置的低,占空比小。
- 輸出比較單元的最開始是CCR捕獲/比較寄存器。
- 輸出模式控制單元里是PWM模式1的執行邏輯。
- REF是一個頻率可調,占空比可調的PWM波形。再通過極性選擇,輸出使能,最終通向GPIO口。這樣就能完成PWM波形的輸出了。
- 藍色CNT從0開始自增,一直到黃色ARR,也就是99,之后清0繼續自增,紅色線CCR=30,綠色是輸出REF,CNT < CCR置高電平,CNT > CCR置低電平。當CNT溢出清0后,CNT又小于CCR,所以置高電平。這樣下去,REF電平就不斷變化,并且它的占空比是受CCR值的調控的,如果CCR設置高一點,輸出的占空比就變大;CCR設置低一些,輸出的占空比就變小。
1.7 參數計算
PWM頻率等于計數器的更新頻率
2. 舵機和直流電機
2.1 舵機簡介
- 舵機是一種根據輸入PWM信號占空比來控制輸出角度的裝置;
- 輸入PWM信號要求:周期為20ms,高電平寬度為0.5ms~2.5ms。
舵機內部是由直流電機驅動的,內部還有一個控制電路板,是一個電機的控制系統板。舵機內部執行邏輯:PWM信號輸入到控制板,給控制板一個指定的目標角度,電位器檢測輸出軸的當前角度,若大于目標角度,電機就會反轉,小于目標角度,電機正轉,最終使輸出軸固定在指定角度。
這里的PWM波形(輸入信號脈沖寬度)是當作一個通信協議來使用的。
2.1.2 硬件電路
2.2 直流電機及驅動簡介
- 直流電機是一種將電能轉換為機械能的裝置,有兩個電極,當電極正接時,電機正轉,當電極反接時,電機反轉。
- 直流電機屬于大功率器件,GPIO口無法直接驅動,需要配合電機驅動電路來操作。
- TB6612是一款雙路H橋型的直流電機驅動芯片,可以驅動兩個直流電機并且控制其轉速和方向。里面一路有四個開關管,所以可以控制正反轉。
H橋電路由兩個推挽電路組成,中間接電機:左邊,上管導通,下管斷開,左邊輸出就是接在VM的電機電源正極;下管導通,上管斷開,就是接在PGND的電源負極。左上和右下導通,電流從左流向右邊;右上和左下導通,電流從右流向左邊。
H橋可以控制電流流過的方向,所以能控制電機正反轉。
2.3 電機驅動硬件電路
PWMA引腳要接PWM信號輸出端,其他兩個引腳AIN2和AIN1可以任意接兩個普通的GPIO口,這三個引腳給一個低功率的控制信號,驅動電路就會從VM汲取電流,來輸出到電機,這樣就能完成低功率的控制信號控制大功率設備的目的了,
當IN1和IN2都為高電平,兩個輸出沒有電壓差,電機是不會轉的。當IN1和IN2都為低電平,兩個輸出直接關閉,電機也是不會轉的。
IN1低電平,IN2高電平,電機處于反轉狀態,但轉與不轉取決于PWM,PWM給高電平,那輸出就是一低一高,有電壓差了,就可以轉,定義為反轉;如果PWM給低電平,那輸出兩個低電平,電機還是不轉。如果PWM是一個不斷翻轉的信號,那電機就是快速地反轉、停止、反轉、停止,如果PWM頻率足夠快,那電機就可以連續穩定地反轉了,并且速度取決于PWM信號的占空比。這里的PWM就是使用PWM來等效一個模擬量的功能。
3. 輸出比較庫函數及代碼
3.1 輸出比較庫函數
// stm32f10x_tim.h
// 用結構體來初始化輸出比較單元的。配置輸出比較,參數1(TIMx):選擇定時器,參數2:輸出比較的參數
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);// 用來給輸出比較結構體賦一個默認值的
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);// 配置強制輸出模式
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);// 用來配置CCR寄存器的預裝功能的,即寫入的值不會立即生效,而是在更新事件才會生效
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);// 用來配置快速使能的
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);// 外部事件時清除REF信號
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);// 更改輸出極性,帶N的就是高級定時器里互補通道的配置,
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);// 用來單獨修改輸出使能參數
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);// 用來單獨更改輸出比較模式的函數
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);// 用來單獨更改CCR寄存器值的函數,用于更改占空比
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
引腳重映射:TIM2_CH1能從PA0映射到PA15,實現這一功能需要使用AFIO,則就要開啟AFIO時鐘。
// 引腳重映射配置
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
3.2 PWM驅動LED呼吸燈
3.2.1 硬件連接
LED的正極連接在PA0引腳。
- 實現功能:通過更改CCR的值來更改占空比。(占空比決定了PWM等效出來的模擬電壓的大小,占空比越大,等效的模擬電壓就越趨近于高電平;占空比越小,等效的模擬電壓就越趨近于低電平,等效關系一般來說是線性的。)因此通過調節占空比的大小可以控制PA0引腳的電平,控制LED的亮度。
3.2.2 代碼流程
- PWM初始化
- RCC開啟時鐘,TIM外設和GPIO外設時鐘打開;
- 配置時基單元,包括前面的時鐘源選擇;
- 配置輸出比較單元,里面包括CCR的值、輸出比較模式、極性選擇、輸出使能;
- 配置GPIO,把PWM對應的GPIO口初始化為復用推挽輸出的配置(使用TIM2定時器–輸出比較通道,所以GPIO配置為復用推挽輸出)
- 運行控制,啟動計數器,這樣就能輸出PWM了。
- ARR和PSC設置
- PSC = 720 - 1;ARR = 100 - 1。因此輸出的PWM波形頻率為1KHz。
- 占空比Duty = CCR / (ARR + 1) = CCR / 100
- main函數
- 通過更改CCR的值(調用TIM_SetCompare1函數)來調節占空比的值。
3.2.3 代碼
- PWM.c
#include "stm32f10x.h" // Device header/*** 函 數:PWM初始化* 參 數:無* 返 回 值:無*/
void PWM_Init(void)
{/*開啟時鐘 -- TIM2_CH1_ETR在PA0引腳*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //開啟TIM2的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘/*GPIO重映射*/
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //開啟AFIO的時鐘,重映射必須先開啟AFIO的時鐘
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //將TIM2的引腳部分重映射,具體的映射方案需查看參考手冊
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //將JTAG引腳失能,作為普通GPIO引腳使用/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //將PA0引腳初始化為復用推挽輸出 //受外設控制的引腳,均需要配置為復用模式 /*配置時鐘源*/TIM_InternalClockConfig(TIM2); //選擇TIM2為內部時鐘,若不調用此函數,TIM默認也為內部時鐘/*時基單元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定義結構體變量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分頻,選擇不分頻,此參數用于配置濾波器時鐘,不影響時基單元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數器模式,選擇向上計數TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //計數周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //預分頻器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重復計數器,高級定時器才會用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //將結構體變量交給TIM_TimeBaseInit,配置TIM2的時基單元/*輸出比較初始化*/TIM_OCInitTypeDef TIM_OCInitStructure; //定義結構體變量TIM_OCStructInit(&TIM_OCInitStructure); //結構體初始化,若結構體沒有完整賦值//則最好執行此函數,給結構體所有成員都賦一個默認值//避免結構體初值不確定的問題TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //輸出比較模式,選擇PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性,選擇為高,若選擇極性為低,則輸出高低電平取反TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //輸出使能TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值TIM_OC1Init(TIM2, &TIM_OCInitStructure); //將結構體變量交給TIM_OC1Init,配置TIM2的輸出比較通道1/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定時器開始運行
}/*** 函 數:PWM設置CCR* 參 數:Compare 要寫入的CCR的值,范圍:0~100* 返 回 值:無* 注意事項:CCR和ARR共同決定占空比,此函數僅設置CCR的值,并不直接是占空比* 占空比Duty = CCR / (ARR + 1)*/
void PWM_SetCompare1(uint16_t Compare)
{TIM_SetCompare1(TIM2, Compare); //設置CCR1的值
}
- main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"uint8_t i; //定義for循環的變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化PWM_Init(); //PWM初始化while (1){for (i = 0; i <= 100; i++){PWM_SetCompare1(i); //依次將定時器的CCR寄存器設置為0~100,PWM占空比逐漸增大,LED逐漸變亮Delay_ms(10); //延時10ms}for (i = 0; i <= 100; i++){PWM_SetCompare1(100 - i); //依次將定時器的CCR寄存器設置為100~0,PWM占空比逐漸減小,LED逐漸變暗Delay_ms(10); //延時10ms}}
}
3.3 PWM驅動舵機
3.3.1 硬件連接
實現功能:通過設置占空比來控制舵機的角度,并在OLED上顯示舵機角度。
3.3.2 代碼流程
- 開啟時鐘(TIM2和GPIOA)
- 配置GPIO
- 配置時鐘(時鐘源、時基單元(ARR=20000,PSC=72)、輸出比較單元、TIM2使能)
- 角度值轉換為占空比:
周期:20ms,高電平:0.5ms ~2.5ms。
高電平 | 角度 | 占空比 |
---|---|---|
0.5ms | -90° | 500/20000 |
1ms | -45° | 1000/20000 |
1.5ms | 0° | 1500/20000 |
2ms | 45° | 2000/20000 |
2.5ms | 45° | 2500/20000 |
轉換為:Angle / 180 * 2000 + 500
角度 | CCR |
---|---|
0 | 500 |
180 | 2500 |
- main函數
- 按鍵1按下,角度每次自增30,當角度超過180度后,角度值清零
- OLED顯示角度
3.3.3 代碼
- PWM.c
#include "stm32f10x.h" // Device header/*** 函 數:PWM初始化* 參 數:無* 返 回 值:無*/
void PWM_Init(void)
{/*開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //開啟TIM2的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //將PA1引腳初始化為復用推挽輸出 //受外設控制的引腳,均需要配置為復用模式/*配置時鐘源*/TIM_InternalClockConfig(TIM2); //選擇TIM2為內部時鐘,若不調用此函數,TIM默認也為內部時鐘/*時基單元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定義結構體變量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分頻,選擇不分頻,此參數用于配置濾波器時鐘,不影響時基單元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數器模式,選擇向上計數TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; //計數周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //預分頻器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重復計數器,高級定時器才會用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //將結構體變量交給TIM_TimeBaseInit,配置TIM2的時基單元/*輸出比較初始化*/ TIM_OCInitTypeDef TIM_OCInitStructure; //定義結構體變量TIM_OCStructInit(&TIM_OCInitStructure); //結構體初始化,若結構體沒有完整賦值//則最好執行此函數,給結構體所有成員都賦一個默認值//避免結構體初值不確定的問題TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //輸出比較模式,選擇PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性,選擇為高,若選擇極性為低,則輸出高低電平取反TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //輸出使能TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值TIM_OC2Init(TIM2, &TIM_OCInitStructure); //將結構體變量交給TIM_OC2Init,配置TIM2的輸出比較通道2/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定時器開始運行
}/*** 函 數:PWM設置CCR* 參 數:Compare 要寫入的CCR的值,范圍:0~100* 返 回 值:無* 注意事項:CCR和ARR共同決定占空比,此函數僅設置CCR的值,并不直接是占空比* 占空比Duty = CCR / (ARR + 1)*/
void PWM_SetCompare2(uint16_t Compare)
{TIM_SetCompare2(TIM2, Compare); //設置CCR2的值
}
- Servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"/*** 函 數:舵機初始化* 參 數:無* 返 回 值:無*/
void Servo_Init(void)
{PWM_Init(); //初始化舵機的底層PWM
}/*** 函 數:舵機設置角度* 參 數:Angle 要設置的舵機角度,范圍:0~180* 返 回 值:無*/
void Servo_SetAngle(float Angle)
{PWM_SetCompare2(Angle / 180 * 2000 + 500); //設置占空比//將角度線性變換,對應到舵機要求的占空比范圍上
}
- main.c
#include "OLED.h"
#include "Servo.h"
#include "Key.h"uint8_t KeyNum; //定義用于接收鍵碼的變量
float Angle; //定義角度變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化Servo_Init(); //舵機初始化Key_Init(); //按鍵初始化/*顯示靜態字符串*/OLED_ShowString(1, 1, "Angle:"); //1行1列顯示字符串Angle:while (1){KeyNum = Key_GetNum(); //獲取按鍵鍵碼if (KeyNum == 1) //按鍵1按下{Angle += 30; //角度變量自增30if (Angle > 180) //角度變量超過180后{Angle = 0; //角度變量歸零}}Servo_SetAngle(Angle); //設置舵機的角度為角度變量OLED_ShowNum(1, 7, Angle, 3); //OLED顯示角度變量}
}
3.4 PWM驅動直流電機
3.4.1 硬件連接
實現功能:通過占空比控制直流電機的速度
電機驅動模塊的PWMA接在PA2引腳(TIM2_CH3),AIN1接在PA4引腳,AIN2接在PA5引腳。
3.4.2 代碼流程
-
開啟時鐘(TIM2和GPIOA)
-
配置GPIO
-
配置時鐘(時鐘源、時基單元、輸出比較單元、TIM2使能)
-
配置電機:
a. 設置速度(范圍:-100~100):
電機正轉,速度值大于0,PA4為高電平,PA5為低電平;
電機反轉,速度值小于0,PA4為低電平,PA5為高電平。 -
main函數
- 按鍵按下,速度值自增20
- OLED顯示速度
3.4.3 代碼
- PWM.c
#include "stm32f10x.h" // Device header/*** 函 數:PWM初始化* 參 數:無* 返 回 值:無*/
void PWM_Init(void)
{/*開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //開啟TIM2的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //將PA2引腳初始化為復用推挽輸出 //受外設控制的引腳,均需要配置為復用模式/*配置時鐘源*/TIM_InternalClockConfig(TIM2); //選擇TIM2為內部時鐘,若不調用此函數,TIM默認也為內部時鐘/*時基單元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定義結構體變量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分頻,選擇不分頻,此參數用于配置濾波器時鐘,不影響時基單元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數器模式,選擇向上計數TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //計數周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //預分頻器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重復計數器,高級定時器才會用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //將結構體變量交給TIM_TimeBaseInit,配置TIM2的時基單元/*輸出比較初始化*/ TIM_OCInitTypeDef TIM_OCInitStructure; //定義結構體變量TIM_OCStructInit(&TIM_OCInitStructure); //結構體初始化,若結構體沒有完整賦值//則最好執行此函數,給結構體所有成員都賦一個默認值//避免結構體初值不確定的問題TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //輸出比較模式,選擇PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性,選擇為高,若選擇極性為低,則輸出高低電平取反TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //輸出使能TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值TIM_OC3Init(TIM2, &TIM_OCInitStructure); //將結構體變量交給TIM_OC3Init,配置TIM2的輸出比較通道3/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定時器開始運行
}/*** 函 數:PWM設置CCR* 參 數:Compare 要寫入的CCR的值,范圍:0~100* 返 回 值:無* 注意事項:CCR和ARR共同決定占空比,此函數僅設置CCR的值,并不直接是占空比* 占空比Duty = CCR / (ARR + 1)*/
void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare3(TIM2, Compare); //設置CCR3的值
}
- Motor.c
#include "stm32f10x.h" // Device header
#include "PWM.h"/*** 函 數:直流電機初始化* 參 數:無* 返 回 值:無*/
void Motor_Init(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //將PA4和PA5引腳初始化為推挽輸出 PWM_Init(); //初始化直流電機的底層PWM
}/*** 函 數:直流電機設置速度* 參 數:Speed 要設置的速度,范圍:-100~100* 返 回 值:無*/
void Motor_SetSpeed(int8_t Speed)
{if (Speed >= 0) //如果設置正轉的速度值{GPIO_SetBits(GPIOA, GPIO_Pin_4); //PA4置高電平GPIO_ResetBits(GPIOA, GPIO_Pin_5); //PA5置低電平,設置方向為正轉PWM_SetCompare3(Speed); //PWM設置為速度值}else //否則,即設置反轉的速度值{GPIO_ResetBits(GPIOA, GPIO_Pin_4); //PA4置低電平GPIO_SetBits(GPIOA, GPIO_Pin_5); //PA5置高電平,設置方向為反轉PWM_SetCompare3(-Speed); //PWM設置為負的速度值,因為此時速度值為負數,而PWM只能給正數}
}
- main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"uint8_t KeyNum; //定義用于接收按鍵鍵碼的變量
int8_t Speed; //定義速度變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化Motor_Init(); //直流電機初始化Key_Init(); //按鍵初始化/*顯示靜態字符串*/OLED_ShowString(1, 1, "Speed:"); //1行1列顯示字符串Speed:while (1){KeyNum = Key_GetNum(); //獲取按鍵鍵碼if (KeyNum == 1) //按鍵1按下{Speed += 20; //速度變量自增20if (Speed > 100) //速度變量超過100后{Speed = -100; //速度變量變為-100//此操作會讓電機旋轉方向突然改變,可能會因供電不足而導致單片機復位//若出現了此現象,則應避免使用這樣的操作}}Motor_SetSpeed(Speed); //設置直流電機的速度為速度變量OLED_ShowSignedNum(1, 7, Speed, 3); //OLED顯示速度變量}
}