51单片机出租车计价器(汇编语言)

要求

  1. 使用信号发生器作为模拟出租车轮胎转速
  2. 使用数码管可显示费用,里程,速度
  3. 按钮按下开始计费
  4. 按钮抬起后停止计费
  5. 按钮再次按下后清零
  6. 里程达到2km前费用均为8元
  7. 里程达到2km后每千米2.6元
  8. 代码部分使用汇编语言完成,不可使用c语言编译

效果图

  1. 里程达到2km前费用均为8元
  2. 里程达到2km后每公里2.6元
  3. 按钮抬起后里程不再增加
  4. 按钮再次按下后清零

理论基础

指令系统
数据传送: 寄存器寻址,寄存器间接寻址等
中断: 外部中断
89C51单片机定时器的4种模式及其应用

实现思路

数码管显示

要实现数码管的显示,需要同时输出段码和位码才能让数码管显示

位码

控制数码管的显示位

段码

控制数码管的具体显示
共阳极段码表

DB   0C0H, 0F9H, 0A4H, 0B0H, 99H, 92H, 82H, 0F8H, 80H, 90H  //[0~9]
DB  88H, 83H, 0C6H, 0A1H, 86H, 8EH  //[A~F]
DB  89H, 0C7H, 8CH, 0C1H, 91H, 0FFH  //[H L P U Y 灭]

这里建议在程序调试初期使用0-F的16进制段码表
实例:

MOV P2,#00H      ;位码
MOV P1,#0C0H        ;段码

此时数码管的第1位会显示为数字0

定时刷新

本程序中需要12位数码管全部显示,所以需要定时刷新所有数码管
而刷新程序周期为5ms (下面会解释)
如果一次只刷新1位会导致显示不稳定
(即每次自增显示位,然后与某数ANL)

Display:LCALL DelayMOV    A, cDisplayBitMOV    P2, AMOV    DPTR, #DisplayTable1MOV    A, #cDisplayBufferADD    A, cDisplayBitMOV    R0, AMOV    A, @R0MOVC   A, @A+DPTRMOV    P1, AINC cDisplayBitANL cDisplayBit,#0FH

以上程序调用一次只会刷新一个数码管,刷新范围0-15位(0FH)
所以需要一次全部刷新所有数码管

Display:MOV R5,#0CH      ;需要扫描12个位
D1: LCALL DelayMOV    A, cDisplayBitMOV    P2, A
-------------------------------------;译码MOV    DPTR, #DisplayTable1MOV    A, #cDisplayBufferADD    A, cDisplayBitMOV    R0, AMOV    A, @R0MOVC   A, @A+DPTRMOV    P1, A
--------------------------------------INC cDisplayBitDJNZ R5,D1MOV cDisplayBit,#00H     ;自增,清零RET

在本程序中设置了两个显示缓冲区

cDisplayBuffer 存储单字节BCD码 如 08H 07H 有12位
BCD 存储双字节BCD码 如 12H 60H 有6位

BCD送入cDisplayBuffer的程序

LOOSE:        MOV R5,#06H        ;总共5个字节,需要循环5次MOV R0,#BCD    ;6位双字节首地址MOV R1,#cDisplayBuffer        ;12位单字节缓冲区地址LOOP1:MOV A,@R0ANL A,#0F0H     ;取高位SWAP AMOV @R1,AINC R1MOV A,@R0        ;存高位ANL A,#0FH      ;取低位MOV @R1,A      ;存低位INC R0INC R1DJNZ R5,LOOP1RET

小数点显示

带小数点的共阳极段码表

DB 0BFH, 86H, 0DBH, 0CFH, 0E6H, 0EDH, 0FDH, 87H, 0FFH, 0EFH

只需要在显示程序后追加一个程序,指定段码,使用同样的BCD译码进行叠加显示即可看到小数点

    LCALL DelayMOV    P2, #01HMOV    DPTR, #DisplayTable2MOV    A, #cDisplayBufferADD    A, #01HMOV    R0, AMOV    A, @R0MOVC   A, @A+DPTRMOV    P1, ALCALL DelayMOV    P2, #05HMOV    DPTR, #DisplayTable2MOV    A, #cDisplayBufferADD    A, #05HMOV    R0, AMOV    A, @R0MOVC   A, @A+DPTRMOV    P1, ALCALL DelayMOV    P2, #0AHMOV    DPTR, #DisplayTable2MOV    A, #cDisplayBufferADD    A, #0AHMOV    R0, AMOV    A, @R0MOVC   A, @A+DPTRMOV    P1, ARET

