【STM32F103】1-Wire&&DS18B20(含ESP8266代码)

1-Wire 单总线

1-Wire是一种串行通信总线协议,由美国芯片制造商Dallas Semiconductor(现为Maxim Integrated)开发。这种协议主要用于连接和通信各种设备,并在多个领域得到了广泛应用,如温度传感器、电池管理、智能卡等。

1-Wire协议基于单数据线进行串行通信,允许多个设备通过共享这一数据线与控制设备进行通信。这种通信方式极大地简化了硬件布线和设备连接的复杂性。每个1-Wire设备都具有一个唯一的64位地址,这使得系统可以轻松地识别和区分不同的设备。数据传输使用脉冲编码调制(Pulse Code Modulation, PCM)技术,通过发送不同脉冲的组合来表示不同的数据。

1-Wire技术具有节省IO资源、结构简单、成本低廉、便于总线扩展维护等优点。同时,它支持在总线上挂接多个从器件,数量基本上不受到任何限制。此外,每个1-Wire技术的从器件都具备独一无二的64位识别码,这既提高了安全性,也避免了地址冲突的问题。1-Wire技术还具备可达8kV HBM的ESD保护等级,以保证系统的稳定性。

总的来说,1-Wire技术作为一种串行通信总线协议,具有简单可靠、线路长度灵活、低功耗设计等特点,被广泛应用于各种设备和系统的通信中,为连接和通信各种设备提供了重要工具。

以上介绍来自文心一言。

我们需要知道的是1-Wire只需要一根通信线就可以进行通信,但仅适用于单主机的系统中,我们今天的主角DS18B20用的就是这个通信协议。

DS18B20

DS18B20是由DALLAS半导体公司推出的一种数字温度传感器,它采用了“一线总线”接口。这种温度传感器相较于传统的热敏电阻等测温元件,具有诸多优势,如体积小、适用电压宽、与微处理器接口简单等。

DS18B20的工作原理主要基于温度对半导体材料电阻值的影响。其内部包含一个精确的温度传感器,该传感器由一对金属电极和一个细丝电阻器组成。当温度升高时,电阻值会相应增加,反之亦然。通过测量电阻值的变化,DS18B20能够准确地确定环境温度。

在数据传输方面,DS18B20通过单线串行总线与主设备通信,仅需一根数据线即可实现数据的传输。这种通信方式不仅简化了硬件连接,还降低了成本。在通信过程中,主设备会发送指令给DS18B20,然后DS18B20将温度数据以数字形式传输回主设备。

DS18B20的温度测量范围为-55℃至+125℃,精度可达±0.5℃。它可以直接读出被测温度,并且可以通过简单的编程实现9~12位的数字值读数方式。此外,DS18B20还具备高抗干扰性,其现场温度直接以“一线总线”的数字方式传输,这大大提高了系统的稳定性。

在供电和工作模式方面,DS18B20既可以通过总线线路提供供电,也可以通过外部电源供电。其工作电压范围广泛,为3~5.5V,这使得它可以灵活地应用于各种嵌入式系统中。

DS18B20的应用领域十分广泛,包括温度监控、室内温度采集、冷链物流温度监控、工业生产温度监测等。其高精度和便捷的特点使得在各种场景下都能发挥重要作用。同时,DS18B20还具有多种封装形式,如管道式、螺纹式、磁铁吸附式等,这使得它可以根据实际应用场合的需求进行灵活选择。

总的来说,DS18B20数字温度传感器是一种功能强大、应用广泛的测温设备,其高精度、高可靠性和易用性使其在多个领域中都得到了广泛应用。

以上介绍来自文心一言。

可以看得出来DS18B20的厂商和1-Wire的开发公司是同一家,因此DS18B20自然是使用1-Wire进行通信。

上面一大段话没啥用,我们只要知道它是用来测温度的就行。

时序

初始化时序

以上为初始化时序,我们首先先拉低总线(1-Wire默认为上拉状态)480us,接着释放总线。

当DS18B20接收到来自主机的探测信号之后,等待15~60us后会发出一段60~240us的低电平。

以上流程顺利的话,就算是初始化完成了。

写时序

以上为写时序,写每个bit的时序时长最短为60us,最长为120us。两个时序之间需要至少1us的恢复时间。

我们先拉低总线15us,接着发送一个bit的数据保持总线上的电平至少60us。DS18B20会在时序开始的15us~60us这个窗口进行采样。

我们重复上面的时序8次就是发送一个字节,1-Wire上发送数据是从低位到高位的。

读时序

读时序和写时序一样,都必须是最少60us,并且两个时序之间需要至少1us的恢复时间。

