好久没用过18B20了,今天项目用到了,从网上找了一份现成的,原文地址https://blog.csdn.net/qq_34885669/article/details/89521045 但是作者是用的位段方式,初始化也是用的操作寄存器,和我的习惯不太一样,改了一下改成了库文件操作,亲测可用。18B20没什么难度。主要就是时序问题,一般不行的话都是延时定时器或者滴答配置错误,建议用滴答做us延时,如果用的操作系统,建议操作18B20时先关中断,
18B20.c
/*******************************************
DS18B20 单总线对延时精度要求非常高!!!
本程序使用了系统嘀嗒定时器来当做精准延时。
DQ :GPIOB_6
*******************************************/
static void DS18B20_OutPut_Mode(){
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}
static void DS18B20_InPut_Mode(void){
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}
static uint8_t DS18B20_IN(void){
return GPIO_ReadInputDataBit(DS18B20_PORT, DS18B20_PIN);
}
static void Delay_Us1(n){
Delay_Us(n);
}
/*功能:DS18B20初始化*/
void DS18B20_Init(void)
{
/*1.GPIOC口初始化*/
// RCC->APB2ENR |= 1<<3;
// GPIOB->CRL &= 0x0FFFFFFF;
// GPIOB->CRL |= 0x30000000;
// GPIOB->ODR |= 1<<7;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DS18B20_PORT_RCC, ENABLE );
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
/*2.检测DS18B20设备是否正常*/
switch(DS18B20_CheckDevice())
{
case 0: {
printf("DS18B20_Init OK!\n");
}break;
case 1: {
printf("DS18B20设备响应复位信号失败!\n");
}break;
case 2: {
printf("DS18B20设备释放总线失败!\n");
}break;
}
}
/*功能:向DS18B20发送一个复位信号*/
void DS18B20_SendRestSingle(void)
{
/*主机发送复位信号*/
DS18B20_OutPut_Mode();
DS18B20_OUT_CLR; //拉低总线480~960 us ,对 DS18B20 芯片进行复位
Delay_Us1(750);
DS18B20_OUT_SET;
// Delay_Us1(15); //释放总线15~60 us
Delay_Us1(15); //释放总线15~60 us
}
/*
功能:检测DS18B20存在脉冲
返回值:
0 DS18B20设备正常
1 DS18B20设备响应复位信号失败
2 DS18B20设备释放总线失败
*/
u8 DS18B20_CheckReadySingle(void)
{
u8 cnt=0;
/*1.检测存在脉冲*/
DS18B20_InPut_Mode();
while((DS18B20_IN() == 0X01) && (cnt < 240)) //等待DS18B20 拉低总线 (60~240 us 响应复位信号)
{
Delay_Us1(1);
cnt++;
}
if(cnt>240) return 1;
/*2.检测DS18B20是否释放总线*/
cnt=0;
DS18B20_InPut_Mode();
while((!DS18B20_IN()) && (cnt<240)) //判断DS18B20是否释放总线(60~240 us 响应复位信号之后会释放总线)
{
Delay_Us1(1);
cnt++;
}
if(cnt>240) return 2;
else return 0;
}
/*
功能:检测DS18B20设备是否正常
返回值:
0 DS18B20设备正常
1 DS18B20设备响应复位信号失败
2 DS18B20设备释放总线失败
*/
u8 DS18B20_CheckDevice(void)
{
DS18B20_SendRestSingle();/*1.主机发送复位信号*/
return DS18B20_CheckReadySingle();/*2.检测存在脉冲*/
}
/*功能:向DS18B20写一个字节数据(命令)*/
u8 BS18B20_WriteByte(u8 cmd)
{
u8 i=0;
u8 data = cmd;
DS18B20_OutPut_Mode();
for(i=0;i<8;i++)
{
DS18B20_OUT_CLR;
Delay_Us1(2); //主机拉低总线写数据时间隙2us
if(data&0x01){ //如果该写入位为1,必须在15us之内把总线拉高,为0 保持60us即可。
DS18B20_OUT_SET
}else{
DS18B20_OUT_CLR
}
Delay_Us1(60);
DS18B20_OUT_SET; //一位发送完成
data >>=1;
Delay_Us1(2); //位间隙2us
}
return 0;
}
/*功能:从DS18B20读取一个字节数据*/
u8 DS18B20_ReadByte(void)
{
u8 i,data=0;
for(i=0;i<8;i++)
{
DS18B20_OutPut_Mode();//初始化为输出模式
DS18B20_OUT_CLR;
Delay_Us1(2); //主机拉低总线读数据时间隙2us
DS18B20_OUT_SET; //释放总线,准备读取位数据
DS18B20_InPut_Mode(); //初始化为输入模式
Delay_Us1(10); //等待DS18B20的数据输出
data >>=1 ; //高位补0,默认以0为准
if(DS18B20_IN()){
data |=0x80; //eB这里注意下,看看对不对
}
Delay_Us1(60); //延时确保DS18B20采样周期已经过去(非常重要)
// DS18B20_OUT_SET; //释放总线准备读取下一位位数据
}
return data;
}
/*
函数功能: 读取一次DS18B20的温度数据
返 回 值: 读取的温度数据
考虑的情况: 总线上只是接了一个DS18B20的情况
*/
u16 DS18B20_GetTemperature(void)
{
u16 temp=0;
u8 temp_H,temp_L;
int intT,decT;
DS18B20_CheckDevice(); //发送复位脉冲、检测存在脉冲
BS18B20_WriteByte(0xCC); //跳过ROM序列检测
BS18B20_WriteByte(0x44); //启动一次温度转换
//等待温度转换完成
while(DS18B20_ReadByte()!=0xFF){}
DS18B20_CheckDevice(); //发送复位脉冲、检测存在脉冲
BS18B20_WriteByte(0xCC); //跳过ROM序列检测
BS18B20_WriteByte(0xBE); //读取温度
temp_L=DS18B20_ReadByte(); //读取的温度低位数据
temp_H=DS18B20_ReadByte(); //读取的温度高位数据
temp=temp_L|(temp_H<<8); //合成温度
intT = temp>>4 ; /*合成实际温度整数部分****精度相对上面的更高*/
decT = temp&0xF ; /*合成实际温度小数部分*/
//printf("温度:%d.%d ℃ \n",intT,decT);
return temp;
}
/*功能:从DS18B20读取ROM信息 (ROM_ID= 28-92-AF-AC-17-13-1-F1)*/
u8 DS18B20_ReadRomInfo(void)
{
u8 i=0;
BS18B20_WriteByte(0x33); /*4.启动读取ROM*/
for(i=0;i<8;i++)
{
ROM_ID[i]=DS18B20_ReadByte();
}
//printf("ROM_ID= ");
for(i=0;i<8;i++)
{
//printf("%X",ROM_ID[i]);
if(i==7) {
//printf("\n");
}
else{
//printf("-");
}
}
return 0;
}
/*功能:匹配 DS18B20 ROM信息*/
u8 DS18B20_MatchROM(void)
{
u8 i=0;
BS18B20_WriteByte(0x55); /*4.匹配64位 ROM 信息*/
for(i=0;i<8;i++)
{
BS18B20_WriteByte(ROM_ID[i]);
}
return 0;
}
18b20.h
#ifndef DS18B20_H
#define DS18B20_H
#include "stm32f10x.h"
#define DS18B20_PORT_RCC RCC_APB2Periph_GPIOB
#define DS18B20_PORT GPIOB
#define DS18B20_PIN GPIO_Pin_6
#define DS18B20_OUT_SET GPIO_SetBits(DS18B20_PORT, DS18B20_PIN);
#define DS18B20_OUT_CLR GPIO_ResetBits(DS18B20_PORT, DS18B20_PIN);
void DS18B20_Init(void);
u8 DS18B20_CheckDevice(void);
void DS18B20_SendRestSingle(void);
u8 DS18B20_CheckReadySingle(void);
u8 BS18B20_WriteByte(u8 cmd);
u8 BS18B20_ReadByte(void);
u16 DS18B20_GetTemperature(void);
u8 DS18B20_ReadRomInfo(void);
u8 DS18B20_MatchROM(void);
#endif
delay.c
/*********************************滴答方式延时**********************************************/
void Delay_Init(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
p_us=SystemCoreClock/8000000;
p_ms=(u16)p_us*1000;
}
/*******************************************************************************
* Function Name : Delay_Us
* Description : Microsecond Delay Time.
* Input : n:Microsecond number.
* n * p_us < 0xFFFFFF
* Return : None
*****************************************************************************/
void Delay_Us(u32 n)
{
u32 i;
SysTick->LOAD=n*p_us;
SysTick->VAL=0x00;
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
do
{
i=SysTick->CTRL;
}while((i&0x01)&&!(i&(1<<16)));
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL =0X00;
}
/*********************************定时器方式延时**********************************************/
//定时器这里说一下,HCLK主频是72M, APB2是36M,定时器主频就是 72/2*2还是72M,不懂的可以看一下手册的时钟树。
void Delay_Init(void)
{
//TIM3
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = 65534; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =0; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_Cmd(TIM3,DISABLE);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
}
//最大延时 65536/72 = 910 us
void Delay_Us(u32 n)
{
u16 nTime = n;
if (nTime >910)
{
nTime = 910;
}
nTime*=72;
TIM3->CNT = 0;
TIM_Cmd(TIM3,ENABLE);
while( (TIM3->CNT) < nTime);
TIM_Cmd(TIM3,DISABLE);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
}
后话: 忘记了18B20的转换时间非常长,在750ms左右,这样如果用上面的程序的话,需要死等750ms,有点浪费系统性能,做个状态机比较好。顺便做一下直接返回负温度
/**********带操作系统,不死等,进行分步处理***************/
//0失败 1成功
u8 DS18B20_GetTemp1(void){
u8 ret = 0x00;
if(DS18B20_CheckDevice() == 0){ //发送复位脉冲、检测存在脉冲
BS18B20_WriteByte(0xCC); //跳过ROM序列检测
BS18B20_WriteByte(0x44); //启动一次温度转换
ret = 0x01;
}
return ret;
}
//0失败 1成功
u8 DS18B20_GetTemp2(void){
if(DS18B20_ReadByte() != 0xFF){
return 0x00;
}
return 0x01;
}
//直接返回带正负
float DS18B20_GetTemp3(void){
u16 temp=0;
u8 temp_H,temp_L;
s16 temperaturebuffer;
DS18B20_CheckDevice(); //发送复位脉冲、检测存在脉冲
BS18B20_WriteByte(0xCC); //跳过ROM序列检测
BS18B20_WriteByte(0xBE); //读取温度
temp_L=DS18B20_ReadByte(); //读取的温度低位数据
temp_H=DS18B20_ReadByte(); //读取的温度高位数据
temp=temp_L|(temp_H<<8); //合成温度
temperaturebuffer = temp;
return ((float)(temperaturebuffer>>4)) + ((temperaturebuffer&0x0F)/16.0);
}
//应用 OS_ENTER_CRITICAL 和EXIT_CRITICAL是操作系统开关中断函数, 可自行替换成适合自己的应用的。base为线程的基本延时时间,本例是20
switch(dsState){
//2秒读一次
case 0:{
if(dsCount++ >= 2000/base){
dsCount = 0;
OS_ENTER_CRITICAL();
if(DS18B20_CheckDevice() == 0){
if(DS18B20_GetTemp1() == 0x01){ //启动转换
dsState = 1;
}else{
dsState = 0;
}
}
OS_EXIT_CRITICAL();
}
}break;
//1S后再读出数据
case 1:{
if(dsCount++ >= 1000/base){
dsCount = 0;
if(DS18B20_GetTemp2() == 0x01){
dsState = 2;
}else{
dsState = 0;
}
}
}break;
//转换换算
case 2:{
OS_ENTER_CRITICAL();
fTemp = DS18B20_GetTemp3();
OS_EXIT_CRITICAL();
dsState = 0;
}break;
default:{
dsState = 0;
dsCount = 0;
}break;
}
OSTimeDlyHMSM(0, 0, 0, base);