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
- 六大全新产品系列推出,MCX A微控制器家族迎来创新
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 从控制到系统:TI利用边缘AI重塑嵌入式MCU的边界
- 模组复用与整机重测在SRRC、CCC、CTA/NAL认证中的实践操作指南
- 有源晶振与无源晶振的六大区别详解
- 英飞凌持续巩固全球微控制器市场领导地位
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- 蓝牙信道探测技术原理与开发套件实践
- LoRa、LoRaWAN、NB-IoT与4G DTU技术对比及工业无线方案选型分析
- Microchip 推出生产就绪型全栈边缘 AI 解决方案,赋能MCU和MPU实现 智能实时决策




