1、DS18B20封装
单总线DQ,内部9个字节的寄存器和64 bit的ROM(包含唯一标识ID),暂存寄存器包含了转换温度、报警温度上下限。
空闲状态下,总线以上拉形式保持高电平。
数据传输从低位(LSB)开始。
2、18B20复位时序
(1)复位:控制器(单片机)拉低总线480-950μs,然后释放总线(拉高)。此时,单片机引脚需设置为输出。
(2)控制器释放总线后60-240μs,18B20拉低总线,表示应答。然后拉高释放总线。
(3)在第二步18B20拉低总线时,控制器检测总线状态,如果是低电平,则可判定复位成功。此时,单片机引脚需设置为输入。这里可以加一个超时检测,以防超过240μs后,仍未收到复位应答。
(4)检测到复位应答信号后,设置单片机引脚为输出,拉高总线至少1μs,释放总线。
3、向18B20写一个字节的数据
(1)单片机引脚设置为输出,拉低总线12μs。
(2)从最低位开始发送,根据最低位的值,设置引脚为低或高,延时40μs。
(3)单片机拉高总线,保持2μs左右。
(4)对发送字节数据进行逻辑右移一位,回到第(1)步,直到8位数据都发送完成。
4、从18B20读取一个字节的数据
(1)每次读取新的位时,先将读取数据的变量逻辑右移一位。
(2)单片机引脚设置为输出,拉低总线,保持12μs左右,再拉高总线,释放,加几个NOP指令延时即可。
(3)单片机引脚设置为输入,加几个NOP指令延时。
(4)检测引脚状态,收到第一位数据(第一次为最低位)。
(5)读取一位数据后,延时40μs,将单片机引脚设置为输出,拉高总线,延迟2μs以上。
(6)回到第(1)步,读取下一位数据,直到8位数据读取完成。
5、一个完整的温度转换流程
(1)复位(初始化)
(2)发送ROM命令
(3)发送寄存器命令
(4)数据传输
单个18B20连接时,可以在复位后,发送0xCC,调过ROM命令。
寄存器命令0x44为温度转换命令,发送后,等待750ms以上(12位精度),才可进行数据读取。
寄存器命令0xBE为读取寄存器数据的命令(9个寄存器),大部分情况下,只需要读取开始的两个字节数据(温度转换结果),当读取两个字节后,可以再次复位18B20,以结束后续无关数据的读取。
实际代码时序: 复位,发送0xCC,发送0x44,延时800ms; 复位,发送0xCC,发送0xBE,调用读字节函数,保存低字节数据,调用读字节函数,保存高字节数据。
6、关于温度转换数据的处理
(1)数据一共16bit,高5个bit为符号位,如果温度值为正数,则高5位全0,温度值为负数,则高5位全1。
(2)最低4个bit为温度值的小数部分,中间的7个bit为温度值的整数部分。
(3)小数位和温度值的转换过程:将低4位乘以0.0625即为温度的小数位。 如低4位为0011,则小数部分为 3x0.0625=0.1875。程序中可以预先将小数位的值算出来,取合适的精度,用查表(00-FF,16个数值)的方式来得到小数位。
7、代码参考,温度读取后的数据处理是为了在数码管上显示,分离出符号位、整数位和小数位(小数点后两位精度):
INCLUDE 'derivative.inc' ; 这一句必须加,否则会报 value is truncated to one byte错误
; PJ1->DQ(18B20)
XDEF init_18b20,convert_18b20
XREF DS18B20_RST,r_data_H,r_data_L,r_buff,shuma_data,shuma_table,shuma_point_table,float_table
init_18b20:
PSHB ; 每个函数里面都包含一对入栈和出栈指令,以防破坏外部操作使用 B/X/Y寄存器; A寄存器自动入栈
PSHX
PSHY
BSET DDRJ,mDDRJ_DDRJ1 ; DJ1 OUTPUT输出模式
BCLR PTJ,mPTJ_PTJ1 ; DJ1 -> 0
LDAA #$C7
LDX #$77
CALL delay_n_us ; 延时 600μs (实际延时略大于600)
BSET PTJ,mPTJ_PTJ1 ; DJ1 -> 1
LDAA #$C7
LDX #$B
CALL delay_n_us ; 延时60μs
BCLR DDRJ,mDDRJ_DDRJ1 ; DJ1 INPUT输入模式
LDX #2000 ; 设置应答超时,以防进入死循环
time_out:
DEX
CPX #0
BEQ time_out_err
BRSET PTJ,mPTJ_PTJ1,time_out ; 使用PTJ, 勿使用PTIJ
LDAA #1 ; 正常检测到应答信号,返回1,复位成功
STAA DS18B20_RST
BSET DDRJ,mDDRJ_DDRJ1 ; DJ1 OUTPUT输出模式 检测到复位应答后,单片机将总线拉高,保持1μs
BSET PTJ,mPTJ_PTJ1 ; DJ1 -> 0
NOP
LDAA #$13
LDX #$1
CALL delay_n_us ; 延时1μs
PULY
PULX
PULB
RTC
time_out_err: ;超时返回0
LDAA #0
STAA DS18B20_RST
PULY
PULX
PULB
RTC
convert_18b20:
PSHB
PSHX
PSHY
CALL init_18b20
LDAB #$CC
CALL w_18b20
LDAB #$44
CALL w_18b20
LDY #2
delay_800ms: LDAA #$F9
LDX #$F9FF
CALL delay_n_us
DECY
CPY #0
BNE delay_800ms
CALL init_18b20
LDAB #$CC
CALL w_18b20
LDAB #$BE
CALL w_18b20
CALL r_18b20
MOVB r_buff,r_data_L
CALL r_18b20
MOVB r_buff,r_data_H
CALL init_18b20 ; BE命令会连续返回所有9个字节,这里复位18b20可以中断后面的 数据返回。否则18b20会出错。
; 处理数据,赋值给数码管相关变量
data_pro:
LDY #shuma_data
LDAA r_data_H
ANDA #$F0
CMPA #$F0
BNE pos_data
MOVB #$BF,1,Y+ ; negative data
pos_data: MOVB #$FF,1,Y+
LDAA r_data_H
LDAB r_data_L
ANDA #$07
LDX #4 ; 左移 4位, 计算整数位
lsr_4: LSRD
DECX
CPX #0
BNE lsr_4
LDY #$0000 ; 计算百位
LDX #100
EDIV
XGDX ; D 和 X 交换, 保存余数 D到X中
XGDY ; 将商保存在D中, 实际用到B
LDY #shuma_table ; 基址
ABY ; 加上B中的 偏移量
XGDX ; 回恢复余数到 D
LDX #shuma_data
MOVB Y,1,+X
LDY #$0000 ; 计算十位
LDX #10
EDIV
XGDX ; D 和 X 交换, 保存余数 D到X中
XGDY ; 将商保存在D中, 实际用到B
LDY #shuma_table ; 基址
ABY ; 加上B中的 偏移量
XGDX ; 恢复余数到 D
LDX #shuma_data
MOVB Y,2,+X
LDY #shuma_point_table ; 表基址 个位
ABY ; 加上B中的值 ,偏移量
LDX #shuma_data
MOVB Y,3,+X
; 小数位 计算
LDAB r_data_L ; 计算小数位 , 先查表出小数位的十进制表示
ANDB #$0F ; 屏蔽高4位
LDY #float_table
ABY
MOVB Y,r_data_L
LDAB r_data_L
LDY #$0000
LDX #10
EDIV
XGDX ; D 和 X 交换, 保存余数 D到X中
XGDY ; 将商保存在D中, 实际用到B
LDY #shuma_table ; 基址
ABY ; 加上B中的 偏移量
XGDX ; 恢复余数到 D
LDX #shuma_data
MOVB Y,4,+X
LDY #shuma_table ; 表基址 个位
ABY ; 加上B中的值 ,偏移量
LDX #shuma_data
MOVB Y,5,+X
PULY
PULX
PULB
RTC
; Write a byte to 18b20 ,入口参数 需要写的数据 存放到 B 寄存器
w_18b20:
PSHB
PSHX
PSHY
LDY #8
BSET DDRJ,mDDRJ_DDRJ1
w_start: BCLR PTJ,mPTJ_PTJ1 ; 输出0
LDAA #$17
LDX #$13
CALL delay_n_us ; 延时12μs
TBA
ANDA #$01
BEQ w0
w1: BSET PTJ,mPTJ_PTJ1 ; 写 1
LDAA #$C7
LDX #$7
CALL delay_n_us ; 延时40μs
BSET PTJ,mPTJ_PTJ1
LSRB
LDAA #$13
LDX #$3
CALL delay_n_us ; 延时2μs
DECY
CPY #0
BNE w_start
PULY
PULX
PULB
RTC
w0: BCLR PTJ,mPTJ_PTJ1 ; 写 0
LDAA #$C7
LDX #$7
CALL delay_n_us ; 延时40μs
BSET PTJ,mPTJ_PTJ1
LSRB
LDAA #$13
LDX #$3
CALL delay_n_us ; 延时2us
DECY
CPY #0
BNE w_start
PULY
PULX
PULB
RTC
; Read a byte from 18b20 ,返回的数据保存在 r_buff 内,返回数据不要保存在寄存器A/B/X/Y中,因为有出栈操作!
r_18b20:
PSHB
PSHX
PSHY
LDAB #$00
LDY #8
r_start: LSRB
BSET DDRJ,mDDRJ_DDRJ1
BCLR PTJ,mPTJ_PTJ1 ; 输出0
LDAA #$17
LDX #$13
CALL delay_n_us ; 延迟12μs
BSET PTJ,mPTJ_PTJ1 ; 释放总线
NOP
NOP
BCLR DDRJ,mDDRJ_DDRJ1 ;设置输入
NOP
NOP
NOP
BRCLR PTJ,mPTJ_PTJ1,r0 ; 不能使用PTIJ寄存器 ,在12μs处 采样
r1: ORAB #$80
LDAA #$C7
LDX #$7
CALL delay_n_us ; 延迟40μs
BSET DDRJ,mDDRJ_DDRJ1
BSET PTJ,mPTJ_PTJ1
LDAA #$0F
LDX #$13
CALL delay_n_us ; 延迟8μs
DECY
CPY #0
BNE r_start
STAB r_buff
PULY
PULX
PULB
RTC
r0: ORAB #$00
LDAA #$C7
LDX #$7
CALL delay_n_us ; 延迟40μs
BSET DDRJ,mDDRJ_DDRJ1
BSET PTJ,mPTJ_PTJ1
LDAA #$0F
LDX #$13
CALL delay_n_us ; 延迟8μs
DECY
CPY #0
BNE r_start
STAB r_buff
PULY
PULX
PULB
RTC
; 使用PIT的通道0,入口参数 微时基参数放在 A, PIT加载寄存器值放在 X
; 除去前面的设置指令耗时, PIT延时= (A+1)*(X+1)*0.025 μs
delay_n_us:
PSHB
PSHX
PSHY
MOVB #$00,PITINTE ; 关PIT中断
MOVB #$00,PITMUX ; 选择微时基0
STAA PITMTLD0 ; 设置微时基值
XGDX ; 交换D/X寄存器值,将PIT计数值放到D中
STD PITLD0 ; 设置PIT加载寄存器
MOVB #$81,PITCFLMT
MOVB #$01,PITCE ; 启动计数
BRCLR PITTF,mPITTF_PTF0,* ; 查询方式,检查是否计数溢出
MOVB #$01,PITTF ; 清除TF标志位
MOVB #$00,PITCE
MOVB #$00,PITCFLMT ; 关PIT
PULY
PULX
PULB
RTC