尧图精选

基于STM32与Proteus的智能宠物喂食器仿真设计实战

🕒 发布时间:2026/10/2 18:46:12 📁 来源:尧图网络
1. 项目缘起与整体设计思路1.1 为什么选择STM32做智能宠物喂食器养宠家庭最头疼的事情之一就是出差或者加班回不了家猫狗饿肚子。市面上成品喂食器动辄几百块功能还未必合心意——出粮量固定、不能远程看状态、卡粮了也不知道。自己动手做一个成本能压到百元以内而且每个环节都可控。主控选STM32理由很直接资源够用、资料多、价格便宜。具体到型号我推荐STM32F103C8T6也就是常说的“蓝板”或“最小系统板”。72MHz主频、64KB Flash、20KB SRAM跑一个喂食器逻辑绰绰有余。关键是这块芯片的教程铺天盖地从GPIO点灯到定时器中断、PWM输出、I2C通信每个外设都有现成例程可以参考。对于学生做毕业设计或者工创赛作品来说选它意味着遇到问题能快速搜到答案不至于卡在一个冷门芯片上浪费时间。LCD显示部分我选的是LCD1602字符型液晶屏配合I2C转接板使用。为什么不用TFT彩屏因为喂食器需要显示的信息很有限——当前时间、下次喂食时间、剩余食物量、系统状态这些用两行16个字符完全能表达清楚。LCD1602价格便宜十块钱以内、驱动简单、功耗低而且I2C接口只占用两个IO口接线清爽。如果你非要用ILI9341那种彩屏当然也可以但驱动复杂度会上升一个量级对于以功能实现为主的项目来说性价比不高。仿真平台用Proteus 8.9以上版本。Proteus的好处是你不需要先焊板子就能验证整个逻辑——LCD能不能正常显示、按键响应对不对、电机转不转全都能在电脑上跑通。等仿真没问题了再照着电路图去焊实物成功率会高很多。这个思路特别适合在校学生实验室元器件不全、焊接工具不够用的情况下仿真就是你的第一道保险。1.2 系统整体架构拆解整个系统按功能划分成四个模块主控模块、显示模块、输入模块、执行模块。主控模块就是STM32F103C8T6最小系统负责协调所有外设、跑定时器、处理逻辑判断。显示模块是LCD1602加I2C转接板接在PB6SCL和PB7SDA上用硬件I2C驱动。输入模块用三个独立按键分别接PA0、PA1、PA2配置为上拉输入按下时接地产生下降沿。执行模块包括两部分一个是SG90舵机用来控制出粮口的开合接在PA8上利用TIM1_CH1输出PWM另一个是有源蜂鸣器接在PB0用于喂食提醒和故障报警。这里解释一下为什么用舵机而不是步进电机。五线四相步进电机确实能精确控制出粮量但驱动电路复杂需要ULN2003或者专用驱动芯片代码量也大。SG90舵机只需要一路PWM信号转角范围0到180度通过控制转角就能实现“开闸”和“关闸”两个状态。对于定量喂食来说你只需要控制开闸时间长短来调节出粮量比如开闸500ms对应一份粮开闸1000ms对应两份粮。这样既简化了硬件代码也好写。电源部分要注意STM32和LCD用3.3V或5V都可以但舵机必须用5V供电而且舵机堵转时电流能冲到1A以上。所以仿真里我用了独立的5V电源给舵机实物中建议用LM2596降压模块从12V降到5V再给舵机和单片机分别供电避免舵机动作时把单片机电压拉低导致复位。1.3 仿真设计的核心价值很多人觉得仿真就是“走个过场”其实不是。仿真能帮你提前发现三类问题电路连接错误、逻辑时序冲突、资源分配不合理。比如LCD1602的I2C地址实物上通常是0x27或0x3F但Proteus里的I2C转接板模型地址可能不一样。如果你不先在仿真里跑一遍焊好板子发现屏幕不亮就得拿万用表一根根线查费时费力。再比如舵机和定时器的关系TIM1_CH1输出PWM时如果你同时用TIM1做其他中断就可能冲突。仿真里跑一下示波器一看波形不对马上就能定位问题。还有一点很实际Proteus支持源码级调试。你可以把Keil编译出来的.hex文件加载到STM32模型里设置断点、单步执行、查看变量。这比在实物上拿LED灯调试高效得多。我自己的习惯是所有逻辑先在Proteus里跑通包括按键消抖、定时器中断、LCD刷新确认没问题了再动手焊板子。这样实物调试阶段基本就是“一次点亮”省心很多。2. 核心细节解析与实操要点2.1 LCD1602的I2C驱动与显示优化LCD1602本身是并行接口有8位数据线和3位控制线直接接STM32要占用11个IO口。加上I2C转接板之后只需要SCL和SDA两根线IO占用大幅减少。转接板上的芯片通常是PCF8574这是一个8位I2C扩展芯片把并行信号转成I2C协议。在Proteus里你需要搜索“PCF8574”或者“LCD1602 I2C”来找到对应的模型。接线时注意转接板的SCL接STM32的PB6SDA接PB7VCC接5VGND接地。STM32的I2C引脚需要配置为复用开漏输出并且外接4.7kΩ上拉电阻到5V。Proteus里可以在SCL和SDA线上各放一个上拉电阻实物中如果转接板自带上拉就不用额外加了。驱动代码方面我建议自己写一个简单的I2C底层而不是直接用STM32的硬件I2C库。原因有两个一是硬件I2C在某些情况下会卡死比如从机不响应时二是软件模拟I2C更灵活换引脚方便。软件I2C的时序很简单起始条件、发送地址、发送数据、等待应答、停止条件。每个bit的延时用delay_us(2)就够了STM32F103跑72MHz2微秒的延时对应144个时钟周期足够PCF8574响应。显示内容规划上第一行显示当前时间和系统状态比如12:30 READY第二行显示下次喂食时间和剩余份数比如NEXT 18:00 x2。这里有个细节LCD1602每行只能显示16个字符所以时间格式要用24小时制状态用缩写。我试过显示“FEEDING”和“LOW FOOD”这样的英文单词刚好能塞进去。如果你要显示中文那就得换带中文字库的LCD12864但驱动会复杂很多对于喂食器来说没必要。注意Proteus里的LCD1602模型有时候对I2C时序要求比较严格如果屏幕不显示先把I2C时钟降到100kHz以下试试。另外PCF8574的地址在Proteus里默认是0x20而实物转接板通常是0x27代码里要对应修改。2.2 定时器与喂食逻辑的实现喂食器的核心逻辑是“定时喂食”这就要用到STM32的定时器。我用TIM2做系统时基配置为1ms中断一次。在中断服务函数里维护一个全局变量sys_tick每进一次中断加1。这样sys_tick每1000就代表1秒每60000就代表1分钟。主循环里通过比较sys_tick的差值来判断是否到了喂食时间。为什么不用RTCSTM32F103有内置RTC但需要外接32.768kHz晶振而且RTC的寄存器操作比较繁琐。对于仿真项目来说用定时器模拟时钟更简单也更容易在Proteus里验证。当然如果你要做实物并且需要断电保存时间那就得加DS3231模块通过I2C读取时间。DS3231的精度比STM32内置RTC高很多而且自带电池座断电后时间不丢。喂食逻辑的伪代码大概是这样if (current_hour feed_hour current_minute feed_minute feed_flag 0) { servo_open(); // 舵机转到开闸角度 delay_ms(feed_duration); // 保持开闸状态控制出粮量 servo_close(); // 舵机转回关闸角度 feed_flag 1; // 标记本次喂食已完成 buzzer_beep(2); // 蜂鸣器响两声提示 } if (current_minute ! feed_minute) { feed_flag 0; // 分钟变了重置标志位 }feed_duration这个参数需要根据实际出粮机构来调。我用的是SG90舵机加一个旋转挡板开闸500ms大概出粮10克左右。你可以先设一个值然后在仿真里观察舵机动作时间再根据实物出粮量微调。舵机角度方面关闸时给0度PWM脉宽0.5ms开闸时给90度脉宽1.5ms。TIM1的PWM频率设50Hz预分频系数和自动重装载值这样算72MHz / (72 * 20000) 50Hz所以预分频器设为71自动重装载值设为19999。按键部分要处理消抖。机械按键按下时会有10到20ms的抖动如果不处理一次按下可能被识别成多次。我的做法是在定时器中断里每20ms扫描一次按键状态连续三次检测到同一状态才确认。这样既消抖了又不占用主循环时间。三个按键的功能分别是模式键进入设置模式、加键增加数值、减键减少数值。设置模式下LCD第二行显示可调参数比如喂食时间的小时和分钟用加/减键调整模式键确认。2.3 舵机PWM控制与电源设计SG90舵机的控制信号是周期20ms的PWM波高电平持续时间决定转角0.5ms对应0度1.5ms对应90度2.5ms对应180度。STM32的TIM1_CH1PA8可以输出这样的PWM。配置步骤使能TIM1和GPIOA时钟PA8配置为复用推挽输出TIM1预分频器设为71自动重装载值设为19999捕获比较模式设为PWM模式1使能输出比较预装载。计算过程72MHz时钟预分频71后得到1MHz计数频率即每个计数周期1微秒。自动重装载值19999意味着计数从0到19999共20000个周期对应20ms。比较值设为500就是0.5ms高电平设为1500就是1.5ms。代码里用__HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, 500)就能改变舵机角度。电源方面仿真里可以直接用Proteus的DC电源给舵机供5V。实物中要注意舵机的电流波动很大空载时几十毫安堵转时能到1A以上。如果和STM32共用一路5V舵机动作时电压会被拉低可能导致STM32复位。我的做法是用12V电源适配器输入经过LM2596降到5V给舵机再经过AMS1117-3.3降到3.3V给STM32。两路电源共地但各自独立滤波。舵机电源线上并一个1000μF的电解电容能有效吸收电流尖峰。提示Proteus里的舵机模型有时候不响应PWM信号检查一下信号极性。如果舵机不动先用示波器看PA8有没有波形输出。另外舵机的PWM信号线不要和LCD的I2C线走得太近避免干扰。2.4 蜂鸣器与状态指示蜂鸣器用有源蜂鸣器接在PB0上配置为推挽输出。有源蜂鸣器内部自带振荡电路给高电平就响给低电平就停控制简单。无源蜂鸣器需要输出方波才能响代码上要多写一个PWM没必要。蜂鸣器的使用场景有三个喂食开始时响两声提示宠物来吃饭食物不足时响一声提醒主人加粮按键操作时短响一声提供触觉反馈。代码里封装一个buzzer_beep(uint8_t times)函数参数是响的次数每次响100ms间隔100ms。食物不足的检测仿真里可以用一个按键模拟。实物中可以用红外对管或者超声波模块来检测粮仓余量。超声波模块HC-SR04比较常用Trig接PB1Echo接PB2通过定时器捕获测量回波时间。不过超声波模块在Proteus里没有现成模型仿真阶段可以用按键代替等实物阶段再换成超声波。这里要提醒一句HC-SR04是5V供电Echo输出也是5V直接接STM32的3.3V IO可能会损坏芯片中间要加一个电阻分压或者电平转换模块。3. 实操过程与核心环节实现3.1 Proteus仿真电路搭建步骤打开Proteus 8.9新建工程选择“Create a new project”然后按以下步骤操作。第一步放置元器件。在元件库搜索并添加STM32F103C8主控、LCD1602显示、PCF8574I2C转接、SG90舵机、BUZZER蜂鸣器、BUTTON按键、RES电阻、POT-HG电位器用于调节LCD对比度、DC电源和地。注意LCD1602要选带I2C接口的模型或者用PCF8574加普通LCD1602组合。第二步连接电路。STM32的PB6接PCF8574的SCLPB7接SDAPCF8574的P0到P7接LCD1602的D0到D7RS、RW、E分别接PCF8574的P2、P1、P0具体对应关系看转接板定义。PA8接舵机信号线PB0接蜂鸣器正极PA0、PA1、PA2各接一个按键到地。LCD的V0接电位器中间脚电位器两端接5V和地用来调对比度。第三步加载程序。在Keil里编译好.hex文件双击STM32模型在“Program File”栏选择.hex文件晶振频率设为8MHz外部晶振或72MHz内部PLL后。Proteus里STM32模型默认用8MHz晶振但代码里SystemInit会把时钟倍频到72MHz所以这里填8MHz就行。第四步运行仿真。点击左下角播放按钮观察LCD是否显示、按键是否响应、舵机是否转动。如果LCD不亮先调电位器再检查I2C地址。如果舵机不转用Proteus的示波器看PA8波形。3.2 Keil工程配置与代码结构Keil工程用标准库或者HAL库都可以。我习惯用标准库因为代码量小、执行效率高而且Proteus对标准库的兼容性更好。新建工程时选择STM32F103C8勾选“Copy CMSIS core files”和“Copy startup code”然后手动添加标准库的驱动文件stm32f10x_gpio.c、stm32f10x_rcc.c、stm32f10x_tim.c、stm32f10x_i2c.c如果用硬件I2C、stm32f10x_usart.c调试用。代码结构分五个文件main.c主循环和逻辑、lcd_i2c.cLCD驱动、timer.c定时器配置和中断、servo.c舵机控制、key.c按键扫描。每个文件对应一个.h头文件声明对外接口。main.c的主循环长这样int main(void) { SystemInit(); delay_init(); LCD_Init(); TIM2_Init(); // 1ms中断 TIM1_PWM_Init(); // 舵机PWM KEY_Init(); BUZZER_Init(); LCD_ShowString(0, 0, Pet Feeder V1.0); delay_ms(1000); LCD_Clear(); while (1) { KEY_Scan(); // 按键扫描 Update_Display(); // 刷新LCD Check_Feed_Time(); // 检查喂食时间 Check_Food_Level(); // 检查食物余量 } }Update_Display()函数每200ms调用一次避免LCD刷新太快导致闪烁。刷新时先算好要显示的字符串再一次性写入LCD不要一个字符一个字符地写那样会看到明显的滚动效果。3.3 关键参数计算与调试记录定时器中断频率TIM2挂载在APB1总线上时钟频率36MHzAPB1预分频系数为2时定时器时钟倍频到72MHz。预分频器设为71得到1MHz计数频率自动重装载值设为999这样每1000个计数溢出一次即1ms中断一次。公式中断频率 72MHz / ((711) * (9991)) 72M / 72M 1Hz不对算错了。72MHz / 72 1MHz1MHz / 1000 1kHz即1ms。对就是这样。舵机PWMTIM1挂载在APB2总线上时钟72MHz。预分频71计数频率1MHz。自动重装载19999周期20ms。比较值500对应0.5ms1500对应1.5ms。LCD刷新率主循环里用sys_tick % 200 0判断每200ms刷新一次。实测这个频率人眼看起来是连续的又不会因为刷新太快导致I2C总线占用过高。调试时遇到一个问题LCD显示的数字会跳动比如时间从12:30变成12:3然后才变成12:31。原因是刷新时先清了整行再写新内容清行和写内容之间有时间差。解决办法是先把新字符串拼好再用LCD_ShowString一次性覆盖不要先清屏。另外数字变化时如果位数不同比如9变成10要在前面补空格保持字符串长度一致。3.4 实物焊接与联调要点仿真跑通之后就可以焊实物了。元器件清单STM32F103C8T6最小系统板一块、LCD1602加I2C转接板一套、SG90舵机一个、有源蜂鸣器一个、轻触按键三个、AMS1117-3.3稳压模块一个、LM2596降压模块一个、12V电源适配器一个、洞洞板一块、杜邦线若干。焊接顺序先焊电源部分测好5V和3.3V输出正常再焊STM32最小系统板烧录一个点灯程序确认芯片工作然后焊LCD转接板写个测试程序显示“Hello”接着焊按键和蜂鸣器最后接舵机。每焊一部分就测试一部分不要全焊完了再上电那样出了问题很难定位。实物调试时舵机可能会干扰LCD显示。我遇到过一次舵机一转LCD就花屏。原因是舵机电源和LCD电源共用了一根杜邦线电流波动通过电源线耦合到了LCD。解决办法是给LCD的电源线单独走一根并且在LCD的VCC和GND之间并一个100μF的电容。另外舵机的信号线用屏蔽线或者双绞线能减少辐射干扰。注意STM32的BOOT0引脚要接地BOOT1随便。如果用SWD下载PA13和PA14不要被占用。LCD的I2C地址如果和代码里不一致用这个函数扫描一下for(addr0; addr128; addr) { if(I2C_CheckDevice(addr)0) printf(Found: 0x%02X\n, addr); }。4. 常见问题与排查技巧实录4.1 仿真阶段典型问题速查问题现象可能原因排查方法解决方案LCD不显示任何内容对比度不对调电位器调至字符清晰LCD显示方块I2C地址错误用地址扫描改代码中的地址舵机不转PWM没输出示波器看PA8检查TIM1配置按键无响应上拉没生效万用表测电平配置上拉输入蜂鸣器不响极性接反交换正负极有源蜂鸣器分正负仿真跑飞堆栈溢出看PC指针增大堆栈或优化代码Proteus里还有一个坑STM32模型默认的晶振频率是8MHz但如果你在代码里用了HSE外部高速时钟而Proteus里没有接晶振芯片就不工作。解决办法是在Proteus的STM32属性里把晶振频率改成8MHz或者在代码里用HSI内部高速时钟。我一般直接用HSI省事精度也够用。4.2 实物调试踩坑记录坑一LCD1602显示乱码。第一次焊好之后LCD显示的是乱码不是预期的字符。查了半天发现是I2C转接板的地址搞错了。实物转接板是PCF8574T地址是0x27但代码里写的是0x20。改过来就好了。后来我养成了一个习惯拿到任何I2C模块先跑地址扫描程序确认地址再写驱动。坑二舵机抖动。舵机上电后不停抖动角度也不对。用示波器看PWM信号发现周期是20ms没错但高电平时间不稳定有时候1.5ms有时候1.8ms。原因是TIM1的PWM输出和TIM2的中断冲突了——TIM2中断优先级比TIM1高中断发生时PWM输出被暂停了一下。解决办法是把TIM1的PWM输出配置为“强制输出”模式或者在TIM2中断里不要做太耗时的操作。我把按键扫描从TIM2中断里移到了主循环问题就解决了。坑三喂食时间不准。设了18:00喂食结果17:58就喂了。原因是sys_tick是32位变量每1ms加1大概49天会溢出一次。溢出时如果正好在比较时间就会误判。解决办法是用(current_tick - last_tick) interval的方式判断时间差而不是直接比较绝对值。这样即使溢出差值计算也是正确的。坑四蜂鸣器干扰LCD。蜂鸣器响的时候LCD会闪一下。原因是蜂鸣器的工作电流通过地线影响了LCD。解决办法是在蜂鸣器的VCC和GND之间并一个100nF的陶瓷电容并且蜂鸣器的地线单独走一根到电源地不要和LCD共用。4.3 代码优化与功能扩展建议代码层面有几个可以优化的地方。第一把LCD显示的数字转换成字符串时用sprintf比手动拆位方便但sprintf会占用较多Flash空间。如果Flash紧张可以自己写一个Int2Str函数。第二按键扫描用状态机比用延时消抖更高效状态机不阻塞主循环响应也更快。第三喂食记录可以存在STM32的Flash里用FLASH_ProgramHalfWord写入这样断电后还能知道今天喂了几次。功能扩展方面可以加一个超声波模块检测粮仓余量当余量低于阈值时LCD显示“LOW FOOD”并蜂鸣报警。还可以加一个红外接收头用遥控器手动触发喂食适合临时加餐。如果要做联网功能可以加一个ESP8266模块通过串口和STM32通信把喂食记录上传到云端。不过联网部分涉及的内容比较多建议先把本地功能做稳定了再考虑。提示STM32的Flash写入次数有限约1万次不要频繁写喂食记录。可以每天写一次或者只在喂食次数变化时写。另外写Flash之前要先擦除整个页STM32F103的页大小是1KB。4.4 元器件选型避坑指南LCD1602买的时候注意看是不是“I2C转接板已焊接”的版本。有些商家卖的是裸屏需要自己焊转接板对新手不友好。另外蓝色背光比绿色背光好看但蓝色背光的对比度调节范围窄电位器要调得比较准。SG90舵机市面上仿品很多仿品的齿轮是塑料的容易扫齿。正品SG90的齿轮是尼龙加纤维耐用一些。如果出粮机构阻力大建议用MG90S金属齿轮舵机扭矩更大。STM32最小系统板买的时候注意看晶振是8MHz还是12MHz。大部分是8MHz但有些板子是12MHz代码里的SystemInit要对应修改。另外板子上的LED通常接在PC13调试时可以用它指示程序运行状态。电源模块LM2596降压模块效率高但输出纹波大给舵机供电没问题给STM32供电最好再加一级LC滤波。AMS1117-3.3线性稳压器纹波小但发热大输入5V输出3.3V时如果电流200mA功耗就是(5-3.3)*0.20.34W不加散热片会烫手。这个项目我从仿真到实物大概花了一周时间其中仿真调试占了两天实物焊接和调试占了三天剩下两天写代码和整理文档。最大的体会是仿真不是万能的但没有仿真万万不能。仿真能帮你排除80%的电路和逻辑错误剩下的20%是实物特有的问题比如接触不良、电源干扰、元器件公差。把仿真做扎实了实物调试就是水到渠成的事。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →