⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 text2.asm

📁 18B20和51单片机的好程序 我实验过可以通过的
💻 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 + -