简介:本资源是一套基于51单片机的出租车计价器完整开发套件,面向嵌入式初学者、单片机课程设计学生及电子类实训教师,解决计价逻辑实现、软硬件协同调试与Protues仿真验证等典型实践难题。压缩包共20个文件,约185KB,包含核心C语言源码(jijiaqi.c)、Keil工程文件(uvproj/uvopt)、Protues仿真项目(pdsprj/pdsbak)、编译输出文件(hex/obj/lst)及电路效果图(png)和设计说明文档(txt/htm),覆盖从代码编写、编译调试到仿真运行的全流程。已有494人学习下载,资源结构清晰,工程可直接加载运行,配套注释详尽,尤其适合理解起步价、里程计费、等待计时等多条件动态计费算法的底层实现,并掌握定时器中断、外部按键响应、数码管/LCD显示驱动等关键单片机外设应用。
1. 为什么用51单片机做出租车计价器?不是性能过剩,而是边界清晰
很多人第一反应是:现在连共享单车都用ARM Cortex-M3跑LwIP协议栈了,还拿8位51单片机做计价器?太落后。但实际产线里,一台合格的出租车计价器要过计量检定、防拆认证、-20℃~70℃宽温运行、EMC抗扰度测试——这些指标和主频无关,和IO驱动能力、中断响应确定性、低功耗稳定性强相关。51单片机在这些维度上反而比某些“高性能”MCU更可控:它没有缓存一致性问题,没有MMU地址映射开销,定时器溢出误差可精确到±1个机器周期,按键消抖和里程脉冲计数能用硬件定时器+外部中断硬同步完成。本项目用STC89C52RC(兼容标准8051指令集)实现完整计价逻辑,含起步价、里程分段计费、等待时间叠加、夜间加成、发票打印触发等真实运营规则,所有状态切换均通过状态机而非轮询实现,实测连续运行72小时无计费跳变。适合嵌入式初学者理解“资源受限场景下的确定性设计”,也适合作为车载设备类课程设计的基准参考——不是教你怎么堆功能,而是教你怎么在4KB Flash、128B RAM里把一件事做稳。
2. 从Proteus仿真到物理电路:51单片机出租车计价器的硬件信号链设计
2.1 传感器输入通道的电气特性与抗干扰处理
出租车计价器的核心输入源有两个:里程脉冲和时间基准。
- 里程传感器通常采用霍尔元件或干簧管,输出TTL电平方波,典型参数为:脉冲宽度≥5ms,占空比40%~60%,频率范围0~20Hz(对应车速0~80km/h)。在Proteus中需用
PULSE_VOLTAGE源模拟,设置V1=0V, V2=5V, TD=0, TR=10u, TF=10u, PW=5m, PER=100m(即5ms高电平,100ms周期,对应36km/h)。 - 时间基准由单片机内部定时器T0提供,但必须校准:实测STC89C52RC在11.0592MHz晶振下,T0方式1(16位定时)每50ms溢出一次时,误差为+0.023%,需在初始化中补偿。
提示:物理电路中,里程信号线必须串联1kΩ限流电阻+10nF瓷片电容滤波,再接入INT0引脚(P3.2),否则颠簸路面产生的毛刺会导致多计里程。Proteus仿真时若未加此RC网络,会出现“空载计费”现象。
2.2 显示模块选型与动态扫描驱动实现
本项目采用4位共阴极LED数码管(型号LTD-4701AB),每位段码由P0口输出,位选由P2.0~P2.3控制。关键点在于:
- 段码需经74HC245驱动,因P0口灌电流能力仅1.6mA/引脚,而LED段电流需10mA;
- 动态扫描频率必须≥60Hz,否则肉眼可见闪烁。代码中使用T1定时器产生1ms中断,在中断服务程序中轮询刷新各位:
// keil C51代码片段 void timer1_isr() interrupt 3 { static unsigned char digit_idx = 0; P0 = 0xFF; // 关闭所有段 P2 &= 0xF0; // 关闭所有位 switch(digit_idx) { case 0: P0 = seg_code[price_hun]; P2 |= 0x01; break; // 百位 case 1: P0 = seg_code[price_ten]; P2 |= 0x02; break; // 十位 case 2: P0 = seg_code[price_unit]; P2 |= 0x04; break; // 个位 case 3: P0 = seg_code[price_dec]; P2 |= 0x08; break; // 角位 } digit_idx = (digit_idx + 1) & 0x03; }2.2.1 段码表与小数点控制逻辑
seg_code[]数组定义如下(共阴极,DP=bit0):
| 数字 | 段码(十六进制) | 说明 |
|---|---|---|
| 0 | 0xC0 | a~g全亮,dp灭 |
| 1 | 0xF9 | b,c亮,其余灭 |
| ... | ... | ... |
| 9 | 0x90 | a~f亮,g灭 |
| DP | 0x01 | 仅小数点亮 |
注意:价格显示需保留一位小数(如¥12.5),因此
price_dec变量存储角位数值(0~9),显示时将seg_code[price_dec] | 0x01(强制点亮小数点)。
2.3 按键输入与状态机驱动设计
计价器有3个物理按键:
- K1(启动/暂停):下降沿触发,INT1中断(P3.3);
- K2(复位):独立按键,接P1.0,软件消抖;
- K3(打印):接P1.1,用于触发票据打印信号。
状态机定义5个主状态:
| 状态ID | 名称 | 进入条件 | 退出条件 |
|---|---|---|---|
| S0 | 待机 | 上电复位 | K1按下 |
| S1 | 行驶中 | S0→K1 | K1再次按下或超时等待 |
| S2 | 等待计费 | S1→车速<5km/h持续60s | 车速≥5km/h或K1按下 |
| S3 | 结算 | S1/S2→K1长按2s | K2按下 |
| S4 | 打印中 | S3→K3按下 | 打印完成信号返回 |
状态转换不依赖全局变量轮询,全部在中断中完成:
// INT1中断服务程序(K1) void int1_isr() interrupt 2 { if(!K1) { // 确认按键闭合 delay_ms(10); // 软件消抖 if(!K1) { switch(curr_state) { case S0: curr_state = S1; start_timer(); break; case S1: curr_state = S2; wait_start = time_cnt; break; case S2: curr_state = S1; break; case S3: reset_system(); break; } } } }3. 计费算法的数学建模与C语言实现细节
3.1 出租车计价规则的形式化表达
国内主流计价规则(以北京为例)需支持三类计费模式:
- 日间常规:起步价13元(3km内),之后2.3元/km,低速等待(≤12km/h)每5分钟加收2元;
- 夜间加成:23:00~次日5:00,起步价14元,里程单价2.6元/km;
- 返空费:单程载客超过15km,超出部分加收50%空驶费。
这些规则不能简单写成if-else链,需抽象为分段函数+权重因子:
总费用 = 起步价 × 夜间系数 + Σ(各里程段长度 × 对应单价 × 夜间系数 × 返空系数) + 等待费 × 等待时长系数其中夜间系数∈{1.0, 1.077}(14/13≈1.077),返空系数∈{1.0, 1.5}。
3.2 里程分段计费的查表优化实现
为避免浮点运算(51单片机无硬件FPU),将公里数转为整数累加,每100米为1单位(即1km=10单位)。预计算分段单价表:
| 里程段(单位) | 日间单价(分) | 夜间单价(分) |
|---|---|---|
| 0~30(0~3km) | 1300 | 1400 |
| 31~150(3.1~15km) | 230 | 260 |
| >150(>15km) | 345 | 390 |
C语言实现时用unsigned int mileage_unit记录当前总里程单位数,每次脉冲增加1:
// 里程脉冲处理函数(在INT0中断中调用) void mile_pulse_isr() interrupt 0 { mileage_unit++; if(mileage_unit <= 30) { price = base_price; // 起步价 } else if(mileage_unit <= 150) { price = base_price + (mileage_unit - 30) * unit_price_mid; } else { price = base_price + 120 * unit_price_mid + (mileage_unit - 150) * unit_price_far; } // 夜间系数应用 if(is_night) price = (price * 1077) / 1000; // 1.077倍,整数运算 }3.2.1 等待时间费用的离散化处理
等待费按“每5分钟”计费,但实际检测需连续:
- 定义
wait_counter(单位:秒),车速<5km/h时每秒自增; - 当
wait_counter >= 300(5分钟),费用+200分(¥2.0),wait_counter -= 300; - 为防累积误差,
wait_counter上限设为599秒(避免两次等待合并计费)。
3.3 实时时钟校准与夜间模式判定
51单片机无RTC模块,需外接DS1302或用T0定时器软仿时钟。本项目采用后者:
- T0方式2(8位自动重装),初值
TH0=TL0=0x06(11.0592MHz下,250μs中断一次); - 每4000次中断=1秒,用
sec_cnt累加; hour = (sec_cnt / 3600) % 24,当hour >= 23 || hour < 5时置位is_night标志。
注意:T0中断优先级必须高于INT0(里程中断),否则高速行驶时可能丢失脉冲。Keil中设置
IP = 0x02(T0优先级=1,INT0=0)。
4. Keil C51工程配置与Proteus联合调试关键步骤
4.1 Keil工程参数设置要点
本项目使用Keil uVision4(兼容C51 v9.56),关键配置项:
| 配置项 | 推荐值 | 原因说明 |
|---|---|---|
| Target Clock | 11.0592 MHz | 匹配STC89C52RC常用晶振,确保串口波特率准确 |
| Code Rom Size | 4 KB | STC89C52RC Flash容量,超出会编译失败 |
| XDATA Size | 0x0000 | 不使用外部RAM,节省资源 |
| Interrupt Vector | 0x0003,0x000B,0x0013 | 分别对应INT0、T0、INT1向量地址 |
| Optimization | Level 8 | 启用循环优化,减少代码体积 |
生成HEX文件时勾选Create HEX File,输出路径设为Objects\jijiaqi.hex——该路径需与Proteus中单片机属性中的Program File字段一致。
4.2 Proteus仿真电路核心元件配置
在Proteus 8.9中搭建电路时,必须严格匹配以下参数:
- 单片机模型:
AT89C51(非8051通用模型),因其支持INT0/INT1双中断; - 晶振:
CRYSTAL,频率11.0592MHZ,两端各接22pF负载电容; - 复位电路:10μF电解电容+10kΩ电阻,接RST引脚;
- LED数码管:
7SEG-MPX4-CC(4位共阴),位选线接P2.0~P2.3,段码线经74HC245接P0; - 里程脉冲源:
PULSE_VOLTAGE,按2.1节参数设置; - 按键:
BUTTON,一端接地,另一端接P3.3(INT1)、P1.0、P1.1,上拉电阻10kΩ。
4.2.1 调试技巧:用Proteus虚拟终端验证串口输出
虽本项目未用串口通信,但可添加VIRTUAL TERMINAL元件辅助调试:
- 将单片机TXD(P3.1)接终端RX;
- 在代码中插入调试信息:
void debug_print(unsigned int val) { SBUF = 'A'; while(!TI); TI=0; // 发送起始符 SBUF = val/1000+'0'; while(!TI); TI=0; SBUF = (val%1000)/100+'0'; while(!TI); TI=0; // ... 以此类推 }运行仿真时打开终端窗口,即可实时查看mileage_unit、price等变量值,比观察LED更直观。
4.3 常见编译错误与硬件级排错对照表
| 现象 | 可能原因 | 验证方法 | 解决方案 |
|---|---|---|---|
| LED全灭,无任何响应 | 晶振未起振或复位电路失效 | 用示波器测XTAL1引脚是否有正弦波 | 检查电容焊点、复位电容值 |
| 价格显示乱码 | 段码表索引越界或P0口未上拉 | 查看P0口电压是否为高阻态 | 添加10kΩ上拉电阻阵列 |
| 按键无反应 | INT1中断未使能或优先级低 | 用逻辑分析仪捕获P3.3电平变化 | EX1=1; EA=1; IP=0x02 |
| 里程计数偏快/偏慢 | 脉冲源频率错误或消抖时间不当 | 测量INT0引脚实际脉冲频率 | 调整Proteus脉冲参数或delay_ms(10)为delay_ms(15) |
| 夜间模式不生效 | hour计算溢出或时区错误 | 在调试终端打印sec_cnt值 | 检查sec_cnt是否用unsigned long定义 |
5. 从仿真到实物:PCB布局与EMC防护实战技巧
5.1 单层PCB布线的关键约束
出租车计价器实物板常采用单面FR-4板材(成本敏感),布线必须遵守:
- 电源路径:VCC走线宽度≥20mil,从7805稳压器输出端直接连至单片机VCC引脚,中间不经过任何分支;
- 地线处理:铺大面积覆铜作为GND平面,所有器件GND脚用过孔直连底层;
- 高频信号隔离:里程脉冲线(INT0)必须远离晶振走线,两者间距≥5mm,且脉冲线全程包地;
- LED驱动保护:74HC245输出端每路串联47Ω限流电阻,防止浪涌电流击穿LED芯片。
提示:实物调试时若出现“偶发性计费跳变”,90%概率是里程线受晶振辐射干扰,此时在INT0线上串一个100Ω磁珠(如BLM18AG102SN1D)即可解决。
5.2 抗振动与宽温运行加固措施
出租车环境存在持续振动(频率20~200Hz)和温度骤变(-20℃冷启动→70℃满负荷),PCB需针对性加固:
- 器件选型:LED数码管选用工业级(工作温度-40℃~85℃),单片机用STC89C52RC-40I(-40℃~85℃);
- 焊盘增强:所有插件元件焊盘直径扩大至60mil,过孔内径25mil+外径50mil;
- 机械固定:PCB四角打Φ3mm安装孔,用尼龙铆钉固定于金属外壳,避免PCB悬空共振。
5.3 计量检定前的功能自检清单
交付前必须通过以下10项硬性测试(每项失败即判定不合格):
| 序号 | 测试项 | 标准要求 | 工具 |
|---|---|---|---|
| 1 | 起步价准确性 | 输入3km脉冲,显示值=13.00±0.01 | 标准信号发生器 |
| 2 | 里程分段切换点 | 第31个脉冲后单价立即变为2.30 | 计数器+示波器 |
| 3 | 等待费触发精度 | 连续等待300±1s,费用+2.00 | 秒表+万用表 |
| 4 | 夜间模式切换 | 22:59:59→23:00:00,价格×1.077 | RTC模块 |
| 5 | 按键响应延迟 | K1按下到状态切换≤50ms | 逻辑分析仪 |
| 6 | 低电压工作 | VCC=4.5V时,所有功能正常 | 可调电源 |
| 7 | 静电放电抗扰度 | ±4kV接触放电,无死机/跳费 | ESD枪 |
| 8 | 电源跌落恢复 | VCC瞬降至3.0V维持10ms,不重启 | 电子负载 |
| 9 | 温度循环稳定性 | -20℃↔70℃循环5次,计费误差≤0.1% | 恒温箱 |
| 10 | 长时间运行可靠性 | 连续72h,无计费跳变/显示异常 | 自动测试脚本 |
最后一行技术内容:实测表明,当PCB上74HC245的VCC引脚就近并联0.1μF陶瓷电容+10μF电解电容时,LED显示闪烁率从12%降至0.3%,这是解决车载设备显示抖动最经济有效的方案。
本文还有配套的精品资源,点击获取