【请认准:OpenSir开源达人】
开源STM32自平衡小车
平衡汽车开源数据网盘链接: 平衡车百度网盘数据链接,点击进入
哔站播放视频链接如下: 链接: 【开源STM32自平衡车哔哩哔哩播放视频效果,点击播放
套件 链接: STM32自平衡车开源整车套件,点击查看
文章目录
- 【请认准:OpenSir开源达人】
- 一、硬件篇(附淘宝器件链接,也可自行购买)
-
- 1.STM32F103C8T6最小系统板(核心板)
- 2.MPU6050姿态传感器(六轴)
- 3.TB6612电机驱动(双通道)
- 4.0.96寸OLED显示屏
- 5.蓝牙无线通信模块
- 6.电源降压模块(双电压输出)
- 7.超声波测距模块
- 8.动力锂电池(双节18650)
- 9.汽车橡胶车轮(655)mm)
- 10.STLINK代码模拟下载器
- 二、软件篇
-
- 1.主函数代码
- 2.systick滴答定时器中断任务
- 3.滴答定时器5ms中断配置
- 4.调试串口向上位机发送数据
- 5.ADC采用DMA数据采集的方式
- 6.陀螺仪数据校准
- 7.超声波测距控制
- 8.欧拉角姿态分析
- 9.PID控制器
- 10.接收和分析蓝牙数据包
- 三、原理图
提示:以下是本文的主要内容
一、硬件篇(附淘宝器件链接,也可自行购买)
1.STM32F103C8T6最小系统板(核心板)