我们先拉低总线至少1us接着释放,这标志着读时序的开始。接着我们在15us内采样(读取总线上的电平),然后等待45us(保证一个时序至少是60us)。

重复上述8个时序即可读取一个字节。

命令

我们只需要记住三个命令。
第一个是0x44,温度转换指令,发送一次,DS18B20就会转换一次温度,我们也就可以读取了。

第二个是0xCC,忽略ROM,因为1-Wire可以挂载多个从机,因此ROM是用来确定具体是哪个从机的,但是我们一般情况下不会挂载多个,因此先发送这个指令,下一个指令就不需要ROM了,这样会比较快。

最后一个是0xBE,发送完我们就可以读取寄存器里的值了,一共是九个字节。

我们再了解一下DS18B20的寄存器。

我们需要的数据在前两个字节,因此在读完两个字节之后我们就可以停止读取了。

那么了解了上述内容之后我们就可以开始敲代码了。

完整代码(STM32)

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"


#define DS18B20_DQ GPIO_Pin_0

void Z_DS18B20_Output(void){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef itd;
    itd.GPIO_Mode=GPIO_Mode_Out_PP;    //推挽输出
    itd.GPIO_Pin=DS18B20_DQ; 
    itd.GPIO_Speed=GPIO_Speed_50MHz;                   
    GPIO_Init(GPIOA,&itd);
}

void Z_DS18B20_Input(void){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef itd;
    itd.GPIO_Mode=GPIO_Mode_IPU;        //上拉输入    
    itd.GPIO_Pin=DS18B20_DQ; 
    itd.GPIO_Speed=GPIO_Speed_50MHz;                   
    GPIO_Init(GPIOA,&itd);
}

void Z_DS18B20_SetDQ(BitAction val){
    GPIO_WriteBit(GPIOA,DS18B20_DQ,val);
}

uint8_t Z_DS18B20_GetDQ(void){
    return GPIO_ReadInputDataBit(GPIOA,DS18B20_DQ);
}

void Z_DS18B20_WriteByte(uint8_t data){
    Z_DS18B20_Output();
    for(uint8_t i=0;i<8;++i){
        Z_DS18B20_SetDQ(0);     //拉低总线15us
        Delay_us(15);
        
        Z_DS18B20_SetDQ(data&0x01); //将数据从低位开始放到总线上60us
        Delay_us(60);
        
        Z_DS18B20_SetDQ(1);     //释放总线后延时1us
        Delay_us(1);
        data>>=1;
    }
}


uint8_t Z_DS18B20_ReadBit(void){
    uint8_t res=0;
    Z_DS18B20_Output();
    Z_DS18B20_SetDQ(0);     //拉低总线1us后释放表示开始读取字节
    Delay_us(1);
    Z_DS18B20_SetDQ(1);
    
    Z_DS18B20_Input();
    Delay_us(10);           //等待10us(在15us之内)后读取总线
    res=Z_DS18B20_GetDQ();
    
    Delay_us(50);           //延时50us以保证每个时序至少有60us.
    return res;
}

uint8_t DS18B20_Read_Byte(void){
  uint8_t res=0x00;
  for(uint8_t i=0;i<8;++i){
    if(Z_DS18B20_ReadBit()==1) res|=(0x01<<i);
  }
  return res;
}

uint8_t Z_DS18B20_Init(void){
    Z_DS18B20_Output();
    Z_DS18B20_SetDQ(0);     //复位
    Delay_us(480);
    Z_DS18B20_SetDQ(1);
    Delay_us(15);
    
    uint8_t waitTime=0;
    Z_DS18B20_Input();
    while(Z_DS18B20_GetDQ() && waitTime<=45){   //等待从机拉低总线
        waitTime++;
        Delay_us(1);
    }
    if(waitTime>45) return 1;
    
    waitTime=0;
    while(!Z_DS18B20_GetDQ() && waitTime<=240){ //等待从机松开总线
        waitTime++;
        Delay_us(1);
    }
    if(waitTime>240||waitTime<60) return 1;
    return 0;
}

float Z_DS18B20_GetTemperture(void){
	uint16_t temp;
	uint8_t Hdata,Ldata;
    
    Z_DS18B20_Init();
    Z_DS18B20_WriteByte(0xCC);
    Z_DS18B20_WriteByte(0x44);  //温度转换指令
    Z_DS18B20_Init();
	Z_DS18B20_WriteByte(0xCC);   
	Z_DS18B20_WriteByte(0xBE);  //读取寄存器指令  
	Ldata=DS18B20_Read_Byte();     
	Hdata=DS18B20_Read_Byte();    
	temp=Hdata;
	temp=(temp<<8)|Ldata;
	return temp*0.0625;    
}