Delay为防抖程序
DisplayTable2为带小数点的共阳极段码表
如果不想写的这么冗杂也可以直接置小数点

    LCALL DelayMOV    P2, #01HMOV    P1,#80HLCALL DelayMOV    P2, #05HMOV    P1,#80HLCALL DelayMOV    P2, #0AHMOV    P1,#80H

80H为共阳极 “.” 的段码

计速

频率捕捉

速度需要显示为1s内平均速度,我们要先捕捉到信号发生器的频率再进行计算操作
所以我们需要使用到89C51单片机的两个计时器的两个功能
本程序中T0计时器使用边缘中断模式
T1计时器使用计时器模式,产生5ms的定时程序
在程序开头需要指明中断程序的入口地址

   ORG 0000HSJMP MAINORG 0003HLJMP 外部中断ORG 001BHLJMP 定时器

T0对应的中断子程序,每次对速度自增,1s后取值清零实现频率捕捉

   INC cache  ;速度所在地址单元RETI

T1对应的中断子程序
为了实现1s的计时需要使用到一个Temp标志位,
每次自增,在MAIN程序中判断是否达到200即可实现1s的定时功能
定时5ms的中断程序
该单片机晶振为22.1184MHz
计算过程略

   MOV TH1,#0DCH    MOV TL1,#00HINC cTempRETI

部分主程序

M1:MOV   A,cTemp   ;5ms定时,计200次为1秒XRL A,#200JNZ   M1      ;不满200继续循环LCALL 速度计算MOV cTemp,#0MOV cache,#0    ;速度置零SJMP   M1  ;死循环

速度计算

轮胎每秒转1圈折合km/h为

1 r/s = 6.588 km/h

这里需要精确显示,所以需要使用到双字节乘法和双字节译码程序
51单片机的MUL指令是针对16进制数的,不要天真的以为是10进制数乘法

双字节乘法例子

12 34H * 56 78H = 06 26 00 60H

结果有4个16进制数,存放于4个地址中
双字节译码程序
R0是待译码双字节数高位地址
R1是译码结果的高字节地址
调用前先将R0和R1寄存器赋值

BinDec:  ;双字节BCD译码CLR AMOV @R1,AINC R1MOV @R1,AINC R1MOV @R1,APUSH 7MOV R7,#16
BD1:CLR CINC R0MOV A,@R0RLC AMOV @R0,ADEC R0MOV A,@R0RLC AMOV @R0,APUSH 1MOV A,@R1ADDC A,@R1DA  AMOV @R1,ADEC R1MOV A,@R1ADDC A,@R1DA  AMOV @R1,ADEC R1MOV A,@R1ADDC A,@R1DA  AMOV @R1,APOP 1DJNZ R7,BD1POP 7RET

@Ri表示寄存器内地址位所储存的16进制数

译码前
@R0: 01H       @R0+1:01H
译码后R0会被清零所以需要和数据缓存位分开
译码后
@R0: 00H       @R0+1:00H
@R1: 00        @R1+1:02      @R1+2:57

所以速度计算的具体思路就是使用双字节乘法
每秒转过的圈数 * 6.588 即可
由于小数点的影响我们有以下几种方案
先假设频率是10,速度65.8km/h

6588 * 10 = 06 58 80

嗯,看起来很简单
看一下16进制

16 BCH * 0AH = 01 01 58

哦豁完蛋,溢出了,译码程序只能够译码2位16进制数
01 01H = 0257
01 58H = 0344
和原来的数完全不沾边
那么就需要一点骚操作了

10进制
65 * 10 = 06 50
16进制
41H * 0AH = 02 8A H

缺了后面的8但已经大差不差
那我们能不给强行补上呢
事实上是可以的,而且还挺准的

0E0H * 0AH = 08 C0H

然后取高位加到前面的低位上去

02 8AH08H
02 92H = 06 58

锵锵,6588就出现了
你问我E0咋来的?
猜的(真切)
在调试的时候我已经把译码程序都加上去了,然后实在无奈了
对着低位一波操作结果发现数字对了,甚至还很准确
再把小数点加上就很nice了
效果图

(谁家出租车能跑到600多 23333~~)

计程

里程追加

如题所示,在多次使用乘法之后我放弃了
难点

183 * 1000 = 18 30 00

