单片机
返回首页

51单片机汇编语言通过LCD1602、DS1302、DS18B20显示时间温度等

2025-11-06 来源:bilibili

ai只适合在不了解的时候写个框架,具体细节还是得看书、数据手册等等

;指示灯接P1.0

;舵机接P1.4

;DS1302的命令字,bit7固定为1

;bit6决定操作对象,0表示对时间寄存器操作,1表示对RAM操作

;bit1到5为要操作的寄存器地址

;bit0控制读写,0为写,1为读

;当温度为正时,TEMP_H的高五位忽略,低三位对应2^6,2^5,2^4

;TEMP_L的高四位对应2^3,2^2,2^1,2^0,低四位对应小数部分

;小数部分的有效位取决于分辨率,如九位分辨率只有bit3有效,非有效位为0

;DS18B20的参考资料主要为https://zhuanlan.zhihu.com/p/453052826

;和https://blog.csdn.net/RONG_YAO/article/details/106114163

;11.0592MHZ晶振

;P0为LCD1602数据口

RS BIT P2.6

RW BIT P2.5

E BIT P2.7

CLK BIT P2.0

DAT BIT P2.1

RST BIT P2.2   ;DS1302三个接口

DQ BIT P2.3   ;DS18B20接口

; 时间数据

SECOND EQU 60H   ;秒

MINUTE EQU 61H   ;分

HOUR   EQU 62H   ;时

DAY    EQU 63H   ;日

MONTH  EQU 64H   ;月

WEEK   EQU 65H   ;周

YEAR   EQU 66H   ;年

;温度数据

TEMP_H EQU 40H

TEMP_L EQU 41H

TEMP_INT EQU 42H

TEMP_POINT EQU 43H

CAR_NUM EQU 50H   ;车辆数

;中断、延迟这部分代码最好放到前面来

;放到最后面,编译时可能会报错

;比如说code-address expected,或者是标识符未定义之类的

ORG 0000H

LJMP INIT

ORG 0003H

LJMP INT0_ISR

ORG 000BH

LJMP TIME0

ORG 0013H

LJMP INT1_ISR

ORG 001BH

LJMP TIME1

ORG 0030H

INIT:

   ACALL SETTING

   ACALL SG90_INIT

   ACALL DS1302_INIT

   ACALL DS18B20_INIT

   ACALL LCD_INIT

   LJMP MAIN

;注意,舵机和车辆是通过外部中断01来控制,而中断会影响温度的读取

;因为DS18B20对时序要求严格,因此如果要使用舵机,读取温度时要关中断

;如果使用舵机,外部中断的信号可以是由按键提供,也可以是红外探测等提供

;如果是按键,最好在代码里加上软件消抖部分,参考之前的4*4矩阵键盘

INT0_ISR:   ;车辆加一,闸门开合,指示灯亮

   INC CAR_NUM

   MOV P1, #11H

   SETB TR1

   JB TR1, $

   MOV P1, #01H

   ACALL DELAY_3S

   MOV P1, #11H

   SETB TR0

   JB TR0, $

   MOV P1, #00H

   RETI

INT1_ISR:   ;车辆减一,闸门开合,指示灯亮

   DEC CAR_NUM

   MOV P1, #11H

   SETB TR1

   JB TR1, $

   MOV P1, #01H

   ACALL DELAY_3S

   MOV P1, #11H

   SETB TR0

   JB TR0, $

   MOV P1, #00H

   RETI

DELAY_3S:   ;用直接寻址单元替代寄存器,节省寄存器资源

   MOV 7DH, #27

   MOV 7EH, #200

LOOP1:

   MOV 7FH, #250

   DJNZ 7FH, $

LOOP2:

   DJNZ 7EH, LOOP1

   MOV 7EH, #200

   DJNZ 7DH, LOOP1

   RET

DELAY_480US:

   MOV R0, #225

   DJNZ R0, $

   RET

DELAY_60US:

   MOV R0, #30

   DJNZ R0, $

   RET

DELAY_100MS:

   MOV R0, #200

LOOP3:

   MOV R1, #250

LOOP4:

   DJNZ R1, LOOP4

   DJNZ R0, LOOP3

   RET