int main(void){
    OLED_Init();
    while(1){
        OLED_ShowNum(1,1,Z_DS18B20_GetTemperture(),3);
        OLED_ShowNum(1,5,(int)(Z_DS18B20_GetTemperture()*100)%100,2);
        Delay_ms(300);
    }
}

完整代码(ESP8266)

ESP8266的代码在一些细节上与上面STM32的略有不同,因为ESP8266是我最早借鉴的别人的代码修改的。

而STM32的代码是我查阅了手册之后自己根据时序图写出的。

但是两个版本都是可以正常驱动DS18B20的。

#define DS18B20_DQ_GPIO D2

void DS18B20_Input_Init(void){
  pinMode(DS18B20_DQ_GPIO, INPUT_PULLUP);
}

void DS18B20_Output_Init(void){
  pinMode(DS18B20_DQ_GPIO, OUTPUT);
}

void DS18B20_Reset(void)	   
{                 
  DS18B20_Output_Init();
	digitalWrite(DS18B20_DQ_GPIO,LOW);
	delayMicroseconds(750);
	digitalWrite(DS18B20_DQ_GPIO, HIGH);
	delayMicroseconds(15);
}

uint8_t DS18B20_Check(void){
  uint8_t waitTime=0;
  DS18B20_Input_Init();
  while(digitalRead(DS18B20_DQ_GPIO) && waitTime<200){
    waitTime++;
    delayMicroseconds(1);
  }
  if(waitTime>=200) return 1;
  else waitTime=0;

  while((0==digitalRead(DS18B20_DQ_GPIO)) && waitTime<240){
    waitTime++;
    delayMicroseconds(1);
  }
  if(waitTime>=240) return 1;
  return 0;
}


uint8_t DS18B20_Read_Bit(void){
  uint8_t res=0;
  DS18B20_Output_Init();
  digitalWrite(DS18B20_DQ_GPIO, LOW);
  delayMicroseconds(2);
  digitalWrite(DS18B20_DQ_GPIO, HIGH);
  DS18B20_Input_Init();
  delayMicroseconds(12);
  res=digitalRead(DS18B20_DQ_GPIO);
  delayMicroseconds(50);
  return res;
}

uint8_t DS18B20_Read_Byte(void){
  uint8_t res=0x00;
  for(uint8_t i=0;i<8;++i){
    if(DS18B20_Read_Bit()==1) res|=(0x01<<i);
  }
  return res;
}

void DS18B20_Write_Byte(uint8_t data){
  DS18B20_Output_Init();
  for(uint8_t i=0;i<8;++i){
    if(data&0x01==1){
      digitalWrite(DS18B20_DQ_GPIO,LOW);
      delayMicroseconds(2);
      digitalWrite(DS18B20_DQ_GPIO, HIGH);
      delayMicroseconds(60);
    }else{
      digitalWrite(DS18B20_DQ_GPIO,LOW);
      delayMicroseconds(60);
      digitalWrite(DS18B20_DQ_GPIO, HIGH);
      delayMicroseconds(2);
    }
    data>>=1;
  }
}
 
void DS18B20_Start(void){
  DS18B20_Reset();
  DS18B20_Check();
  DS18B20_Write_Byte(0xCC);
  DS18B20_Write_Byte(0x44);
}
 	 
uint8_t DS18B20_Init(void){
  DS18B20_Output_Init();
 	DS18B20_Reset();
	return DS18B20_Check();
}  
 

float DS18B20_GetTemperture(void){
	uint16_t temp;
	uint8_t a,b;
	float value;
	
	DS18B20_Start();                  
	DS18B20_Reset();
	DS18B20_Check();	 
	DS18B20_Write_Byte(0xCC);// skip rom
	DS18B20_Write_Byte(0xBE);// convert	    
	a=DS18B20_Read_Byte(); // LSB   
	b=DS18B20_Read_Byte(); // MSB   
	temp=b;
	temp=(temp<<8)+a;
	if((temp&0xF800)==0xF800){
		temp=(~temp)+1;
		value=temp*(-0.0625);
	}else{
		value=temp*0.0625;	
	}
	return value;    
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Hello, STM32!");
  pinMode(D1, OUTPUT);
  digitalWrite(D1, HIGH);
  DS18B20_Init();  
}
int count=0;
void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(D1, !digitalRead(D1));
  delay(1000); // this speeds up the simulation
  Serial.println(DS18B20_GetTemperture());
}

感兴趣的小伙伴也可以关注我的公众号“折途想要敲代码”回复关键词“DS18B20”,即可免费下载DS18B20相关的资料手册。

  • 34
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值