网上关于STM32F1相关DS18B20的例程,虽然F4与F1代码相差不大,但可供新手开发者借鉴;
一般例程都是检测一个传感器,代码一般都是跳过ROM检测,直接获取温度值。这种写法并不适用于单总线上挂载多个DS18B20的情况,本文针对这种情况完善的单总线挂多个DS18B20检测,实现获取每个DS18B20的序列号和温度。
国产18B20在配置IO相关内容略有区别,除了基础的时序代码内容外,增加了读取位操作、检测总线传感器数目以及读取指定传感器温度。
核心代码如下:
QT18B20M.c文件代码:
/***********************************************************************
文件名称:QT18B20.C 功放温度
功 能:完成对对QT18B20的基本操作
编写时间:2024.3.12
编 写 人:
注 意:
***********************************************************************/
#include "QT18B20PA.h"
#include "delay.h"
#include "M_Global.h"
/* 定义GPIO端口 PD0 */
#define RCC_DQ RCC_AHB1Periph_GPIOC
#define PORT_DQ GPIOC
#define PIN_DQ GPIO_Pin_12
//IO方向设置
#define QT18B20M_IO_IN() {GPIOD->MODER&=~(3<<(12*2));GPIOD->MODER|=0<<12*2;} //PG9输入模式
#define QT18B20M_IO_OUT() {GPIOD->MODER&=~(3<<(12*2));GPIOD->MODER|=1<<12*2;} //PG9输出模式
IO操作函数
#define QT18B20M_DQ_OUT PCout(12) //数据端口 PG9
#define QT18B20M_DQ_IN PCin(12) //数据端口 PG9
//复位QT18B20M
void QT18B20M_Rst(void)
{
QT18B20M_IO_OUT(); //SET PG11 OUTPUT
QT18B20M_DQ_OUT=0; //拉低DQ
DelayUs(750); //拉低750us
QT18B20M_DQ_OUT=1; //DQ=1
DelayUs(15); //15US
}
//等待QT18B20M的回应
//返回1:未检测到QT18B20M的存在
//返回0:存在
u8 QT18B20M_Check(void)
{
u8 retry=0;
QT18B20M_IO_IN();//SET PG11 INPUT
while (QT18B20M_DQ_IN&&retry<200)
{
retry++;
DelayUs(1);
};
if(retry>=200)return 1;
else retry=0;
while (!QT18B20M_DQ_IN&&retry<240)
{
retry++;
DelayUs(1);
};
if(retry>=240)return 1;
return 0;
}
//从QT18B20M读取一个位
//返回值:1/0
u8 QT18B20M_Read_Bit(void) // read one bit
{
u8 data;
QT18B20M_IO_OUT();//SET PG11 OUTPUT
QT18B20M_DQ_OUT=0;
DelayUs(2);
QT18B20M_DQ_OUT=1;
QT18B20M_IO_IN();//SET PG11 INPUT
DelayUs(12);
if(QT18B20M_DQ_IN)data=1;
else data=0;
DelayUs(50);
return data;
}
u8 QT18B20M_2Read_Bit(void)
{
u8 data=0,i;
for (i = 0;i<2;i++)
{
data <<= 1;
if(QT18B20M_Read_Bit())
{
data |=1;
}
DelayUs(2);
}
//printf("data:0x%x\r\n",data);
return data;
}
//从QT18B20M读取一个字节
//返回值:读到的数据
u8 QT18B20M_Read_Byte(void) // read one byte
{
u8 i,j,dat;
dat=0;
for (i=1;i<=8;i++)
{
j=QT18B20M_Read_Bit();
dat=(j<<7)|(dat>>1);
}
return dat;
}
//写入一个位
//dat:要写入的字节
void QT18B20M_Write_Bit(u8 dat)
{
QT18B20M_IO_OUT(); //SET PG11 OUTPUT;
if(dat&0x01)
{
QT18B20M_DQ_OUT=0; // Write 1
DelayUs(2);
QT18B20M_DQ_OUT=1;
DelayUs(60);
}
else
{
QT18B20M_DQ_OUT=0; // Write 0
DelayUs(60);
QT18B20M_DQ_OUT=1;
DelayUs(2);
}
}
//写一个字节到QT18B20M
//dat:要写入的字节
void QT18B20M_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
QT18B20M_IO_OUT();//SET PG11 OUTPUT;
for (j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if (testb)
{
QT18B20M_DQ_OUT=0;// Write 1
DelayUs(2);
QT18B20M_DQ_OUT=1;
DelayUs(60);
}
else
{
QT18B20M_DQ_OUT=0;// Write 0
DelayUs(60);
QT18B20M_DQ_OUT=1;
DelayUs(2);
}
}
}
//开始温度转换
void QT18B20M_Start(void)// ds1820 start convert
{
QT18B20M_Rst();
QT18B20M_Check();
QT18B20M_Write_Byte(0xcc);// skip rom
QT18B20M_Write_Byte(0x44);// convert
}
//初始化QT18B20M的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在
u8 QT18B20M_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_DQ, ENABLE);//使能GPIOG时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; /* 设为输出口 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; /* 设为开漏模式 */
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; /* 上下拉电阻不使能 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* IO口最大速度 */
GPIO_InitStructure.GPIO_Pin = PIN_DQ;
GPIO_Init(PORT_DQ, &GPIO_InitStructure);
QT18B20M_Rst();
return QT18B20M_Check();
}
//从QT18B20M得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250)
float QT18B20M_Get_Temp(u8 arr[8])
{
u8 temp;
u8 TL,TH;
short tem;
float t;
QT18B20M_Start (); // ds1820 start convert
QT18B20M_Rst();
QT18B20M_Check();
QT18B20M_Write_Byte(0x55); //匹配ROM指令
for(temp = 0;temp < 8;temp ++) //发送8个字节的序列号
{
QT18B20M_Write_Byte(arr[temp]);
}
DelayUs(10);
QT18B20M_Write_Byte(0xbe); //读取温度指令
TL = QT18B20M_Read_Byte(); //低8位数据
TH = QT18B20M_Read_Byte(); //高8位数据
if(TH>7)
{
TH=~TH;
TL=~TL;
temp=0;//温度为负
}else temp=1;//温度为正
tem=TH; //获取高8位
tem<<=8;
tem|=TL;//获取低8位
t=(float)tem*0.0625;//转换
if(temp)return t; //返回温度值
else return -(t+1);
}
/*************************************************************************************************************************
*函数名:u8 QT18B20M_SearchROM(u8 (*pID)[8],u8 Num)
*功能 :查询QT18B20M的ROM
*参数 :(1)、 pID:总线上QT18B20M的ID存储的缓冲区指针
(2)、 Num:QT18B20M的个数,在MAXNUM中有定义
*返回 :搜索到总线上QT18B20M的个数
*************************************************************************************************************************/
u8 QT18B20M_SearchROM(u8 (*pID)[8])
{
u8 k,m,n,x=0;
u8 s=0,conflict=0;
u8 num = 0;
u8 Value[64]={0}; //每一位的值
u8 Flag[MAXNUM]={0};//标志位
while(num<MAXNUM)
{
QT18B20M_Rst(); //复位QT18B20M总线
QT18B20M_Check(); //等待回应
QT18B20M_Write_Byte(0xF0); //搜索ROM
x=0;
for(m=0;m<8;m++)
{
for(n=0;n<8;n++)
{
k=QT18B20M_2Read_Bit(); // 读两位数据
k&=0x03;
s= s>>1;
switch(k)
{
case 0: //如果读取到的数据为00,则有冲突,需进行冲突位判断
conflict=1; //置1说明至少2台设备
Flag[x]=8*m+n; //表明在8*m+n位有冲突
if(Value[Flag[x]]==0)QT18B20M_Write_Bit(0);//写0,使总线上为0的器件响应
else
{
s=s|0x80;
QT18B20M_Write_Bit(1);//写1,使总线上为1的器件响应
}
x++;
break;
case 1: //0000 0001 如果读到的数据为0
QT18B20M_Write_Bit(0);//写0,使总线上为0的器件响应
break;
case 2: //0000 0010 如果读到的数据为1
s=s|0x80;
QT18B20M_Write_Bit(1);//写1,使总线上为1的器件响应
break;
case 3: //0000 0011 ,说明没有设备存在
return 0; //搜索完成,返回搜索到的个数
default: //其他结果,忽略
break;
}
DelayUs(5);
}
pID[num][m]=s;
//printf("pID[%d][%d]=0x%02x\r\n",num,m,s);
s=0;
}
if(conflict==0)return 1;//如果冲突标志位为0,说明只有一台设备
for(n=0;n<x;n++) //排除算法
{
if(n)
{
for(m=0;m<n;m++)Value[Flag[x-m-1]]=0;
}
if(Value[Flag[x-n-1]]==0)
{
Value[Flag[x-n-1]]=1;
break;
}
}
if(n==x)return ++num;
num++;
}
return num; //返回搜索到的个数
}
/*************************************************************************************************************************
*函数 : s16 QT18B20M_ReadDesignateTemper(u8 pID[8])
*功能 : 读取指定ID的QT18B20M温度
*参数 : pID:QT18B20M ID,必须事先知道,如果不知道请启动ROM搜索
*返回 : 温度值
*依赖 : 底层宏定义
*说明 : 温度值扩大了100倍,温度值是个有符号数.
*************************************************************************************************************************/
s16 QT18B20M_ReadDesignateTemper(u8 pID[8])
{
u8 th, tl;
s16 data;
// if(QT18B20M_Rst() == FALSE)
// {
// return 0xffff; //返回错误
// }
QT18B20M_Write_Byte(0xcc); //跳过读序列号
QT18B20M_Write_Byte(0x44); //启动温度转换
QT18B20M_Rst();
QT18B20M_Write_Byte(0x55); //发送序列号匹配命令
for(data = 0;data < 8;data ++) //发送8byte的序列号
{
QT18B20M_Write_Byte(pID[data]);
}
DelayUs(10);
QT18B20M_Write_Byte(0xbe); //读取温度
tl = QT18B20M_Read_Byte(); //读取低八位
th = QT18B20M_Read_Byte(); //读取高八位
data = th;
data <<= 8;
data |= tl;
data *= 6.25; //温度值扩大100倍,精确到2位小数
return data;
}
QT18B20M.h文件代码:
/***********************************************************************
文件名称:QT18B20.H
功 能:完成对对QT18B20的基本操作
编写时间:2024.3.12
编 写 人:
注 意:
***********************************************************************/
#ifndef QT18B20PA_H
#define QT18B20PA_H
#include "stm32f4xx.h"
#define MAXNUM 12
void QT18B20M_Write_Bit(u8 dat);//写入一个位
u8 QT18B20M_2Read_Bit(void);//读出两个位
u8 QT18B20M_SearchROM(u8 (*pID)[8]);//搜索ROM
s16 QT18B20M_ReadDesignateTemper(u8 pID[8]); //读取指定ID的QT18B20M温度
u8 QT18B20M_Init(void); //初始化QT18B20M
float QT18B20M_Get_Temp(u8 arr[8]);//获取温度
void QT18B20M_Start(void); //开始温度转换
void QT18B20M_Write_Byte(u8 dat);//写入一个字节
u8 QT18B20M_Read_Byte(void); //读出一个字节
u8 QT18B20M_Read_Bit(void); //读出一个位
u8 QT18B20M_Check(void); //检测是否存在QT18B20M
void QT18B20M_Rst(void); //复位QT18B20M
#endif
main.c文件代码:
#include "stm32f4xx.h"
#include "ds18b20.h"
#include "sys.h"
#include "delay.h"
#include <stdio.h>
#define DS18B20_NUM 4
u8 ID_Buff[DS18B20_NUM][8];
int main(void)
{
OS_CPU_SR cpu_sr;
s16 temp;
u8 buff[16];
u8 i,j,num;
SYSTEM_ClockInit(9); //初始化系统时钟72MHz
JTAG_Set(SWD_ENABLE); //只开启SWD调试模式
DevInit();
uart_printf("start system ...\r\n\r\n");
num = DS18B20_SearchROM(ID_Buff,DS18B20_NUM);
uart_printf("总线上实际挂载DS18B20数量: %d\r\n",DS18B20_NUM);
uart_printf("搜索到的DS18B20数量: %d\r\n",num);
for(i = 0;i < num;i ++)
{
uart_printf("\r\n DS18B20 No%d ID: ",i);
for(j = 0;j < 8;j ++)
{
uart_printf("%02X ",ID_Buff[i][j]);
}
}
i = 0;
while(1)
{
LED1 = ~LED1;
Delay_MS(1000);
temp = DS18B20_ReadDesignateTemper(ID_Buff[i ++]);
uart_printf("\r\n DS18B20 No%d Temp:%d ",i,temp);
if(i == 4)
{
i = 0;
uart_printf("\r\n");
}
}
}