📄 text2.asm
字号:
;*****************************************************************
;name:测温传感器DS18B20和24C02的结合应用
;bye :zxinfeng
;dat :05.08.07
;功能:主要目的是one-wrie协议和I2C协议的综合运用。
;数字温度计测温范围为-50-125度,精度误差在0.1度之内
;LED直接读显示温度.在显示的过程中,如果有按键按下且是功能键盘的话,
;就判断是哪个功能键,KEY10把当前显示的温度值存入24C02,KEY11把已经
;存入到24C02中的数调用显示在数码管的前四位上;KEY12把已经存入到24C02
;中的数清,同时,数码管前四位显示0
;*****************************************************************
scl bit p3.6 ;24C02与单片机接口
sda bit p3.7
ack bit 29h ;24C02应答位变量
flag equ 2ah ;按键标志位
dat equ p3.0 ;18B20与单片机接口
temph equ 30h ;读18B20温度存放寄存器
templ equ 31h
sla data 32h ;24c02器件从地址变量
suba data 33h ;24c02器件子地址变量
bcd_0 equ 34h ;温度小数位寄存器,同时32H也是24C02发送缓冲区首址
bcd_1 equ 35h ;个位
bcd_10 equ 36h ;十位
bcd_100 equ 37h ;百位
tempzheng equ 38h ;18b20的温度调整好存放寄存器
lie equ 39h ;键盘扫描列数寄存器
keyvalue equ 3ah ;键值寄存器
mrd equ 3bh ;24c02接收缓冲区首址
mrd1 equ 3ch
mrd2 equ 3dh
mrd3 equ 3eh
;-------------------------程序开始----------------------
org 0000h
ajmp main
main: nop
mov sp,#55h
lcall proinit
loop:
lcall get_temper ;调用得到温度程序
lcall read_temper ;调用读温度程序
lcall display
lcall keyscan
mov a,flag
cjne a,#0ffh,next ;无键接下,跳转
mov a,keyvalue ;有键按下,判断大不大于10
cjne a,#9,next1 ;以确定是不是功能键
next: ajmp loop ;是数字键,返回
next1: jc next ;小于9(即C=0),转数字键处理
lcall canskey ;是功能键,调用相应的程序
ajmp loop
;--------程序初始化程序------------
proinit:
clr f0
clr ack
mov p3,#0
mov templ,#0
mov temph,#0
mov bcd_0,#0
mov bcd_1,#0
mov bcd_10,#0
mov bcd_100,#0
mov mrd,#0
mov mrd1,#0
mov mrd2,#0
mov mrd3,#0
mov sla,#0
mov suba,#0
mov lie,#0
mov keyvalue,#0
ret
;------------------------------------------------------------
;取得温度程序段
;启动温度转换命令,这时候,DS18B20开始开始测得温度并把其存入TH处TL中
;用到寄存器有:R2、R4
;------------------------------------------------------------
get_temper:
lcall ds18b20init ;发ds18b20复位和存在脉冲
jnb f0,get_end
mov a,#0cch ;总线上只有一个18B20,使用跳过ROM就可以了
lcall writebyte ;写一个字节程序段
mov a,#44h ;发温度转换命令
lcall writebyte
get_end:
ret
;-------------------------------------------------------------
;读出温度程序段
;先发送DS18B20复位脉冲和跳过ROM,(每次对寄存器操作之前,
;都要有这两步)之后,读取暂存区的温度值,并进行调整.
;用到寄存器有R2、R4、R5、R0,出口参数:31H(TEMPH)、30H(TEMPL)
;tempzheng(38h)
;-------------------------------------------------------------
read_temper:
lcall ds18b20init
jnb f0,read_end
mov a,#0cch
lcall writebyte ;再次跳过ROM
mov a,#0beh ;读暂存器内部命令
lcall writebyte
lcall readtemp ;调用读RAM程序
;把1820中的温度值计到CPU中的程序段
lcall temp_cov ;把读出的温度值进行调整(因为整数部分在TH的低四位和TL的高四位
;要把它们调整到一个寄存器里面)
read_end:
ret
;---------ds18b20复位和存在脉冲----------
ds18b20init:
clr ea
int10: setb dat
nop
mov r2,#200
int11: clr dat ;p3.0作为在DS18B20的单线接口
djnz r2,int11 ;主机复位脉冲是3us*200=600us,(18B20停在低电平超过480us会发生复位)
setb dat ;释放总线
mov r2,#30 ;延时60us,等待状态稳定
int12: djnz r2,int12
clr c
orl c,dat ;看总线有没有变低(即有没有存在脉冲)
jc endint ;C中的数=1,表示DAT是高电平,没有存在脉冲,返回主程序
setb f0 ;有DS18B02存在,建立存在标志位。
mov p1,#0feh ;存在DS18B20,使P1.0口的灯亮
mov r2,#120
int13: djnz r2,int13 ;延时240us,这是存在脉冲的时间
clr c
orl c,dat
jnc int13
ret
endint:
clr f0
mov p1,#7fh ;不存在,使p1.1口的LED亮
ret
;----------DS18B20写一个字节子程序--------
writebyte:
clr ea
mov r4,#8 ;要写入的字节数
setb dat
nop
nop
wr10: clr dat
mov r2,#8
wr11: djnz r2,wr11 ;等待16us
rrc a
mov dat,c
mov r2,#40
wr12: djnz r2,wr12
setb dat
djnz r4,wr10
ret
;-----把DS18B20中的温度值读到CPU中的程序段-----
readtemp:
mov r5,#2 ;将温度高位和低位都读出
mov r0,#31h ;将低位存入31H,(高位存入30H)
rd10: clr ea
clr c
mov r4,#8
setb dat
nop
nop
rd11:
clr dat
nop
nop
nop
setb dat ;上述几条指令,是为了产生读时间隙
mov r2,#8
rd12: djnz r2,rd12 ;等待状态稳定,delay16us
mov c,dat ;依次读入DS18B20的数据
rrc a
mov r2,#30
rd14: djnz r2,rd14 ;延时60us
djnz r4,rd11 ;判断8位读出完没有?
mov @r0,a ;把读出的数送出
dec r0
djnz r5,rd10 ;判断16位是否全读出,没有的话反回继续读
setb dat
ret
;------------DS18B20温度调整程序-----------
temp_cov:
mov a,temph
anl a,#80h
jz tempz ;最高位是0,跳到正数处理
ajmp tempf ;最高位是1,跳到负数处理
ret
tempf:
clr c
mov a,templ
cpl a
add a,#01h ;取反后的结果加1,这是补码的形式
mov templ,a
mov a,temph
cpl a
addc a,#00h
mov temph,a
tempz:
mov a,templ
anl a,#0fh ;屏蔽掉高四位,只保留小数部分
mov dptr,#temptab
movc a,@a+dptr
mov bcd_0,a ;把小数部分BCD码送入bcd_0
mov a,templ ;整数部分
anl a,#0f0h
swap a
mov templ,a
mov a,temph
anl a,#0fh
swap a
orl a,templ ;把整数部分在高低两个寄存器里的值,合到一个寄存器里
mov tempzheng,a ;把整合到一起的整数放在整数寄存器里
lcall p16_bcd
ret
;---------------------------------------
p16_bcd:
mov a,tempzheng
mov b,#100
div ab
mov bcd_100,a ;把百位数存放在R7中
mov a,b ;把低位再次取到A中
mov b,#10
div ab
mov bcd_10,a ;十位数入BCD_10
mov bcd_1,b ;个位数入bcd_1
ret
;--------------------------------------------------------------
;键盘扫描程序段
;判断有无按键按下,并求得按键值
;用到R3、R4、flag、lie、出口参数是keyvalue
;--------------------------------------------------------------
keyscan:
lcall keys ;判断有键按下没有
jnz keyd
ajmp knd ;没有按键按下反回主程序(不返回keyscan,
;是为了在没有按键的情况下,执行显示程序,实现动态扫描。)
keyd: acall delay8ms
acall keys
jz knd ;同上
mov flag,#0ffh
mov a,#0efh
mov lie,a
mov p2,a
mov r4,#00h ;列号送给r4
loop1: mov a,p2
jb acc.0,lone ;无键按下,转查第一行
mov r3,#0 ;;首行键号入r3
ljmp keyacc
lone: jb acc.1,ltwo
mov r3,#4
ljmp keyacc
ltwo: jb acc.2,lthr
mov r3,#8
ljmp keyacc
lthr: jb acc.3,next3 ;第三行无键按下,改查下一列
mov r3,#12
ljmp keyacc
next3:
jnb acc.7,knd ;查到最后一列没有
inc r4
mov a,lie
rl a
mov lie,a ;修改LIE中的数值
mov p2,a
ajmp loop1
keyacc: mov a,r3
add a,r4
mov keyvalue,a
wait: acall keys ;等待键释放
jnz wait
ret
keys: mov a,#0fh ;判断有没有键按下的程序断
mov p2,a
mov a,p2
cpl a ;变正逻辑,以高电平表示有键按下
anl a,#0fh
ret
knd: mov flag,#00h
ret ;查到最后一列,没有键按下,属于误操作,返回
;-------------------------------------------------------------
;功能键处理程序段
;-------------------------------------------------------------
canskey:
cjne a,#10,cannext1
lcall iwrnbyte ;转到存入子程序
ajmp endcan
cannext1:
cjne a,#11,cannext2
lcall irdnbyte ;转入调出子程序
lcall r_display ;把调出的数字送显
ajmp endcan
cannext2:
cjne a,#12,endcan
lcall clear0 ;转到清零24c02子程序
endcan: ret
;--------------------------------------------
;24C02处理程序段
;--------------------------------------------
;------向器件指定地址写数据------
iwrnbyte:
mov 39h,#4
mov sla,#0a0h
mov suba,#00h ;指定器件内部地址,即要把数据存放在24C02中的区域!!!
wrloop:
lcall start ;调用启动I2C总线子程序
mov a,sla ;指定器件地址
lcall wrbyte ;指定器件地址后,调用发送字节子程序
lcall cack ;调用检查应答位子程序
jnb ack,endiwr ;无应答,退出
mov a,suba
lcall wrbyte ;指定器件地址后,调用发送字节子程序
lcall cack ;调用检查应答位子程序
mov a,bcd_0
lcall wrbyte ;开始写入数据(其入口参数是ACC,所以有上述两条指令)
lcall cack
jnb ack,wrloop
mov a,bcd_1
lcall wrbyte ;开始写入数据(其入口参数是ACC,所以有上述两条指令)
lcall cack
jnb ack,wrloop
mov a,bcd_10
lcall wrbyte ;开始写入数据(其入口参数是ACC,所以有上述两条指令)
lcall cack
jnb ack,wrloop
mov a,bcd_100
lcall wrbyte ;开始写入数据(其入口参数是ACC,所以有上述两条指令)
lcall cack
jnb ack,wrloop
endiwr: lcall stop ;数据写完,调用结束总线子程序
ret
;-------从24C02指定地址读取数据------
irdnbyte:
mov 40h,#4 ;要读出的字节数
mov sla,#0a0h
mov r1,#00h ;确定24C02内部地址
mov r0,#mrd ;把接收缓冲区首址送给R0.
rdloop:
lcall start
mov a,sla
lcall wrbyte ;指定器件地址后,调用发送字节子程序
lcall cack ;调用检查应答位子程序
jnb ack,endird ;无应答,退出
mov a,r1
lcall wrbyte
lcall cack
lcall start ;重新启动总线
; mov a,sla ;读取第0个寄存器的内容
; inc a ;准备进行读操作
mov a,#0a1h ;代替上面两条指令
lcall wrbyte ;把24C02中00单元的内容写到总线上
lcall cack
jnb ack,irdnbyte
lcall rdbyte ;读操作开始 (读操作的出口参数是ACC)
mov @r0,a ;从低字节开始读取到显示缓冲区
inc r0
inc r1
djnz 40h,rdloop
endird: lcall stop
ret
;--------------------------------
;把24C02中存储的数清0,即再次向24c02的存储区中写0
clear0:
mov 42h,#4
mov sla,#0a0h ;指定地址
mov r1,#00h ;指定器件子地址
clearloop:
lcall start ;调用启动I2C总线子程序
mov a,sla
lcall wrbyte ;指定器件地址后,调用发送字节子程序
lcall cack ;调用检查应答位子程序
jnb ack,endc ;无应答,退出
mov a,r1
lcall wrbyte ;指定器件地址后,调用发送字节子程序
lcall cack ;调用检查应答位子程序
mov a,#0 ;把00H写到SDA总线上,再送入24C02内部(即清0)
lcall wrbyte ;开始写入数据(其入口参数是ACC,所以有上一条指令)
lcall cack
jnb ack,clearloop
mov a,#0 ;把00H写到SDA总线上,再送入24C02内部(即清0)
lcall wrbyte ;开始写入数据(其入口参数是ACC,所以有上一条指令)
lcall cack
jnb ack,clearloop
mov a,#0 ;把00H写到SDA总线上,再送入24C02内部(即清0)
lcall wrbyte ;开始写入数据(其入口参数是ACC,所以有上一条指令)
lcall cack
jnb ack,clearloop
mov a,#0 ;把00H写到SDA总线上,再送入24C02内部(即清0)
lcall wrbyte ;开始写入数据(其入口参数是ACC,所以有上一条指令)
lcall cack
jnb ack,clearloop
mov mrd,#0
mov mrd1,#0
mov mrd2,#0
mov mrd3,#0
lcall r_display ;清0之后,把MTD中的数送显,给人能看出清0的结果
endc: lcall stop
ret
;---------启动I2C总线子程序-------
start:
setb sda
nop
setb scl ;发送开始条件的时钟信号
lcall delay4ms
clr sda
lcall delay4ms
clr scl ;锁住总线,准备发送数据
nop
ret
;结束子程序
stop:
clr sda
nop
setb scl ;发送结束条件的时钟信号
lcall delay4ms
setb sda ;结束总线
lcall delay4ms
ret
;检查应答位子程序(返回值,ACK=1时。表示有应答)
cack:
setb sda
nop
nop
setb scl
clr ack
nop
nop
mov c,sda
jc cend
setb ack
cend: nop
clr scl
nop
ret
;写字节子程序
wrbyte:
mov r2,#8 ;要发送的位数送R0
wlp: rlc a ;取移位数据(调用前已经把求得的键值送入ACC了)
jc wr1 ;C=0,转到WR1
sjmp wr3
wlp1: djnz r2,wlp
nop
ret
wr3: clr sda ;发送0
nop
setb scl
lcall delay4ms
clr scl
sjmp wlp1
wr1: setb sda ;发送1
nop
setb scl
lcall delay4ms
clr scl
sjmp wlp1
;读取字节子程序
rdbyte:
mov r2,#08h
rlp: setb sda
nop
setb scl ;时钟线为高,接收数据
nop
nop
mov c,sda ;读取数据位
clr scl
rlc a
nop
nop
nop
nop
djnz r2,rlp
ret
;--------------------------------------------------------------
;显示子程序
;后四位显示的是DS18B20测得的温度,
;前四位显示的是24C02中存储的温度值
;入口参数是tempzheng
;--------------------------------------------------------------
display:
mov a,bcd_0 ;小数位显示程序
anl a,#0fh ;把其中的整数部分屏蔽掉.
orl a,#70h
mov p0,a
lcall delay8ms
mov a,bcd_1
orl a,#60h ;打开相应的位选(第二位)
mov p0,a
lcall delay8ms
mov a,bcd_10
orl a,#50h
mov p0,a
lcall delay8ms
mov a,bcd_100
orl a,#40h
mov p0,a
lcall delay8ms
r_display:
mov a,mrd
orl a,#30h
mov p0,a
acall delay8ms
mov a,mrd1
orl a,#20h
mov p0,a
acall delay8ms
mov a,mrd2
orl a,#10h
mov p0,a
acall delay8ms
mov a,mrd3
orl a,#00h
mov p0,a
acall delay8ms
ret
;-----------------延时程序------------------------------
delay4ms:
nop
nop
nop
nop
nop
ret
delay8ms:
mov r6,#2h
d: mov r7,#20h
d0: djnz r7,d0
djnz r6,d
ret
;--------------小数部分表----------------------------
temptab:
db 00h,00h,01h,01h,02h,03h,03h,04h,05h,05h,06h
db 06h,07h,08h,08h,09h,09h
;----------------------------------------------------
end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -