1. 项目背景与核心功能解析
这个基于STM32F103的真实项目来自某米1代扫地机器人,是一个完整的嵌入式系统开发案例。作为扫地机器人的主控系统,它需要同时处理多种传感器数据、执行复杂的路径规划算法,并精确控制各种执行机构。项目采用Keil MDK开发环境,使用C语言编写,代码结构清晰规范,非常适合嵌入式工程师学习参考。
核心功能实现包括:
- 延边清扫时的智能避障
- 防跌落保护机制
- 自动回充功能
- 多传感器数据融合处理
- 实时任务调度与管理
提示:这个项目的特别价值在于它展示了如何在一个资源有限的Cortex-M3 MCU上,通过合理的架构设计实现复杂的智能设备功能。
2. 硬件架构深度剖析
2.1 主控芯片选型与特性
STM32F103C8T6作为项目的主控芯片,是一款基于ARM Cortex-M3内核的微控制器,主要特性包括:
- 72MHz主频
- 64KB Flash + 20KB SRAM
- 丰富的外设接口:3个USART、2个SPI、2个I2C、1个USB等
- 16通道12位ADC
- 7通道DMA控制器
选择这款芯片的考量因素:
- 性能足够处理扫地机器人的实时控制需求
- 丰富的外设接口完美匹配各种传感器和执行器
- 成本控制在消费级产品可接受范围
- 成熟的生态和开发工具支持
2.2 关键传感器与驱动电路
2.2.1 BMI160陀螺仪姿态传感器
BMI160是一款6轴惯性测量单元(IMU),包含3轴加速度计和3轴陀螺仪,主要参数:
- 加速度计量程:±2g/±4g/±8g/±16g
- 陀螺仪量程:±125°/s至±2000°/s
- 通信接口:I2C/SPI
- 典型功耗:950μA
在扫地机器人中的应用:
- 实时监测机器人姿态
- 辅助里程计提高定位精度
- 检测碰撞事件
- 跌落预警
c复制// BMI160初始化示例代码
void BMI160_Init(void)
{
// 配置加速度计和陀螺仪的量程
BMI160_WriteReg(ACC_RANGE, ACC_RANGE_4G);
BMI160_WriteReg(GYR_RANGE, GYR_RANGE_500DPS);
// 设置输出数据速率
BMI160_WriteReg(ACC_CONF, ACC_ODR_100HZ | ACC_BWP_NORMAL);
BMI160_WriteReg(GYR_CONF, GYR_ODR_100HZ | GYR_BWP_NORMAL);
// 启用传感器
BMI160_WriteReg(CMD, ACC_PM_NORMAL);
BMI160_WriteReg(CMD, GYR_PM_NORMAL);
}
2.2.2 BQ24733电源管理IC
BQ24733是一款智能电池充电管理芯片,主要特性:
- 支持2-4节锂电池充电
- 最大充电电流8A
- 输入电压范围7-24V
- I2C接口可编程控制
在系统中的关键作用:
- 管理锂电池充放电过程
- 实时监测电池状态
- 提供过充/过放保护
- 优化充电效率
3. 软件架构设计与实现
3.1 整体软件架构
项目采用分层架构设计,从上到下分为:
- 应用层:扫地机器人业务逻辑
- RTOS层:FreeRTOS实时操作系统
- 驱动层:硬件外设驱动程序
- 硬件抽象层:CMSIS标准接口
mermaid复制graph TD
A[应用层] -->|调用| B[RTOS层]
B -->|管理| C[驱动层]
C -->|操作| D[硬件抽象层]
D -->|控制| E[硬件层]
3.2 FreeRTOS任务划分与调度
系统将功能划分为多个独立任务,每个任务有明确的优先级:
| 任务名称 | 优先级 | 功能描述 | 执行周期 |
|---|---|---|---|
| 电机控制任务 | 4 | 控制左右轮电机PWM输出 | 1ms |
| 传感器采集任务 | 3 | 读取各类传感器数据 | 10ms |
| 路径规划任务 | 2 | 计算清扫路径和避障策略 | 50ms |
| 系统监控任务 | 1 | 监测电池状态和系统健康 | 1s |
| 用户交互任务 | 0 | 处理按键和指示灯 | 事件触发 |
任务间通信机制:
- 事件组:用于任务同步
- 消息队列:传输传感器数据
- 信号量:保护共享资源
3.3 关键驱动实现细节
3.3.1 I2C驱动实现
I2C总线用于连接多个传感器,驱动实现要点:
- 硬件I2C配置
c复制void I2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
// 配置GPIO
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// 配置I2C
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStruct.I2C_OwnAddress1 = 0x00;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = 400000; // 400kHz
I2C_Init(I2C1, &I2C_InitStruct);
I2C_Cmd(I2C1, ENABLE);
}
- 带超时的读写函数实现
c复制I2C_Status I2C_ReadBytes(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint8_t len)
{
uint32_t timeout = I2C_TIMEOUT;
// 发送设备地址+写标志
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {
if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
}
// 发送寄存器地址
I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
}
// 重新发送START条件
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {
if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
}
// 读取数据
I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Receiver);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {
if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
}
// 读取数据字节
while(len) {
if(len == 1) {
I2C_AcknowledgeConfig(I2C1, DISABLE);
I2C_GenerateSTOP(I2C1, ENABLE);
}
if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) {
*data++ = I2C_ReceiveData(I2C1);
len--;
}
if((timeout--) == 0) return I2C_TIMEOUT_ERROR;
}
return I2C_OK;
}
3.3.2 PWM驱动实现
PWM用于电机速度控制,关键配置:
c复制void PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 配置GPIO
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置定时器基础
TIM_TimeBaseStruct.TIM_Prescaler = 72 - 1; // 1MHz计数频率
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStruct.TIM_Period = 1000 - 1; // 1kHz PWM频率
TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
// 配置PWM通道
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 0; // 初始占空比0%
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStruct);
TIM_OC2Init(TIM2, &TIM_OCInitStruct);
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
4. 核心算法实现
4.1 PID控制算法
用于电机速度闭环控制,算法实现:
c复制typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
float output_limit;
} PID_Controller;
void PID_Init(PID_Controller *pid, float Kp, float Ki, float Kd, float limit)
{
pid->Kp = Kp;
pid->Ki = Ki;
pid->Kd = Kd;
pid->integral = 0;
pid->prev_error = 0;
pid->output_limit = limit;
}
float PID_Update(PID_Controller *pid, float setpoint, float measurement, float dt)
{
float error = setpoint - measurement;
pid->integral += error * dt;
// 抗积分饱和
if(pid->integral > pid->output_limit) pid->integral = pid->output_limit;
else if(pid->integral < -pid->output_limit) pid->integral = -pid->output_limit;
float derivative = (error - pid->prev_error) / dt;
pid->prev_error = error;
float output = pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative;
// 输出限幅
if(output > pid->output_limit) output = pid->output_limit;
else if(output < -pid->output_limit) output = -pid->output_limit;
return output;
}
4.2 防跌落算法实现
基于红外传感器的防跌落逻辑:
c复制#define FALL_THRESHOLD 50 // 红外反射值阈值
#define FALL_COUNT_THRESHOLD 3 // 连续检测次数阈值
typedef struct {
uint8_t front : 1;
uint8_t left : 1;
uint8_t right : 1;
} Fall_Status;
Fall_Status CheckFall(void)
{
static uint8_t fall_count[3] = {0};
Fall_Status status = {0};
// 读取三个方向的传感器值
uint16_t front_val = ADC_Read(FRONT_SENSOR_CH);
uint16_t left_val = ADC_Read(LEFT_SENSOR_CH);
uint16_t right_val = ADC_Read(RIGHT_SENSOR_CH);
// 前向检测
if(front_val < FALL_THRESHOLD) {
if(++fall_count[0] >= FALL_COUNT_THRESHOLD) {
status.front = 1;
fall_count[0] = FALL_COUNT_THRESHOLD;
}
} else {
fall_count[0] = 0;
}
// 左侧检测
if(left_val < FALL_THRESHOLD) {
if(++fall_count[1] >= FALL_COUNT_THRESHOLD) {
status.left = 1;
fall_count[1] = FALL_COUNT_THRESHOLD;
}
} else {
fall_count[1] = 0;
}
// 右侧检测
if(right_val < FALL_THRESHOLD) {
if(++fall_count[2] >= FALL_COUNT_THRESHOLD) {
status.right = 1;
fall_count[2] = FALL_COUNT_THRESHOLD;
}
} else {
fall_count[2] = 0;
}
return status;
}
5. 系统优化与调试技巧
5.1 内存优化策略
在资源有限的STM32F103上,内存优化至关重要:
-
堆栈空间配置
- 通过FreeRTOSConfig.h调整任务堆栈大小
- 使用uxTaskGetStackHighWaterMark()监控堆栈使用情况
-
内存池管理
- 为高频使用的数据结构预分配内存
- 使用静态内存分配替代动态分配
-
代码优化
- 使用-O2优化级别
- 关键函数使用__attribute__((section(".fastcode")))
- 减少全局变量使用
5.2 实时性保障措施
- 中断优先级配置
c复制void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 电机控制中断 - 最高优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 传感器中断 - 中优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
// 通信接口中断 - 低优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 10;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
}
- 任务优先级设计原则
- 直接影响设备安全的任务优先级最高
- 实时性要求高的控制任务次之
- 非实时任务优先级最低
5.3 常见问题排查指南
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| 电机控制不响应 | PWM配置错误 | 检查定时器配置和GPIO复用 |
| 传感器数据异常 | I2C通信问题 | 用逻辑分析仪抓取I2C波形 |
| 系统随机重启 | 堆栈溢出 | 检查任务堆栈使用情况 |
| 电池检测不准 | ADC参考电压不稳 | 测量VREF电压稳定性 |
| FreeRTOS任务卡死 | 优先级反转 | 检查资源互斥使用情况 |
6. 项目扩展与进阶方向
6.1 功能扩展建议
-
增加SLAM算法
- 使用低成本激光雷达实现简单建图
- 基于IMU和编码器的航迹推算
-
手机APP控制
- 通过蓝牙模块连接手机
- 开发简单的控制界面
-
语音交互功能
- 集成离线语音识别模块
- 实现基本语音控制
6.2 性能提升方案
-
硬件升级
- 更换为STM32F4系列提高处理能力
- 增加外部SRAM扩展内存
-
算法优化
- 使用定点数运算替代浮点
- 实现传感器数据融合算法
-
电源管理优化
- 增加低功耗模式
- 优化电机驱动效率
这个项目展示了如何在资源受限的嵌入式平台上实现复杂的智能设备功能。通过合理的架构设计、精细的资源管理和优化的算法实现,STM32F103完全能够胜任扫地机器人的主控任务。对于嵌入式开发者来说,深入理解这个项目的实现细节,将大大提升在嵌入式系统设计和实时控制方面的能力。
