DS18B20驱动(单总线协议)

        DS18B20是单片机一个可以做到测量温度的模块。在CT107D型号(stc15系列)单片机中使用单总线协议通讯。这里总结针对于在代码实现的经验。     

DS18B20的特点有以下几种:

                                                                         1-采用单总线(1-Wire)协议.

        2-拥有独立64位ID

        3-多点测温应用

        4-无需外部元件

        5-可以由数据线供电

        6-测量温度范围:-55℃~+125℃

        7-在-10℃~+85℃区间内有±0.5℃的误差

        8-可以选择9~12位温度分辨率(二进制数)

        9-转换12位温度分辨率最多需要750ms

DS18B20电路图

  • GND:电源地线                                                
  • DQ:数字信号输入/输出端。
  • VDD:外接供电电源输入端。
  • ROM:只读存储器。
  • SCRATCHPAD:暂存器。
  • MEMORY CONTROL LOGICl:存储器控制逻辑

        这里着重看右半部分的工作,只读存储器(ROM)跟暂存器(SCRATCHPAD)依靠存储器控制逻辑(MEMORY CONTROL LOGICl)来控制双方的数据交换。只读存储器使用单总线协议用一个串口跟外界完成数据交换。暂存器则需要(从上到下)与温度传感器,警告触发寄存器,配置寄存器,2字节用户寄存器,8位循环冗余校验产生器交换数据。

DS18B20初始化

          通过DS18B20来实现测温,一定要通过单总线协议实现对芯片的读写。首先使用之前我们要确定DS18B20的存在。这个过程也能叫做初始化DS18B20(一般而言单总线协议遇到同型号的多个单元要多确定地址,但是CT107D只有一个DS18B20,所以不考虑)。

        

    线型注释

  • 黑粗线:总线控制器拉低电平 
  • 灰粗线:DS18B20拉低电平
  • 黑细线:上拉电阻拉高电平      

         根据以上时序图,我将整个初始化DS18B20的过程总括为几个步骤:总线(DQ)首先发送复位脉冲,将电平拉低。在至少480微秒之后,上拉电平释放总线,上拉电阻会在15到60微秒区间将电位拉高。在上拉电阻开始上拉电平后的60到240微秒区间内DS18B20会主动将电平拉低——也就是说,如果DS18B20不存在或者异常,那么电平在这个区间内保持高电平。度过这个区间之后就要重新上拉电平。

        总结这个历程,我们就是要判断第一次上拉电平释放总线后的60到240微秒区间读取电平,读到低电平,则DS18B20存在,反之DS18B20异常或者不存在。

        下面是DS18B20初始化的代码操作。首先补充这里的单总线协议由单片机的P14口完成,其次延时函数可以通过stc-isp来生成。

bit DQ = P1^4;

bit DS18B20_Init(void)
{
    bit flag;         //接收电平数据

    DQ = 1;           //先将电平置为高电平
    Delay_10us(5);    
    DQ = 0;           //从这里历程正式开始,将电平拉低
    Delay_10us(55);   //延时550us,至少要大于480us
    DQ = 1;           //第一次上拉电平
    Delay_10us(10);   //延时100us,电阻要最多60us才能上拉
    flag = DQ;        //读取DQ状态
    Delay_10us(5);    //防止短时间多次操作产生错误,结束历程要延时一会
    
    return flag;     //返回0则初始化成功,返回1则失败
}

DS18B20写操作

        从上面的总括我们可以看到,DS18B20功能还是比较强大的,给我们有多个模式来选择(给我们添了又一道麻烦),这就需要我们通过写操作来配置。

         电平的操作手段也是一样的,这里就不过多赘述。写操作的本质上就是向DS18B20一位一位写入“1”和“0“。那么写操作的核心就是如何向DS18B20写入“1”和“0”。看时序图可以知道:拉低电平之后历程开始,此后如果60到120微秒内保持低电平,那么DS18B20将会视作写入”0“。那么在1到无穷大时间内保持高电平就是会被视作写入1。看到下面的DS18B20的采样历程,实际上将高或低电平覆盖DS18B20的采样历程就是会被视作写入一个”1“或”0“。

        总结就是拉低电平之后,要写入1就立马拉高电平,写入0就在60到120微秒区间内拉高电平就行了。由于我们通常是一个字节一个字节进行写操作,那将历程执行八次就行了。下面是代码操作。

bit DQ = P1^4;

void DS18B20_Write(unsigned char item)
{
    unsigned char i = 0;
    for(i;i<8;i++)
    {    
        DQ = 0;           //拉低电平
        DQ = item&0x01;   //读取输入数据的最低一位
        Delay_10us(8);    //延时80us,至少60us
        DQ = 1;           //拉高电平
        item >>= 1;       //输入数据右移一位
    }
    Delay_10us(5);        //每次操作完成要间隔一会
}

        

