STM32 18B20程序

好久没用过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);

  • 14
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值