一、引言
關(guān)于STM32兩輪平衡車的設(shè)計,我想在讀者閱讀本文之前應(yīng)該已經(jīng)有所了解,所以本文的重點是代碼的分享和分析。至于具體的原理,我覺得讀者不必閱讀長篇大論的文章,只需按照本文分享的代碼自己親手制作一輛平衡車,其原理并不言而喻了。源完整代碼工程在文章末尾百度網(wǎng)盤鏈接,請需要的讀者自行下載即可。
另外,由于平衡車的精髓在于PID算法的運用,有需要了解PID算法的讀者可以參考以下兩篇文章:
PID算法詳解(代碼詳解篇),位置式PID、增量式PID(通用)_pid 代碼-CSDN博客
PID算法詳解(精華知識匯總)_小小_掃地僧的博客-CSDN博客
二、所需材料
1、STM32F03C8T6
2、MPU6050
3、藍牙模塊
4、編碼電機
5、TB6612
6、電源+穩(wěn)壓模塊
7、OLED顯示模塊
三、接線強調(diào)
1、TB6612接線
2、藍牙模塊與單片機之間
單片機? ? ? ? ? ? ? ??藍牙模塊
?TX? ?? ?——>? ? ?RX ?
?RX? ?? ?——>? ? ?TX ?
3、MPU6050?
使用IIC通信,所以對照代碼接SDA、SCL、GND、VCC、IN(中斷觸發(fā)線)
四、功能介紹
1、兩輪平衡直立
2、藍牙APP控制運動狀態(tài)
3、遙控手柄控制
4、超聲波避障
五、關(guān)鍵算法
PID算法對編碼電機的控制
1.位置閉環(huán)控制
????????位置閉環(huán)控制就是根據(jù)編碼器的脈沖累加測量電機的位置信息,并與目標(biāo)值進行比較,得到控制偏差,然后通過對偏差的比例、積分、微分進行控制,使偏差趨向于零的過程。 位置閉環(huán)控制就是根據(jù)編碼器的脈沖累加測量電機的位置信息,并與目標(biāo)值進行比較,得到控制偏差,然后通過對偏差的比例、積分、微分進行控制,使偏差趨向于零的過程.
1.1理論分析
1.2控制原理圖?
1.3C語言實現(xiàn)?
int Position_PID (int Encoder, int Target)
{
static float Bias, Pwm,Integral_bias,Last_Bias;
Bias=Encoder-Target;//計算偏差
Integral_bias+=Bias; //求出偏差的積分
Pwm=Position_KP*Bias+Position_KI*Integral_bias+Position_KD*(Bias-Last_Bias);Last_Bias=Bias; //保存上一次偏差
return Pwm; //輸出
}
入口參數(shù)為編碼器的位置測量值和位置控制的目標(biāo)值,返回值為電機控制PWM(現(xiàn)在再看一下上面的控制原理圖是不是更加容易明白了)。
第一行是相關(guān)內(nèi)部變量的定義。
第二行是求出速度偏差,由測量值減去目標(biāo)值。第三行通過累加求出偏差的積分。
第四行使用位置式PID控制器求出電機 PWM。第五行保存上一次偏差,便于下次調(diào)用。最后一行是返回。
然后,在定時中斷服務(wù)函數(shù)里面調(diào)用該函數(shù)實現(xiàn)我們的控制目標(biāo):Moto=Position_PID(Encoder, Target_Position);
Set_Pwm(Moto) ;//===賦值給PWM寄存器
2、速度閉環(huán)控制
速度閉環(huán)控制就是根據(jù)單位時間獲取的脈沖數(shù)(這里使用了M法測速)測量電機的速度信息,并與目標(biāo)值進行比較,得到控制偏差,然后通過對偏差的比例、積分、微分進行控制,使偏差趨向于零的過程。
一些PID的要點在位置控制中已經(jīng)有講解,這里不再贅敘。
需要說明的是,這里速度控制20ms一次,一般建議10ms或者5ms,因為在這里電機是使用USB供電,速度比較慢,20ms可以延長獲取速度的單位時間,提高編碼器的采值。
?2.1理論分析
根據(jù)增量式離散PID公式 根據(jù)增量式離散PID公式
Pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
e(k):本次偏差
e(k-1):上一次的偏差e (k-2):上上次的偏差
Pwm 代表增量輸出在我們的速度控制閉環(huán)系統(tǒng)里面只使用PI控制,因此對PID控制器可簡化為以下公式:
Pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)
2.2 控制原理圖
2.3 C語言實現(xiàn)
增量式PI控制器具體通過C語言實現(xiàn)的代碼如下:
?
int Incremental_PI (int Encoder,int Target)
{
static float Bias, Pwm, Last_bias;
Bias=Encoder-Target;//計算偏差
Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias;//增量式PI控制器
Last_bias=Bias;//保存上一次偏差
return Pwm;//增量輸出
}
入口參數(shù)為編碼器的速度測量值和速度控制的目標(biāo)值,返回值為電機控制PWM。
第一行是相關(guān)內(nèi)部變量的定義。
第二行是求出速度偏差,由測量值減去目標(biāo)值。第三行使用增量PI控制器求出電機PWM。
第四行保存上一次偏差,便于下次調(diào)用。最后一行是返回。
然后,在定時中斷服務(wù)函數(shù)里面調(diào)用該函數(shù)實現(xiàn)我們的控制目標(biāo):Moto=Incremental_PI(Encoder, Target_Velocity);Set_Pwm(Moto);//===賦值給對應(yīng)MCU的PWM寄存器
六、關(guān)鍵代碼分析
1、編碼電機PID算法控制
#include "control.h"
#include "usart2.h"
/**************************************************************************
函數(shù)功能:所有的控制代碼都在這里面
5ms定時中斷由MPU6050的INT引腳觸發(fā)
嚴(yán)格保證采樣和數(shù)據(jù)處理的時間同步
在MPU6050的采樣頻率設(shè)置中,設(shè)置成100HZ,即可保證6050的數(shù)據(jù)是10ms更新一次。
讀者可在imv_mpu.h文件第26行的宏定義進行修改(#define DEFAULT_MPU_HZ (100))
**************************************************************************/
#define SPEED_Y 100 //俯仰(前后)最大設(shè)定速度
#define SPEED_Z 80//偏航(左右)最大設(shè)定速度
int Balance_Pwm,Velocity_Pwm,Turn_Pwm,Turn_Kp;
float Mechanical_angle=8;
float Target_Speed=0; //期望速度(俯仰)。用于控制小車前進后退及其速度。
float Turn_Speed=0; //期望速度(偏航)
//針對不同車型參數(shù),在sys.h內(nèi)設(shè)置define的電機類型
float balance_UP_KP=BLC_KP; // 小車直立環(huán)PD參數(shù)
float balance_UP_KD=BLC_KD;
float velocity_KP=SPD_KP; // 小車速度環(huán)PI參數(shù)
float velocity_KI=SPD_KI;
float Turn_Kd=TURN_KD;//轉(zhuǎn)向環(huán)KP、KD
float Turn_KP=TURN_KP;
void EXTI9_5_IRQHandler(void)
{
static u8 Voltage_Counter=0;
if(PBin(5)==0)
{
EXTI->PR=1<<5; //清除LINE5上的中斷標(biāo)志位
mpu_dmp_get_data(&pitch,&roll,&yaw); //得到歐拉角(姿態(tài)角)的數(shù)據(jù)
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //得到陀螺儀數(shù)據(jù)
Encoder_Left=Read_Encoder(2); //讀取編碼器的值,保證輸出極性一致
Encoder_Right=-Read_Encoder(3); //讀取編碼器的值
Led_Flash(100);
Voltage_Counter++;
if(Voltage_Counter==20) //100ms讀取一次電壓
{
Voltage_Counter=0;
Voltage=Get_battery_volt(); //讀取電池電壓
}
if(KEY_Press(100)) //長按按鍵切換模式并觸發(fā)模式切換初始化
{
if(++CTRL_MODE>=101)
CTRL_MODE=97;
Mode_Change=1;
}
Get_RC();
Target_Speed=Target_Speed>SPEED_Y?SPEED_Y:(Target_Speed<-SPEED_Y?(-SPEED_Y):Target_Speed);//限幅
Turn_Speed=Turn_Speed>SPEED_Z?SPEED_Z:(Turn_Speed<-SPEED_Z?(-SPEED_Z):Turn_Speed);//限幅( (20*100) * 100)
Balance_Pwm =balance_UP(pitch,Mechanical_angle,gyroy); //===直立環(huán)PID控制
Velocity_Pwm=velocity(Encoder_Left,Encoder_Right,Target_Speed); //===速度環(huán)PID控制
Turn_Pwm =Turn_UP(gyroz,Turn_Speed); //===轉(zhuǎn)向環(huán)PID控制
Moto1=Balance_Pwm-Velocity_Pwm+Turn_Pwm; //===計算左輪電機最終PWM
Moto2=Balance_Pwm-Velocity_Pwm-Turn_Pwm; //===計算右輪電機最終PWM
Xianfu_Pwm(); //===PWM限幅
Turn_Off(pitch,12); //===檢查角度以及電壓是否正常
Set_Pwm(Moto1,Moto2); //===賦值給PWM寄存器
}
}
/**************************************************************************
函數(shù)功能:直立PD控制
入口參數(shù):角度、機械平衡角度(機械中值)、角速度
返回 值:直立控制PWM
**************************************************************************/
int balance_UP(float Angle,float Mechanical_balance,float Gyro)
{
float Bias;
int balance;
Bias=Angle-Mechanical_balance; //===求出平衡的角度中值和機械相關(guān)
balance=balance_UP_KP*Bias+balance_UP_KD*Gyro; //===計算平衡控制的電機PWM PD控制 kp是P系數(shù) kd是D系數(shù)
return balance;
}
/**************************************************************************
函數(shù)功能:速度PI控制
入口參數(shù):電機編碼器的值
返回 值:速度控制PWM
**************************************************************************/
int velocity(int encoder_left,int encoder_right,int Target_Speed)
{
static float Velocity,Encoder_Least,Encoder;
static float Encoder_Integral;
//=============速度PI控制器=======================//
Encoder_Least =(Encoder_Left+Encoder_Right);//-target; //===獲取最新速度偏差==測量速度(左右編碼器之和)-目標(biāo)速度
Encoder *= 0.8; //===一階低通濾波器
Encoder += Encoder_Least*0.2; //===一階低通濾波器
Encoder_Integral +=Encoder; //===積分出位移 積分時間:10ms
Encoder_Integral=Encoder_Integral - Target_Speed; //===接收遙控器數(shù)據(jù),控制前進后退
if(Encoder_Integral>10000) Encoder_Integral=10000; //===積分限幅
if(Encoder_Integral<-10000) Encoder_Integral=-10000; //===積分限幅
Velocity=Encoder*velocity_KP+Encoder_Integral*velocity_KI; //===速度控制
if(pitch<-40||pitch>40) Encoder_Integral=0; //===電機關(guān)閉后清除積分
return Velocity;
}
/**************************************************************************
函數(shù)功能:轉(zhuǎn)向PD控制
入口參數(shù):電機編碼器的值、Z軸角速度
返回 值:轉(zhuǎn)向控制PWM
**************************************************************************/
int Turn_UP(int gyro_Z, int RC)
{
int PWM_out;
/*轉(zhuǎn)向約束*/
if(RC==0)
Turn_Kd=TURN_KD; //若無左右轉(zhuǎn)向指令,則開啟轉(zhuǎn)向約束
else
Turn_Kd=0; //若左右轉(zhuǎn)向指令接收到,則去掉轉(zhuǎn)向約束
PWM_out=Turn_Kd*gyro_Z + Turn_KP*RC;
return PWM_out;
}
void Tracking()
{
TkSensor=0;
TkSensor+=(C1<<3);
TkSensor+=(C2<<2);
TkSensor+=(C3<<1);
TkSensor+=C4;
}
void Get_RC()
{
static u8 SR04_Counter =0;
static float RATE_VEL = 1;
float RATE_TURN = 1.6;
float LY,RX; //PS2手柄控制變量
int Yuzhi=2; //PS2控制防抖閾值
switch(CTRL_MODE)
{
case 97:
SR04_Counter++;
if(SR04_Counter>=20) //100ms讀取一次超聲波的數(shù)據(jù)
{
SR04_Counter=0;
SR04_StartMeasure(); //讀取超聲波的值
}
if(SR04_Distance<=30)
{
Target_Speed=0,Turn_Speed=40;
}
else
{
Target_Speed=30,Turn_Speed=0;
}
break;
case 98://藍牙模式
if((Fore==0)&&(Back==0))
Target_Speed=0;//未接受到前進后退指令-->速度清零,穩(wěn)在原地
if(Fore==1)
Target_Speed--;//前進1標(biāo)志位拉高-->需要前進
if(Back==1)
Target_Speed++;//
/*左右*/
if((Left==0)&&(Right==0))
Turn_Speed=0;
if(Left==1)
Turn_Speed-=30; //左轉(zhuǎn)
if(Right==1)
Turn_Speed+=30; //右轉(zhuǎn)
break;
case 99://循跡模式
Tracking();
switch(TkSensor)
{
case 15:
Target_Speed=0;
Turn_Speed=0;
break;
case 9:
Target_Speed--;
Turn_Speed=0;
break;
case 2://向右轉(zhuǎn)
Target_Speed--;
Turn_Speed=15;
break;
case 4://向左轉(zhuǎn)
Target_Speed--;
Turn_Speed=-15;
break;
case 8:
Target_Speed=-10;
Turn_Speed=-80;
break;
case 1:
Target_Speed=-10;
Turn_Speed=80;
break;
}
break;
case 100://PS2手柄遙控
if(PS2_Plugin)
{
LY=PS2_LY-128; //獲取偏差
RX=PS2_RX-128; //獲取偏差
if(LY>-Yuzhi&&LY<Yuzhi)
LY=0; //設(shè)置小角度的死區(qū)
if(RX>-Yuzhi&&RX<Yuzhi)
RX=0; //設(shè)置小角度的死區(qū)
if(Target_Speed>-LY/RATE_VEL)
Target_Speed--;
else if(Target_Speed<-LY/RATE_VEL)
Target_Speed++;
Turn_Speed=RX/RATE_TURN;
}
else
{
Target_Speed=0,Turn_Speed=0;
}
break;
}
}
?2、編碼電機編碼值采集
#include "encoder.h"
/**************************************************************************
函數(shù)功能:把TIM2初始化為編碼器接口模式
入口參數(shù):無
返回 值:無
**************************************************************************/
void Encoder_Init_TIM2(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定時器4的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //根據(jù)設(shè)定參數(shù)初始化GPIOB
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 預(yù)分頻器
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //設(shè)定計數(shù)器自動重裝值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//選擇時鐘分頻:不分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上計數(shù)
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用編碼器模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新標(biāo)志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//Reset counter
TIM_SetCounter(TIM2,0);
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************
函數(shù)功能:把TIM3初始化為編碼器接口模式
入口參數(shù):無
返回 值:無
**************************************************************************/
void Encoder_Init_TIM3(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能定時器4的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //根據(jù)設(shè)定參數(shù)初始化GPIOB
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 預(yù)分頻器
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //設(shè)定計數(shù)器自動重裝值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//選擇時鐘分頻:不分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上計數(shù)
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用編碼器模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);//清除TIM的更新標(biāo)志位
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
//Reset counter
TIM_SetCounter(TIM3,0);
TIM_Cmd(TIM3, ENABLE);
}
/**************************************************************************
函數(shù)功能:單位時間讀取編碼器計數(shù)
入口參數(shù):定時器
返回 值:速度值
**************************************************************************/
int Read_Encoder(u8 TIMX)
{
int Encoder_TIM;
switch(TIMX)
{
case 2:
Encoder_TIM= (short)TIM2 -> CNT;
TIM2 -> CNT=0;
break;
case 3:
Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=0;
break;
default: Encoder_TIM=0;
}
return Encoder_TIM;
}
3、PWM配置
#include "pwm.h"
//PWM輸出初始化
//arr:自動重裝值
//psc:時鐘預(yù)分頻數(shù)
//TIM1_PWM_Init(7199,0);//PWM頻率=72000/(7199+1)=10Khz
void TIM1_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外設(shè)時鐘使能
//設(shè)置該引腳為復(fù)用輸出功能,輸出TIM1 CH1 CH4的PWM脈沖波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11; //TIM_CH1 //TIM_CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr; //設(shè)置在下一個更新事件裝入活動的自動重裝載寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //設(shè)置用來作為TIMx時鐘頻率除數(shù)的預(yù)分頻值 不分頻
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設(shè)置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數(shù)模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根據(jù)TIM_TimeBaseInitStruct中指定的參數(shù)初始化TIMx的時間基數(shù)單位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈沖寬度調(diào)制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_Pulse = 0; //設(shè)置待裝入捕獲比較寄存器的脈沖值
TIM_OCInitStructure.TIM_Pulse = arr >> 1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根據(jù)TIM_OCInitStruct中指定的參數(shù)初始化外設(shè)TIMx
TIM_OC4Init(TIM1, &TIM_OCInitStructure); //根據(jù)TIM_OCInitStruct中指定的參數(shù)初始化外設(shè)TIMx
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主輸出使能
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1預(yù)裝載使能
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH4預(yù)裝載使能
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的預(yù)裝載寄存器
TIM_Cmd(TIM1, ENABLE); //使能TIM1
}
4、藍牙控制
#include "usart2.h"
/**************************************************************************
函數(shù)功能:串口2初始化
入口參數(shù): bound:波特率
返回 值:無
**************************************************************************/
void uart2_init(u32 bound)
{
//GPIO端口設(shè)置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能UGPIOB時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能USART2時鐘
//USART2_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART2_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART 初始化設(shè)置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數(shù)據(jù)格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數(shù)據(jù)流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發(fā)模式
USART_Init(USART2, &USART_InitStructure); //初始化串口2
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//開啟串口接受中斷
USART_Cmd(USART2, ENABLE); //使能串口2
}
/**************************************************************************
函數(shù)功能:串口2接收中斷
入口參數(shù):無
返回 值:無
**************************************************************************/
u8 Fore,Back,Left,Right;
void USART2_IRQHandler(void)
{
int Uart_Receive;
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)//接收中斷標(biāo)志位拉高
{
Uart_Receive=USART_ReceiveData(USART2);//保存接收的數(shù)據(jù)
BluetoothCMD(Uart_Receive);
}
}
void BluetoothCMD(int Uart_Receive)
{
switch(Uart_Receive)
{
case 90://停止
Fore=0,Back=0,Left=0,Right=0;
break;
case 65://前進
Fore=1,Back=0,Left=0,Right=0;
break;
case 72://左前
Fore=1,Back=0,Left=1,Right=0;
break;
case 66://右前
Fore=1,Back=0,Left=0,Right=1;
break;
case 71://左轉(zhuǎn)
Fore=0,Back=0,Left=1,Right=0;
break;
case 67://右轉(zhuǎn)
Fore=0,Back=0,Left=0,Right=1;
break;
case 69://后退
Fore=0,Back=1,Left=0,Right=0;
break;
case 70://左后,向右旋
Fore=0,Back=1,Left=0,Right=1;
break;
case 68://右后,向左旋
Fore=0,Back=1,Left=1,Right=0;
break;
default://停止
Fore=0,Back=0,Left=0,Right=0;
break;
}
}
void Uart2SendByte(char byte) //串口發(fā)送一個字節(jié)
{
USART_SendData(USART2, byte); //通過庫函數(shù) 發(fā)送數(shù)據(jù)
while( USART_GetFlagStatus(USART2,USART_FLAG_TC)!= SET);
//等待發(fā)送完成。 檢測 USART_FLAG_TC 是否置1; //見庫函數(shù) P359 介紹
}
void Uart2SendBuf(char *buf, u16 len)
{
u16 i;
for(i=0; i<len; i++)Uart2SendByte(*buf++);
}
void Uart2SendStr(char *str)
{
u16 i,len;
len = strlen(str);
for(i=0; i<len; i++)Uart2SendByte(*str++);
}
5、中斷處理函數(shù)
void EXTI9_5_IRQHandler(void)
{
static u8 Voltage_Counter=0;
if(PBin(5)==0)
{
EXTI->PR=1<<5; //清除LINE5上的中斷標(biāo)志位
mpu_dmp_get_data(&pitch,&roll,&yaw); //得到歐拉角(姿態(tài)角)的數(shù)據(jù)
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //得到陀螺儀數(shù)據(jù)
Encoder_Left=Read_Encoder(2); //讀取編碼器的值,保證輸出極性一致
Encoder_Right=-Read_Encoder(3); //讀取編碼器的值
Led_Flash(100);
Voltage_Counter++;
if(Voltage_Counter==20) //100ms讀取一次電壓
{
Voltage_Counter=0;
Voltage=Get_battery_volt(); //讀取電池電壓
}
if(KEY_Press(100)) //長按按鍵切換模式并觸發(fā)模式切換初始化
{
if(++CTRL_MODE>=101)
CTRL_MODE=97;
Mode_Change=1;
}
Get_RC();
Target_Speed=Target_Speed>SPEED_Y?SPEED_Y:(Target_Speed<-SPEED_Y?(-SPEED_Y):Target_Speed);//限幅
Turn_Speed=Turn_Speed>SPEED_Z?SPEED_Z:(Turn_Speed<-SPEED_Z?(-SPEED_Z):Turn_Speed);//限幅( (20*100) * 100)
Balance_Pwm =balance_UP(pitch,Mechanical_angle,gyroy); //===直立環(huán)PID控制
Velocity_Pwm=velocity(Encoder_Left,Encoder_Right,Target_Speed); //===速度環(huán)PID控制
Turn_Pwm =Turn_UP(gyroz,Turn_Speed); //===轉(zhuǎn)向環(huán)PID控制
Moto1=Balance_Pwm-Velocity_Pwm+Turn_Pwm; //===計算左輪電機最終PWM
Moto2=Balance_Pwm-Velocity_Pwm-Turn_Pwm; //===計算右輪電機最終PWM
Xianfu_Pwm(); //===PWM限幅
Turn_Off(pitch,12); //===檢查角度以及電壓是否正常
Set_Pwm(Moto1,Moto2); //===賦值給PWM寄存器
}
}
七、PCB板設(shè)計
八、代碼開源
1、寄存器版本
鏈接:https://pan.baidu.com/s/1NlMHsgMF2Cu8sz955n27Eg?pwd=zxf1?
提取碼:zxf1?
--來自百度網(wǎng)盤超級會員V2的分享文章來源:http://www.zghlxwxcb.cn/news/detail-754226.html
2、HAL庫版本
鏈接:https://pan.baidu.com/s/1rW5M7Dz-TK4IWJxNp57mBw?pwd=zxf1?
提取碼:zxf1?
--來自百度網(wǎng)盤超級會員V2的分享文章來源地址http://www.zghlxwxcb.cn/news/detail-754226.html
到了這里,關(guān)于STM32兩輪平衡小車原理詳解(開源)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!