完美实现STM32单总线挂多个DS18B20

2023-10-29 11:48

本文主要是介绍完美实现STM32单总线挂多个DS18B20,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  一般常见的STM32的关于DS18B20的例程都是检测一个传感器,代码一般都是跳过ROM检测,直接获取温度值。这种写法并不适用于单总线上挂载多个DS18B20的情况,Sandeepin的这个代码就是针对这种情况完善的单总线挂多个DS18B20检测,实现获取每个DS18B20的ID和温度。

  主要的DS18B20时序代码没变,增加了搜索ROM函数,获取温度时先匹配ID。

  核心代码如下:

  DS18B20.c文件代码:

#include "DS18B20.h"
#include "Delay.h"
#include "stdio.h" // printf用#define DS18B20_GPIO_NUM				 GPIO_Pin_5
#define DS18B20_GPIO_X					GPIOC
#define RCC_APB2Periph_DS18B20_GPIO_X	RCC_APB2Periph_GPIOC#define DS18B20_DQ_OUT_Low			GPIO_ResetBits(DS18B20_GPIO_X,DS18B20_GPIO_NUM) 
#define DS18B20_DQ_OUT_High			GPIO_SetBits(DS18B20_GPIO_X,DS18B20_GPIO_NUM) 
#define DS18B20_DQ_IN				GPIO_ReadInputDataBit(DS18B20_GPIO_X,DS18B20_GPIO_NUM) #define MaxSensorNum 8
unsigned char DS18B20_ID[MaxSensorNum][8];	// 存检测到的传感器DS18B20_ID的数组,前面的维数代表单根线传感器数量上限
unsigned char DS18B20_SensorNum;			// 检测到的传感器数量(从1开始,例如显示1代表1个,8代表8个)// 配置DS18B20用到的I/O口
void DS18B20_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_DS18B20_GPIO_X, ENABLE);GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);GPIO_SetBits(DS18B20_GPIO_X, DS18B20_GPIO_NUM);
}// 引脚输入
void DS18B20_Mode_IPU(void)
{GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);
}// 引脚输出
void DS18B20_Mode_Out(void)
{GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);}// 复位,主机给从机发送复位脉冲
void DS18B20_Rst(void)
{DS18B20_Mode_Out();DS18B20_DQ_OUT_Low;		// 产生至少480us的低电平复位信号Delay_us(480);DS18B20_DQ_OUT_High;	// 在产生复位信号后,需将总线拉高Delay_us(15);
}// 检测从机给主机返回的应答脉冲。从机接收到主机的复位信号后,会在15~60us后给主机发一个应答脉冲
u8 DS18B20_Answer_Check(void)
{u8 delay = 0;DS18B20_Mode_IPU(); // 主机设置为上拉输入// 等待应答脉冲(一个60~240us的低电平信号 )的到来// 如果100us内,没有应答脉冲,退出函数,注意:从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲while (DS18B20_DQ_IN&&delay < 100){delay++;Delay_us(1);}// 经过100us后,如果没有应答脉冲,退出函数if (delay >= 100)//Hu200return 1;elsedelay = 0;// 有应答脉冲,且存在时间不超过240uswhile (!DS18B20_DQ_IN&&delay < 240){delay++;Delay_us(1);}if (delay >= 240)return 1;return 0;
}// 从DS18B20读取1个位
u8 DS18B20_Read_Bit(void)
{u8 data;DS18B20_Mode_Out();DS18B20_DQ_OUT_Low; // 读时间的起始:必须由主机产生 >1us <15us 的低电平信号Delay_us(2);DS18B20_DQ_OUT_High;Delay_us(12);DS18B20_Mode_IPU();// 设置成输入,释放总线,由外部上拉电阻将总线拉高if (DS18B20_DQ_IN)data = 1;elsedata = 0;Delay_us(50);return data;
}// 从DS18B20读取2个位
u8 DS18B20_Read_2Bit(void)//读二位 子程序
{u8 i;u8 dat = 0;for (i = 2; i > 0; i--){dat = dat << 1;DS18B20_Mode_Out();DS18B20_DQ_OUT_Low;Delay_us(2);DS18B20_DQ_OUT_High;DS18B20_Mode_IPU();Delay_us(12);if (DS18B20_DQ_IN)	dat |= 0x01;Delay_us(50);}return dat;
}// 从DS18B20读取1个字节
u8 DS18B20_Read_Byte(void)	// read one byte
{u8 i, j, dat;dat = 0;for (i = 0; i < 8; i++){j = DS18B20_Read_Bit();dat = (dat) | (j << i);}return dat;
}// 写1位到DS18B20
void DS18B20_Write_Bit(u8 dat)
{DS18B20_Mode_Out();if (dat){DS18B20_DQ_OUT_Low;// Write 1Delay_us(2);DS18B20_DQ_OUT_High;Delay_us(60);}else{DS18B20_DQ_OUT_Low;// Write 0Delay_us(60);DS18B20_DQ_OUT_High;Delay_us(2);}
}// 写1字节到DS18B20
void DS18B20_Write_Byte(u8 dat)
{u8 j;u8 testb;DS18B20_Mode_Out();for (j = 1; j <= 8; j++){testb = dat & 0x01;dat = dat >> 1;if (testb){DS18B20_DQ_OUT_Low;// 写1Delay_us(10);DS18B20_DQ_OUT_High;Delay_us(50);}else{DS18B20_DQ_OUT_Low;// 写0Delay_us(60);DS18B20_DQ_OUT_High;// 释放总线Delay_us(2);}}
}//初始化DS18B20的IO口,同时检测DS的存在
u8 DS18B20_Init(void)
{DS18B20_GPIO_Config();DS18B20_Rst();return DS18B20_Answer_Check();
}// 从ds18b20得到温度值,精度:0.1C,返回温度值(-550~1250),Temperature1返回浮点实际温度
float DS18B20_Get_Temp(u8 i)
{//u8 flag;u8 j;//匹配的字节u8 TL, TH;short Temperature;float Temperature1;DS18B20_Rst();DS18B20_Answer_Check();DS18B20_Write_Byte(0xcc);// skip romDS18B20_Write_Byte(0x44);// convertDS18B20_Rst();DS18B20_Answer_Check();// DS18B20_Write_Byte(0xcc);// skip rom//匹配ID,i为形参DS18B20_Write_Byte(0x55);for (j = 0; j < 8; j++){DS18B20_Write_Byte(DS18B20_ID[i][j]);}DS18B20_Write_Byte(0xbe);// convertTL = DS18B20_Read_Byte(); // LSB   TH = DS18B20_Read_Byte(); // MSB  if (TH & 0xfc){//flag=1;Temperature = (TH << 8) | TL;Temperature1 = (~Temperature) + 1;Temperature1 *= 0.0625;}else{//flag=0;Temperature1 = ((TH << 8) | TL)*0.0625;}return Temperature1;
}// 自动搜索ROM
void DS18B20_Search_Rom(void)
{u8 k, l, chongtuwei, m, n, num;u8 zhan[5];u8 ss[64];u8 tempp;l = 0;num = 0;do{DS18B20_Rst(); //注意:复位的延时不够Delay_us(480); //480、720DS18B20_Write_Byte(0xf0);for (m = 0; m < 8; m++){u8 s = 0;for (n = 0; n < 8; n++){k = DS18B20_Read_2Bit();//读两位数据k = k & 0x03;s >>= 1;if (k == 0x01)//01读到的数据为0 写0 此位为0的器件响应{DS18B20_Write_Bit(0);ss[(m * 8 + n)] = 0;}else if (k == 0x02)//读到的数据为1 写1 此位为1的器件响应{s = s | 0x80;DS18B20_Write_Bit(1);ss[(m * 8 + n)] = 1;}else if (k == 0x00)//读到的数据为00 有冲突位 判断冲突位{//如果冲突位大于栈顶写0 小于栈顶写以前数据 等于栈顶写1chongtuwei = m * 8 + n + 1;if (chongtuwei > zhan[l]){DS18B20_Write_Bit(0);ss[(m * 8 + n)] = 0;zhan[++l] = chongtuwei;}else if (chongtuwei < zhan[l]){s = s | ((ss[(m * 8 + n)] & 0x01) << 7);DS18B20_Write_Bit(ss[(m * 8 + n)]);}else if (chongtuwei == zhan[l]){s = s | 0x80;DS18B20_Write_Bit(1);ss[(m * 8 + n)] = 1;l = l - 1;}}else{//没有搜索到}}tempp = s;DS18B20_ID[num][m] = tempp; // 保存搜索到的ID}num = num + 1;// 保存搜索到的个数} while (zhan[l] != 0 && (num < MaxSensorNum));DS18B20_SensorNum = num;//printf("DS18B20_SensorNum=%d\r\n",DS18B20_SensorNum);
}

  DS18B20.h文件代码:

#ifndef __DS18B20_H
#define __DS18B20_H #include "stm32f10x.h"u8 DS18B20_Init(void);
u8 DS18B20_Read_Byte(void);
u8 DS18B20_Read_Bit(void);
u8 DS18B20_Answer_Check(void);
void  DS18B20_GPIO_Config(void);
void  DS18B20_Mode_IPU(void);
void  DS18B20_Mode_Out(void);
void  DS18B20_Rst(void);
void  DS18B20_Search_Rom(void);
void  DS18B20_Write_Byte(u8 dat);
float DS18B20_Get_Temp(u8 i);#endif
  main.c文件代码:

#include "stm32f10x.h"
#include "stdio.h"
#include "string.h"//strlen、memset用到
#include "USART.h"
#include "Delay.h"
#include "DS18B20.h"extern unsigned char DS18B20_ID[8][8];//检测到的传感器ID存数组
extern unsigned char DS18B20_SensorNum;int main(void)
{u8 num=0;USART1_init(9600);while(DS18B20_Init())//初始化DS18B20,兼检测18B20{printf("DS18B20 Check Failed!\r\n");  }printf("DS18B20 Ready!\r\n");while(1){	  DS18B20_Search_Rom();printf("DS18B20_SensorNum:%d\r\n",DS18B20_SensorNum);for(num=0;num<DS18B20_SensorNum;num++){printf("ID:%02x%02x%02x%02x%02x%02x%02x%02x TM:%.2f\r\n",DS18B20_ID[num][0],DS18B20_ID[num][1],DS18B20_ID[num][2],DS18B20_ID[num][3],DS18B20_ID[num][4],DS18B20_ID[num][5],DS18B20_ID[num][6],DS18B20_ID[num][7],DS18B20_Get_Temp(num));}printf("\r\n");Delay_s(2);}	
}

  运行结果如图:


  帮严博士出本科题的时候,出了一个DS18B20的分布式温度检测系统,要求肯定不仅仅是这篇文章的简略例子了。不仅单总线,一块单片机还要挂多总线,实现更多传感器数据采集,最好还配上上位机,反正把自己能想到的东西都加进来了,把一个简单的DS18B20包装得高大上。

这篇关于完美实现STM32单总线挂多个DS18B20的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/300431

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

通过React实现页面的无限滚动效果

《通过React实现页面的无限滚动效果》今天我们来聊聊无限滚动这个现代Web开发中不可或缺的技术,无论你是刷微博、逛知乎还是看脚本,无限滚动都已经渗透到我们日常的浏览体验中,那么,如何优雅地实现它呢?... 目录1. 早期的解决方案2. 交叉观察者:IntersectionObserver2.1 Inter

Spring Gateway动态路由实现方案

《SpringGateway动态路由实现方案》本文主要介绍了SpringGateway动态路由实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前沿何为路由RouteDefinitionRouteLocator工作流程动态路由实现尾巴前沿S