DELAY_15US:

   MOV R0, #7

   DJNZ R0, $

   RET

DELAY_5US:

   MOV R0, #2

   DJNZ R0, $

   RET

TIME0:

   CLR TR0

   MOV TH0, #0F1H

   MOV TL0, #014H

   RETI

TIME1:

   CLR TR1

   MOV TH1, #0B7H

   MOV TL1, #014H

   RETI

SETTING:

   MOV SP, #70H   ;堆栈指针初始化

   SETB IT0

   SETB IT1

   SETB EA

   SETB EX0

   SETB EX1

   CLR PX1

   CLR PX0   ;设置外部中断0、1

   MOV TMOD, #00H   ;定时器0、1使用13位计数

   MOV TH0, #0F1H

   MOV TL0, #014H   ;定时器0计数0.5ms

   MOV TH1, #0B7H

   MOV TL1, #019H   ;定时器1计数2.5ms

   SETB ET0

   SETB ET1

   SETB PT0

   SETB PT1   ;设置定时器0、1

   RET

SG90_INIT:

   MOV P1, #00H

   SETB P1.4

   SETB TR0

   JB TR0, $

   CLR P1.4   ;SG90初始化

DS1302_INIT:

;关闭写保护

   CLR RST   ;启动传输

   CLR CLK

   SETB RST

   MOV B, #8EH   ;写保护的写命令字

   ACALL DS1302_WRITE_BYTE

   MOV B, #00H   ;允许写入(WP=0)

   ACALL DS1302_WRITE_BYTE

   CLR RST

;设置初始时间,DS1302的数据是BCD码

   MOV SECOND, #00H   ;秒=00(bit7=0即时钟运行)

   MOV MINUTE, #30H   ;分=30

   MOV HOUR,  #20H   ;时=20(bit7=0即24小时制)

   MOV DAY,   #18H   ;日=18

   MOV MONTH, #06H   ;月=06

   MOV WEEK,  #07H   ;周=7(周日)

   MOV YEAR,  #25H   ;年=25(2025)

;写入时间数据

   MOV R0, #SECOND   ;数据起始地址(60H)

   MOV R1, #80H   ;起始写命令字

   MOV R2, #7   ;写入DS1302的7个时间寄存器

WRITE_TIME_LOOP:

   CLR RST

   CLR CLK

   SETB RST

   MOV B, R1   ;输入写命令字

   ACALL DS1302_WRITE_BYTE

   MOV B, @R0   ;输入数据

   ACALL DS1302_WRITE_BYTE

   INC R0   ;下一个数据

   INC R1

   INC R1   ;到下一个写命令字需要+2

   CLR RST

   DJNZ R2, WRITE_TIME_LOOP

;启用写保护

   CLR RST

   CLR CLK

   SETB RST

   MOV B, #8EH   ;写保护的写命令字

   ACALL DS1302_WRITE_BYTE

   MOV B, #80H   ;WP=1(bit7=1即禁止写入)

   ACALL DS1302_WRITE_BYTE

   CLR RST

   RET

DS1302_WRITE_BYTE:   ;向DS1302写入单字节,B=命令字或写入的数据

   MOV R3, #8   ;8位数据

WRITE_BIT:

   MOV A, B

   RRC A   ;移出最低位到C

   MOV DAT, C   ;输出到数据线

   SETB CLK   ;上升沿写入

   CLR CLK

   MOV B, A   ;更新剩余数据

   DJNZ R3, WRITE_BIT

   RET

DS1302_READ_TIME:   ;读取DS1302时间到60H-66H

   MOV R0, #SECOND   ;数据存储地址(60H)

   MOV R1, #81H   ;起始读命令字

   MOV R4, #7   ;读取DS1302的7个时间寄存器

READ_TIME_LOOP:

   CLR RST

   CLR CLK

   SETB RST

   MOV B, R1   ;输入读命令字

   ACALL DS1302_WRITE_BYTE

   ACALL DS1302_READ_BYTE

   MOV @R0, A   ;存储数据

   INC R0   ;下一个数据

   INC R1

   INC R1   ;到下一个读命令字需要+2

   CLR RST

   DJNZ R4, READ_TIME_LOOP

   RET

