zhange 发表于 2024-5-18 16:15:59


读/写时序
DS18B20 的数据读写是通过时序处理位来确认信息交换的。
写时序
由两种写时序:写 1 时序和写 0 时序。总线控制器通过写 1 时序写逻辑 1 到DS18B20,写 0 时序写逻辑 0 到 DS18B20。所有写时序必须最少持续 60us,包括两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到
低电平的时候,写时序开始。
总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。当总线被释放的时候,5K 的上拉电阻将拉高总线。总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采样。如果线上是高电平,就是写 1。如果线上是低电平,就是写 0。





void DS18b20_Write_0(void)                //写逻辑0码
{
        DQ = 0;                                //输出低电平
        Delay60us();
        DQ = 1;                                //输出高电平
        Delay1us();               
        Delay1us();       
}




void DS18b20_Write_1(void)                //写逻辑1码
{
        DQ = 0;                                //输出低电平
        Delay1us();       
        Delay1us();       
        DQ = 1;                                //输出高电平
        Delay60us();       
}




bit DS18b20_Read(void)                //读取电平
{
        bit state ;
       
        DQ = 0;                                //输出低电平
        Delay1us();       
        Delay1us();       
        DQ = 1;                                //输出高电平
        Delay1us();       
        Delay1us();       
        state = DQ;                        //读取当前电平

        Delay60us();

        return         state;
}


所有读时序必须最少 60us,包括两个读周期间至少 1us 的恢复时间。当总线控制器把数据线从高电平拉到低电平时,读时序开始,数据线必须至少保持 1us,然后总线被释放。在总线控制器发出读时序后,DS18B20 通过拉高或拉低总线上来传输 1 或 0。当传输逻辑 0 结束后,总线将被释放,通过上拉电阻回到上升沿状态。从 DS18B20 输出的数据在读时序的下降沿出现后 15us 内有效。因此,总线控制器在读时序开始后必须停止把 I/O 脚驱动为低电平 15us,以读取I/O 脚状态。


void DS18b20_WriteByte(u8 dat)        //写一个字节
{
        u8 i;
        for( i=0;i<8;i++ )                //循环八次
        {
                if( dat & 0x01 )
                        DS18b20_Write_1();
                else
                        DS18b20_Write_0();
                dat >>=1;
        }
}
// 1000 0000
// x100 0000
// xx10 0000
u8 DS18b20_ReadByte(void)                //读取一个字节
{
        u8 i;
        u8 dat=0;
        for( i=0;i<8;i++ )                //循环八次
        {
                dat >>=1;
               
                if( DS18b20_Read() )        //如果读取到的是1,
                        dat |= 0x80;
        }
        return dat;
}



soma 发表于 2024-5-18 17:33:31

和dht11有点像都是单线的

xiangzichen 发表于 2024-5-19 01:51:05

晓飛飛 发表于 2024-5-18 11:08
别弄DS18B20了,来试试国产的单线温度传感器NST1001吧,精度更高,编程更简单。
https://www.stcaimcu.com/ ...

我买了几颗,还在路上.如果这个有塑封的就好了,我的项目就可以直接替换了,18b20国产的不够好,进口的价格太高.

zhange 发表于 2024-5-19 07:32:19

接着搞开始写代码了


思路:循环扫描2个ds18b20的温度

1.需要首先获取每个ds18b20的唯一标识,获取标识的时候需要只接一个ds18b20一个一个获取(网上找的别人的,我移植过来了)

/**
* @brief读取ROM码
* @param存储DS18B20 ROM码的数组
* @retval None
*/
void ReadRomCode(unsigned char* romcode)
{
        bit ack;
        unsigned char i;
        ack = DS18b20_Reset1(); //复位
        if(ack == 0)
        {
                P20 = 0;
                DS18b20_WriteByte(0x33);   // 发送读取ROM命令
                Delay1ms();
                for(i = 0; i < 8; i++)
                {
                        romcode = DS18b20_ReadByte();    // 读取64位ROM码
                }
               
        }
}

2.将获取到的DS18B20器件的序列号通过数组保存起来


我代码里是直接把rom码发给串口,获取的


unsigned char ROM_data0 = {0x28,0x8e,0xe2,0x83,0x00,0x00,0x00,0xef};    //存储dsrom码

unsigned char ROM_data1 = {0x28,0x61,0x07,0x85,0x00,0x00,0x00,0xe3};    //存储dsrom码

3.main函数如下:基础就是冲哥课程里的代码

#include "COMM/stc.h"                //调用头文件
#include "COMM/stc32_stc8_usb.h"
#include "stdio.h"
#include "stc32g.h"
#include "18b20.h"