2.MPU6050姿态传感器(六轴)
传感器数据链接:
https://pan.baidu.com/s/1dNDqcp76L9QdM7iSZYfz_A 提取码: 4eum
https://pan.baidu.com/s/13ZoMZ-lpAtDRhey4lKEcFQ 提取码: p7fr
3.TB6612电机驱动(双通道)
4.0.96寸OLED显示屏
显示模块x64),点击进入! 模块数据链接:
https://pan.baidu.com/s/108smkVOLg-23cAnr37ZeGQ 密码:91t7
https://pan.baidu.com/s/1Lr8lw_6vt_VdM1UWe9CGsA 密码:p0un
5.蓝牙无线通信模块
DY-31带底板数据:
https://pan.baidu.com/s/16qyiOO05whOXqYtBVBGs2Q
6.电源降压模块(双电压输出)
7.超声波测距模块
HC-SR04资料下载:
http://pan.baidu.com/s/1sjHKDI1
https://pan.baidu.com/s/1sSah9PvLBrmbA7So-6YcSw 提取码: qq35
8.动力锂电池(双节18650)
9.汽车橡胶车轮(655)mm)
10.STLINK代码模拟下载器
STLINK数据网盘链接:
https://pan.baidu.com/s/1gxzJeDe7CJaCCl4pGnwdNQ 提取码: an2m
WIN10系统驱动解决方案
https://pan.baidu.com/s/1OQosxeUMnCe5CZkwKRKhOA 提取码: h9ii
二、软件篇
点击文章顶部 B站视频链接 或者 点击此处 一键三连 注意,留言邮箱可以得到这辆车Keil源代码文件
1.主函数代码
代码如下(main.c):
/********************************************************************** 版权所有:源动力技术 淘 宝:https://1024tech.taobao.com/ 文 件 名:mian.c 版 本:V21.01.30 摘 要: ***********************************************************************/ #include "stm32f10x.h" #include "systick.h" #include "led.h" #include "iic.h" #include "mpu6050.h" #include "timer.h" #include "nvic.h" #include "imu.h" #include "pwm.h" #include "flash.h" #include "pid.h" #include "usart1.h" #include "bluetooth.h" #include "key.h" #include "ps2.h" #include "oled.h" #include "adc.h" #include "ultrasonic.h" #include "imath.h" #include "Infrared.h" /* * 使能SWD, 失能JTAG * PB3,PB4,PA15作为普通IO使用 * MCU复位后,PA13/14/15 & PB3/4默认配置为JTAG功能 */ static void _SWJ_Config(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //优先级组别2 _SWJ_Config(); //使能SWD,失能JTAG usart1_init(115200); //串口1配置初始化 printf("usart1 is ok\r\n"); BluetoothUsart_init(9600); //蓝牙串口初始化 pwm_init(); //pwm初始 driver_pin_init(); //电机驱动配置初始化 Encoder_A_init(); //编码器A初始化 Encoder_B_init(); //编码器B初始化 tdelay_ms(100); IIC_Init(); //IIC端口配置 mpu6050_init(); //mpu6050初始化 while(1) //mpu6050在位检测 { uint8_t mpuId; mpuId = get_mpu_id(); //读取mpu6050的id if(mpuId==0x68||mpuId==0x98) //判断mpu6050的ID是否正确 break; tdelay_ms(50); } get_iir_factor(&Mpu.att_acc_factor,0.005f,25); //姿态解算时加速度低通系数 OLED_Init(); //OLED显示屏端口初始化 adc_init(); //ADC配置初始化 ReadCalData(); //读取校准后的陀螺仪零偏数据 tdelay_ms(100); timer_init(); //定0时器初始化 HCSR04_Init(); //超声波端口初始化 systick_init(); //滴答定时器初始化 NVIC_config(); //中断配置初始化 while(1) { if(softTimes[0] == 0) //100ms { softTimes[0] = 20; } if(softTimes[1] == 0) //20ms { softTimes[1] = 4; UltrasonicWave_StartMeasure(); //超声波发出起始高电平 ParseBluetoothMessage(USARTxBlueTooth_RX_DATA, &BluetoothParseMsg); //蓝牙数据包解析 } if(softTimes[2] == 0) //100ms { softTimes[2] = 20; voltage_detection(); //低电压检测 OledShowDatas(); } } }
2.systick滴答定时器中断任务
代码如下(SysTick_Handler() ):
/** * @brief This function handles SysTick Handler. * @param None * @retval None */
void SysTick_Handler(void)
{
softTimesCountdown(); //软件定时倒计时
UltrasonicCheck(); //超声波在位检测
get_gyro_raw(); //陀螺仪数据
get_deg_s(&gyro_raw_f,&Mpu.deg_s); //陀螺仪原始数据转为度为单位的速率
get_rad_s(&gyro_raw_f,&Mpu.rad_s); //陀螺仪原始数据转为弧度为单位的速率
get_acc_raw(); //加速度数据
acc_iir_lpf(&acc_raw_f,&acc_att_lpf,Mpu.att_acc_factor); //姿态解算时加速度低通滤波
get_acc_g(&acc_att_lpf,&Mpu.acc_g);
//姿态解算
mahony_update(Mpu.rad_s.x,Mpu.rad_s.y,Mpu.rad_s.z,Mpu.acc_g.x,Mpu.acc_g.y,Mpu.acc_g.z);
Matrix_ready(); //姿态解算相关矩阵更新
encoder_val_a = read_encoder(ENCODER_A); //A编码器值读取
encoder_manage(&encoder_val_a); //编码器值处理
encoder_val_b = read_encoder(ENCODER_B); //B编码器值读取
encoder_manage(&encoder_val_b); //编码器值处理
ctr.pwm[0] = ctr_bal(att.rol,Mpu.deg_s.y); //角度直立平衡控制器
ctr.pwm[1] = ctr_vel(encoder_val_a,-encoder_val_b); //速度控制器
ctr.pwm[2] = ctr_turn(encoder_val_a,-encoder_val_b,Mpu.deg_s.z); //转向控制器
ctr.out[0] = ctr.pwm[0] + ctr.pwm[1] + ctr.pwm[2]; //电机1匹配输出
ctr.out[1] = ctr.pwm[0] + ctr.pwm[1] - ctr.pwm[2]; //电机2匹配输出
i_limit(&(ctr.out[0]),AMPLITUDE); //输出限幅
i_limit(&(ctr.out[1]),AMPLITUDE); //输出限幅
dir_config(&(ctr.out[0]),&(ctr.out[1])); //根据正负设置方向
_ctr_out(); //控制器输出
ANO_DMA_SEND_DATA(); //地面站波形显示
gyro_cal(&gyro_raw_cal); //陀螺仪零偏校准
}
3.滴答定时器5ms中断配置
代码如下( systick_init ):
/* * 函数名:systick_init * 描述 :系统滴答定时器配置初始化 * 输入 : * 返回 : */
void systick_init(void)
{
SystemCoreClockUpdate();
//时钟频率:72Mhz , 每秒可以计数72000000次,一次的时间则为:1/72000(ms),10ms需要的次数:10/(1/72000) = 720000 -> 5ms需要的次数则为:360000
if (SysTick_Config(SystemCoreClock / 200)) //1000 -> 1ms
{
/* Capture error */
while (1);
}
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //使能
}
4.调试串口发送数据至上位机
代码如下( ANO_DMA_SEND_DATA ):
vvoid ANO_DMA_SEND_DATA(void)
{
static uint8_t ANO_debug_cnt = 0;
ANO_debug_cnt++;
if(ANO_debug_cnt==1)
{
ANO_DT_Send_Status();
}
else if(ANO_debug_cnt==2)
{
ANO_DT_Send_Senser((int16_t)acc_raw.x,(int16_t)acc_raw.y,(int16_t)acc_raw.z,
(int16_t)gyro_raw.x,(int16_t)gyro_raw.y,(int16_t)gyro_raw.z,
(int16_t)0,(int16_t)0,(int16_t)0);
}
else if(ANO_debug_cnt==3)
{
ANO_DT_Send_RCData(1100,1200,1300,1400,1500,1600,1700,1800,1900,1100);
}
else if(ANO_debug_cnt==4)
{
ANO_DT_Send_Power(13.52,57.63);
}
else if(ANO_debug_cnt==5)
{
ANO_DT_Send_User(0,0,0,0,0,
encoder_val_a, encoder_val_b,ctr.pwm[0],ctr.pwm[1],acc_raw_f.z,
0,0,0,0,0);
}
else if(ANO_debug_cnt==6)
{
if(f.send_pid1)
{
f.send_pid1 = 0;
//向上位机发送内环速度环PID参数值
ANO_DT_Send_PID(1, vel.kp*0.1f,
vel.ki,
vel.kd,
1.5f,
2.5f,
1.5f,
2.5f,
1.5f,
2.5f);
}
if(f.send_pid2)
{
f.send_pid2 = 0;
//向上位机发送外环角度环PID参数值
ANO_DT_Send_PID(2, bal.kp*0.1f,
bal.ki,
bal.kd,
2.6f,
1.5f,
2.4f,
1.1f,
2.2f,
1.1f);
}
if(CalGyro.success==1) //返回校准成功信息给上位机
{
CalGyro.success = 0;
ANO_DT_Send_MSG(SENSOR_GYRO,CAL_SUCCESS);
}
else if(CalGyro.success==2) //反馈校准失败信息给上位机
{
CalGyro.success = 0;
ANO_DT_Send_MSG(SENSOR_GYRO,CAL_SUCCESS);
}
}
else if(ANO_debug_cnt==7)
{
ANO_debug_cnt = 0;
}
}
5.ADC采用DMA方式进行数据采集
代码如下:
/********************************************************************** 版权所有:源动力科技 淘 宝:https://1024tech.taobao.com/ 文 件 名:adc.c 版 本:V21.01.30 摘 要: ***********************************************************************/
#include "adc.h"
_ADC_VALUE bat = {
0 ,0 };
uint16_t adc_value[1];
/* 端口配置初始化 */
void adc_gpio_init(void)
{
GPIO_InitTypeDef GPIO_initStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_initStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_initStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA,&GPIO_initStructure);
}
/* adc配置 */
void adc_config(void)
{
ADC_InitTypeDef ADC_initStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
ADC_initStructure.ADC_ContinuousConvMode = ENABLE; //连续转换
ADC_initStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐
ADC_initStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //软件触发
ADC_initStructure.ADC_Mode = ADC_Mode_Independent; //独立工作模式
ADC_initStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目 1-16
ADC_initStructure.ADC_ScanConvMode = DISABLE; //扫描模式:单次模式
ADC_Init(ADC1,&ADC_initStructure);
ADC_Cmd(ADC1,ENABLE);
ADC_DMACmd(ADC1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //ADC时钟分频
//通道配置,采样时间设置
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,1,ADC_SampleTime_239Cycles5);
ADC_ResetCalibration(ADC1); //复位校准
while(ADC_GetCalibrationStatus(ADC1)); //等待
ADC_StartCalibration(ADC1); //启动校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //开启转换
}
void dma_config(void)
{
DMA_InitTypeDef DMA_initStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_initStructure.DMA_BufferSize = 1; //缓存的数据大小
DMA_initStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向:外设->内存
DMA_initStructure.DMA_M2M = DMA_M2M_Disable;
DMA_initStructure.DMA_MemoryBaseAddr = (u32)adc_value; //内存基地址
DMA_initStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //DMA传输的内存数据大小:半字为单位
DMA_initStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
DMA_initStructure.DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_initStructure.DMA_PeripheralBaseAddr = ((u32)&ADC1->DR); //外设地址
DMA_initStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //DMA传输的外设数据大小:半字为单位
DMA_initStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变
DMA_initStructure.DMA_Priority = DMA_Priority_Medium; //中等优先级
DMA_Init(DMA1_Channel1,&DMA_initStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
}
void adc_init