STM32-OC輸出比較和PWM

本內容基于江協科技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個通道額外擁有死區生成和互補輸出的功能。
    image.png

1.2 PWM(脈沖寬度調制)

  • 在具有慣性的系統中,可以通過對一系列脈沖的寬度進行調制,來有效地獲得所需要的模擬參量,常應用于電機控速等領域。

  • PWM參數:
    頻率 = 1 / TS
    占空比 = TON / TS
    分辨率 = 占空比變化步距
    image.png
    高低電平跳變的數字信號可以等效為中間這個虛線所表示的模擬量,當上面電平時間長一點,下面電平短一點的時候,等效的模擬量就偏向于上面;當下面電平時間長一點,上面電平時間短一點的時候,等效的模擬量就偏向于下面。

  • 頻率
    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 輸出比較通道(高級)

image.png
image.png

1.4 輸出比較通道(通用)

image.png
image.png

  • 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;黃色線:ARR;紅色線:CCR;綠色線:輸出REF
不需要更新事件的中斷申請。在配置好時基單元后,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 參數計算

image.png
image.png
PWM頻率等于計數器的更新頻率

2. 舵機和直流電機

2.1 舵機簡介

  • 舵機是一種根據輸入PWM信號占空比來控制輸出角度的裝置;
  • 輸入PWM信號要求:周期為20ms,高電平寬度為0.5ms~2.5ms。

舵機內部是由直流電機驅動的,內部還有一個控制電路板,是一個電機的控制系統板。舵機內部執行邏輯:PWM信號輸入到控制板,給控制板一個指定的目標角度,電位器檢測輸出軸的當前角度,若大于目標角度,電機就會反轉,小于目標角度,電機正轉,最終使輸出軸固定在指定角度。
image.pngimage.png
這里的PWM波形(輸入信號脈沖寬度)是當作一個通信協議來使用的。

2.1.2 硬件電路

image.pngimage.png

2.2 直流電機及驅動簡介

  • 直流電機是一種將電能轉換為機械能的裝置,有兩個電極,當電極正接時,電機正轉,當電極反接時,電機反轉。
  • 直流電機屬于大功率器件,GPIO口無法直接驅動,需要配合電機驅動電路來操作。
  • TB6612是一款雙路H橋型的直流電機驅動芯片,可以驅動兩個直流電機并且控制其轉速和方向。里面一路有四個開關管,所以可以控制正反轉。

H橋電路由兩個推挽電路組成,中間接電機:左邊,上管導通,下管斷開,左邊輸出就是接在VM的電機電源正極;下管導通,上管斷開,就是接在PGND的電源負極。左上和右下導通,電流從左流向右邊;右上和左下導通,電流從右流向左邊。
H橋可以控制電流流過的方向,所以能控制電機正反轉。
image.png

2.3 電機驅動硬件電路

image.png
PWMA引腳要接PWM信號輸出端,其他兩個引腳AIN2和AIN1可以任意接兩個普通的GPIO口,這三個引腳給一個低功率的控制信號,驅動電路就會從VM汲取電流,來輸出到電機,這樣就能完成低功率的控制信號控制大功率設備的目的了,
image.png
當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的亮度。

image.png

3.2.2 代碼流程

  1. PWM初始化
    1. RCC開啟時鐘,TIM外設和GPIO外設時鐘打開;
    2. 配置時基單元,包括前面的時鐘源選擇;
    3. 配置輸出比較單元,里面包括CCR的值、輸出比較模式、極性選擇、輸出使能;
    4. 配置GPIO,把PWM對應的GPIO口初始化為復用推挽輸出的配置(使用TIM2定時器–輸出比較通道,所以GPIO配置為復用推挽輸出)
    5. 運行控制,啟動計數器,這樣就能輸出PWM了。
  2. ARR和PSC設置
    1. PSC = 720 - 1;ARR = 100 - 1。因此輸出的PWM波形頻率為1KHz。
    2. 占空比Duty = CCR / (ARR + 1) = CCR / 100
  3. main函數
    1. 通過更改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上顯示舵機角度。
image.png

3.3.2 代碼流程

  1. 開啟時鐘(TIM2和GPIOA)
  2. 配置GPIO
  3. 配置時鐘(時鐘源、時基單元(ARR=20000,PSC=72)、輸出比較單元、TIM2使能)
  4. 角度值轉換為占空比:
    周期:20ms,高電平:0.5ms ~2.5ms。
高電平角度占空比
0.5ms-90°500/20000
1ms-45°1000/20000
1.5ms1500/20000
2ms45°2000/20000
2.5ms45°2500/20000