#define MAIN_Fosc 22118400L        //定义主时钟
unsigned char ROM_data;    //存储dsrom码

unsigned char ccc = 0xee;

char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";

bitTIM_10MS_Flag;                        //10ms的标志位


void sys_init();        //函数声明
void delay_ms(u16 ms);        //unsigned int

void Delay3000ms(void)        //@22.1184MHz
{
        unsigned long edata i;

        _nop_();
        _nop_();
        i = 16588798UL;
        while (i) i--;
}

void main()                                        //程序开始运行的入口
{
        int dat = 0;                        //初始数值
        unsigned int wendu;
        unsigned int wendu1;
        sys_init();                                //USB功能+IO口初始化
        usb_init();                                //usb库初始化
        EUSB = 1;          //中断允许位
        EA = 1;                                        //CPU开放中断,打开总中断。
        while( DeviceState != DEVSTATE_CONFIGURED );

        while(1)                //死循环
        {
                delay_ms(2);
               
                if( bUsbOutReady )                                                               
                {
//                        USB_SendData(UsbOutBuffer,OutNumber);
                        usb_OUT_done();
                }

          Delay3000ms();
               
//读取当前ds18的标识码               
//    ReadRomCode(ROM_data);
//                printf( "当前rom1:%x-", ROM_data);
//                printf( "当前rom2:%x-", ROM_data);
//                printf( "当前rom3:%x-", ROM_data);
//                printf( "当前rom4:%x-", ROM_data);
//                printf( "当前rom5:%x-", ROM_data);
//                printf( "当前rom6:%x-", ROM_data);
//                printf( "当前rom7:%x-", ROM_data);
//                printf( "当前rom8:%x\r\n", ROM_data);
               
                P01 = !P01;

                wendu = DS18b20_ReadTemp2(0);                                                                //300ms到达,读取一次温度
                if( MinusFlag==1 )
                {
                        printf( "当前温度0:-%.01f\r\n",(float)((float)wendu/10) );
                }
                else
                {
                        printf( "当前温度0:%.01f\r\n",(float)((float)wendu/10) );
                }
               
                Delay3000ms();
                wendu1 = DS18b20_ReadTemp2(1);                                                                //300ms到达,读取一次温度
                if( MinusFlag==1 )
                {
                        printf( "当前温度1:-%.01f\r\n",(float)((float)wendu1/10) );
                }
                else
                {
                        printf( "当前温度1:%.01f\r\n",(float)((float)wendu1/10) );
                }
               
               
                       
        }
}




void sys_init()                //函数定义
{
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

        P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
       
    P3M0 = 0x00;
    P3M1 = 0x00;

    P3M0 &= ~0x03;
    P3M1 |= 0x03;

        USBCON = 0X00;                                        //usb初始化
        USBCLK = 0X00;
        IRC48MCR = 0X00;
        delay_ms(10);

    //设置USB使用的时钟源
    IRC48MCR = 0x80;    //使能内部48M高速IRC
    while (!(IRC48MCR & 0x01));//等待时钟稳定

    USBCLK = 0x00;        //使用CDC功能需要使用这两行,HID功能禁用这两行。
    USBCON = 0x90;
}


void delay_ms(u16 ms)        //unsigned int
{
        u16 i;
        do
        {
                i = MAIN_Fosc/6000;
                while(--i);
        }while(--ms);
}





lovelong 发表于 2024-11-20 08:37:55

大神,请教一下,如果同时使用10个DS18B20,分开10个IO接DS18B20,去控制10个继电器工作?怎么写??代码?{:4_165:}{:4_165:}{:4_165:}{:4_165:}能不能实现?

D_Works 发表于 2024-11-20 08:55:42

22.1184MHz,楼主改下吧!22.1184Hz,啥也干不了

angmall 发表于 2024-11-20 16:28:31

lovelong 发表于 2024-11-20 08:37
大神,请教一下,如果同时使用10个DS18B20,分开10个IO接DS18B20,去控制10个继电器工作?怎么写??代码? ...

如果同时使用10个DS18B20,1个IO接DS18B20就可以了。
单线总线可以支持,127个DS18B20理论上。实际上也可以20~30个。

angmall 发表于 2024-11-20 16:43:19

网上的18b20程序,很多是用Delay延时直接去写的,目的更多是让你了解这个芯片的工作原理
但这种程序在现实的量产产品中肯定是不行的,因为单片机可能还要做其它的事
可以用定时器来进行非阻塞式的通讯

zhange 发表于 2024-11-21 09:09:49

D_Works 发表于 2024-11-20 08:55
22.1184MHz,楼主改下吧!22.1184Hz,啥也干不了

标记下   。。
页: 1 [2]
查看完整版本: 降龙棍外接ds18b20 记录