早就溢出了,这才转了1000圈,才1.83km
而且需要储存走过的圈数至少需要2位16进制位才够用
这样译码器负担更重
即使是

1 * 1000
8 * 1000
3 * 1000

也还是存在问题
如果带上一个系数,比如说
每转10圈再对路程进行更新
然而该方法我尝试过同样也行不通

所以我最后采用的方案是进行追加操作
使用4个地址位来作为我的缓存位
前两位可以直接接到显示缓冲区

00 00 00 0018 30

使用DA和ADDC命令便可轻松做到

DA 命令可以简单理解为 出现字母的数转化为10进制数
MOV A,#0AH
DA A
A的值为 10H
MOV A,#10H
DA A
A的值同样是10H
而当A大于100后使用DA命令会将C置1
对高位进行ADDC 高位,#00H操作即可实现进位

程序较为简单,这里不给出了

计价

里程判断

需要对里程进行判断,小于等于2km时不对价格进行追加
这里我选用的方案是
减2后判断是否出现进位
若没有出现则退出并置数码管为8

   MOV A,BCD+2CLR CSUBB A,#02H  ;判断是否大于2JC PRET ;小于2直接置8然后退出
PRET:MOV BCD+2,#08HRET

价格追加

和里程追加同理
只是这里每一圈追加的数是

26 * 183 = 4758

同样也需要4个缓冲位
小数点自己处理一下

按钮功能

抬起后价格不增加

在外部中断程序中加入JB对按钮进行监听
速度显示还是得放外面,不然成0去了
只要信号发生器还开着速度就不会是0

   INC cache  ;speedJB P3.7,EXIT LCALL 路程LCALL 价格RETI

再次按下后清零

这里需要涉及到
循环中只进行一次的操作
回想到高级程序语言,我们可以这样

mark = 1 标志位置1
while(1):if mark == 1:只运行一次的程序mark = 0 标志位置0循环程序

这样就实现了每次按下按钮之后都对地址进行一次清零
最终的外部中断程序修改如下

外部中断:INC cache  ;speedJB P3.7,EXIT ;按下放通,抬起就置mark为0LCALL 里程LCALL 价格MOV A,mark    ;取markXRL A,#1  ;判断markJZ 清零    ;清零内存RETI
EXIT:MOV mark,#01H  ;mark置1RETI

这里给个清零程序
(直接FF干光算了,其实到80H就可以了)

清零:MOV   mark,#00H   ;mark清零MOV R1,#0FFHMOV R0,#20H  ;地址清零
C1:MOV @R0,#00HINC R0DJNZ R1,C1RETI

汇编源代码

   ORG 0000HSJMP MAINORG 0003HLJMP TRIGGER       ;外部中断ORG 001BHLJMP TIMER        ;定时器DisplayTable1: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH,77H,7CH,39H,5EH,79H,71H,76H,38H,73H,3EH,6EH,00H
