ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于51/STM32的出租车计价器设计:从原理图到PCB与代码实现

基于51/STM32的出租车计价器设计:从原理图到PCB与代码实现 简介一套基于51内核单片机的出租车计价器完整设计资源适合电子、嵌入式方向学生课程设计、毕业设计参考也适合单片机爱好者学习功能逻辑与软硬件联调。资源共34个文件压缩包约6.08MB包含C语言与头文件源码主程序、LCD1602显示、DS3231时钟、延时等模块、编译生成的HEX及OBJ/LST中间文件、Altium Designer绘制的原理图与PCB以及PDF说明文档可配合查看电路连接与底层驱动。功能覆盖无乘客时显示时间、行驶计时、金额显示元末位为小数、起步里程3公里可调并支持白天/黑夜两套独立费率设定贴近真实计价器场景。已有1197人学习浏览文件类型覆盖源码、工程配置、设计图纸与文档目录结构清晰便于按需查阅从模块化代码到PCB版图有助于完整理解计价逻辑、显示驱动、时钟管理以及参数设置的实现路径。1. 一块“会算钱”的板子到底难在哪出租车计价器在很多人眼里就是个“里程乘以单价”的算术题轮子转一圈里程加一点到最后显示个金额。但真正把它当成一个单片机项目做下来你会发现核心问题根本不在乘法而在“怎么让误差可控地测量、在颠簸和电磁干扰里稳定计数、再把 PCB 和结构件装进一个防拆的盒子里”。网上能搜到大量 51 单片机的计价器代码却很少有人把硬件链路讲透传感器信号进来怎么整形、单片机用哪个定时器、PCB 地线怎么铺才不影响脉冲计数、电源纹波怎么压住。这篇文章不站在“源码搬运”的角度而是按“你需要自己画出原理图、走完 PCB、烧进代码”的顺序把一套能跑通的最小系统拆开讲覆盖 STC/STM32 两种常见选型并给出可直接参考的电路结构、PCB 布线和计价逻辑代码。适合手里有开发板但没独立画过完整板子的人读老手可以在抗干扰和误差标定部分找回一点信息量。2. 计价器必须做到的三个基本量距离、时间和分段计价2.1 距离不是“测轮子转了多少圈”而是“数脉冲”计价器的距离信号通常来自变速箱或轮轴上的霍尔传感器、光电编码器或干簧管。每转一圈产生一个或多个脉冲单片机要做的不是“算圈数”而是用外部中断或定时器捕获脉冲沿再结合“每公里脉冲数 K”换算距离。换算公式为距离km 脉冲计数 N / 每公里脉冲数 KK 的标定是硬骨头不同车型轮胎直径不一样同样一圈走过的实际距离不同。常见做法是在实车上推 1 公里记录编码器输出的脉冲总数把结果写进 EEPROM。代码里不能把 K 写成常量要留一个“标定模式”长按某个按键进入后每收到一个脉冲就计数第二次数到目标脉冲时自动算出 K 并保存。脉冲输入不是直接接单片机引脚就完事。传感器输出可能是开漏、推挽或只有 mV 级感应电压必须经过整形电路。最简单的做法是电压比较器加迟滞防止轮轴振动导致同一圈输出多个毛刺。比较器可选 LM393参考电压取信号中间值反馈电阻加 100kΩ 到 1MΩ 量级形成约 10~50mV 的迟滞。如果你用的是接近开关输出 NPN 开漏那只需要上拉电阻加一个 RC 低通再把信号送进单片机。2.2 计价逻辑要能区分低速等待和正常行驶现实中出租车在红灯前停着轮子不转但不代表不收费。计价规则通常分三块起步价覆盖前若干公里或若干分钟、超过起步里程后按每公里单价累加、低速或等待时按时间计费一般 5 分钟折合一公里。这些规则不能写死在主循环里否则定时漂移会直接造成多收或少收。推荐把计价拆成三个独立的量总里程、低速累计时间、总金额用同一个 1 秒时基去更新。这里有一个新手常见的坑用while循环里加delay(1000)来做 1 秒计时。一旦外部中断频繁触发主循环被拉开秒周期会失真。更稳定的做法是让定时器 T0 工作在方式 1产生 10ms 中断主循环只做显示和键盘处理计价状态机放在中断外的一个标志位驱动。代码结构大致这样unsigned int tick_10ms; unsigned int sec_counter; unsigned char flag_1s; void Timer0_ISR(void) interrupt 1 { TH0 0xFC; // 12MHz晶振10ms重装值 TL0 0x18; tick_10ms; if (tick_10ms 100) { tick_10ms 0; flag_1s 1; // 主循环通过标志位感知秒变化 } } void main() { init_timer0(); while (1) { if (flag_1s) { flag_1s 0; update_fare(); // 加减速、等待时间、金额都在这里算 } refresh_display(); scan_keyboard(); } }这段代码里update_fare()每秒只执行一次外部中断只负责把脉冲计数变量加一。两个任务互不阻塞显示刷新和按键扫描不会影响计费精度。2.3 金额计算的定点数陷阱一般人不喜欢用浮点数做金额累加因为单片机资源有限且浮点误差会在长期累加中显形。推荐把金额表示为“分”的整数例如起步价 8 元存成 800单价每公里 1.6 元存成 160。里程用“米”做整数等待时间用“秒”做整数。每秒更新时按“分/秒”计算增量虽然计算过程中会产生小数但可以用整数除法加余数积累long fare_cents 0; long total_meters 0; int wait_seconds 0; int wait_cents_acc 0; // 余数积累 void update_fare(void) { int added_cents 0; if (total_meters START_METERS) { // 超过起步距离按每米单价增加 added_cents (total_meters - START_METERS) * PRICE_PER_METER_CENTS; fare_cents START_PRICE_CENTS added_cents; } else { fare_cents START_PRICE_CENTS; } if (is_low_speed) { wait_seconds; // 每低速等待5分钟原本按一公里里程折算 wait_cents_acc PRICE_PER_METER_CENTS * WAIT_FALLBACK_METERS; fare_cents wait_cents_acc / (WAIT_FALLBACK_SECONDS); wait_cents_acc % WAIT_FALLBACK_SECONDS; } }这里的PRICE_PER_METER_CENTS会得到每米对应的分数值很小但不会丢失因为最终累加只在超起步距离后发生。所有常量比如START_PRICE_CENTS、START_METERS、WAIT_FALLBACK_SECONDS都建议用宏定义并标成“可在标定菜单里改”方便运营者调价。3. 原理图设计从传感器接口到电源、显示和防拆3.1 最小系统与电源拓扑把 12V 车载电瓶压成 5V 和 3.3V出租车车载电源是 12V/24V 铅酸或锂电但启动瞬间电压会跌到 9V甚至可能叠加较大的 ripple。常见做法是先用一颗 DC-DC buck 降到 5V再用 LDO 降到单片机需要的 3.3V 或直接 5V 供电给 51/STM32。不建议从 12V 直接线性稳压到 5V因为压差大、发热严重行车记录仪、计价器和充电插头共用一个保险丝盒时线性稳压很容易过热。原理图上需要防反接二极管、TVS 管和一个自恢复保险丝。防反接用 SS14 肖特基或 P-MOS 管都可以简单起见放一个 SS14 串在主电源正极地线直接接底盘。TVS 选 24V 双向型号放在 DC-DC 输入端能吸收抛负载时的瞬态高压。电感和滤波电容的布局要在 PCB 阶段重点照顾。[12V汽车电瓶] - [自恢复保险丝 0.5A] - [SS14防反接] - [TVS] - [MP1584/LM2596 5V] - [AMS1117-3.3] - MCU VCC这个拓扑里AMS1117 接受 5V 输入输出 3.3V 给 STM32数据手册推荐输入输出各接一个 10μF 钽电容。如果是 51 单片机可以用 5V 供电省掉 LDO。要注意的是 DC-DC 输出的开关纹波会影响 ADC 采样角速度传感器和温度传感器如果共用同一路电源建议在传感器 VCC 上串 10Ω 电阻加 100nF 滤波。3.2 脉冲输入整形电路比较器与 RC 滤波选型输入信号不整形直接进 MCU 是故障率最高的设计。以磁电式车速传感器为例它输出的是近似正弦波幅值随车速变化很大低速时可能只有 0.5V高速时 15V 以上。这个信号必须送进迟滞比较器。LM393 接成同相迟滞比较器后正反馈电阻取 100kΩ输入分压网络设定阈值假设目标阈值在 2.5VR1 10kΩ 接信号源 R2 10kΩ 接 GND 比较器正输入接 R1/R2 分压点 比较器负输入接 TL431 2.5V 基准 输出通过 10kΩ 上拉到 5V再串 100Ω 进入 MCU只要外界振动让信号在阈值附近反复穿越迟滞能避免振荡输出引脚上的毛刺数量会跟实际轮轴脉冲数一致。比较器输出之后还不算完建议在 MCU 引脚前再串一个 1kΩ 电阻并联 100pF 电容形成低通。如果你打算用 STM32 的输入捕获模式注意 STM32 引脚耐压是 3.3V比较器输出上拉到 5V 会打坏引脚。必须在比较器输出和 MCU 之间加 1kΩ 串联电阻再用 3.3V 稳压管嵌位或者直接让比较器输出上拉到 3.3V。3.3 显示方案数码管、LCD 与掉电保存的取舍出租车计价器需要显示金额、里程、单价和等待时间。最经济的是 4 位或 6 位 LED 数码管动态扫描显示抗强光、寿命长。缺点是信息量少JJJ 状态下可能需要通过按键切换显示单价和等待时间。如果想做得现代一点LCD1602 或 OLED 可以显示更多文字但低温性能不如数码管而且 OLED 在高温暴晒下有亮度衰减。推荐用两个 4 位一体数码管或用两个 74HC595 做段选和位选单片机只需要 3 根 I/O 线。复用动态扫描时扫描频率不能低于 50Hz不然会有闪烁感。严格计费场景下显示刷新和计费更新要共享同一缓冲区避免在显示中途修改金额导致数码管出现瞬间进位重影。掉电保存建议使用 AT24C02 或 MCU 片内 EEPROM。计价器可能在正常行驶中突然断电里程和累计金额必须按“最近一次完整计费周期”为准保存不能每秒钟写一次 EEPROM因为 EEPROM 寿命有限。折中方案是每分钟写一次同时在每次收到 STOP 标志时强制写一次。写 EEPROM 期间应短暂屏蔽外部中断防止“正在写时被脉冲打断导致半字节错误”。4. PCB 设计从原理图到生产板的完整落地流程4.1 用 AD20 或嘉立创 EDA 建工程时先规划好三层结构你最终交付的不只是代码而是一套能打样的工程文件。常见的开源项目结构是sch/、pcb/、src/、doc/四个目录。如果你用 AD20从创建工程开始就要把原理图库和 PCB 库统一管理不要临时从网上拉别人的库因为封装对不对直接关系到能否打样和贴片。JLCEDA 的库比较全如果从立创商城买物料可以直接复用封装。设计约束里线宽优先级、间距、过孔大小这些规则先设好再开始摆件。计价器板子外形一般受限于出租车计价器的外壳常见的是长条型不可能做成任意形状所以建议提前和外壳供应商要一份机械图。板厚 1.6mm铜厚 1oz 是够用的不需要为高频做特殊阻抗控制。4.2 PCB 布线规则电源、地、脉冲线怎么走不打架出租车车内电磁环境其实比实验室恶劣得多点火线圈、雨刷电机、门锁执行器都能耦合干扰。PCB 布线时至少做到以下几条大电流路径和脉冲信号路径完全分开。地线采用单点接地DC-DC 地、LDO 地、MCU 地、数码管驱动地先在一点汇合再回到电源输入地。脉冲线从输入接插件到比较器这一段要短且两侧包地走线不要和晶振线平行。晶振尽量靠近 MCU 引脚晶振下方和周围不走其他信号线。对于数码管动态扫描段选线的瞬间电流可以达到几十毫安如果这些线靠近复位或晶振走线显示刷新的一瞬间可能把 MCU 复位。经验做法是把数码管驱动 IC 放在板子另一侧并且段选线用地线逐根隔离。关键是电源平面和地平面的分割。如果板子层数只有两层不建议做完整地平面。在双层板上把顶层布满地铜底层走信号地铜在关键信号两侧用 20mil 以上的铺铜连接并用过孔将顶层地和底层地缝合。0.3mm 以上过孔电流能力有限电源路径的高速脉冲电流需要至少两个 0.5mm 过孔冗余。如果你用的是 0.3mm 线宽要核算能过多少电流一般 1oz 铜厚下 10mil0.254mm线宽大概承载 0.5A 左右计价器最大功耗不会超过 300mA所以信号线用 10mil、电源线用 30~50mil 就够。4.3 在 AD20 中做 DRC 检查与 Gerber 导出布线完成不代表能直接下单。DRC设计规则检查是最后一道防线。AD20 中执行Tools - Design Rule Check重点检查Width、Clearance、Hole Size和Unrouted Net。一个常见问题是orcap-11010这类原理图页码报错在 AD20 里如果有多张原理图页必须确保Page Number不重复否则导入 PCB 时网络标号会错乱。我一般会先用 AD20 的Report - Generate Gerber Files导出光绘再把 Gerber 用 CAM350 或者嘉立创的在线 DFM 重新读一遍。不建议直接用 PCB 源文件下单因为某些打样厂对 AD 版本兼容性有问题在转出 Gerber 时会出现弧线丢失或铜皮挖空的隐患。导出 Gerber 时分辨率设置选 6:0格式选 RS-274X钻孔文件格式选 Excellon。生产前还要检查所有封装是否都映射了正确的丝印标识二极管极性、防反接方向丝印不能被盖住。5. 代码实现与标定如何在真实量程内稳住计价精度5.1 外部中断 定时器配合时的优先级设计写代码时最怕外部中断和定时器中断同时发生。以 STC89C52RC 为例外部中断 0INT0设为最高优先级因为一个脉冲丢失就是好几米里程差。定时器 0 设为次高优先级。处理中注意在中断服务函数里不要做除法或乘法只做简单的计数累加。比如volatile unsigned long pulse_count 0; void Ext0_ISR(void) interrupt 0 { pulse_count; // 只做加法 }主循环每秒钟把pulse_count和上一个 1 秒的值做差就得到这一秒的轮速脉冲数然后折算成瞬时里程。这里有一个细节pulse_count是 unsigned long主循环读它时如果外部中断恰好在修改高位会读到撕裂值。所以读取时应先关闭外部中断// 读取当前脉冲数原子操作 EA 0; current_count pulse_count; EA 1;这样不会错过多个脉冲最多中断中的一次计数被延迟到 EA 打开后继续。5.2 计价器状态机空闲、载客、低速、结束把计价流程拆成状态机可读性和可维护性会好很多。状态定义一般为状态含义进入条件退出条件IDLE空车上电或 STOP 键按下 START 键RUNNING正常计价按下 START 键按下 STOP 键WAITING低速/停止计费车速小于阈值车速恢复状态迁移只发生在主循环的按键处理和秒更新中不要放在中断里。你在中断里能做的只是把vehicle_speed_hz算出来真正的低速判断延时可以做成“连续 10 秒内平均速度低于 10km/h 才进入等待”避免在堵车时频繁跳变。状态机的另一个好处是方便调试。在串口打印状态码、里程和金额时你能立刻分辨出是传感器计数异常还是状态迁移错误。量产时可以通过串口输出格式化数据在实车标定时记录一整趟数据。STM32 版本可以加一个 RS485 接口modbus 帧接收数据程序可以直接复用在计价器上做远程读取但普通 51 版本没必要上协议栈一个 UART 就够了。5.3 实车标定的操作步骤如果你跟我一样第一次做样机不要急着焊一块新板子直接上车。先在桌面上用信号发生器输出幅值 5V、频率 1Hz 到 100Hz 的方波模拟车速传感器。按键进入“标定模式”把显示清零然后喂入 1000 个脉冲看在屏幕上显示的距离是否等于预设值。如果偏差超过 2%检查是不是比较器阈值不对导致漏脉冲或者触发方式选错了沿。实车标定时用一个 GPS 或一个“已知路程长度”的操场进行。常规做法是计算理论 K 值轮胎周长 × 每圈脉冲数 × 减速比。烧入代码并进入标定模式。驾驶车辆行驶 1km停下后记下读到的脉冲数。把实际脉冲数写入 EEPROM退出标定模式。注意公里计数会有累计误差多跑几个 1km 段位对比偏差。我见过因为轮胎气压变化导致一箱油跑完就差 200 米的现象所以正式标定建议在标准胎压下进行并且把 K 值允许修正在 ±10% 范围内。6. 最后的经验集按键消抖、电源倒灌与 DRC 前要查的三个细节当你把板子打样回来开始焊接调试时最常出问题的反而不是核心电路而是小地方6.1 用状态机做按键消抖而不是 delay大多数示例代码用delay(20)消抖但这会造成按键按下时主循环被阻塞计费更新变慢。用状态机结合 10ms 定时器检测按键状态并记录连续稳定时间。当抖动时间小于 15ms 时不触发按键事件。开机时不要把按键事件响应放在第一秒避免上车时误触。6.2 防止电源倒灌到单片机 I/O 引脚多路传感器共用电源时某一路传感器短路后5V 会通过 I/O 保护二极管灌进 MCU 的 VCC断电后单片机依然有电压维持运行导致掉电保存逻辑失效。对策是每个传感器 VCC 后串 100Ω 磁珠或电阻并在 MCU VCC 上加大电容到 470μF 左右保证掉电瞬间能有足够时间保存数据。6.3 DRC 前检查丝印和孔径AD20 中一个很常见的杂项是丝印压住了过孔导致打样后丝印不明显。另外孔径设置小于 0.2mm 的过孔很多板厂收额外费用不如直接用 0.3mm 孔径。钻孔文件里如果包含非金属化槽或邮票孔需要单独确认生产文案。若无特殊要求所有过孔都做成金属化通孔方便飞线。给 PCB 下单前最后用 AD20 做一次完整的Design Rule Check同时把未连接的网络列表导出确保没有任何孤立焊盘。拿到板后先用万用表量一下各点亮不亮再烧代码。别急着接 12V先用 USB 对 5V 部分单独供电跑通最小系统再接入汽车电瓶模拟否则一个短路就能让 TVS 和 DC-DC 一起报废。最后一个关于 PCB 铜皮的小提醒若你在铺铜时用了网格铜会干扰比较器附近的电流回流建议脉冲输入区域用实心铜皮其他区域网格无所谓。本文还有配套的精品资源点击获取
返回列表