第一章:售货机系统概述
各位同学,今天咱们聊聊售货机这个看似简单、实则门道不少的系统。我做了十几年嵌入式,售货机项目接过不下五个,每次都能发现新坑。嗯,先从最基础的硬件架构说起。
1.1 硬件架构:主控、电机、传感器、支付模块
售货机的硬件,说白了就是四个核心部分:主控、电机、传感器、支付模块。我习惯把它们比作人的大脑、手脚、眼睛和钱包。
主控芯片
主控是整台机器的决策中心。我个人习惯用STM32F4系列或者国产的GD32F4,主频150MHz以上,带硬件浮点运算。为什么?因为电机控制需要PWM,支付模块需要串口,传感器需要GPIO中断——这些外设一个都不能少。
关键参数:
- 主频:≥150MHz
- Flash:≥512KB(代码+UI资源)
- RAM:≥128KB(任务栈+数据缓存)
- 外设:至少3路UART、2路定时器、1路I2C
电机系统
电机这块我踩过坑。早期项目用了普通直流减速电机,结果出货后频繁卡货。后来换成步进电机+闭环控制,问题才解决。售货机一般需要:
- 出货电机:步进电机,带编码器反馈
- 升降电机(如果有):直流有刷电机,限位开关保护
- 制冷风扇:无刷直流电机,PWM调速
传感器
传感器是售货机的「眼睛」。我建议至少配置:
- 货道检测:红外对射管,检测货物是否掉落
- 门状态:磁簧开关,防止非法开门
- 温度传感器:DS18B20,冷链必备
- 硬币/纸币识别:专用模块,串口通信
避坑指南:我曾经在一个项目中用了廉价的红外传感器,结果阳光直射时误触发,导致系统认为货物已出货而实际没出。后来全部换成带调制解调的红外对射管,问题才解决。
支付模块
支付模块现在主流是微信/支付宝扫码+IC卡。我建议用串口通信,波特率115200,数据格式固定。注意:支付模块的电源要独立,否则电机启动时电压跌落会导致支付模块复位——这个坑我帮客户排查了整整两天。
1.2 软件分层:驱动层、OS层、应用层
软件架构我坚持三层分离。你想想看,如果所有代码揉在一起,后期维护就是噩梦。我见过一个项目,驱动代码和应用代码混写,改个电机参数要翻遍整个工程。
驱动层
驱动层直接操作硬件寄存器。我习惯把每个外设封装成一个.c/.h文件:
// motor_driver.h
void Motor_Init(void);
void Motor_SetSpeed(uint8_t speed);
void Motor_Stop(void);
uint8_t Motor_GetStatus(void);
// sensor_driver.h
uint8_t Sensor_CheckDrop(void);
uint8_t Sensor_GetDoorStatus(void);
float Sensor_ReadTemperature(void);
// payment_driver.h
void Payment_Init(uint32_t baud);
uint8_t Payment_Scan(void);
uint32_t Payment_GetAmount(void);
驱动层的原则:只做硬件操作,不做业务逻辑。比如电机驱动只管发PWM,不管什么时候该转。
OS层
OS层我选FreeRTOS,轻量、稳定、文档全。任务划分是关键:
- 主控任务:状态机管理,优先级最高
- 电机控制任务:实时性要求高,优先级次之
- 支付处理任务:串口通信,中等优先级
- UI刷新任务:可以低优先级,但别饿死
- 看门狗任务:最低优先级,喂狗用
个人经验:任务栈大小别拍脑袋。我习惯先给每个任务分配256字节,跑起来后用vTaskGetRunTimeStats()看实际使用量,再调整。曾经有个同事给UI任务分配了1024字节,结果实际只用128,白白浪费内存。
应用层
应用层是业务逻辑的集合。我习惯用状态机实现:
typedef enum {
STATE_IDLE, // 待机
STATE_SELECTING, // 用户选择商品
STATE_PAYING, // 支付中
STATE_DISPENSING, // 出货中
STATE_COMPLETE, // 完成
STATE_ERROR // 错误处理
} VendingState;
VendingState g_state = STATE_IDLE;
void App_Task(void *param) {
while(1) {
switch(g_state) {
case STATE_IDLE:
// 等待用户操作
if(Button_Pressed()) g_state = STATE_SELECTING;
break;
case STATE_SELECTING:
// 处理商品选择
break;
// ... 其他状态
}
vTaskDelay(10); // 100Hz轮询
}
}
1.3 实时性需求分析
实时性,说白了就是「在规定时间内必须做完该做的事」。售货机对实时性的要求,我总结为三个等级:
| 等级 | 响应时间 | 典型场景 | 后果 |
|---|---|---|---|
| 硬实时 | <1ms | 电机堵转保护、传感器中断 | 设备损坏或安全事故 |
| 软实时 | 10-100ms | 支付超时、出货确认 | 用户体验差,可能丢单 |
| 非实时 | >100ms | UI刷新、日志记录 | 影响不大,但别太慢 |
为什么会这样?因为电机堵转时,如果1ms内没检测到并停止,线圈可能烧毁。支付超时如果超过100ms,用户可能以为没支付成功而重复扫码。
实时性设计要点:
- 中断服务程序要短小精悍,只做标志位设置
- 高优先级任务不要阻塞等待,用信号量或消息队列
- 临界区保护要精准,别用大段关中断
- 看门狗超时时间要合理,我一般设3-5秒
嗯,这里要注意:实时性不是越快越好。我曾经有个项目,把UI刷新任务优先级设得特别高,结果电机控制反而被抢了CPU时间,导致出货卡顿。后来把优先级调低,问题解决。所以,实时性设计要平衡,不是所有任务都需要「立即响应」。
好了,第一章就聊这么多。下一章咱们深入FreeRTOS的任务调度,看看怎么在售货机里合理分配CPU时间。记住:硬件是骨架,软件是灵魂,实时性是血液——三者缺一不可。