轉換為:Angle / 180 * 2000 + 500

角度CCR
0500
1802500
  1. main函數
    1. 按鍵1按下,角度每次自增30,當角度超過180度后,角度值清零
    2. 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引腳。
image.png

3.4.2 代碼流程

  1. 開啟時鐘(TIM2和GPIOA)

  2. 配置GPIO

  3. 配置時鐘(時鐘源、時基單元、輸出比較單元、TIM2使能)

  4. 配置電機:
    a. 設置速度(范圍:-100~100):
    電機正轉,速度值大于0,PA4為高電平,PA5為低電平;
    電機反轉,速度值小于0,PA4為低電平,PA5為高電平。

  5. main函數

    1. 按鍵按下,速度值自增20
    2. 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顯示速度變量}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/42188.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/42188.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/42188.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MATLAB常用語句總結7

MATLAB總結7&#xff1a;常見錯誤歸納 本篇專門用于記錄一些應試技巧 文章目錄 MATLAB總結7&#xff1a;常見錯誤歸納前言一、一些小定義和小技巧二、蒙塔卡羅求解方法1.函數的定義2.函數引用3.代碼量較少的蒙塔卡羅 三、函數引用與多變量四、矩陣引用五、非線性函數&#xff…

14-39 劍和詩人13 - 頂級大模型測試分析和建議

????? 隨著對高級語言功能的需求不斷飆升&#xff0c;市場上涌現出大量語言模型&#xff0c;每種模型都擁有獨特的優勢和功能。然而&#xff0c;駕馭這個錯綜復雜的生態系統可能是一項艱巨的任務&#xff0c;開發人員和研究人員經常面臨選擇最適合其特定需求的模型的挑戰。…

哈弗架構和馮諾伊曼架構

文章目錄 1. 計算機體系結構 2. 哈弗架構&#xff08;Harvard Architecture&#xff09; 3. 改進的哈弗架構 4. 馮諾伊曼架構&#xff08;Von Neumann Architecture&#xff09; 5. 結構對比 1. 計算機體系結構 計算機體系結構是指計算機系統的組織和實現方式&#xff0c…

Python | Leetcode Python題解之第220題存在重復元素III

題目&#xff1a; 題解&#xff1a; class Solution(object):def containsNearbyAlmostDuplicate(self, nums, k, t):from sortedcontainers import SortedSetst SortedSet()left, right 0, 0res 0while right < len(nums):if right - left > k:st.remove(nums[left]…

Python基礎問題匯總

為什么學習Python&#xff1f; 易學易用&#xff1a;Python語法簡潔清晰&#xff0c;易于學習。廣泛的應用領域&#xff1a;適用于Web開發、數據科學、人工智能、自動化腳本等多種場景。強大的庫支持&#xff1a;擁有豐富的第三方庫&#xff0c;如NumPy、Pandas、TensorFlow等…

Sass 語法

文章目錄 編譯變量 \$嵌套 {} > \~導入 import注釋 // /*\* \**/混入 mixin/include繼承 extend數據類型運算控制 if/for/each/while函數 function媒體查詢 media根發出 at-root警告warn/錯誤error/調試debug 編譯 編譯命令 單文件轉換命令 sass input.scss output.css單…

數學基礎 -- 反函數

反函數技術文檔 反函數的定義 反函數&#xff08;inverse function&#xff09;是指一種將函數的輸出反過來作為輸入&#xff0c;從而恢復原來輸入的函數。具體來說&#xff0c;如果有一個函數 f f f&#xff0c;它把一個值 x x x 映射到一個值 y y y&#xff0c;即 f ( …

68.WEB滲透測試-信息收集- WAF、框架組件識別(8)

免責聲明&#xff1a;內容僅供學習參考&#xff0c;請合法利用知識&#xff0c;禁止進行違法犯罪活動&#xff01; 內容參考于&#xff1a; 易錦網校會員專享課 上一個內容&#xff1a;67.WEB滲透測試-信息收集- WAF、框架組件識別&#xff08;7&#xff09; 右邊這些是waf的…

Mean teacher are better role models-論文筆記

論文筆記 資料 1.代碼地址 2.論文地址 https://arxiv.org/pdf/1703.01780 3.數據集地址 CIFAR-10 https://www.cs.utoronto.ca/~kriz/cifar.html 論文摘要的翻譯 最近提出的Temporal Ensembling方法在幾個半監督學習基準中取得了最先進的結果。它維護每個訓練樣本的標簽…

