基于STM32的平衡车外设控制应用案例,提供C++源码

基于STM32的平衡车外设控制应用案例**

下面是一个使用STM32控制平衡车的简单应用案例,包含姿态传感器读取、电机控制和串口通信功能。

主要功能

  1. 使用MPU6050传感器读取姿态数据
  2. 使用PID控制器调整平衡车姿态
  3. 通过串口输出调试信息
  4. 电机速度控制

C++源代码

#include "stm32f10x.h"
#include <math.h>

// 定义常量
#define PWM_MIN 1000
#define PWM_MAX 2000
#define PWM_MID 1500

// 全局变量
volatile float roll = 0.0f;  // 姿态角-横滚角
volatile float pitch = 0.0f; // 姿态角-俯仰角
volatile float yaw = 0.0f;   // 姿态角-偏航角
volatile float motorPWM[2] = {PWM_MID, PWM_MID}; // 两个电机的PWM信号

// 定义PID控制器参数
typedef struct {
    float Kp;
    float Ki;
    float Kd;
    float integral;
    float prevError;
} PID_Controller;

PID_Controller pidPitch;

// 初始化定时器
void TIM1_Init(void) {
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能定时器1和GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 配置PA8和PA9为复用推挽输出(TIM1_CH1和TIM1_CH2)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置TIM1
    TIM_TimeBaseStructure.TIM_Period = 999; // 计数周期
    TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频器
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    // 配置PWM输出
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = PWM_MID; // 初始占空比
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);
    TIM_OC2Init(TIM1, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);

    // 使能定时器1
    TIM_Cmd(TIM1, ENABLE);
}

// 初始化串口
void USART1_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 使能GPIOA和USART1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    // 配置PA9为复用推挽输出(USART1_TX)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置PA10为浮空输入(USART1_RX)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置USART1
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);

    // 使能USART1
    USART_Cmd(USART1, ENABLE);
}

// 串口发送数据
void USART1_SendString(char *str) {
    while (*str != '\0') {
        USART_SendData(USART1, *str);
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
        str++;
    }
}

// 初始化I2C
void I2C1_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef I2C_InitStructure;

    // 使能GPIOB和I2C1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 配置PB6为I2C1_SCL
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 配置PB7为I2C1_SDA
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 配置I2C1
    I2C_InitStructure.I2C_ClockSpeed = 100000;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2C1, &I2C_InitStructure);

    // 使能I2C1
    I2C_Cmd(I2C1, ENABLE);
}

