公司动态
基于51单片机GSM自动售货机设计:硬件接线与程序框架
在实际课程设计或电子竞赛中自动售货机项目经常被选作综合应用题目它把显示、按键输入、电机控制、存储接口和通信协议放在同一个系统里跑通。这里给出一个“基于51单片机GSM自动售货机”的完整设计思路和可复现框架。系统以51单片机为控制核心使用LCD1602显示商品信息4x4矩阵键盘完成商品选择和模拟投币ULN2003驱动直流电机模拟出货GSM模块负责交易通知和缺货报警。下面按“功能边界、硬件接线、软件结构、GSM短信、联调验证、常见坑”的顺序展开最后给出一份适合答辩前检查的清单。1. 先把自动售货机的功能边界和硬件链路理清楚1.1 系统要实现什么效果课程设计要求的功能通常包括显示多路商品和单价、用户选择商品、模拟投入金额、余额足够时触发电机出货并扣减库存、库存不足时提示缺货。加入GSM模块之后系统还能把交易结果或缺货信息发送短信到管理员手机。这种“本地控制 远程通知”的结构和真实售货机相比简化了两个地方支付环节用按键模拟投币货道结构用单个电机加模型货道代替弹簧机构。但控制逻辑是完整的适合作为学习项目。硬件工作流程大致如下上电后单片机从AT24C02读取商品价格和库存。LCD1602显示“A1商品 3元 余5件”这样的待机画面。用户按矩阵键盘选择货道再按投币键模拟投入1元、5元或10元。余额足够时单片机把对应电机开启一段时间模拟推出商品。成功交易后库存减一余额清零LCD更新。如果库存为0系统向管理员发送缺货短信并停留在缺货提示界面。真实售货机里的支付、退币、多货道控制都是在这个基础上扩展出来的。1.2 为什么选用51单片机加GSM模块这里以STC89C52RC或者AT89S52这类51内核MCU为例。原因是课程设计平台成熟资料多学习成本低。它内部有8KB Flash、256B RAM外接11.0592MHz晶振后足够完成LCD刷新、键盘扫描、I2C读写和串口AT指令解析。GSM模块以SIM800C或SIM900A为例。这类模块通过串口接收AT指令可以发短信、打电话、做GPRS数据传输。自动售货机场景只需要短信通知所以用文本模式发送短信就够了。相比ESP8266加云平台GSM方案不依赖WiFi和服务器适合在没有网络覆盖的校内项目场景中使用缺点是必须插SIM卡功耗和资费要提前考虑。选型依据可以整理成一张表器件型号示例作用选型原因主控STC89C52RC控制逻辑资料多、烧录方便、价格低显示LCD1602商品/金额/状态显示字符显示足够代码简单键盘4x4矩阵键盘商品选择/投币模拟减少IO占用存储AT24C02保存价格和库存I2C非易失存储掉电不丢电机驱动ULN2003驱动直流电机内部达林顿管可直接驱动小电机GSMSIM800C/SIM900A短信通知串口AT指令不依赖WiFi提示蜂鸣器操作提示便于交互反馈1.3 软件状态怎么划分程序不要把全部逻辑都写在主循环里建议用状态机组织。最小状态集合如下IDLE待机显示等待用户选择商品。SELECT用户已选择某货道显示商品价格等待投币。PAY投币累加显示当前金额和剩余金额。DELIVER余额满足运行电机扣库存发短信回到IDLE。EMPTY缺货提示发短信后回到IDLE。状态迁移通过按键事件和金额判断触发这样主循环保持简单也方便扩展“退币”“改价”等新功能。2. 硬件接线是第一道关卡必须电平兼容、电源可靠、信号共地2.1 接线总表下面是学习环境下的接线方案。不同开发板引脚定义不同接线前要先看原理图。模块引脚51单片机引脚说明LCD1602RSP2.0命令/数据选择LCD1602RWP2.1读写选择可接地LCD1602ENP2.2使能脉冲LCD1602D0-D7P0.0-P0.78位数据线P0要接10K上拉蜂鸣器I/OP2.5提示音矩阵键盘行P1.0-P1.34行矩阵键盘列P1.4-P1.74列AT24C02SCLP2.3I2C时钟AT24C02SDAP2.4I2C数据需要上拉ULN2003IN1P3.4电机控制ULN2003OUT1电机负极电机另一端接VCCGSMRXDP3.1 TXD单片机发送到模块GSMTXDP3.0 RXD模块发送到单片机GSMGNDGND必须共地这里有两个关键点。第一P0口内部没有上拉电阻接LCD数据线时必须外接上拉否则显示会出现乱码。第二GSM模块发短信时功耗波动很大最好独立供电不能直接从单片机最小系统的稳压管取大电流。2.2 GSM模块串口连接差异点SIM800C和SIM900A模块的串口大多是TTL电平可以直连51单片机的P3.0和P3.1。连接时遵循“发送接接收”的交叉原则51 TXD(P3.1) - 模块 RXD 51 RXD(P3.0) - 模块 TXD GND - 模块 GND如果使用RS232接口版本的GSM模块中间要加MAX232转换。判断方法很简单查看模块手册确认串口电平是TTL还是RS232。TTL模块不能直接用电脑USB转RS232线连接要先弄清楚两端电平。还要注意51单片机串口是TTL 5V电平部分GSM模块的RXD引脚只能承受3.3V。虽然很多模块内部做了耐压处理但稳妥做法是在单片机TXD到模块RXD之间串联1K电阻分压或者使用电平转换芯片。这个电阻在很多项目中都能避免模块串口被烧坏。2.3 电源设计的学习环境和生产环境对比学习环境下常犯的错误是让单片机开发板的USB供电同时带GSM模块。GSM在注册网络或发短信瞬间电流可能超过1AUSB口电压会被拉低随后出现单片机复位、GSM模块重启、短信发送失败等连锁问题。推荐学习环境下这样供电负载供电方式注意事项单片机开发板USB线或5V适配器先单独上电GSM模块独立5V/2A适配器必须与开发板共地电机独立5V电源不经过主板弱电LCD/EEPROM/蜂鸣器开发板5V电流小可以共用生产环境则应该设计成单电源系统用12V或24V适配器输入经过DC-DC或LDO降压到5V给单片机、传感器和GSM模块供电每个大电流器件加滤波电容GSM模块电源引脚旁放置100uF和0.1uF电容。还要考虑电压跌落检测和看门狗电路。3. C语言程序怎么组织显示、按键、出货各模块如何分工3.1 程序模块划分程序按功能拆成独立文件方便调试和答辩讲解main.c 主流程、状态机 lcd1602.c LCD显示驱动 lcd1602.h matrix_key.c 矩阵键盘扫描 matrix_key.h uart.c 串口初始化、发送函数 uart.h gsm.c GSM AT指令控制 gsm.h motor.c 电机驱动 motor.h at24c02.c EEPROM读写 at24c02.h config.h 引脚定义、商品价格、容量宏51单片机的资源有限模块之间尽量通过全局状态变量或函数返回值传递数据不要做大而全的控制类。比如键盘扫描只负责返回键值不直接操作电机交易状态由main里的状态机决定。3.2 LCD1602显示模块LCD1602用8位模式连接P0口初始化时先写命令0x38设置8位双行显示再写0x0C关闭光标最后清屏。void LcdWriteCmd(unsigned char cmd) { LCD_RS 0; LCD_RW 0; P0 cmd; LCD_EN 1; DelayShort(2); LCD_EN 0; } void LcdWriteDat(unsigned char dat) { LCD_RS 1; LCD_RW 0; P0 dat; LCD_EN 1; DelayShort(2); LCD_EN 0; } void LcdInit(void) { LcdWriteCmd(0x38); // 8位、2行、5x7点阵 LcdWriteCmd(0x0C); // 显示开光标关 LcdWriteCmd(0x06); // 地址指针递增 LcdWriteCmd(0x01); // 清屏 } void LcdShowString(unsigned char row, unsigned char col, unsigned char *str) { unsigned char i 0; if (row 0) LcdWriteCmd(0x80 col); else LcdWriteCmd(0xC0 col); while (str[i] ! \0 i 16) { LcdWriteDat(str[i]); i; } }LCD1602显示乱码或者不显示多半不是代码问题而是硬件问题P0没接上拉、对比度电位器没调好、RW引脚悬空导致读写异常。先做“写命令清屏”的测试再用串口或LED确认程序是否运行到LCD初始化。3.3 矩阵键盘和交易状态机矩阵键盘扫描用行列反转法。下面给出一个低成本的按键扫描函数返回1-16表示不同按键返回0表示无按键。unsigned char KeyScan(void) { unsigned char row, col; P1 0x0F; // 行输出0列输入 if ((P1 0x0F) 0x0F) return 0; DelayKey(); if ((P1 0x0F) 0x0F) return 0; row P1 0x0F; P1 0xF0; // 列输出0行输入 col P1 0xF0; return (row | col); }在main中把扫描到的键值映射为“选择商品”“投币1元”“投币5元”“确认购买”等事件。这里要提醒一点矩阵键盘在键值映射时不要直接在按键处理函数里写业务流程而应把处理逻辑放在状态机分支中否则一次长按会重复触发出货。下面是简化的状态机片段void TradeStateMachine(unsigned char key) { switch (state) { case IDLE: if (key 1 key 8) { currentItem key - 1; if (stock[currentItem] 0) { state EMPTY; LcdShowEmpty(); GsmSendStockWarning(currentItem); } else { state SELECT; LcdShowPrice(currentItem); } } break; case SELECT: if (key COIN_1) { balance 1; state PAY; LcdShowBalance(); } else if (key COIN_5) { balance 5; state PAY; LcdShowBalance(); } break; case PAY: if (key COIN_1) { balance 1; LcdShowBalance(); } else if (key COIN_5) { balance 5; LcdShowBalance(); } else if (key CONFIRM balance price[currentItem]) { state DELIVER; } break; case DELIVER: MotorStart(2000); stock[currentItem]--; balance - price[currentItem]; SaveStock(currentItem); GsmSendTradeNotify(phoneAdmin, currentItem); state IDLE; LcdShowIdle(); break; case EMPTY: if (key KEY_RETURN) state IDLE; break; default: state IDLE; break; } }这个状态机是最高层的业务逻辑。实际代码还需要处理余额清零和超时返回否则用户投币后不购买金额会一直停留在界面上。3.4 电机出货和库存扣减直流电机驱动最简单的方式是ULN2003。它内部是达林顿管输入端接单片机IO输出端可以驱动十几伏、几百毫安的小电机。典型控制代码sbit MotorIn P3^4; void MotorStart(unsigned int ms) { unsigned int t; MotorIn 1; for (t 0; t ms; t) { Delay1ms(); } MotorIn 0; }电机转多久需要根据货道结构标定。如果转太少商品卡住如果转太久会把下一个商品也带出来。常见做法是先手动测试记录正常出一次货需要的时间再减去200ms到300ms作为余量。库存信息建议保存在AT24C02里每次扣减后立即写回。EEPROM写入有寿命限制不能每次按键都写。实际项目通常是交易完成后写一次初始化时读一次。4. GSM短信发送AT指令是核心难点4.1 文本模式发送短信的工作原理GSM模块内部集成了协议栈单片机不需要理解短信编码细节只要通过串口发AT指令。文本模式发送短信的完整交互过程如下单片机发送: AT\r\n 模块返回: OK 单片机发送: ATCMGF1\r\n 模块返回: OK 单片机发送: ATCMGS13800138000\r\n 模块返回: 单片机发送: Hello, sold out.\x1A 模块返回: CMGS: 108 OK其中\r\n是回车换行CMGF1设置短信格式为文本模式CMGS设置收件人号码。模块返回后输入短信内容最后以十六进制0x1A作为结束符。如果发送0x1B则取消发送。这里的关键是结束符0x1A是单字节不能用字符串方式发送必须用串口发送单字节函数。4.2 串口发送AT指令代码先实现串口初始化和发送函数unsigned char uartRxBuf[64]; unsigned char uartRxLen 0; void UartInit(void) { SCON 0x50; // 模式18位UART允许接收 TMOD 0x0F; TMOD | 0x20; // 定时器1模式2 TH1 0xFD; // 11.0592MHz9600波特率 TL1 0xFD; TR1 1; ES 1; } void UartSendByte(unsigned char dat) { SBUF dat; while (!TI); TI 0; } void UartSendString(unsigned char *s) { while (*s) { UartSendByte(*s); } } void UartIsr() interrupt 4 { unsigned char dat; if (RI) { RI 0; dat SBUF; if (uartRxLen sizeof(uartRxBuf) - 1) { uartRxBuf[uartRxLen] dat; } } }注意串口中断服务函数中RI和TI都需要处理。如果程序进入串口中断后不返回要先检查中断标志位是否被正确清除。4.3 等待模块返回OK和发短信发送短信时要等待模块回复。可以写一个函数在指定时间内搜索接收缓存是否包含目标字符串bit GsmWaitAck(unsigned char *expect, unsigned int timeoutMs) { unsigned int t; uartRxLen 0; for (t 0; t timeoutMs; t) { Delay1ms(); if (FindInRxBuf(expect)) { uartRxLen 0; return 1; } } uartRxLen 0; return 0; }完整发送流程bit GsmSendSms(unsigned char *phone, unsigned char *msg) { if (!GsmWaitAck(OK, 1000)) return 0; UartSendString(ATCMGF1\r\n); if (!GsmWaitAck(OK, 1000)) return 0; UartSendString(ATCMGS\); UartSendString(phone); UartSendString(\\r\n); if (!GsmWaitAck(, 2000)) return 0; UartSendString(msg); UartSendByte(0x1A); if (!GsmWaitAck(OK, 5000)) return 0; return 1; }51单片机没有操作系统阻塞式等待虽然简单但在等待期间无法扫描键盘和刷新LCD。比较稳妥的做法是把短信发送放到一个标志位之后在主线循环里分步执行或至少给每个等待函数设置合理超时避免模块无响应时程序一直卡住。4.4 交易通知和缺货提醒的触发时机在状态机的DELIVER分支中交易成功后调用GsmSendSms。短信内容可以是[Vending] A1 sold. stock4.在EMPTY分支中发送[Vending] A2 stock empty. please refill.GSM模块发送中文短信需要设置字符集或者使用PDU模式直接发Unicode容易乱码。如果项目答辩时只需要证明短信能力优先发英文短信最稳妥。很多课程设计代码里直接发中文然后出现乱码原因就出在字符编码上。5. 从串口助手到整机联调按顺序验证每一层5.1 学习环境下的启动验证流程整个系统不要一次性联调建议按下面顺序逐层验证第一步验证单片机最小系统。给51单片机烧录一个LED闪烁程序确认晶振、复位、电源正常。第二步验证LCD1602。烧录LCD显示程序显示一行固定字符。确认对比度、上拉电阻和数据线。第三步验证矩阵键盘。烧录键盘扫描程序按下按键后让蜂鸣器响或LED亮。确认键值映射。第四步验证电机和EEPROM。按键触发电机转动重启开发板后确认库存数字被保存。第五步验证GSM模块。先用电脑USB转TTL串口助手连接GSM模块发送AT观察返回OK再发送ATCSQ查看信号最后手动发送一条短信。第六步把GSM模块接到51单片机串口烧录完整程序测试交易和缺货短信。5.2 正常现象与异常现象对照表层次正常现象常见异常检查方向最小系统数码管或LED正常闪烁无反应、晶振不起振电源、复位、晶振LCD1602固定字符清晰显示黑块、乱码、无显示对比度、上拉、RW、EN时序矩阵键盘每按一个键有唯一键值按键无反应、键值重复行列接线、消抖时间电机通电后转固定时间不转、发热电源、ULN2003、IO默认电平EEPROM重启后库存保持不变重启后恢复初始值I2C时序、上拉、器件地址GSM串口返回OK短信到达无返回、ERROR交叉接线、波特率、SIM卡、网络5.3 故障排查通用链路GSM相关故障优先按以下顺序排查信号线和地线是否连接正确。检查“TXD接RXD、RXD接TXD、GND接GND”这三根线。模块是否开机。SIM800C的NETLIGHT指示灯不亮说明模块没开机慢闪说明已注册网络快闪说明正在搜网。串口波特率是否一致。模块默认通常是9600确认单片机TH1初值对应11.0592MHz晶振。是否关闭回显。程序发送AT时返回“AT\r\nOK”一般不影响搜索关键字OK但发送CMGS时回显可能干扰解析。SIM卡是否识别。发送ATCPIN?返回READY才说明SIM卡正常。信号是否足够。发送ATCSQ返回值如“CSQ: 15,0”第一项越大越好。是否注册网络。发送ATCREG?返回“0,1”或“0,5”表示已注册。如果程序自己发送短信失败先不要改代码而是用PC串口助手手动走一遍AT流程。手动流程能通问题多半在程序时序手动流程就不通问题在模块、SIM卡、天线或电源。还有一个细节GSM模块在等待时返回内容可能是\r\n。搜索时建议使用strstr在缓存里匹配不要只看最后一个字符。6. 这些坑最容易在课程设计和答辩时暴露6.1 三个典型坑和解决方案坑一程序一执行到发短信就卡住。现象LCD停在交易成功界面电机已经转但程序不再响应按键。原因GsmWaitAck使用阻塞式延时GSM在信号弱的区域长时间不返回OK导致主循环被卡死。解决给等待函数增加超时超时返回失败如果仍不够把GSM发送改成非阻塞状态机