1.ds18b20基于单线协议(1-wire协议)与主机通信 ,是 美信公司的
2.什么是1-wire协议?
1-wire协议是一个具有单总线主机和一个或多个从机的系统
3.什么是主、从设备?
如本工程项目中, DS18B20是从机设备,STM8为主机设备
4.主从设备的通信引脚?
DS18B20的DQ引脚------------------------------STM8板子上J4模块的2号引脚DS18B20_DQ
5.电路连接图如下所示:
注意:
(1).GND接地;
(2). DQ为数字信号输入/输出端;
(3). VDD为外接供电电源输入端(在寄生电源接线方式时接地)
(4)STM8上PD0引脚复用
1.DS18B20温度传感器的内部存储器包括一个高速暂存RAM和一个非易失性的可电擦除的E2RAM,如图
2.中间结果暂存存储器包含了8个连续字节
暂存储器的头两个字节为测得温度信息的低位和高位字节;
第3, 4字节是TH和TL的易失性拷贝, 在每次电复位时都会被刷新;
第5字节是配置寄存器的易失性拷贝, 同样在电复位时被刷新;第6、7、8个字节用于内部计算,保留
第9字节是前面8个字节的CRC检验值(冗余检验字节)
3.配置寄存器的命令内容如下
低五位一直都是1 ,TM是测试模式位,用于设置DS18B20在工作模式还是在测试模式。在DS18B20出厂时该位被设置为0,用户不要去改动。R1和R0用来设置分辨率,如下表所示:(DS18B20出厂时被设置为12位)
MSB LSB
TM | R1 | R0 | 1 | 1 | 1 | 1 | 1 |
温度分辨率设置表:
R1 |
R0 |
分辨率 |
温度最大转换时间 | 温度分辨率 |
0 |
0 |
9位 |
93.75ms | 0.5℃ |
0 |
1 |
10位 |
187.5ms | 0.25℃ |
1 |
0 |
11位 |
375ms | 0.125℃ |
1 |
1 |
12位 |
750ms | 0.0625℃ |
4.温度数据的存储格式:
S18B20 通过编程,可以实现最高 12 位的温度存储值,在寄存器中,以补码的格式存储,如图所示。一共 2 个字节,LSB 是低字节,MSB 是高字节,其中 MSb 是字节的高位,LSb 是字节的低位。大家可以看出来,二进制数字,每一位代表的温度的含义,都表示出来了。其中 S表示的是符号位,低 11 位都是 2 的幂,用来表示最终的温度。DS18B20 的温度测量范围是从-55 度到+125 度,而温度数据的表现形式,有正负温度,寄存器中每个数字如同卡尺的刻度一样分布。
(1)当温度是正的时候SSSSS=00000 ,将测到的数值乘以0.0625就可以得到实际温度;
(2)当温度是负的时候,高8位的最高5位(符号位)是1时,数值就大于7(00000111),将测到的数值取反加1再乘以0.0625就可以得到实际温度;
5.一些温度与转换后输出的数字参照如下:
举个例子吧,比如说-55℃的前12位是符号位+整数部分,后4位是小数部分:
整数部分 | 55原码 | 0000 0011 0111 |
-55原码 | 1000 0011 0111 | |
55补码 | 1111 1100 1001 | |
小数部分 | 0000 | |
两个8bit的RAM存储 | 1111 1100 1001 0000 |
1. 初始化抽象了解:
就是用主机stm8发信号叫醒温度传感器ds18B20,让它起床工作,首先,先看他还在不在床上,如果温度传感器还在床上、被叫醒了就会给一个应答,说,哦,我醒了,可以准备开始工作了!如果不在床上,就返回一个错误报告。这个过程就是初始化。
2.单线总线上的所有操作都是从初始化开始的. 过程如下:
1)请求: 主机通过拉低单线480us以上, 产生复位脉冲, 然后释放该线, 进入Rx接收模式. 主机释放总线时, 会产生一个上升沿脉冲.
DQ : 1 -> 0(480us+) -> 1
2)响应: DS18B20检测到该上升沿后, 延时15~60us, 通过拉低总线60~240us来产生应答脉冲.
DQ: 1(15~60us) -> 0(60~240us)
3)接收响应: 主机接收到从机的应答脉冲后, 说明有单线器件在线. 至此, 初始化完成.
DQ: 0
3.初始化时序图
4.要用的引用文件
/*要用到的头文件*/
#include
#include
#include "TES_ds18b20.h"
#include "timing_delay.h"
#include "stm8_board.h"
#include "conf_eeprom.h"
#include "sensor_proc.h"
/*要用到的定义和调用文件*/
/*****************stm8board.h*******************/
#define MULFUN_PD0 GPIOD, GPIO_Pin_0 /*PD0: 多功能复用管脚 */
#define TES_SENSOR_PIN MULFUN_PD0 /*PD0: 温度传感器采样管脚 */
#define SWT_SWITCH_PIN MULFUN_PD0 /*PD0: 常开门开关量管脚 */
/****************stm8l15x_gpio.c****************/
BitStatus GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin)
{
return ((BitStatus)(GPIOx->IDR & (uint8_t)GPIO_Pin));
} //设置GPIO引脚为高电平的函数
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint8_t GPIO_Pin)
{
GPIOx->ODR &= (uint8_t)(~GPIO_Pin);
} //设置GPIO引脚为低电平的函数
BitStatus GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin)
{
return ((BitStatus)(GPIOx->IDR & (uint8_t)GPIO_Pin));
} //读取指定端口管脚的输入:ReadValue = GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_0);
/*****************THS_ds18b20.h*******************/
#define DS18B20_DQ_PIN TES_SENSOR_PIN /*定义DQ引脚为温度传感器引脚*/
#define DS18B20_IO_OUT() GPIO_Init(DS18B20_DQ_PIN, GPIO_Mode_Out_PP_High_Fast) /*设置stm8上的DQ引脚为输出模式*/
#define DS18B20_IO_IN() GPIO_Init(DS18B20_DQ_PIN, GPIO_Mode_In_PU_No_IT)/*设置stm8上的DQ引脚为输入模式*/
#define DS18B20_DQ_HIGH GPIO_SetBits(DS18B20_DQ_PIN)
#define DS18B20_DQ_LOW GPIO_ResetBits(DS18B20_DQ_PIN);
5.初始化函数
/* 复位DS18B20并等待复位应答, 返回值:0 成功 1 失败 */
int DS18B20_Rst(void)
{
u8 retry=0;//重复次数初始化
/*************************
* 复位DS18B20 *
*************************/
DS18B20_IO_OUT(); //设置stm8上的DQ引脚为输出模式
/*Master send a reset signal*/
DS18B20_DQ_LOW; //单片机把DQ引脚拉低(拉低总线) stm8->0->ds18B20 主机发送复位脉冲(480-960us)
udelay(700); //等待700us
DS18B20_DQ_HIGH; //把DQ引脚拉高
/*Slave will wait for 15~60us*/
udelay(40);
/*************************
* 等待DS18B20的复位应答 *
*************************/
DS18B20_IO_IN(); /* 设置DQ管脚为输入模式 */
/* 在200us的时间内等待复位信号低电平的到来 */
while (DS18B20_DQ_IN&&retry<200)
{
retry++;
udelay(1);
};
if(retry>=200)
return 1; /* 如果在200us内没有低电平输出出来则出错 */
else
retry=0;
/* 再在240us的时间内等待复位信号拉高,表示复位结束 */
while (!DS18B20_DQ_IN&&retry<240)
{
retry++;
udelay(1);
};
/*没有等到复位信号拉高,出错退出 */
if(retry>=240)
return 1;
return 0;
}
当主机检测到应答脉冲, 便可发起ROM操作命令. 共有5类ROM操作命令, 如下表
命令类型 | 命令字节 | 功能 |
Read Rom 读ROM | 33H | 读取激光ROM中的64位,只能用于总线上单个DS18B20器件情况, 多挂时会发生数据冲突 |
Match Rom匹配ROM | 55H | 此命令后跟64位ROM序列号,寻址多挂总线上的对应DS18B20.只有序列号完全匹配的DS18B20才能响应后面的内存操作命令,其他不匹配的将等待复位脉冲.可用于单挂或多挂两种情况. |
Skip Rom 跳过ROM | CCH | 可无须提供64位ROM序列号即可运行内存操作命令, 只能用于单挂. |
Search Rom搜索ROM | F0H | 通过一个排除法过程, 识别出总线上所有器件的ROM序列号 |
Alarm Search告警搜索 | ECH | 命令流程与Search Rom相同, 但DS18B20只有最近的一次温度测量时满足了告警触发条件的, 才会响应此命令. |
命令类型 | 命令字节 | 功能 |
Write Scratchpad |
4EH | 写暂存器中地址2~地址4的3个字节(TH,TL和配置寄存器)在发起复位脉冲之前,3个字节都必须要写. |
Read Scratchpad |
BEH | 读取暂存器内容,从字节0~一直到字节8, 共9个字节,主机可随时发起复位脉冲,停止此操作,通常我们只需读前5个字节. |
Copy Scratchpad |
48H | 将暂存器中的内容复制进EERAM, 以便将温度告警触发字节存入非易失内存. 如果此命令后主机产生读时隙, 那么只要器件还在进行复制都会输出0, 复制完成后输出1. |
Convert T |
44H | 开始温度转换操作. 若在此命令后主机产生时隙, 那么只要器件还在进行温度转换就会输出0, 转换完成后输出1. |
Recall E2 |
B8H | 将存储在EERAM中的温度告警触发值和配置寄存器值重新拷贝到暂存器中,此操作在DS18B20加电时自动产生. |
Read Power Supply |
B4H | 主机发起此命令后每个读数时隙内,DS18B20会发信号通知它的供电方式:0寄生电源, 1外部供电. |
DS18B20要求有严格的时序来保证数据的完整性. 在单线DQ上, 有复位脉冲, 应答脉冲, 写0, 写1, 读0, 读1这6种信号类型. 除了应答脉冲外, 其它都由主机产生. 数据位的读和写是通过读、写时隙实现的.
1) 写时隙:
当主机将数据线从高电平拉至低电平时, 产生写时隙.所有写时隙都必须在60us以上, 各写时隙间必须保证1us的恢复时间.
写"1" : 主机将数据线DQ先拉低, 然后释放15us后, 将数据线DQ拉高;
写"0" : 主机将DQ拉低并至少保持60us以上.
2)读时隙:
当主机将数据线DQ从高电平拉至低电平时, 产生读时隙. 所有读时隙最短必须持续60us, 各读时隙间必须保证1us的恢复时间.
读: 主机将DQ拉低至少1us,. 此时主机马上将DQ拉高, 然后就可以延时15us后, 读取DQ即可.
1)读一个位bit的函数:
/* 从DS18B20读取一个位: 返回值:1/0 */
u8 DS18B20_Read_Bit(void)
{
u8 data; //一位电平
DS18B20_IO_OUT(); //设置DQ引脚位输出模式
/*Generate read time slot 产生阅读时间段* /*/
DS18B20_DQ_LOW; //单片机拉低DQ电平
udelay(2); //延时2us 读时隙间必须保证1us的恢复时间.
DS18B20_DQ_HIGH; //拉高DQ引脚
/*Master must read data from slave in 15us主线必须在15us内从ds18b20从设备读到一个位*/
DS18B20_IO_IN(); //设置DQ引脚位输入模式
udelay(9); //延时9us
if(DS18B20_DQ_IN) //如果读到DQ引脚的电平
data=1; //高电平
else
data=0; //如果没读到DQ引脚的电平,低电平
udelay(50); //延时50us
return data; //返回1或0
}
2)读一个字节dat的函数
/* 从DS18B20读取一个字节 */
u8 DS18B20_Read_Byte(void)
{
u8 i,j,dat;
dat=0;
for (i=1;i<=8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<<7)|(dat>>1);
/*假如第一次读位读到j=1,那么dat=1000 0000 |(0>>1)=1000 0000;
第二次读位读到j=1,那么dat=1000 0000 |(1000 0000>>1)
=1000 0000 | 0100 0000
=1100 0000
第三次读位到j=0,那么dat=0000 0000 |0110 0000
=0110 0000
。。。。
依次类推。
最后得到dat=abcd efgh, h是第一次读位,a是第8次读位*/
}
return dat; //返回8个位的值
}
3)写一个字节,通过读的dat值,写1或者写0的函数
/* 写一个字节到DS18B20 */
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_IO_OUT();//SET PA0 OUTPUT;
for (j=1;j<=8;j++) //低位在先,读取时低位在前,高位在后
//所以在读取的过程中要移位操作,把低位不断的往右移动
{
testb=dat&0x01; //假如读到的一个字节是1111 0101 那么dat=1111 0101
//testb=1111 0101&0000 0001 =(只用算dat最后一位)即1&1=1 此时steb非0 写1
dat=dat>>1; //然后数据右移一位,0111 1010 那么dat=0111 1010
//testb=0&1=0,此时写0
if (testb) //如果testb为非0,则执行写1
{
DS18B20_DQ_LOW;// Write 1
udelay(2);
DS18B20_DQ_HIGH;
udelay(60);
}
else //如果testb为0,则执行写0
{
DS18B20_DQ_LOW;// Write 0
udelay(60);
DS18B20_DQ_HIGH;
udelay(2);
}
}
}
4).开始温度转换
/* 开始温度转换 */
void DS18B20_Start(void)
{
DS18B20_Rst(); //复位
DS18B20_Write_Byte(0xcc);// skip rom当总线上只有一个器件的时候可以跳过rom,不进行rom检测
DS18B20_Write_Byte(0x44);// convert当写入0x44命令后开始进行温度的转换
msleep(750);
}
5)初始化DS18B20的IO口
/* 初始化DS18B20的IO口 DQ 同时检测DS的存在: 1->不存在 0->存在 */
u8 ds18b20_init(void)
{
GPIO_Init(DS18B20_DQ_PIN, GPIO_Mode_Out_PP_High_Fast);//使能stm8上的DS18B20_DQ_PIN引脚
return DS18B20_Rst();
}
6)采样函数
/* 从ds18b20得到温度值(12位精度转换) 返回值:两个字节存放 */
uint16_t sample_TES_ds18b20(void)
{
uint8_t sign; /* 温度值为正或负 */
uint8_t TL,TH; /* DS18B20读出的温度寄存器值,TH存放高8位,TL存放低8位 */
uint16_t tmp; //16位的温度值
uint16_t temp = 0xFFFF; /* 温度值 */
uint8_t byte; //定义一个byte8位
GPIO_Init(DS18B20_DQ_PIN, GPIO_Mode_In_PU_No_IT); //初始化DS18B20的DQ管脚PD0为采样模式
msleep(5);
if( ds18b20_init() )
{
printf("ERROR: DS18B20 temperature sensor initialise failure\n");
goto OUT;
}
DS18B20_Start (); /* 开始温度转换 */
msleep(750);
DS18B20_Rst();
DS18B20_Write_Byte(0xcc);// skip rom跳过线上器件检查(只有ds18b20)
DS18B20_Write_Byte(0xbe);// Read Scratchpad,读暂存器指令 ,开始转化
TL=DS18B20_Read_Byte(); // LSB
TH=DS18B20_Read_Byte(); // MSB
if(TH>7) /* 高5位为1,说明温度为负 */
{
tmp = ~(TH<<8|TL) + 1; //补码
sign=1;//温度为负
}
else
{
sign=0;//温度为正
tmp = TH<<8 | TL;
}
/* 12位精度转换时,TH的低三位和TL的高四位组成温度值的整数部分,
* 而TL的低四位为小数精度部分,并且精度系数为0.0625
*
* RF将使用2个bytes来传输这个温度值,其定义为:
* Byte[0]: 温度数据小数点后两位有效值: 0~99
* Byte[1]: bit[7]符号位 0为正数 1为负数 bit[0:6]:温度数值正数部分:0~127
*/
byte = tmp>>4; /* 温度的整数位 */
byte |= sign<<7; /* 温度的符号位 */
temp = byte << 8; //整部分移到高位
byte = (tmp&0xF)*625/1000;//数点后两位有效值,tmp&0XF,取最低四位(小数位)
temp |= byte; //整数与小数合并