DisplayTable2: DB 0BFH, 86H, 0DBH, 0CFH, 0E6H, 0EDH, 0FDH, 87H, 0FFH, 0EFH
cDisplayBuffer  EQU     20H     ;12位段码实际值的缓冲区 12位
cDisplayBit EQU 2CH
cTemp    EQU    2DH
path    EQU 2FH
mark    EQU 6AH
cache   EQU 36H
result  EQU 75H ;运算结果
BIN EQU 46H ;译码器译码后的BCD
BCD EQU 40H ;40-45 6个BCD位 MAIN:MOV cache,#00HMOV cTemp,#00HMOV TMOD,#10H    MOV TH1,#0DCH       MOV TL1,#00H    SETB ET1    SETB TR1SETB    IT0SETB EX0SETB EAM1:MOV    A,cTemp   ;5ms定时,计200次为1秒XRL A,#200JNZ   M1LCALL SPEEDCOUNTMOV   cTemp,#0MOV cache,#0    ;速度置零SJMP   M1  ;死循环;0 按下,1抬起
TRIGGER:INC cache  ;speedJB P3.7,EXIT ;按下放通,抬起就置mark为0LCALL PATHINCLCALL PRICEINCMOV A,mark ;取markXRL A,#1  ;判断markJZ CCLR  ;清零内存RETITIMER:        ;5ms计时器MOV TH1,#0DCH    MOV TL1,#00HLCALL SHOWINC cTempRETISPEEDCOUNT:MOV A,cacheMOV B,#0E2HMUL ABMOV result+1,BMOV B,#41HMOV A,cacheMUL ABADD A,result+1MOV result+1,AMOV A,BADDC A,#0MOV result,AMOV R0,#resultMOV R1,#BINLCALL BinDecMOV BCD+5,BIN+2 ;LMOV BCD+4,BIN+1 ;M 00 06 58RETBinDec:   ;双字节BCD译码CLR AMOV @R1,AINC R1MOV @R1,AINC R1MOV @R1,APUSH 7MOV R7,#16
BD1:CLR CINC R0MOV A,@R0RLC AMOV @R0,ADEC R0MOV A,@R0RLC AMOV @R0,APUSH 1MOV A,@R1ADDC A,@R1DA  AMOV @R1,ADEC R1MOV A,@R1ADDC A,@R1DA  AMOV @R1,ADEC R1MOV A,@R1ADDC A,@R1DA  AMOV @R1,APOP 1DJNZ R7,BD1POP 7RETPRET:MOV BCD,#08H  ;2km内直接置8返回RETTORET:RETEXIT:MOV mark,#01H   ;mark置1RETICCLR:MOV mark,#00H   ;mark清零MOV R1,#0FFHMOV R0,#20H  ;地址清零
C1:MOV @R0,#00HINC R0DJNZ R1,C1RETIPATHINC:CLR CMOV A,#30H ;LADDC A,cache+2DA AMOV cache+2,AMOV A,#18H   ;HADDC A,cache+1DA AMOV cache+1,AMOV A,BCD+3ADDC A,#0DA AMOV BCD+3,AMOV A,BCD+2ADDC A,#0DA AMOV BCD+2,ARETPRICEINC:JB P3.7,TORET ;检测按钮MOV A,BCD+2CLR CSUBB A,#02H    ;判断是否大于2JC PRET ;小于2直接置8然后退出MOV A,#58H ;LADD A,cache+4 ;LDA AMOV cache+4,AMOV A,#47HADDC A,cache+3DA AMOV cache+3,A   MOV A,#00HADDC A,BCD+1DA AMOV BCD+1,AMOV A,#00HADDC A,BCDDA AMOV BCD,ARETSHOW:LCALL LOOSELCALL DisplayRETLOOSE:        MOV R5,#06H      ;总共6个字节,需要循环6次MOV R0,#BCD       ;时间序列存储首地址MOV R1,#cDisplayBuffer        ;12位缓冲区LOOP1:MOV A,@R0ANL A,#0F0H      ;取高位SWAP AMOV @R1,AINC R1MOV A,@R0        ;存高位ANL A,#0FH      ;取低位MOV @R1,A      ;存低位INC R0INC R1DJNZ R5,LOOP1RETDisplay:                ;通用显示程序MOV R5,#0CH
D1: LCALL DelayMOV    A, cDisplayBitMOV    P2, AMOV    DPTR, #DisplayTable1MOV    A, #cDisplayBufferADD    A, cDisplayBitMOV    R0, AMOV    A, @R0MOVC   A, @A+DPTRMOV    P1, AINC cDisplayBitDJNZ R5,D1MOV cDisplayBit,#00H
D2:LCALL DelayMOV    P2, #01HMOV    P1,#80HLCALL DelayMOV    P2, #05HMOV    P1,#80HLCALL DelayMOV    P2, #0AHMOV    P1,#80HRETDelay:    ;防抖MOV  R0,#10MOV   R1,#10DJNZ  R1,$DJNZ    R0,$-4RETEND

翻译比较生硬,结构不够优雅

仿真文件

传送门

后记

本计价器还有多种实现思路,这里就不作叙述了
希望大家能够有所收获

参考了这篇文章
基于MCS-51单片机使用定时器编写时钟程序(汇编)

