1. 项目概述与核心功能解析
这个基于STM32F103的扫地机器人项目,是我在智能家居设备开发领域接触过的最具教学价值的实战案例之一。整套系统以某米1代扫地机为硬件平台,完整实现了延边清扫、多传感器避障、防跌落检测和自动回充等核心功能。特别值得一提的是,项目中采用的FreeRTOS实时操作系统与硬件驱动层的架构设计,非常符合工业级嵌入式开发的规范要求。
从功能模块来看,系统主要包含以下几个关键部分:
- 运动控制系统:通过编码器输入捕获实现精准测距,配合PID算法控制电机转速
- 传感器融合:整合BMI160陀螺仪姿态数据、红外测距和碰撞检测等多传感器输入
- 电源管理:基于BQ24733芯片的智能充放电控制
- 任务调度:利用FreeRTOS实现多任务并发处理
提示:这个项目特别适合想要从单片机开发进阶到RTOS系统开发的工程师学习,代码中关于任务优先级划分和资源互斥的处理方式值得仔细研究。
2. 硬件架构深度解析
2.1 主控芯片选型考量
STM32F103C8T6作为主控芯片的选择体现了典型的成本与性能平衡:
- Cortex-M3内核:72MHz主频满足实时控制需求
- 外设资源:内置3个USART、2个SPI、2个I2C接口,完美适配扫地机的多传感器需求
- 存储配置:64KB Flash + 20KB SRAM,在运行FreeRTOS时仍有充足余量
实际项目中,我们通过以下方式最大化利用芯片资源:
c复制// 时钟树配置示例(使用外部8MHz晶振)
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 72MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);
}
2.2 关键外设驱动实现
2.2.1 BMI160陀螺仪驱动
这个9轴运动传感器的数据采集采用了典型的I2C通信方案:
c复制#define BMI160_ADDR 0x68<<1
void BMI160_Init(void) {
uint8_t data[2] = {0x7E, 0x11}; // 软复位
HAL_I2C_Master_Transmit(&hi2c1, BMI160_ADDR, data, 2, 100);
HAL_Delay(50);
data[0] = 0x40; // 配置加速度计
data[1] = 0x28; // 200Hz, +/-4g
HAL_I2C_Master_Transmit(&hi2c1, BMI160_ADDR, data, 2, 100);
}
注意:实际项目中我们发现BMI160对I2C时序非常敏感,必须在初始化后添加足够延时,否则会出现通信失败。
2.2.2 电机控制PWM实现
采用TIM1和TIM2产生4路PWM分别控制两个驱动电机:
c复制void PWM_Init(void) {
TIM_OC_InitTypeDef sConfigOC = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 71; // 1MHz计数频率
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 999; // 1kHz PWM
HAL_TIM_PWM_Init(&htim1);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比0%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
}
3. 软件架构设计与实现
3.1 FreeRTOS任务划分策略
项目中创建了5个主要任务,优先级从高到低如下:
| 任务名称 | 优先级 | 堆栈大小 | 主要功能 |
|---|---|---|---|
| EmergencyStop | 6 | 256 | 紧急停止处理 |
| SensorPoll | 5 | 512 | 传感器数据采集 |
| MotionControl | 4 | 512 | 运动控制算法 |
| BatteryMonitor | 3 | 256 | 电池管理 |
| UIHandler | 2 | 384 | 用户界面更新 |
任务创建代码示例:
c复制void StartDefaultTask(void const * argument) {
osThreadDef(EmergencyStop, EmergencyStop_Task, osPriorityHigh, 0, 256);
emergencyStopHandle = osThreadCreate(osThread(EmergencyStop), NULL);
// 其他任务初始化...
}
3.2 延边清扫算法实现
延边功能通过状态机实现,核心逻辑如下:
c复制typedef enum {
WALL_FOLLOW_IDLE,
WALL_FOLLOW_SEARCH,
WALL_FOLLOW_TRACE,
WALL_FOLLOW_TURN
} WallFollowState;
void WallFollow_Handler(void) {
static WallFollowState state = WALL_FOLLOW_IDLE;
static uint32_t wall_last_seen = 0;
switch(state) {
case WALL_FOLLOW_IDLE:
if(user_cmd == CMD_WALL_FOLLOW) {
state = WALL_FOLLOW_SEARCH;
}
break;
case WALL_FOLLOW_SEARCH:
if(IR_GetDistance() < 15) { // 检测到墙壁
state = WALL_FOLLOW_TRACE;
wall_last_seen = HAL_GetTick();
}
break;
case WALL_FOLLOW_TRACE:
// PID控制保持10cm距离
float error = IR_GetDistance() - 10.0f;
motor_speed = PID_Calculate(&wall_pid, error);
if(HAL_GetTick() - wall_last_seen > 500) {
state = WALL_FOLLOW_SEARCH;
}
break;
}
}
4. 关键问题排查与优化经验
4.1 防跌落传感器误触发问题
初期测试中发现机器人在深色地毯上会误触发防跌落传感器,解决方案包括:
- 增加传感器采样频率(从10Hz提升到50Hz)
- 实现滑动窗口滤波算法:
c复制#define WINDOW_SIZE 5
uint16_t ir_values[WINDOW_SIZE] = {0};
uint16_t GetFilteredIR(void) {
static uint8_t index = 0;
uint32_t sum = 0;
ir_values[index] = IR_Read();
index = (index + 1) % WINDOW_SIZE;
for(int i=0; i<WINDOW_SIZE; i++) {
sum += ir_values[i];
}
return sum / WINDOW_SIZE;
}
4.2 充电接触不良问题
原装充电座金属触点容易氧化导致接触不良,我们在软件层面增加了以下保护措施:
- 充电电流缓启动控制
- 接触电阻实时监测
- 自动重试机制(最多3次)
充电状态检测逻辑:
c复制void Charging_Handler(void) {
static uint8_t retry_count = 0;
if(BQ24733_GetStatus() & CHG_CONNECTED) {
if(BQ24733_GetCurrent() < 100) { // 电流过小
retry_count++;
if(retry_count < 3) {
BQ24733_ResetConnection();
} else {
Set_Error(ERR_CHARGING_FAIL);
}
} else {
retry_count = 0;
}
}
}
5. 代码规范与工程管理建议
5.1 值得借鉴的代码风格
项目中体现出的优秀编码习惯:
- 每个函数前都有完整的注释块:
c复制/**
* @brief 读取BMI160加速度计数据
* @param buf : 输出缓冲区(6字节)
* @retval HAL状态
*/
HAL_StatusTypeDef BMI160_ReadAccel(uint8_t *buf) {
// ...实现代码
}
- 使用Doxygen兼容的注释规范
- 所有宏定义集中管理在config.h中
- 硬件相关与硬件无关代码严格分离
5.2 Git版本控制实践
虽然原始项目未提供Git记录,但根据开发经验建议采用以下分支策略:
code复制master
├── release/v1.0
├── dev
│ ├── feature/wall_follow
│ ├── feature/anti_drop
│ └── hotfix/charging_issue
└── docs
关键.gitignore配置:
code复制# Keil工程忽略项
*.uvoptx
*.uvprojx
*.lst
*.dep
*.crf
*.o
*.d
这个项目最让我印象深刻的是它对实时性要求的精准把控——通过合理划分任务优先级,即使在处理IAP升级时也能保证避障功能的实时响应。对于想要从裸机开发转向RTOS的工程师,建议重点研究其中的任务通信机制和中断优先级配置部分。
