完美实现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)//Hu200 return 1; else delay = 0; // 有应答脉冲,且存在时间不超过240us while (!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; else data = 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 1 Delay_us(2); DS18B20_DQ_OUT_High; Delay_us(60); } else { DS18B20_DQ_OUT_Low;// Write 0 Delay_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;// 写1 Delay_us(10); DS18B20_DQ_OUT_High; Delay_us(50); } else { DS18B20_DQ_OUT_Low;// 写0 Delay_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 rom DS18B20_Write_Byte(0x44);// convert DS18B20_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);// convert TL = 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、720 DS18B20_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 小于栈顶写以前数据 等于栈顶写1 chongtuwei = 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包装得高大上。
- 上一篇: C语言字符串处理
- 下一篇: Qt常用UI控件读取、写入方法