资讯动态

基于STM32的超市智能购物车Proteus仿真设计

发布时间:2026/10/14 19:01:06 来源:尧图企业网站定制
简介基于STM32F103R6单片机的超市智能购物车Proteus仿真设计面向嵌入式初学者及课程设计场景完整演示了超声波避障、按键模拟选品与自助结算的工作流程。工程包共181个文件以C语言源程序、头文件、Keil MDK工程配置、HEX烧写文件及Proteus工程文件pdsprj为主体积仅4.81MB源码和仿真图分离存放便于按模块查阅与调试。源程序覆盖HC-SR04超声波测距、中断按键处理、商品单价统计和总价计算等核心代码配合仿真电路可直观观察购物车遇障减速、绕行以及结算页面更新效果。设计使用MDK KEIL完成编写与下载配合Proteus 8.11搭建虚拟外设无需真实硬件即可运行验证STM32F103R6内置Flash也免去了外部下载器依赖。已有478人学习项目从硬件选型、软件逻辑到仿真验证形成完整闭环特别适合作为STM32外设综合应用或电子设计课程设计参考对照源码和仿真图能快速理解单片机系统开发思路、缩短调试周期。1. 基于STM32的超市智能购物车Proteus仿真先想清楚仿真能替你解决什么超市智能购物车这个题目在STM32单片机课程设计和入门项目里出现频率很高核心功能无非是“扫码/输码到商品上。做这个题的人很多但不少人在Proteus仿真这一层就卡住了——要么LCD不亮要么键盘扫描没反应要么程序跑起来但完全不知道对不对。这个题目里真正值得花时间的不是“仿真能不能跑”而是“计价逻辑、称重阈值、状态切换”这些软件行为能否被低成本验证。本文按我自己的做法把仿真原理图、工程代码、HEX装载、参数配置和五个高频翻车点一次讲完新手能照步骤复现熟手可以直接跳到第5章避坑和第6章实物迁移对照。2. 仿真图怎么搭STM32F103C8T6最小系统与五个外设模块很多人在仿真阶段翻车不是因为代码难而是因为一开始就把方案设计得太复杂。Proteus不是万能的RFID读卡器、扫码枪、直流电机这类外设在仿真里要么没有模型要么时序行为与真机差异很大硬堆上去只会得到一堆“黑匣子”。我的习惯是先把购物车功能拆成五个仿真里可验证的模块再决定元件选型和接线。2.1 为什么把购物车功能收敛成五个模块对照真实超市购物车的完整链路商品识别靠扫码枪或RFID重量靠称重传感器加HX711结算靠RFID感应和后台数据库。但Proteus仿真里RFID-RC522模型要写SPI时序HX711模型对SCK脉冲时序要求严格在仿真时钟下经常卡死或读出乱数。因此我一般把“真实功能”映射成“仿真可验证功能”商品识别在仿真里等价于“得到一个商品编号”所以矩阵键盘输入三到四位ID号就模拟了扫码枪扫码后的结果称重传感器加HX711在仿真里等价于“一个随负载变化的电压量”用一个电位计接到STM32的ADC引脚就能模拟出口防盗和结算在仿真里等价于“一个外部触发信号”用一个普通按键模拟红外对管检测到推车出门。这样功能链路一点没少计价、超重报警、结算逻辑全部能跑但绕开了Proteus里最不稳定的两个仿真模型。2.2 各模块选型与Proteus对应元件名我在Proteus里搭建这套系统主控选用STM32F103C6T6或C8T6元件库里直接搜型号就能找到。液晶显示用LM016L这是Proteus里最常见的1602字符液晶模型直接支持8位并行写模式。输入设备用KEYPAD-SMALLCALC四行四列计算器键盘正好当矩阵键盘用。称重用POT-HG三端电位计中心抽头电压随旋钮角度线性变化接到PA0引脚。蜂鸣器用BUZZER有源蜂鸣器模型LED用普通红色LED出口检测用BUTTON按键模拟。还有若干电阻主要用于键盘外部上拉。元件选型时有几个关键参数要提前设好电位计POT-HG的电压源端必须接3.3V网络不能接默认的5V否则ADC读数满量程跟后面计算公式对不上LM016L的对比度引脚也需要一个电位计调节我习惯用POT-LIN拧到中间位置矩阵键盘KEYPAD-SMALLCALC本身不带上拉电阻必须在Proteus里给行线加10k上拉到3.3V否则STM32内部上拉在这个仿真模型里不可靠后面第5章会专门讲这个问题。2.3 完整接线表与关键引脚分配引脚分配决定了代码里GPIO初始化怎么写这一步尽量一次定死。我的分配方案如下LCD1602数据线D0到D7接PB0到PB7RS接PB8EN接PB9RW直接接地只写不读。矩阵键盘四行接PC0到PC3四列接PC4到PC7。ADC称重电压接PA0蜂鸣器接PA1超重LED接PC13出口检测按键接PB10并启用内部上拉。整个系统供电3.3V复位和晶振按STM32F103C6T6最小系统接法晶振频率在Proteus里配置为8MHz。这套分配的优点是把LCD独占一个完整端口读写时序简单排查时逻辑清楚。缺点是PB0和PB1默认是ADC通道输入但代码里显式配置为推挽输出后没有问题。接线顺序建议从主控开始往外走先接电源和复位再接LCD然后键盘、电位计、LED、蜂鸣器、按键。每接完一个模块就用万用表探针确认网络Proteus里最怕悬空引脚尤其LCD的RW如果不接地而是完全悬空背光亮但屏幕永远不显示内容。3. 计价逻辑在代码里怎么落地商品表、状态机与称重标定仿真电路搭完之后真正决定这个项目能不能用的就是程序架构。我的做法是先把商品数据表建好把购物车状态切换做成一个简单的状态机再把称重ADC读数换算成重量。这三件事做完整个购物车逻辑就只剩下按键事件和LCD刷新出问题的地方也就很集中。3.1 用状态机管理“待输入/称重/结算”三个态购物车的工作流拆成四个状态空闲等待输入商品ID输入完成后进入称重确认确认后回到输入态继续加购最后结算态显示总价并等待出口确认。状态机的好处是每个状态下只响应特定按键避免出现“按一下#结果清单清空”这种逻辑混乱。struct定义和商品表如下价格单位统一用“分”避免浮点误差重量单位统一用“克”。typedef struct { char id[4]; char name[17]; unsigned int price; unsigned char byWeight; unsigned int tara; } GoodsItem; const GoodsItem goodsTable[16] { {001, Cola 500ml, 350, 0, 0}, {002, Chips 120g, 650, 0, 0}, {003, Apple, 150, 1, 20}, {004, Banana, 120, 1, 25}, {005, Milk 1L, 1200, 0, 0}, {006, Bread, 850, 0, 0}, };说明一下字段含义byWeight为0表示按件计费总价等于单价乘数量byWeight为1表示按重量计费price字段表示每100克的价格tara字段表示塑料袋皮重。用int类型存储价格和重量计价结果用unsigned long累加避免单件商品价格乘数量后溢出。商品表最多放16条是为了匹配4乘4键盘的输入空间实际开发中改成数组加越界判断就行。3.2 商品数据表与计价规则按件和按重分开算购物车数据结构用一个数组保存当前已加入的商品索引、数量和重量。每次主循环里只维护这个数组LCD显示直接遍历它。计价时不改任何全局状态只读遍历方便后续在结算前复用同一段代码做金额汇总。typedef struct { unsigned char goodsIndex; unsigned char qty; unsigned int weightGram; } CartItem; CartItem cart[20]; unsigned char cartCount 0; unsigned long calcTotalPrice(void) { unsigned long total 0; for (int i 0; i cartCount; i) { const GoodsItem *g goodsTable[cart[i].goodsIndex]; if (g-byWeight) { unsigned int net cart[i].weightGram; if (net g-tara) net - g-tara; else net 0; total (unsigned long)(net / 100) * g-price; total (unsigned long)(net % 100) * g-price / 100; } else { total (unsigned long)g-price * cart[i].qty; } } return total; }按重量计费时net除以100得到整百克数余数部分按比例计费最后统一加进total。这里故意不用浮点数因为STM32的软件浮点库在仿真里会显著拖慢运行速度而且浮点误差在多次累加后会放大。如果你把价格单位从分改成元一定要同时检查所有乘法路径否则总价可能少一位。另有一个容易忽略的点购物车数组上限是20条但商品表只有16项代码里必须做cartCount上限判断否则演示时连续加购会越界把LCD显示数据冲掉。3.3 称重模块的仿真替代电位计加ADC线性标定称重是整个购物车项目里仿真与实物差异最大的一块。真机用HX711加应变式传感器需要先让传感器稳定再读取24位AD值标定过程通常要挂标准砝码做两点校准。仿真里我用电位计模拟这件事电位计中心抽头电压0到3.3V对应重量0到5000克STM32内部ADC是12位所以ADC读数0到4095对应0到5000克。unsigned int readWeightGram(void) { ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); uint16_t adcVal ADC_GetConversionValue(ADC1); unsigned int gram (unsigned int)((unsigned long)adcVal * 50000 / 4095); return gram; }代码里先把ADC值换算成重量50000对应5000克放大10倍是为了让后面去皮和计价时有更高精度。ADC采样时间配置为239.5个周期也是有意为之仿真里采样时间太短会读到跳变的转换结果这个参数直接影响称重稳定性后面避坑章节还会展开。电位计每旋转一点电压线性变化重量值也随之变化对应真实场景里超市员工把商品放上称重台的过程。这里需要说明的是仿真里没有“传感器上电稳定”的过程所以代码里没有加延时等待但真机上必须加上。4. 在Proteus里跑通的最小操作路径HEX加载、时钟配置与提速电路和代码都有了接下来把程序编译成HEX文件装进Proteus里的STM32模型。这个过程并不复杂但每一步都有对应的配置项和常见错误我按自己的操作顺序写一遍你先跟着走后面再处理问题。4.1 Keil工程里打开HEX输出编译出可装载固件我通常在Keil MDK里建标准外设库工程把上面的代码按头文件和源文件组织好在编译前打开HEX输出开关。注意如果你用的是STM32CubeIDE生成的HEX路径和命名规则与Keil不同但最终效果一致关键是Proteus只认HEX文件。Project - Options for Target - Output - 勾选 Create HEX File勾选后重新编译在工程目录的Objects文件夹下就能找到同名HEX文件。Proteus加载它之后仿真运行的是你编译出的机器码不依赖任何外部动态库。有一个容易被忽略的细节编译时如果开了编译器优化某些初始化延时循环会被优化掉LCD初始化时序就乱了所以我建议优化等级选-O0或默认。还有一个是Debug选项卡里的Dialog DLL参数如果是做Keil和Proteus联调需要设置DARMSTM.DLL和-pSTM32F103C6但单独用HEX文件仿真时不需要不用管它。4.2 Proteus加载HEX、配置时钟与电源在Proteus原理图里双击STM32F103C6T6模型弹出Edit Component对话框在Program File一栏右侧点文件夹图标选择刚才编译出的HEX文件。然后检查Crystal Frequency为8MHz这个值必须和代码里的HSE_VALUE一致否则串口波特率和定时器全部偏移。很多人的USART乱码就是这么来的和Proteus本身没什么关系。电源方面整个系统用3.3VProteus默认电源轨是5V需要在设计菜单里添加3.3V网络并把STM32的VDD和LCD等器件接到3.3V网络。检查方法是在仿真运行时用电压探针量VDD引脚如果显示4.99V说明你接错网络了STM32仿真模型对超压敏感可能导致程序随机复位。把这些配好后点击运行如果看到LCD背光亮起来且程序在跑说明基本链路已经通了。如果点击运行后左下角时间不动优先查HEX文件路径是否包含中文字符Proteus对中文路径支持一直不好。4.3 让仿真跑得更快的三个设置STM32的Proteus仿真天然比51单片机慢原因是ARM处理器的周期精确建模开销大。如果程序逻辑已经调通但每次加购商品都要等十几秒才能看到LCD刷新那就需要做提速设置。第一在System菜单下的Animation Options里把动画速率调高并把“Show Voltage and Logic State”关闭取消勾选仿真时刷新引脚的电压颜色能明显降低图形渲染开销。第二把Keil工程主频从72MHz改为36MHz同时把Proteus模型里的Crystal Frequency改成4MHz或者保持8MHz外部晶振但代码里PLL倍频调成4.5倍频这样GPIO翻转速度下降仿真步长可以拉大整体仿真体验流畅很多代价是定时器延时函数的计数值也要对应调整。第三减少LCD刷新的频率不要每检测到一个按键就全屏重写可以只在状态变化时才调用LCD刷新函数否则LCD写入指令会占掉大部分仿真时间。这三个设置做完购物车添加商品时基本能在一秒内看到LCD响应。5. 避坑与排查Proteus仿真STM32购物车的5条实战记录这部分直接给结论每条都是我在不同项目里实际踩过的问题。仿真环境和真机调试最大的区别是你真机调不出来还可以用示波器量波形仿真里出问题经常是“程序没跑”“LCD没反应”“按键失灵”这些特别玄学的现象必须靠现象反推原因。5.1 LCD1602只显示方块程序却已经跑起来现象LCD背光亮但第一行显示一排黑色方块程序里LED闪烁和蜂鸣器测试都正常。原因最常见的是LCD初始化时序里缺了上电等待时间或者RW引脚不是低电平。Proteus的LM016L模型对初始化时序比真实LCD宽容如果代码里初始化序列不完整它会以“显示方块”作为默认输出。另一个常见原因是EN脉冲宽度太短STM32的GPIO翻转在仿真里被拉长但脉冲宽度不足仍然可能被LM016L模型忽略。解决先确认RW接地然后在初始化里加至少30ms上电延时写完命令字后延时不少于1ms如果还是方块把LCD数据线改成8位并行模式并确认PB0到PB7的GPIO配置为推挽输出高频模式。仿真里有个隐藏坑PB0和PB1默认是ADC通道如果你初始化代码里只开了GPIO时钟而没调用PinMode把PB0/PB1设为输出Proteus模型会按默认复用功能处理LCD数据就永远不对。5.2 程序卡死在初始化或外设时序错乱现象点运行时左下角仿真时间在跳动但程序好像卡在某个while循环里单步调试发现PC指针停在ADC或者延时函数里出不来。原因Proteus的STM32模型对时序要求比真机严格得多特别是在未初始化外设寄存器前直接访问ADC数据寄存器模型会返回一个无效标志位导致while等待永远不满足。解决ADC采样前必须执行ADC_Cmd(ADC1, ENABLE)并等待ADC启动稳定这里我建议用下面的安全等待方式ADC_Cmd(ADC1, ENABLE); while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));如果你在真机上没这问题而在Proteus里卡死大概率是代码里直接读了ADC转换结果但没有使能ADC外设时钟或没有启动转换。另一种卡死场景是软件延时函数在时钟配置不匹配时延时不准确尤其你从72MHz工程改成36MHz后如果delayMs函数里的循环次数没改原来20ms的延时可能变成40msLCD初始化时序反而对了矩阵键盘消抖却被拉长到看起来像失灵。排查时用单步执行看PC停在哪个函数是最快的定位方法。5.3 矩阵键盘按下去没反应或按键串键现象按KEYPAD-SMALLCALC的数字键LCD没有任何反应偶尔按一次会识别成旁边的键。原因KEYPAD-SMALLCALC在Proteus里默认没有上拉电阻而STM32内部上拉在这个仿真模型里是直连弱上拉模型的行为和真实芯片有差异串键原因多半是扫描间隔太短没有等列线电平稳定。解决给四根行线在原理图里加10k电阻上拉到3.3V这是我在所有键盘项目里的标准做法。然后键盘扫描代码里每切换一列后加1到2ms延时等列线被拉低逻辑稳定后再读行线。还有一个细节判断按键释放的while循环必须放在消抖之后否则仿真里按键抖动会被当成一次新的按键触发。改造后的扫描核心循环如下for (int col 0; col 4; col) { GPIO_WriteLow(GPIOC, 1 (col 4)); delayMs(2); for (int row 0; row 4; row) { if (GPIO_ReadInputDataBit(GPIOC, 1 row) 0) { delayMs(10); if (GPIO_ReadInputDataBit(GPIOC, 1 row) 0) { while (GPIO_ReadInputDataBit(GPIOC, 1 row) 0); return keys[row][col]; } } } GPIO_WriteHigh(GPIOC, 0x0F 4); }这里用低四行和高四列列线和行线都在GPIOC上读取时要注意GPIO_WriteLow只把当前列拉低其余列保持高避免同一时刻多列同时拉低导致串键。5.4 ADC称重数值跳变现象电位计旋到一个固定位置不动LCD上显示的重量值会在几十克范围内来回跳。原因Proteus里ADC模型在转换过程中对采样时间敏感采样周期设置太短时内部采样电容没有充足时间充电转换结果的低几位会不稳定。另一个原因是ADC通道配置后没有执行校准STM32的ADC在复位后需要进行一次自校准才能保证线性度仿真模型部分实现了这个行为。解决把ADC采样时间配置为最大239.5周期同时在初始化里执行ADC_ResetCalibration和ADC_StartCalibration。如果跳变仍然存在可以读8次取平均值再换算成重量这个滤波方法比任何复杂算法都直接。但我建议不要用一阶低通滤波因为仿真里的噪声不是白噪声而是采样瞬间引起的确定性跳变均值滤波更有效。代码里我一般这样处理uint32_t sum 0; for (int i 0; i 8; i) { sum readAdcValue(); } uint16_t stableAdc sum / 8;均值加采样时间拉长之后称重显示基本能稳定在正负5克以内这个误差在仿真里完全够用。真机上这套逻辑可以保留但真机的应变式传感器噪声特性和仿真完全不同最终还是要依据实际波形去调整。5.5 仿真能跑烧到真板LCD乱码现象同一份代码在Proteus里一切正常一烧到真板上LCD显示乱码或者显示延迟特别严重。原因Proteus的LM016L模型对时序容错性很强真实LCD模块对初始化时序、写命令时序的要求严格得多。常见差异在于Proteus里上电后10ms初始化就能成功真机需要等待电源稳定通常要40ms以上Proteus里EN高脉冲宽度随便几百纳秒就够真机要求至少450纳秒。解决所有LCD时序延时按数据手册最大值实现初始化上电延时取100ms写命令后延时取2ms。如果你的实物是自己焊的最小系统板还要检查晶振起振后代码有没有等外部时钟稳定一般加一个由系统滴答定时器延时的上电等待函数比直接delay更可靠。这个问题是仿真转实物最典型的翻车点我通常会在代码里统一用一个宏定义LCD_DELAY_STEP方便在不同硬件上调整。6. 从仿真到实物哪些能直接复用、哪些必须重写仿真不是终点如果你要做实物心里要有张表状态机和计价逻辑可以百分百复用矩阵键盘扫描基本能用LCD驱动必须增加时序裕量ADC称重代码必须替换掉因为真机不是电位计而是HX711或专用称重芯片。先说LCD实物的初始化延时按第5章加大后直接可用8位并口接线也一致。再说键盘外部10k上拉正好是很多开发板的标配代码可以原样搬过去。最需要重写的是称重部分。真机的HX711读数是24位输出速率默认10Hz代码要改成先等待DOUT拉低表示转换完成再拉高SCK逐个脉冲读出数据满量程标定必须用标准砝码做两点校准仿真里的线性公式只能用于逻辑验证。整个标定函数建议单独放一个文件不要和计价逻辑混在一起原因很简单计价逻辑写完后几乎不会变而称重标定在实物阶段你会反复改缩放系数。我自己曾经吃过这个亏仿真里把购物车状态机调得很顺直接按同一套按键逻辑做了实物结果HX711的读取时序在真机上总是偶尔错一拍重量显示偶尔跳成0。后来才意识到Proteus里电位计是即时的真机传感器有响应时间和噪声必须加软件滤波和零点漂移补偿。从那之后我定的习惯是仿真阶段就把“称重模块”抽象成单独的接口函数让计价逻辑只依赖接口返回值以后换传感器只改一个文件也不影响显示和结算。最后建议你在仿真里把所有商品ID穷举输入一遍检查总价是否符合手算结果再把电位计旋到最大看超重报警阈值是否准时触发。这个验证比任何形式化的测试都实用。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