DS1302_READ_BYTE:   ;从DS1302读取单字节,A=读取的数据

   MOV R5, #8

   MOV A, #00H

READ_BIT:

   MOV C, DAT   ;读取数据位

   RRC A   ;移入累加器

   SETB CLK   ;上升沿触发DS1302输出下一位

   CLR CLK

   DJNZ R5, READ_BIT

   RET

DS18B20_INIT:   ;DS18B20初始化(复位+存在脉冲检测)

   CLR DQ   ;拉低总线480μs

   ACALL DELAY_480US

   SETB DQ   ;释放总线

   ACALL DELAY_60US   ;等待60μs

   JNB DQ, OK   ;检测存在脉冲

;错误处理(未检测到设备)

   RET

OK:

   ACALL DELAY_480US

   RET   ;初始化成功

DS18B20_WRITE_BYTE:   ;写入单字节到DS18B20,A为待写入字节

   MOV R6, #8   ;8位数据

   CLR C

WR_BIT:

   RRC A   ;数据位移入C,判断写0还是写1

   JC WRITE1

WRITE0:

   CLR DQ

   ACALL DELAY_60US

   SETB DQ

   DJNZ R6, WR_BIT

   RET

WRITE1:

   CLR DQ

   ACALL DELAY_5US

   SETB DQ

   ACALL DELAY_60US

   DJNZ R6, WR_BIT

   RET

DS18B20_READ_BYTE:   ;从DS18B20读取单字节,A为读取的字节

   MOV R7, #8

   MOV A, #0

RD_BIT:

   CLR C

   CLR DQ   ;启动读时序

   NOP

   NOP

   SETB DQ   ;释放总线

   ACALL DELAY_15US

   MOV C, DQ   ;读取数据位

   SETB DQ

   RRC A   ;移入累加器

   ACALL DELAY_60US

   DJNZ R7, RD_BIT

   RET

DS18B20_START_CONV:   ;启动温度转换,开始测量温度

   ACALL DS18B20_INIT   ;初始化总线

   MOV A, #0CCH   ;跳过ROM

   ACALL DS18B20_WRITE_BYTE

   MOV A, #4EH   ;写暂存器命令

   ACALL DS18B20_WRITE_BYTE

   MOV A, #00H   ;TH报警值(默认值)

   ACALL DS18B20_WRITE_BYTE

   MOV A, #00H   ;TL

   ACALL DS18B20_WRITE_BYTE

   MOV A, #1FH   ;设置九位分辨率

   ACALL DS18B20_WRITE_BYTE

   ACALL DS18B20_INIT

   MOV A, #0CCH

   ACALL DS18B20_WRITE_BYTE

   MOV A, #44H   ;开启温度转换

   ACALL DS18B20_WRITE_BYTE

   RET

DS18B20_READ_TEMP:   ;读取温度值

   ACALL DS18B20_INIT   ;初始化总线

   MOV A, #0CCH   ;跳过ROM

   ACALL DS18B20_WRITE_BYTE

   MOV A, #0BEH   ;读暂存器

   ACALL DS18B20_WRITE_BYTE

   ACALL DS18B20_READ_BYTE   ;先读取低字节

   MOV TEMP_L, A

   ACALL DS18B20_READ_BYTE   ;再读取高字节

   MOV TEMP_H, A

   ACALL DS18B20_INIT   ;结束读取

   RET

CONVERT_TEMP:   ;温度处理,转换成十进制,不考虑负数与百

   MOV A, TEMP_H

   RL A

   RL A

   RL A

   RL A

   MOV TEMP_H, A

   MOV A, TEMP_L

   ANL A, #0F0H

   SWAP A

   ADD A, TEMP_H

   MOV TEMP_INT, A

   MOV A, TEMP_L

   ANL A, #08H

   CLR C

   MOV C, ACC.3

   JC EXIST

   MOV TEMP_POINT, #0

   RET

EXIST:

   MOV TEMP_POINT, #5

   RET

