文章目錄
- 輸出比較
- PWM
- 輸出比較通道
- 參數計算
- 舵機簡介
- 直流電機簡介
- TB6612
- PWM基本結構
- PWM驅動呼吸燈
- PWM驅動舵機
- PWM控制電機
輸出比較
輸出比較,簡稱OC(Output Compare)。
輸出比較的原理是,當定時器計數值與比較值相等或者滿足某種特定條件時,比較通道會產生一個輸出信號,這個輸出信號可以用來觸發外部事件,如控制其他外設的操作,或者驅動外部電路。
在每個高級定時器和通用定時器都擁有4個輸出比較通道。
高級定時器的前3個通道額外擁有死區生成和互補輸出的功能。
PWM
我們可以利用輸出比較來對外產生一個PWM頻率。
PWM(Pulse Width Modulation)脈沖寬度調制,是一種常用的控制信號技術。通過改變信號的脈沖寬度來控制電力開關裝置的平均功率。在PWM中,周期保持不變,而脈沖的寬度可以根據需要進行調整。
PWM技術廣泛應用于電力電子領域,特別是在電機控制和電源調節方面。通過調整PWM信號的占空比(脈沖寬度與周期之比),可以精確地控制輸出信號的平均電壓或電流。這種控制方式可以實現對電機速度、亮度、電壓等參數的精確控制,具有高效率、高精度和低成本的優點。
頻率=1/Ts;占空比=Ton/Ts;分辨率=占空比的變化步距。
我們可以利用輸出比較,對輸出電平進行一定程度的控制,就能輸出PWM頻率。
輸出比較通道
通用定時器總框圖。
放大效果:
在比較通道左邊,CNT計數器與捕獲/比較寄存器中的值進行比較大小,再根據控制器,就會輸出一定的電平。
ETRF是定時器的小功能,一般不用到。
REF(rreference)實際上就是指這里信號的高低電平。REF可以映射到主模式的TRGO輸出上去;REF的主要去向是去到輸出使能電路,它會先走向一個寄存器,如果寄存器輸出為0,那么電平將不翻轉,保持原樣;如果信號為1,REF會通向一個非門取反,也就是高低電平翻轉的信號;接著通過使能電路,將有一個寄存器(CC1E)控制;最后到OC1引腳,接到CH1通道上。
輸出模式控制器,可以根據自己需求來選擇模式:
參數計算
紅色線表示CCR,也就是比較值;黃色線表示自動裝載寄存器中的值(ARR);藍色線表示計數值CNT;
當CNT<CCR時,輸出高電平;當CNT>=CCR時,輸出低電平。
這里對應的是PWM模式1的向上計數。
PWM頻率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比: Duty = CCR / (ARR + 1)
PWM分辨率: Reso = 1 / (ARR + 1)
舵機簡介
舵機(Servo)是一種常用的電動執行器,通常用于控制機械運動和定位定位。它由一個直流電機、減速裝置、位置反饋裝置和控制電路組成。
舵機的工作原理是控制電路根據輸入信號生成特定的PWM信號,并驅動直流電機和減速裝置運轉,使輸出軸轉動到所需的位置。位置反饋裝置(常用的是旋轉式電位器)會實時監測輸出軸的位置,并將信息反饋給控制電路,以便進行修正和精確控制。
SG90使用要求:輸入PWM信號要求:周期為20ms,高電平寬度為0.5ms~2.5ms
硬件電路:
直流電機簡介
直流電機是一種將電能轉換為機械能的裝置,有兩個電極,當電極正接時,電機正轉,當電極反接時,電機反轉。
直流電機屬于大功率器件,GPIO口無法直接驅動,需要配合電機驅動電路來操作。
TB6612
TB6612是一款雙路H橋型的直流電機驅動芯片,常用于控制直流電機的轉動方向和速度。它具有高效率、低功耗和高輸出等特點,適用于各種電機驅動應用。
TB6612芯片內部集成了H橋驅動電路,可通過控制引腳實現正轉、反轉、制動和浮動等操作。它可以工作于3.3V或5V邏輯電平,支持PWM輸入控制電機速度,并提供過流保護功能,防止電機過載。
硬件電路:
PWM基本結構
通過配置時基單元,,讓計時器驅動與CCR比較,在PWM模式1下輸出電平最后通向GPIO口。
PWM驅動呼吸燈
接線模式:
PWM.h
#ifndef __PWM_H__
#define __PWM_H__void PWM_Init();
void PWM_SetCompare(uint16_t Compare);#endif
PWM.c
#include "stm32f10x.h" // Device headervoid PWM_Init()
{//開啟APB1外設開關RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //復用推挽輸出GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);//配置內部時鐘TIM2TIM_InternalClockConfig(TIM2);//時鐘結構體初始化TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //不分頻TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //計時器模式TIM_TimeBaseInitStructure.TIM_Period=100-1; //自動加載寄存器周期值TIM_TimeBaseInitStructure.TIM_Prescaler=1-1; //預分頻值TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; //指定重復計時器的值,這里不用到TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//配置輸出比較結構體TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; //配置輸出比較模式TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High; //指定輸出極性TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//輸出比較狀態TIM_OCInitStructure.TIM_Pulse=0; //指定要捕獲的脈沖值CCRTIM_OC1Init(TIM2,&TIM_OCInitStructure);//啟用TIM2外設控制TIM_Cmd(TIM2,ENABLE);}
//設置CCR比較值
void PWM_SetCompare(uint16_t Compare)
{TIM_SetCompare1(TIM2,Compare);
}
對于GPIO口來說,不止是接通了外設,還需要將PWM頻率傳輸給外設,所以使用了復用推挽輸出。
輸出比較結構體有多個成員,我們這里一些成員不用到,所以先進行結構體初始化,再進行對一些成員的賦值。
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "PWM.h"uint16_t i;int main()
{OLED_Init();PWM_Init();while(1){for(i=0;i<=100;i++){PWM_SetCompare(i);Delay_ms(10);}for(i=0;i<=100;i++){PWM_SetCompare(100-i);Delay_ms(10);}}
}
PWM驅動舵機
OLED函數所取地址
連接方式:
SYTM32驅動電壓只有3.3V,舵機需要5V的驅動電壓;
Servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"void Servo_Init()
{PWM_Init();
}void Servo_SetAngle(float Angle)
{PWM_SetCompare(Angle/180*200+50);
}
Servo.h
#ifndef __SERVO_H__
#define __SERVO_H__void Servo_Init();
void Servo_SetAngle(float Angle);#endif
這里的PWM頻率是有要求的,所以需要將PWM.c的CNT和ARR進行修改:頻率=72M/720/2000=50Hz。
這里的轉動度數需要根據占空比來進行計算:
Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"void Key_Init(void)
{//設置APB2外設時鐘開關RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//對結構體成員的選擇GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//結構體初始化GPIO_Init(GPIOB, &GPIO_InitStructure);
}//獲取鍵碼
uint8_t Key_GetNum(void)
{uint8_t KeyNum = 0;if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){Delay_ms(20);while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);Delay_ms(20);KeyNum = 1;}if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0){Delay_ms(20);while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);Delay_ms(20);KeyNum = 2;}return KeyNum;
}
Key.h
#ifndef __KEY_H_
#define __KEY_H_void Key_Init(void);
uint8_t Key_GetNum(void);#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint16_t angle;
int main()
{OLED_Init();Servo_Init();Key_Init();OLED_ShowString(1,1,"Angle:");while(1){if(Key_GetNum()==1){angle+=30;if(angle>180){angle=0;}}Servo_SetAngle(angle);OLED_ShowNum(1,7,angle,3);}}
PWM控制電機
接線方式:
Motor.h
#ifndef __MOTOR_H__
#define __MOTOR_H__void Motor_init();
void Motor_GetSpeed(int8_t Speed);#endif
Motor.c
#include "stm32f10x.h" // Device header
#include "PWM.h"void Motor_init()
{//設置APB2外設時鐘開關RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//對結構體成員的選擇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);PWM_Init();
}
//速度輸入函數
void Motor_GetSpeed(int8_t Speed)
{if(Speed>=0){GPIO_SetBits(GPIOA,GPIO_Pin_5);GPIO_ResetBits(GPIOA,GPIO_Pin_4);PWM_SetCompare(Speed);}else{GPIO_SetBits(GPIOA,GPIO_Pin_4);GPIO_ResetBits(GPIOA,GPIO_Pin_5);PWM_SetCompare(-Speed);}
}
PWM的占空比作為速度的調節,兩個接口的正反接作為方向的控制。
對于電機驅動電路所接引腳,需要進行GPIO口的初始化;
當輸入速度為負時,將接口上引腳進行電平翻轉,讀者可以進行嘗試,怎么接為正,怎么接為負;對于小于0的速度,需要加上符號變成正數。
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"
int8_t Speed;
int main()
{OLED_Init();Motor_init();Key_Init();OLED_ShowString(1,1,"Speed:");while(1){if(Key_GetNum()==1){Speed+=20;if(Speed>100){Speed=-100;}}Motor_GetSpeed(Speed);OLED_ShowSignedNum(1,7,Speed,3);}
}