目录
单总线
单总线介绍
- 单总线(1-wire BUS)是由Dallas公司开发的一种通用数据总线
- 使用一根异步、半双工的通信线:DQ
- 单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,可以省去设备的VDD线路,此时供电+通信只需要DQ和GND两根线
单总线的电路规范
- 设备的DQ均要配置成开漏输出模式
- DQ添加一个上拉电阻,阻值一般为4.7k欧左右
- 若此总线的从机采用寄生供电,则主机还应配一个强上拉电阻
注意:第一个为电源供电电路,第二个为寄生供电电路(相对于第一个多了个强上拉)
单总线时序结构
初始化
前言:主机将总线拉低至少480us,然后释放总线,等待15——16us后,存在的从机会拉低总线60——240us以响应主机,之后从机释放总线(总过程至少960us)。
注意:上面弯的时序图部分是电阻的弱上拉作用,电阻拉的高电平是一种弱上拉,不会立即把总线拉高。
代码理解
//单总线初始化
unsigned char OneWire_Init(){
unsigned char i=0,AckBit=0;
OneWire_DQ=1;
OneWire_DQ=0;
i=247;while(--i); //延时500us
OneWire_DQ=1; //释放总线
i=32;while(--i); //延时70us
AckBit=OneWire_DQ; //读出IO口电平
i=247;while(--i); //延时500us
return AckBit;
}
发送一位
前言:主机将总线拉低60-120us,然后释放总线,表示发送0;主机将总线拉低1-15us,然后释放总线表示发送1.从机将在总线拉低30us后(典型值)读取电平,发送的整个时间片应大于60us但不超过120us
代码理解
//单总线发送1位
void OneWire_SendBit(unsigned char Bit){
unsigned char i=0;
OneWire_DQ=0;
i=4;while(--i); //延时10us
OneWire_DQ=Bit; //是1的话此时拉高,是0的话不拉高直接走完时序
i=24;while(i--); //延时50us
OneWire_DQ=1;
}
接收一位
前言:主机将总线拉低1-15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us末端)读取为低电平则为接收0,读取为高电平则为接收1,整个时间片应大于60us。
注意:在15us内若电平变为1,则读取到的为1(主机将线拉下来后释放,释放之后总线立即弹上去说明从机发的是1),若电平依旧是0,则读取到的为0(阴影表示从机随时可能释放总线)
代码理解
//单总线接收一位
unsigned char OneWire_ReceiveBit(){
unsigned char Bit=0,i=0;
OneWire_DQ=0;
i=2;while(i--); //延时5us
OneWire_DQ=1;
i=2;while(i--); //延时5us
Bit=OneWire_DQ;
i=24;while(i--); //延时50us
return Bit;
}
发送一个字节
前言:连续调用8次发送1位的时序,依次发送一个字节的8位(低位在前)
代码理解
//单总线发送1字节
void OneWire_SendByte(unsigned char Byte){
unsigned char i=0;
for(i=0;i<8;i++){
OneWire_SendBit(Byte&(0x01<<i)); //低位在前(任何数当作bit看不为0就为1)
}
}
接收一个字节
前言:连续调用8次接收1位的时序,依次接收一个字节的8位(低位在前)
代码理解
//单总线接收1字节
unsigned char OneWire_ReceiveByte(){
unsigned char i=0,Byte=0;
for(i=0;i<8;i++){
if(OneWire_ReceiveBit()==1){
Byte=Byte|(0x01<<i);
}
}
return Byte;
}
DS18B20
DS18B20介绍
- DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点
- 测温范围:-55摄氏度到+125摄氏度
- 通信接口:1-Wire(单总线)
- 其他特征:可形成总线结构(可以在一条通信线上挂载多个设备),内置温度报警功能、可寄生供电(电源不用接,直接接数据线和GND就可以)
理解:模拟温度传感器如热敏电阻,可以直接测温度,它输出的电压随着温度变化而变化;我们通过AD采集芯片(模拟到数字转化的芯片)将模拟的电压值转化为数字电压值,这样单片机读取后才会知道温度;由此观之模拟温度传感器比较复杂,需要外部电路和模数转换及系数配比才可得到具体温度;而数字传感器就集成了模拟温度传感器以及AD模数转换器以及微控制器,它内部读取模拟温度传感器,然后自动通过内部转化,将温度存在RAM中;因此我们只需通过引脚及单总线协议将温度转化读取出来就可以。
引脚及应用电路
内部结构
理解:
- 最左边的电路为寄生电源电路,正常情况下电源用的是VDD进而让电流走向internal Vdd,若不用VDD,那么电流从DQ进流入internal Vdd(寄生供电使用时需要强上拉)。
- power-supply-sense:电源供给感应模块,它可以感应我们外部VDD是否存在,若不存在(采用寄生供电)他会调节内部的结构来省电
- 64bit ROM:一种光刻ROM,这种ROM标识着我们的ID号(每个DS18B20都有全球唯一的一个ID号)在通信时就作为器件的地址,用于总线的通信寻址;同时其也是单总线的一个接口
- Memory control logic:64bit ROM便会进入,内存控制逻辑,其掌管着内部RAM
- scartchpad:其是内部RAM,内部储存着温度等等一些配置参数,用于总线的数据交互(右边的连线为内部的一些设备)
- 当我们想要通信的时候,经过64bit ROM,确定是对该设备通信,来和Memory control logic交流,若我们想读,那么他就会从scartchpad中取出数据放在总线上,若我们想写,那么他就会把我们的数据写到scartchpad里面
- temperature sensor:内部的模拟温度传感器,其自动的将温度转换都做好了,当我们发出指令让他开始温度转换时,温度传感器就会工作,然后将他的数据放到RAM里面,我们之后通过数据交互就可以把温度值拿出来了
- alarm high trigger:报警高触发计算器,用来存储我们的温度上限阈值,用于温度报警,其存储介质为E2PROM
- alarm low trigger:报警低触发计算器,用来存储我们的温度下限阈值,用于温度报警,其存储介质为E2PROM
- configuration register:配置寄存器,其用来设置我们的分辨率以及精度(出场默认最高分辨率0.0625)我们通过配置两位可以把分辨率降低,最低0.5摄氏度(降低后温度转换速度提升,该字节有8位,其中第六第七位为R0和R1位,通过配置这两位就可以配置不同分辨率)
- 8位crcgenerator:将RAM之前的数据进行校验,得到校验码放到ROM后面(用于通信判断数据是否正确的)
存储器结构
理解:
- 左边暂存器scartchpad总共9个字节,前两个字节共同组成了温度的数据,若上电直接读的话默认85摄氏度(80+5)
- byte2、byte3、byte4分别对应右边的E2PROM;我们不能从外面发送一个数据存到E2PROM里面,我们只能先将数据写入暂存器,然后我们通过发送一条指令进而把暂存器中的数据复制到E2PROM中(读取就从E2PROM中将数据回调到暂存器)上电时会自动地将E2PROM的三个字节搬到暂存器中
- byte5、byte6、byte7为保留位,都没有被使用
- CRC校验:将前面的8个字节做一个运算,然后算出一个校验位跟在后面;我们把暂存器整个读出来后进行相同的运算,看看是不是得到对应的校验位,若是证明数据传输正确
DS18B20操作流程
- 初始化:1.从机复位;2.从机是否响应
- ROM操作:ROM指令,本指令需要的读写操作
- 功能操作:功能指令,本指令需要的读写操作
ROM指令
- 搜寻ROM:SEARCH ROM[F0h](用于确定挂载在同一总线上的DS18B20的个数和识别64位ROM地址)
- 读取ROM:READ ROM[33h](读取DS18B20中ROM中的编码)
- 匹配ROM:MATCH ROM[55h](发出指令后,接着发出64位ROM的编码,访问总线上与该编码相应的DS18B20)
- 跳过ROM:SKIP ROM[CCh](忽略64位ROM地址,直接向DS18B20发送温度变换命令,适用于单片工作)
- 报警搜索:ALARM SEARCH[ECh](执行后,只有温度超过设定值上限或下限的片子才做出响应)
功能指令
- 温度变换:CONVERT T[44h](暂存器温度值从温度传感器中更新)
- 写暂存器:WRITE SCRATCHPAD[4Eh](将字节数据写入到中间的RAM[byte2、3、4])
- 读暂存器:READ SCRATCHPAD[BEh](将暂存器的值全部的读出来)
- 复制暂存器:COPY SCRATCHPAD[48H](将RAM[byte2,3,4]字节写入到E2PROM)
- 回调E2PROM:RECALL E2[B8h](将E2PROM的值覆盖到RAM[byte2,3,4]字节处)
- 读取设备供电模式:READ POWER SUPPLY[B4h](读取DS18B20供电方式,寄生供电时DS1820发送0,外接电源时DS18B20发送1)
DS18B20数据帧
温度变换
前言:初始化——》跳过ROM——》开始温度变换
代码理解
//温度转换
void DS18B20_ConvertT(){
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T);
}
温度读取
前言:初始化——》跳过ROM——》读暂存器——》连续的读操作
注意:LSB和MSB共同组成具体温度.
代码理解
//读取温度
float DS18B20_READT(){
unsigned char TLSB,TMSB;
int Temp=0;
float T=0;
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
TLSB=OneWire_ReceiveByte();
TMSB=OneWire_ReceiveByte();
Temp=(TMSB<<8)|TLSB;
T=Temp/16.0; //直接÷会损失精度,所以要÷浮点数
return T;
}
温度的存储格式
注意:
- MSB前5位均表示符号位,若温度值是负的,那么前五位全为1,若是正的,则前五位全为0.
- LSB后四位均表示小数,bit3-0.5,bit2-0.25,bit1-0.125,bit0-0.0625(最低位变化一次整个温度值变化0.0625)
举例
注意:上面的负数采用了补码的行式。
仿真案例
需求:从传感器中读出温度数据。
电路图
keil文件
#include "reg51.h"
sbit RS=P3^0;
sbit RW=P3^1;
sbit E=P3^2;
void delay(unsigned int n){
unsigned int i=0,j=0;
for(i=0;i<n;i++){
for(j=0;j<120;j++);
}
}
//写指令
void writecom(unsigned char com){
//写指令
RS=0;
RW=0;
E=0;
P2=com;
delay(5);
E=1;
E=0;
}
//写数据
void writedat(unsigned char dat){
//写指令
RS=1;
RW=0;
E=0;
P2=dat;
delay(5);
E=1;
E=0;
}
//初始化液晶屏
void initlcd(){
writecom(0x38);
writecom(0x0c);
writecom(0x06);
writecom(0x01);
}
//显示一个字符
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char){
if(Line==1){
writecom(0x80|(Column-1));
}else{
writecom(0x80|(Column-1)+0x40);
}
writedat(Char);
}
int LCD_Pow(int X,int Y){
unsigned char i;
int Result=1;
for(i=0;i<Y;i++){
Result*=X;
}
return Result;
}
//展示数字
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){
unsigned char i;
if(Line==1){
writecom(0x80|(Column-1));
}else{
writecom(0x80|(Column-1)+0x40);
}
for(i=Length;i>0;i--){
writedat('0'+Number/LCD_Pow(10,i-1)%10);
}
}
//单总线相关
sbit OneWire_DQ=P3^3;
//单总线初始化
unsigned char OneWire_Init(){
unsigned char i=0,AckBit=0;
OneWire_DQ=1;
OneWire_DQ=0;
i=247;while(--i); //延时500us
OneWire_DQ=1; //释放总线
i=32;while(--i); //延时70us
AckBit=OneWire_DQ; //读出IO口电平
i=247;while(--i); //延时500us
return AckBit;
}
//单总线发送1位
void OneWire_SendBit(unsigned char Bit){
unsigned char i=0;
OneWire_DQ=0;
i=4;while(--i); //延时10us
OneWire_DQ=Bit; //是1的话此时拉高,是0的话不拉高直接走完时序
i=24;while(i--); //延时50us
OneWire_DQ=1;
}
//单总线接收一位
unsigned char OneWire_ReceiveBit(){
unsigned char Bit=0,i=0;
OneWire_DQ=0;
i=2;while(i--); //延时5us
OneWire_DQ=1;
i=2;while(i--); //延时5us
Bit=OneWire_DQ;
i=24;while(i--); //延时50us
return Bit;
}
//单总线发送1字节
void OneWire_SendByte(unsigned char Byte){
unsigned char i=0;
for(i=0;i<8;i++){
OneWire_SendBit(Byte&(0x01<<i)); //低位在前(任何数当作bit看不为0就为1)
}
}
//单总线接收1字节
unsigned char OneWire_ReceiveByte(){
unsigned char i=0,Byte=0;
for(i=0;i<8;i++){
if(OneWire_ReceiveBit()==1){
Byte=Byte|(0x01<<i);
}
}
return Byte;
}
//DS18B20模块
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
//温度转换
void DS18B20_ConvertT(){
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T);
}
//读取温度
float DS18B20_READT(){
unsigned char TLSB,TMSB;
int Temp=0;
float T=0;
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
TLSB=OneWire_ReceiveByte();
TMSB=OneWire_ReceiveByte();
Temp=(TMSB<<8)|TLSB;
T=Temp/16.0; //直接÷会损失精度,所以要÷浮点数
return T;
}
float T=0;
void main(){
initlcd();
while(1){
DS18B20_ConvertT();
T=DS18B20_READT();
if(T<0){
LCD_ShowChar(2,1,'-');
T=-T;
}else{
LCD_ShowChar(2,1,'+');
}
LCD_ShowNum(2,2,T,3);
LCD_ShowChar(2,5,'.');
LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);
}
}