资讯详情

【开源STM32自平衡小车】 教你如何自己DIY一辆双轮自平衡小车

【请认准: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

HC-SR04单芯片版本资料下载接:

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

标签: diy如何调电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司