仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
带有全面中文注释的单片机源程序如下:
- ;**********************************************************************
- ;晶振为12MHZ,显示实时时间,温度
- ;测量温度范围为-55~+99摄氏度
- ;***********************************************************************
- T_RST EQU P1.5 //DS1302复位线引脚
- T_CLK EQU P1.6 //DS1302时钟线引脚
- T_IO EQU P1.7 //DS1302数据线引脚
- TEM_L EQU 40H //存放从DS18B20中读出的高、低位温度值
- TEM_H EQU 41H
- TEM_NUM EQU 42H //存放温度转换后的整数部分
- FLAG0 EQU 45H //FLAG0存放温度的符号
- DQ EQU P2.3 //DS18B20数据线
- RS BIT P2.0 //LCD控制线定义
- RW BIT P2.1
- E BIT P2.2
- SKIP_B20 EQU 0CCH //DS18B20跳过ROM命令
- START_B20 EQU 44H //DS18B20温度变换命令
- READDS EQU 0BEH //DS18B20读暂存器命令
-
- ORG 0000H
- AJMP MAIN
- ORG 0100H
- MAIN: MOV SP,#90H
- ACALL LCD_INT //进行LCD初始化
- MOV A,#8BH //在LCD第一行第12列显示WEEK
- LCALL W_COM
- MOV A,#57H //字母'W'的字符库为01010111
- LCALL W_DAT
- MOV A,#45H //字母'E'的字符库为01000101
- LCALL W_DAT
- MOV A,#45H
- LCALL W_DAT
- MOV A,#4BH //字母'K’的字符库为01001011
- LCALL W_DAT
- MOV A,#0C0H //第二行第一列显示TIME
- LCALL W_COM
- MOV A,#54H //字母'T'的字符库为01010100
- LCALL W_DAT
- MOV A,#49H //字母'I'的字符库为01001001
- LCALL W_DAT
- MOV A,#4DH //字母'M'的字符库为01001101
- LCALL W_DAT
- MOV A,#45H //字母'E'的字符库为01000101
- LCALL W_DAT
-
-
- MOV 60H,#00H //设置秒存储单元
- MOV 61H,#00H //设置分存储单元
- MOV 62H,#16H //设置时存储单元
- MOV 63H,#05H //设置日存储单元
- MOV 64H,#01H //设置月存储单元
- MOV 65H,#03H //设置星期存储单元
- MOV 66H,#18H //设置年存储单元
- LCALL SET1302 //向DS1302写入时间
- REP: LCALL GET1302 //读出时间
- LCALL GET_TEM //读出转换后的温度值
- LCALL TEM_COV
- ;;;;;;;;;;在LCD第一行显示年月日和星期;;;;;;;;;;
- MOV A,#80H //显示在LCD第一行第一列
- LCALL W_COM //调用LCD写命令子程序
- MOV A,#'2'
- ACALL W_DAT //调用LCD写数据子程序
- MOV A,#'0'
- ACALL W_DAT
- MOV A,56H //年拆分成十位与个位,转换字符显示
- MOV B,#10H
- DIV AB
- ADD A,#30H //数字的字符库从00110000B即30H开始
- ACALL W_DAT
- MOV A,B
- ADD A,#30H
- ACALL W_DAT
- MOV A,#'-'
- ACALL W_DAT
- MOV A,54H //月拆分成十位与个位,转换字符显示
- MOV B,#10H
- DIV AB
- ADD A,#30H
- ACALL W_DAT
- MOV A,B
- ADD A,#30H
- ACALL W_DAT
- MOV A,#'-'
- ACALL W_DAT
- MOV A,53H //日拆分成十位与个位,转换字符显示
- MOV B,#10H
- DIV AB
- ADD A,#30H
- ACALL W_DAT
- MOV A,B
- ADD A,#30H
- ACALL W_DAT
- MOV A,#8FH //设置LCD显示星期的位置为第一行第十六列
- LCALL W_COM
- MOV A,55H //显示星期
- ADD A,#30H
- LCALL W_DAT
- ;;;;;;;;;;在LCD第二行显示时分秒和温度;;;;;;;;;;;;;
- MOV A,#0C4H //写入显示缓冲区的起始地址为第二行第五列开始
- LCALL W_COM
- MOV A,52H //小时拆分成十位与个位,转换字符显示
- MOV B,#10H
- DIV AB
- ADD A,#30H
- LCALL W_DAT
- MOV A,B
- ADD A,#30H
- LCALL W_DAT
- MOV A, #':'
- LCALL W_DAT
- MOV A,51H //分拆分成十位与个位,转换字符显示
- MOV B,#10H
- DIV AB
- ADD A,#30H
- LCALL W_DAT
- MOV A,B
- ADD A,#30H
- LCALL W_DAT
- MOV A, #':'
- LCALL W_DAT
- MOV A,50H //秒拆分成十位与个位,转换字符显示
- MOV B,#10H
- DIV AB
- ADD A,#30H
- LCALL W_DAT
- MOV A,B
- ADD A,#30H
- LCALL W_DAT
- MOV A,#0CDH //温度显示在LCD第二行第十四列
- LCALL W_COM
- MOV A,FLAG0 //显示温度正负号
- ACALL W_DAT
- MOV A,TEM_NUM //温度整数拆分成十位和个位显示
- MOV B,#0AH
- DIV AB //商送给A,余数送给B
- ADD A,#30H
- CJNE A,#30H,REP1 //如果十位为0,则不显示
- MOV A,#20H //字符库中00100000B(20H)为空白
- REP1: ACALL W_DAT //调用LCD写数据子程序
- MOV A,B //将求得的余数送给A
- ADD A,#30H
- ACALL W_DAT //调用LCD写数据子程序
- LJMP REP
- /******************时间**************************/
- ;;;;;;;;累加器A写一字节到DS1302;;;;;;;;;;;;;
- WE: MOV 70H,#08H //一个字节八位,移动八次
- INB1: MOV A,B //将命令字送给A
- RRC A //通过A移入进位标志位C
- MOV B,A
- MOV T_IO,C //移入到芯片内
- SETB T_CLK
- CLR T_CLK
- DJNZ 70H,INB1
- RET
- ;;;;;;;;读一字节到累加器A中;;;;;;;;;;;;;;;;;
- READ: MOV 70H,#08H //一个字节八位,移动八次
- OUTB1: MOV C,T_IO //从芯片内移动到进位标志位C
- RRC A //通过C再移入累加器A中
- SETB T_CLK
- CLR T_CLK
- DJNZ 70H,OUTB1
- RET
- ;;;;;;;;;向DS1302写入时间子程序;;;;;;;;;;;;;;
- SET1302:CLR T_RST //RST引脚为低电平,数据传送终止
- CLR T_CLK //清零时钟总线
- SETB T_RST //RST引脚为高电平,逻辑控制有效
- MOV B,#8EH //控制命令字(10001110-进行存取时间,写操作)
- LCALL WE //调用写字节命令
- MOV B,#00H //写操作前清写保护位W
- LCALL WE
- SETB T_CLK
- CLR T_RST
- MOV R0,#60H //秒 分 时 日 月 星期 年 数据存储在60-66H单元
- MOV R7,#07H //共7个字节
- MOV R1,#80H //写秒寄存器命令
- S1: CLR T_RST
- CLR T_CLK
- SETB T_RST
- MOV B,R1 //写入写秒指令
- LCALL WE
- MOV A,@R0 //将秒存储单元中的数值送给A
- MOV B,A
- LCALL WE
- INC R0 //R0加一,指向下一个写入的日历、时钟数据
- INC R1 //指向下一个时间寄存器
- INC R1
- SETB T_CLK
- CLR T_RST
- DJNZ R7,S1
- CLR T_RST
- CLR T_CLK
- SETB T_RST
- MOV B,#8EH //控制命令字(10001110-进行存取时间,写操作)
- LCALL WE
- MOV B,#80H //控制寄存器中WP置1
- LCALL WE
- SETB T_CLK
- CLR T_RST //结束写过程
- RET
- ;;;;;;;;;;;;;;;读时间子程序;;;;;;;;;;;;;;;;;
- GET1302:MOV R0,#50H
- MOV R7,#7
- MOV R1,#81H //读秒寄存器命令
- G1: CLR T_RST
- CLR T_CLK
- SETB T_RST
- MOV B,R1 //写入读秒寄存器命令
- LCALL WE
- LCALL READ
- MOV @R0,A //存入读出数据
- INC R0 //指向下一个存放日历、时钟的存储单元
- INC R1 //指向下一个日历、时钟寄存器
- INC R1
- SETB T_CLK
- CLR T_RST
- DJNZ R7,G1 //未读完,继续读下一个
- RET
-
- /********************温度************************/
- ;;;;;;;;;;;;;;DS18B20复位程序;;;;;;;;;;;;;;;;;
- B20_INT:SETB DQ //将数据线置高电平“1”
- NOP //延时
- NOP
- CLR DQ //数据线拉到低电平“1”
- MOV R7,#0CH //延时60*12=720US
- INT_DEL:ACALL DEL60US
- DJNZ R7,INT_DEL
- SETB DQ //数据线拉到高电平“1”
- ACALL DEL60US //延时等待
- ACALL DEL60US
- MOV C,DQ //将数据线的状态送给C,判断初始化是否成功,是否产生一个低电平
- JC ERROR //如果C等于1,则跳出,初始化失败
- ACALL DEL60US //初始化成功,CPU读到数据线上的低电平,继续延时
- ACALL DEL60US
- ACALL DEL60US
- ACALL DEL60US
- RET
- ERROR: CLR DQ //初始化未成功,继续进行初始化
- SJMP B20_INT
- RET
- ;;;;;;;;;;;;;;;;;读DS18B20一个字节到累加器A子程序;;;;;;;;;;;;
- R_BYTE: MOV R7,#08H //读取8位为一字节
- SETB DQ
- NOP
- NOP
- LOOP: CLR DQ //将数据线从高电平拉为低电平时,作为一个读周期的开始
- NOP //延时,保证低电平至少为1US
- NOP
- NOP
- SETB DQ
- MOV R6,#07H //循环到A的最高位
- DJNZ R6,[ DISCUZ_CODE_1 ]nbsp;
- MOV C,DQ //将数据线中的值送给进位标志位C
- ACALL DEL60US
- RRC A //累加器A带进位右移
- SETB DQ
- DJNZ R7,LOOP //将一字节读到累加器A
- ACALL DEL60US
- ACALL DEL60US
- RET
-
- ;;;;;;;;;;;;;累加器A写到DS18B20子程序;;;;;;;;;;
- W_BYTE: MOV R7,#08H //将8位连续数据(一字节)写入A
- SETB DQ //拉高数据线
- NOP
- NOP
- LOOP1: CLR DQ //将数据线拉到低电平,作为写周期的开始
- MOV R6,#07H //循环到累加器A的最高位Acc.7
- DJNZ R6,$
- RRC A //从累加器A的最高位开始写入
- MOV DQ,C //将进位C送给DS18B20(写入时序,0或者1时序)
- ACALL DEL60US //保持60US
- SETB DQ //在15US内将数据线拉为高电平
- DJNZ R7,LOOP1 //将一字节写入A
- RET
- DEL60US:MOV R6,#1EH //延时(30*2)us
- DJNZ R6,$
- RET
- ;;;;;;;;;;;读温度子程序;;;;;;;;;;;;;;;;;;
- GET_TEM:ACALL B20_INT //DS18B20复位程序
- MOV A,#0CCH //DS18B20跳过ROM命令
- ACALL W_BYTE //调用写DS18B20子程序
- ACALL DEL60US
- ACALL DEL60US
- MOV A,#44H //DS18B20温度转换命令,开始装换温度
- ACALL W_BYTE //调用写DS18B20子程序
- ACALL DEL60US
- ACALL B20_INT //写完之后,将DS18B20复位
- MOV A,#0CCH //DS18B20跳过ROM命令
- ACALL W_BYTE //调用写DS18B20子程序
- ACALL DEL60US
- MOV A,#0BEH //DS18B20读暂存器命令
- ACALL W_BYTE //调用写DS18B20子程序
- ACALL DEL60US
- ACALL R_BYTE //读温度低字节
- MOV TEM_L,A //将读出的温度低字节保存在40H
- ACALL R_BYTE //读温度高字节
- MOV TEM_H,A // 将读出的温度高字节保存在41H
- RET
- ;;;;;将从DS18B20中读出的温度转换成整数显示;;;;;;;;;;;;
- TEM_COV:MOV FLAG0,#'+' //设置当前温度为正,并且存放在45H
- MOV A,TEM_H //将读取的温度高字节送给A
- SUBB A,#0F8H //判断C的值是否为1,为1则为正,为0则为负
- JC TEM0 //看温度值是否为负,不是(C=1),转
- MOV FLAG0,#'-' //是,置FLAG0为负
- MOV A,TEM_L //将读取的温度低字节送给A
- CPL A //求此时负值温度的补码
- ADD A,#01H
- MOV TEM_L,A //将求得的补码存放在40H
- MOV A,TEM_H //将读取的温度高字节送给A
- CPL A
- ADDC A,#01H
- MOV TEM_H,A //在将其补码放在41H
- TEM0: MOV A,TEM_L //存放整数部分到TEMPER_NUM
- ANL A,#0F0H //取出温度低字节的高四位
- SWAP A //将取出的高四位存放在A中低四位位置
- MOV TEM_NUM,A //将其存放在42H
- MOV A,TEM_H
- SWAP A
- ORL A,TEM_NUM
- MOV TEM_NUM,A
- RET
- /***************LCD显示*******************/
- ;;;;;;LCD初始化子程序;;;;;;;;;;;;
- LCD_INT:MOV A,#00000001H //清屏
- ACALL W_COM //调用读命令子程序
- MOV A,#00111000B //传输8位数据,显示两行,使用5*7的字符
- LCALL W_COM
- MOV A,#00001100B //显示器开,光标关,字符不闪烁
- LCALL W_COM
- MOV A,#00000110B //字符不动,光标自动右移一格
- LCALL W_COM
- RET
- ;;;;;写入命令子程序(RS=0,RW=0);;;;;;
- W_COM: ACALL F_BUSY
- CLR E
- CLR RS
- CLR RW
- SETB E
- MOV P0,A
- CLR E
- RET
- ;;;;;写入数据子程序(RS=1,RW=0);;;;;
- W_DAT: ACALL F_BUSY
- CLR E
- SETB RS
- CLR RW
- SETB E
- MOV P0,A
- CLR E
- RET
- ;;;;;查忙子程序;;;;;;;;;;;;;
- F_BUSY: PUSH ACC
- MOV P0,#0FFH
- CLR RS //忙标志
- SETB RW
- WAIT: CLR E //给使能端有效信号(下降沿触发)
- SETB E
- JB P0.7,WAIT //直接寻址位P0.7是否为1,为1则忙,继续等待
- POP ACC
- RET
- END
-
复制代码
所有资料51hei提供下载:
LCD1602时钟温度显示+仿真.zip
(77.69 KB, 下载次数: 99)
|