LCD_INIT:   ;LCD初始化

   ACALL BUSY

   MOV P0, #38H   ;8位输入、双行显示、5*8点阵

   ACALL SEND_CMD   ;先MOV再ACALL以写入命令

   ACALL BUSY

   MOV P0, #0CH   ;开显示,关光标

   ACALL SEND_CMD

   ACALL BUSY

   MOV P0, #01H   ;清屏,光标复位

   ACALL SEND_CMD

   ACALL BUSY

   MOV P0, #06H   ;右移输入,不显示移位

   ACALL SEND_CMD

   ACALL CGRAM

   RET

CGRAM:   ;自定义字符写入CGRAM

   MOV DPTR, #ZHOU

   MOV R2, #0

   ACALL BUSY

   MOV P0, #40H

   ACALL SEND_CMD

LOOP_CGRAM:   ;自定义字符表连续,故通过循环写入

   MOV A, R2

   INC R2

   MOVC A,@ A+DPTR

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, R2

   CJNE A, #40, LOOP_CGRAM       ;定义了5个*8行共40行,判断是否写完

   RET

ZHOU:

   DB 1FH, 15H, 17H, 1DH, 17H, 17H, 01H, 00H

CHE:

   DB 04H, 1FH, 0CH, 1FH, 04H, 1FH, 04H, 00H

LIANG_LEFT:

   DB 04H, 1FH, 0CH, 1FH, 04H, 1FH, 04H, 00H

LIANG_RIGHT:

   DB 0EH, 04H, 1FH, 1FH, 15H, 1BH, 11H, 00H

DU:

   DB 18H, 1BH, 04H, 08H, 08H, 04H, 03H, 00H

SEND_CMD:   ;写入命令

   CLR RS

   CLR RW

   CLR E

   SETB E

   RET

SEND_DATA:   ;写入数据

   SETB RS

   CLR RW

   CLR E

   SETB E

   RET

BUSY:   ;判断LCD是否忙

   MOV P0, #0FFH

   CLR RS

   SETB RW

   CLR E

   NOP

   SETB E

   JB P0.7, BUSY

   RET

MAIN:

   ACALL DS1302_READ_TIME

   ACALL DS18B20_START_CONV

   ACALL DELAY_100MS

   ACALL DS18B20_READ_TEMP

   ACALL CONVERT_TEMP

   ACALL BUSY

   MOV P0, #80H   ;显示第一行

   ACALL SEND_CMD

   MOV A, YEAR

   ANL A, #0F0H

   SWAP A

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, YEAR

   ANL A, #0FH

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #'-'

   ACALL SEND_DATA

   MOV A, MONTH

   ANL A, #0F0H

   SWAP A

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, MONTH

   ANL A, #0FH

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #'-'

   ACALL SEND_DATA

   MOV A, DAY

   ANL A, #0F0H

   SWAP A

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, DAY

   ANL A, #0FH

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #00H

   ACALL SEND_DATA

   MOV A, WEEK

   ANL A, #0FH

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, TEMP_INT

   MOV B, #100

   DIV AB

   MOV A, B

   MOV B, #10

   DIV AB

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, B

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #'.'

   ACALL SEND_DATA

   MOV A, TEMP_POINT

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #04H

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #0C0H   ;显示第二行

   ACALL SEND_CMD

   MOV A, HOUR

   ANL A, #0F0H

   SWAP A

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, HOUR

   ANL A, #0FH

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #':'

   ACALL SEND_DATA

   MOV A, MINUTE

   ANL A, #0F0H

   SWAP A

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, MINUTE

   ANL A, #0FH

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #':'

   ACALL SEND_DATA

   MOV A, SECOND

   ANL A, #0F0H

   SWAP A

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, SECOND

   ANL A, #0FH

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #01H

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #02H

   ACALL SEND_DATA

   ACALL BUSY

   MOV P0, #03H

   ACALL SEND_DATA

   MOV A, CAR_NUM

   MOV B, #10

   DIV AB

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   MOV A, B

   ADD A, #'0'

   ACALL BUSY

   MOV P0, A

   ACALL SEND_DATA

   LJMP MAIN

   END


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外遥控音量控制

  • LM317过压保护

  • 12V转110V/220V 500W逆变器

  • DS1669数字电位器

    相关电子头条文章