PCIe驅動開發(1)— 開發環境搭建

PCIe驅動開發&#xff08;1&#xff09;— 開發環境搭建 一、前言 二、Ubuntu安裝 參考: VMware下Ubuntu18.04虛擬機的安裝 三、QEMU安裝 下載網站&#xff1a; https://download.qemu.org 下載文件&#xff1a;qemu-4.1.0-rc5.tar.xz 使用如下命令解壓&#xff1a; tar …

opencv 設置超時時間

經常爬視頻數據&#xff0c;然后用opencv做成圖片 因此設置超時時間很重要 cap.set(cv2.CAP_PROP_FPS, timeout_ms) for idx, row in data.iterrows(): if idx < 400: continue try: # 打開視頻文件 timeout_ms 5000 cap cv2.VideoCapture(row[PLAY_URL]) cap.set(cv2.C…

Linux下使用libiw進行無線信號掃描的實例

打開電腦連接wifi是一件很平常的事情,但這些事情通常都是操作系統下的wifi管理程序替我們完成的,如何在程序中掃描wifi信號其實資料并不多,前面已經有兩篇文章介紹了如何使用ioctl()掃描wifi信號,但其實在Linux下有一個簡單的庫對這些ioctl()的操作進行了封裝,這個庫就是l…

深入追蹤:IPython 中 %tb 命令的異常追蹤棧使用指南

深入追蹤&#xff1a;IPython 中 %tb 命令的異常追蹤棧使用指南 在 IPython 的強大功能中&#xff0c;%tb 命令是一個調試工具&#xff0c;用于在出現異常時查看詳細的異常追蹤棧信息。這對于開發者來說是一個不可或缺的功能&#xff0c;因為它提供了對錯誤發生上下文的深入了…

Unity 中,常用的 UnityEngine.Events 中的幾個重要的事件處理函數

在 Unity 中&#xff0c;常用的 UnityEngine.Events 中的幾個重要的事件處理函數包括&#xff1a; UnityEvent UnityEvent 是 Unity 提供的一種事件系統&#xff0c;可以用來實現腳本與場景中的對象之間的互動。它可以用來定義和響應事件&#xff0c;如按鈕點擊、物體碰撞等。示…

GPT-5或重塑我們的工作與生活

引言 在人工智能發展的浪潮中&#xff0c;每一次技術的革新都如同潮水般涌來&#xff0c;帶來前所未有的機遇與挑戰。當新一代大語言模型GPT-5即將登場的消息傳來&#xff0c;我們不禁要問&#xff1a;它將如何重塑我們的工作和日常生活&#xff1f;又將開啟哪些嶄新的應用場景…

故障模式與影響分析(FMEA)的概念

故障模式與影響分析&#xff08;FMEA&#xff09;的概念 故障模式與影響分析&#xff08;Failure Mode and Effects Analysis&#xff0c;FMEA&#xff09;是一種系統性評估方法&#xff0c;用于識別產品設計或過程中可能發生的潛在故障模式&#xff0c;以及這些故障模式對系統…

制作爬取4399游戲名稱軟件

def 爬取4399(): #發送請求并且拿到源代碼 import requests 鏈接https://www.4399.com/ #網站鏈接 請求頭{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0} #構造請求頭用于爬取網站源代碼使用 網站源代碼…

MySQL遠程登錄

root是超級管理員&#xff0c;默認情況下&#xff0c;root不能作為遠程登錄的用戶名&#xff0c;遠程登錄前&#xff0c;需要將登錄的數據庫在本地登錄&#xff0c;修改權限&#xff0c;輸入&#xff1a; update user set host & where user root ; 回車鍵&#xff0c…

clickhouse高可用可拓展部署

clickhouse高可用&可拓展部署 1.部署架構 1.1高可用架構 1.2硬件資源 部署服務 節點名稱 節點ip 核數 內存 磁盤 zookeeper zk-01 / 4c 8G 100G zk-02 / 4c 8G 100G zk-03 / 4c 8G 100G clikehouse ck-01 / 32c 128G 2T ck-02 / 32c 128G 2T ck-03 / 32c 128G 2T ck-04 /…

[Qt] 控件的QSizePolicy屬性選項

在Qt中&#xff0c;QSizePolicy是一個非常重要的枚舉類&#xff0c;它定義了控件&#xff08;widgets&#xff09;在布局管理&#xff08;layout management&#xff09;中的大小調整策略。這些策略決定了當控件的父布局或窗口大小發生變化時&#xff0c;控件應該如何調整自身的…