51单片机出租车计价器(汇编语言)相关推荐

  1. 基于51单片机出租车计价器(霍尔测速)

    具体实现功能 系统由AT89C52单片机+霍尔传感器模块+LCD1602液晶屏+DS1302时钟模块+AT24C02掉电存储模块+直流电机+按键模块+电源构成. 具体功能: 1.无乘客模式:显示当前时 ...

  2. 单片机c语言出租车计时程序,基于单片机出租车计价器课题设计c语言编写(样例3)...

    <基于单片机出租车计价器课题设计(c语言编写).doc>由会员分享,可免费在线阅读全文,更多与<基于单片机出租车计价器课题设计(c语言编写)>相关文档资源请在帮帮文库(www. ...

  3. Arduino单片机出租车计价器起步价白天黑夜区分LCD1602光电测速

    实践制作DIY- GC0046-出租车计价器 一.功能说明: 基于Arduino单片机设计-出租车计价器 二.功能介绍: ARDUINO 开发板. LCD1602 .光电传感器.5V直流电机.多个电机 ...

  4. c51汇编语言 步进电机,51单片机驱动步进电机(汇编语言)

    在这里介绍一下用51单片机驱动步进电机的方法. 这款步进电机的驱动电压12V,步进角为 7.5度 . 一圈 360 度 , 需要 48 个脉冲完成!!! 该步进电机有6根引线,排列次序如下:1:红色. ...

  5. 用C51汇编语言控制1234灯亮,51单片机:用汇编语言控制LED灯发光

    硬件电路: P1.0 ~ P1.5 外接 6 个 LED,分别称为 D1 ~ D6,低电平发光. P0.0 ~ P0.3 外接 4 个 按键,分别称为 K1 ~ K4. P3.2 ~ P3.3 外接  ...

  6. 基于51单片机的计算器 汇编语言 proteus仿真 数码管显示

    之前讲的计算器都是基于C语言的,接下来讲一下基于汇编语言的简易计算器. 硬件设计 显示器仍然是数码管,MCU是C51,支持整数部分的计算 仿真图: 程序设计 YJ EQU 50H ;结果存放 YJ1 ...

  7. 51单片机课设项目大全

    1-基于51单片机的音乐彩灯 2-基于51单片机的蓝牙智能台灯设计 3-基于51单片机的篮球计分器 4-基于51单片机的无线病床呼叫系统设计 5-基于51单片机的语音实时采集系统 6-基于51单片机的 ...

  8. 基于51单片机的出租车计价器的设计

    引言 在科学技术和经济水平快速发展的21世纪,乘坐出租车和网约车出行已经成为一种非常重要的交通出行方式.行业的转型发展让出租车行业迎来了一场新纪元.但是在出租车行业迅速发展的同时,也出现了很多问题.经 ...

  9. 基于51单片机的出租车计价器(程序+仿真+论文)

    1.主要功能 设计要求 : ①.不同情况具有不同的收费标准. 白天,晚上,途中等待(>10min 开始收费): ②.能进行手动修改单价 : ③.具有数据的复位功能: ④.具有启动计时开关.白天/ ...

最新文章

  1. LeetCode 91. Decode Ways--动态规划DP的Python和Java解法
  2. 日志中的秘密 Windows登录类型知多少?
  3. 安装keras and theano于google colab上
  4. Java Learning Path(四) 方法篇
  5. ios保存gif到相册_iOS相册中的GIF图片的读取与保存
  6. 19-chgrp命令
  7. 金铲铲之战:新版本上线3活动,有实物奖励,小学生被限制游戏
  8. windows无法安装到这个磁盘。选中的磁盘采用GPT分区形式
  9. sap 分摊分配不产生会计凭证的原因_MM 物料凭证没有产生相应的会计凭证...
  10. c html导出成word,html转word-html如何转换成WORD
  11. Linux查看哪些进程占用较多的cpu、内存和磁盘IO的方法
  12. Silverlight玩转控件(二)——Canvas布局
  13. Linux---弹球游戏
  14. paip.提升用户体验-----填写EMAIL时自动提示完成
  15. 输入法快捷键_关于日语输入法,你需要知道的一切
  16. STM32通过IIC驱动MAX30102心率血氧传感器
  17. 带你深入剖析TCP/IP协议、TCP协议和UDP协议、IP协议
  18. 操作系统4.1.8 文件保护
  19. linux下装go环境
  20. linux 命令:ps 详解

热门文章

  1. 虹科方案|虹科Vdoo安全平台-D-Link路由器的安全漏洞
  2. Flutter 路由动画Offset(dx,dy)
  3. mysql1808_mysql表空间传输(ERROR 1808) row_format设置 翰墨文海
  4. php 常驻内存数据,EasyTask: PHP常驻内存定时任务定时器,(PHP resident memory timer, scheduled tasks)...
  5. 或许我们做错了,但绝非一无是处
  6. react-native集成超级强大的图表工具native-echarts
  7. 分析拿下114LA网址导航方法
  8. 软件测试_接口测试之协议和端口汇总(3)
  9. pytest文档75 - 生成 junit-xml 测试报告
  10. Linux 入门 Linux发展史及常用命令