DS18B20读操作

        

           看上图的时序,读操作中,拉低电平之后1微秒以内就可以拉高电平释放总线。也就是拉低电平之后就可以直接执行拉高电平,这时候DS18B20就会视作执行读操作。当我们拉高电平释放总线是,如果DS18B20要输出“0”,那么会保持一段时间的低电平在60微秒以内,输出“1”就会直接拉高电平。手册推荐的采样器采样时间是拉高电平释放总线15微秒的区间,所以微秒释放总线之后就可以直接读取总线状态。

           总结就是,拉低电平就直接拉高电平释放总线,并读取总线状态。下面是代码操作。

bit DQ = P1^4;

unsigned char DS18B20_Read(void)
{
    unsigned char i = 0;
    unsigned char dat = 0x00;    //存储读取的数据
    
    for(i;i<8;i++)    
    {
        DQ = 0;
        dat >>= 1; //读取的数据左移,最高位此时是0
        DQ = 1;
        if(DQ)     //如果读出“1”,就将dat最高位置为1,是“0”就不操作
        {
            DQ |= 0x80;
        }
        Delay_10us(5);    //延时一会
    }
    return dat;
}

DS18B20暂存器

        

        从上往下看,暂存器分为9个区域。Byte0和1存储温度数据,这部分是与温度传感器交换数据的区域,Byte0存储低位数据,Byte1存储高位数据;Byte2和3存储警报触发值,这部分与警报触发寄存器交换数据,同样的Byte2存储低位数据,Byte3存储高位数据;Byte4存储配置寄存器数据,与配置寄存器交换数据,这里的数据决定温度精度; Byte4、5、6存储保留位与用户寄存器交换数据。Byte8则是与冗余校验发生器交换数据。

        下面着重看到温度存储区域。

        可以看出,LS BTYE是Btye0,MS BYTE是Byte1。数据存储方式如图所示,温度都是以2进制数,不同位数存储,从-4位一直到6位。它们的最大值就是大约85℃。另外高位的s是符号(sign),看到表格,全为0则位为正数,全为1则是负数。

DS18B20指令

        DS18B20的操作指令已经设计了,只需要按照手册用写函数输入对应指令就行。

         跳过寄存器指令(0xcc) :前文有提到操作DS18B20需要定位设备位置,但是由于单片机上只有一个DS18B20,那么我们就不需要定位,直接跳过这个流程。 

    

         写暂存器(0x4e)               读暂存器(0xbe):也就是读操作

         启动温度转换(0x44):启动后DS18B20会将测出来的温度转换成数据(16进制)

DS18B20代码操作

        综合上面的板块我们就可以完成读取温度的操作了,我们先看看这个读DS18B20温度参数的过程。

float fRead_Temperature()
{
	unsigned char low,high;
	float temp;
	DS18B20_Init();
	DS18B20_Write(0xCC);        //跳过ROM
	DS18B20_Write(0x44);        //启动温度转换
	DS18B20_Init();
	DS18B20_Write(0xCC);        //跳过ROM
	DS18B20_Write(0xBE);        //获取暂存器数据
	low = DS18B20_Read();		//获取低8位数据
	high = DS18B20_Read();		//获取高8位数据
	temp = (high<<8|low)*0.0625;
	return temp;
}

        这里看到读和写是两次操作,写了就要重新初始化才能读。读取的数值也要注意,DS18B20测出来的数据是4位16进制数,需要两个u8类型的变量或者一个u32数据来存储(因为我们写的读数据函数一次读一个字节,所以这里读两次)。乘以0.0625是为了加上小数点,将2进制数转化位10进制数。

        最后配合计时器数码管等加入主函数,将数值显示在数码管上。

        

#include <STC15F2K60S2.H>
#include "intrins.h"
#include "System.h"
#include "onewire.h"
#include "Device.h"
#include "Timer.h"

void vDevice_Ctrl(unsigned char p0,unsigned char p2)
{
	P0 = p0;
	P2 = P2&0x1f|p2;	//与运算0x1f是为了保证iic通讯的正常
	P2 &= 0x1f;
}

void vSystem_Init()    
{
	vDevice_Ctrl(0x00,0xa0);
	vDevice_Ctrl(0xff,0x80);
	vDevice_Ctrl(0xff,0xe0);
}