// 读取MPU6050数据
void MPU6050_ReadData(void) {
    // 发送I2C起始信号
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    // 发送MPU6050从机地址和写模式
    I2C_Send7bitAddress(I2C1, 0x68, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    // 发送寄存器地址
    I2C_SendData(I2C1, 0x3B); // 从0x3B寄存器开始读取加速度计和陀螺仪数据
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    // 发送I2C重复起始信号
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    // 发送MPU6050从机地址和读模式
    I2C_Send7bitAddress(I2C1, 0x68, I2C_Direction_Receiver);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

    // 读取14字节数据(加速度计、温度、陀螺仪)
    uint8_t data[14];
    for (int i = 0; i < 14; i++) {
        data[i] = I2C_ReceiveData(I2C1);
        if (i < 13) {
            I2C_AcknowledgeConfig(I2C1, ENABLE);
        } else {
            I2C_AcknowledgeConfig(I2C1, DISABLE);
        }
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
    }

    // 解析数据
    int16_t accelXRaw = (int16_t)((data[0] << 8) | data[1]);
    int16_t accelYRaw = (int16_t)((data[2] << 8) | data[3]);
    int16_t accelZRaw = (int16_t)((data[4] << 8) | data[5]);
    int16_t tempRaw = (int16_t)((data[6] << 8) | data[7]);
    int16_t gyroXRaw = (int16_t)((data[8] << 8) | data[9]);
    int16_t gyroYRaw = (int16_t)((data[10] << 8) | data[11]);
    int16_t gyroZRaw = (int16_t)((data[12] << 8) | data[13]);

    // 转换为实际值
    float accelX = accelXRaw / 16384.0f; // 加速度计灵敏度为16384 LSB/g
    float accelY = accelYRaw / 16384.0f;
    float accelZ = accelZRaw / 16384.0f;
    float gyroX = gyroXRaw / 131.0f; // 陀螺仪灵敏度为131 LSB/(°/s)
    float gyroY = gyroYRaw / 131.0f;
    float gyroZ = gyroZRaw / 131.0f;

    // 计算姿态角(简化版,实际应用中应使用更复杂的滤波算法)
    roll = atan2(accelY, accelZ) * 180.0f / M_PI;
    pitch = atan2(-accelX, sqrt(accelY * accelY + accelZ * accelZ)) * 180.0f / M_PI;
    yaw = gyroZ * 0.01f; // 假设时间步长为10ms

    // 生成停止信号
    I2C_GenerateSTOP(I2C1, ENABLE);
}

// 初始化MPU6050
void MPU6050_Init(void) {
    // 发送I2C起始信号
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    // 发送MPU6050从机地址和写模式
    I2C_Send7bitAddress(I2C1, 0x68, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    // 发送寄存器地址
    I2C_SendData(I2C1, 0x6B); // 配置寄存器
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    // 发送配置数据(清除睡眠模式)
    I2C_SendData(I2C1, 0x00);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    // 生成停止信号
    I2C_GenerateSTOP(I2C1, ENABLE);
}

// 初始化PID控制器
void PID_Init(PID_Controller *pid, float Kp, float Ki, float Kd) {
    pid->Kp = Kp;
    pid->Ki = Ki;
    pid->Kd = Kd;
    pid->integral = 0.0f;
    pid->prevError = 0.0f;
}

// PID控制器更新
float PID_Update(PID_Controller *pid, float setPoint, float processVar, float dt) {
    float error = setPoint - processVar;

    // 比例项
    float proportional = pid->Kp * error;

    // 积分项
    pid->integral += pid->Ki * error * dt;

    // 微分项
    float derivative = pid->Kd * (error - pid->prevError) / dt;

    // 计算输出
    float output = proportional + pid->integral + derivative;

    // 保存当前误差
    pid->prevError = error;

    return output;
}

// 更新电机PWM信号
void UpdateMotorPWM(float pwm[2]) {
    // 限制PWM信号范围
    for (int i = 0; i < 2; i++) {
        if (pwm[i] < PWM_MIN) pwm[i] = PWM_MIN;
        if (pwm[i] > PWM_MAX) pwm[i] = PWM_MAX;
    }

    // 更新定时器占空比
    TIM_SetCompare1(TIM1, (uint32_t)pwm[0]);
    TIM_SetCompare2(TIM1, (uint32_t)pwm[1]);
}

// 主函数
int main(void) {
    // 初始化外设
    TIM1_Init();
    USART1_Init();
    I2C1_Init();
    MPU6050_Init();

    // 初始化PID控制器
    PID_Init(&pidPitch, 50.0f, 5.0f, 2.0f);

    // 主循环
    while (1) {
        // 读取传感器数据
        MPU6050_ReadData();

        // 计算控制信号
        float dt = 0.01f; // 假设控制周期为10ms
        float pitchError = 0.0f - pitch;

        float pitchControl = PID_Update(&pidPitch, 0.0f, pitch, dt);

        // 计算电机PWM信号
        motorPWM[0] = PWM_MID + pitchControl; // 左电机
        motorPWM[1] = PWM_MID - pitchControl; // 右电机

        // 更新电机PWM信号
        UpdateMotorPWM(motorPWM);

        // 通过串口发送调试信息
        char debugStr[100];
        sprintf(debugStr, "Pitch: %.2f, PWM_L: %.2f, PWM_R: %.2f\r\n", pitch, motorPWM[0], motorPWM[1]);
        USART1_SendString(debugStr);

        // 延时10ms(100Hz控制频率)
        for (int i = 0; i < 10000; i++);
    }
}

代码说明

1. 定时器初始化

使用TIM1生成PWM信号控制两个电机。

2. 串口初始化

初始化USART1用于调试输出。

3. I2C初始化

初始化I2C1用于与MPU6050传感器通信。

4. MPU6050初始化

配置MPU6050传感器。

5. PID控制器

实现简单的PID控制器用于姿态控制。

6. 主循环

  • 读取传感器数据。
  • 计算姿态角。
  • 使用PID控制器计算电机控制信号。
  • 更新电机PWM信号。
  • 发送调试信息。

运行说明

  • 该代码基于STM32F10x系列微控制器,使用STM32标准外设库。
  • 在实际应用中,需要根据具体的硬件配置和传感器连接进行相应的调整。
  • 代码中包含简化版的姿态角计算,在实际项目中可能需要使用更复杂的滤波算法(如互补滤波或卡尔曼滤波)。
  • PID参数需要根据实际情况进行调整以获得最佳的控制效果。

扩展建议

  1. 增加更多传感器:可以添加距离传感器、摄像头等,实现更复杂的平衡车功能。
  2. 实现无线通信:添加Wi-Fi或蓝牙模块,实现远程控制。
  3. 优化控制算法:使用更复杂的控制算法,如卡尔曼滤波,提高控制精度。
  4. 增加安全机制:实现紧急停止、障碍物检测等安全功能。

这个案例展示了如何使用STM32实现平衡车的外设控制,包括电机PWM信号生成、传感器数据读取和简单的姿态控制算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员Thomas

谢谢您的打赏,我将会更好创作。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值