float temperature;
unsigned long temp;
unsigned char cnt;
unsigned char smg_ctrl[]={0x01,0x02,0x04,0x08,0x10,0x20,0x040,0x80};
unsigned char smg_code[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char smg_buf[8];

void smg_process()
{
	smg_buf[2] = smg_code[temp/100000];
	smg_buf[3] = smg_code[temp/10000%10]|0x80;
	smg_buf[4] = smg_code[temp/1000%10];
	smg_buf[5] = smg_code[temp/100%10];
	smg_buf[6] = smg_code[temp/10%10];
	smg_buf[7] = smg_code[temp%10];
}

void smg_display()
{
	static unsigned char i;
	vDevice_Ctrl(~smg_buf[i],0xe0);
	vDevice_Ctrl(smg_ctrl[i],0xc0);
	i++;
	if(i == 8) i = 0;
}

void vTimer2_Init()
{
	AUXR |= 0x04;			//定时器时钟1T模式
	T2L = 0x20;				//设置定时初始值
	T2H = 0xD1;				//设置定时初始值
	AUXR |= 0x10;			//定时器2开始计时
	IE2 |= 0x04;			//定时器2中断
	EA = 1;						//开启总中断
}

void main()
{
	vSystem_Init();
	vTimer2_Init();
	while(1)
	{
		if(cnt >= 100)
		{
			temperature = fRead_Temperature();
			temp = temperature*10000;
			cnt = 0;
		}
		smg_process();
	}
}

void Timer2_IRC() interrupt 12
{
	cnt++;
	smg_display();
}

 

 下载到板子的现象就是如图

         至此,本篇结束,如有纰漏,希望读者斧正。

### 单总线协议下的灯珠实现方案 #### DS18B20单总线协议的关系 单总线协议是一种基于单一信号线完成数据传输的方式,其核心在于通过高低电平的持续时间来区分逻辑状态“0”和“1”。这种特性使得像DS18B20这样的温度传感器能够以极低的硬件资源需求实现高精度测量功能[^3]。 #### LED灯珠控制中的单总线应用 尽管传统意义上的LED灯珠并不直接支持单总线协议,但在某些场景下,可以通过间接方法将其纳入单总线架构中。例如,在WS2812灯带的应用中,虽然它本身采用的是改进版的单总线通信机制(依赖精确的时序脉冲),但从概念上仍可视为一种扩展形式的单总线技术[^1]。 以下是几种可能的实现路径: 1. **结合光敏电阻与LED的小夜灯设计** 可以借鉴某团队的设计思路,将光敏电阻用于环境光照强度监测,并借助单片机解析这些模拟量输入值。一旦检测到光线不足,则触发相应的LED照明电路开启。此过程虽未严格遵循标准定义上的单总线模式,但确实体现了类似的简化接口设计理念[^4]。 2. **利用ESP32 RMT模块驱动WS2812灯带** ESP32微控制器内置了远程发射器(Remote Control Transmitter,简称RMT)单元,非常适合用来生成那些对时间和顺序敏感的波形序列——比如针对每颗RGB像素点发送独立的颜色配置指令给连接起来的一串WS2812器件阵列。不过需要注意的是,在实际部署过程中可能会遇到干扰现象,特别是当无线网络服务(WiFi/Bluetooth)也被启用的情况下,这可能导致个别色彩更新失败等问题发生。 ```c #include <driver/rmt.h> #define GPIO_WS2812 27 // 定义输出引脚号为GPIO27 void setup() { rmt_config_t config; memset(&config, 0, sizeof(config)); /* 配置参数 */ config.gpio_num = GPIO_WS2812; config.channel = RMT_CHANNEL_0; config.mem_block_num = 1; config.clk_div = 80; config.tx_config.loop_en = false; config.tx_config.carrier_en = false; config.tx_config.idle_output_en = true; config.tx_config.rest_level = RMT_IDLE_LEVEL_LOW; rmt_set_source_clk(config.channel,RMT_BASECLK_APB); rmt_config(&config); rmt_driver_install(config.channel,(1<<9),0); } // 发送函数省略... ``` 以上代码片段展示了如何初始化ESP32内部的RMT外设以便后续操作具体灯具对象。 #### PWM调制实现渐变效果 除了上述提到的技术手段之外,还可以考虑运用基础的PWM(脉宽调制)原理达成更加细腻的变化过渡体验。如下所示的例子演示了一个简单的程序流程图样,其中通过调整占空比让目标指示灯呈现出由暗至明再返回初始状态的过程[^2]: ```plaintext For time=0 To 100 Step 1 Do Begin Set High Level Duration As Time Milliseconds ; Wait For (Time / Frequency ) Seconds ; End ; Repeat Above Block With Decreasing Value Of 'time' From 100 Downwards Until Zero Reached . ``` 当然这里仅提供伪码示意而非真实可用源文件内容;真正开发阶段需依据所选平台API文档编写对应版本的具体实现细节。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值