RYMCU

NO13.DS18B20实验-NEBULA-VSCODE

devcui 3 年前
# nebula-vscode

所属作品集

1.什么是 DS18B20

温度传感器

2.封装图和线路图

image.png

image.png

VD 连接 VCC,GND 连接 GND,只剩下一个 IO 口 DQ 连接 P37

3.温度数据

  • 1.DS18B20 是数字温度传感器
  • 2.格式可设置为 9,10,11,12 默认为 12 格式
  • 3.写入温度转换字 0x44 后传感器完成温度采集
  • 4.温度数据以 16 位存储在 2 个 8 位存储单元中

image.png

  • a.高 5 位 S 存储符号 0000 0 为 正度,1111 1 为负度,以补码形式存储度数
  • b.写入读温度指令 0xBE 后 两个 8 位存储由低到高依次输出

4.操作过程

  • a.初始化
  • b.Rom 操作
  • c.存储器操作指令
  • d.处理数据

4.1 初始化

image.png

  • a.由主机也就是单片机拉低总线引脚,持续时间为 480us 到 960us
  • b.主机释放总线,即单片机拉高总线引脚,等待 15us 到 60us;
  • c. DS18B20 拉低总线 60us 到 240us,即返回存在脉冲,表示传感器存在于总线上并可以接受操作指令了
  • d.DS18B20 主动释放总线,总线引脚自动被电阻拉高
void InitDS18B20(void)
{
// 拉低
DS18B20 = 0;
// 持续600us
DelayT_10us(60);
// 拉高
DS18B20 = 1;
// 持续60us
DelayT_10us(6);
// 直到DS18B20高阻抗被拉高
while (DS18B20 != 1)
;
// 持续60us
DelayT_10us(6);
}

4.2 写一个字节

image.png

  • a.图分两个部分,左边写 0,右边写 1
  • b.左边,先看最上方, 60us<Tx<120us
  • c.将总线拉低 60us-120us,DS18B20 最快 15us,典型 15us+15us,最慢 15us+15us+30us 时间内,采集到总线低电平,将 0 写入传感器
  • d.右边,1us<tREC< 正无穷
  • e.将总线拉低持续时间大于 1us 后立刻拉高,DS18B20 最快在 15us,典型 15us+15us,最慢在 15us+15us+30us 时间内,采集到总线高电平,将 1 写入传感器

image.png

void WrByte_18B20(unsigned char dat)
{
    unsigned char flag;
    for (int j = 1; j <= 8; j++)
    {
        // 拿到数据位
        flag = dat & 0x01;
        // 右移1位
        dat >>= 1;
        // 拉低
        DS18B20 = 0;
        // >1us
        _nop_();
        _nop_();
        // 给数据位
        DS18B20 = flag;
        // 延时60us
        DelayT_10us(60);
        // 写入完成后拉高
        DS18B20 = 1;
    }
}

4.3 读一个字节

image.png

  • a.图分为两部分,左侧为读 0
  • b.读 0,主机拉低,15us 内读取总线值,此时总线处于低电平所以是读 0,然后释放总线。
  • c.右侧为读 1
  • d.主机拉低,延时超过 1us,15us 内读取总线值,此时总线在高电平上,所以是读 1
unsigned char RdByte_18B20(void)
{
    unsigned char data, flag;
    for (unsigned char j = 1; j <= 8; j++)
    {
        // 主机将DS18B20拉低
        DS18B20 = 0;
        // 延时2us
        _nop_();
        _nop_();
        // 主机将DS18B20拉高
        // 这里和温度计并无关系,是RYMCU 51单片机设计
        // 读取数据时需要拉高
        DS18B20 = 1;
        // 延时2us
        _nop_();
        _nop_();
        // 回到正常时序图,读取数据给flag
        flag = DS18B20;
        // 延时60us结束周期
        DelayT_10us(6);
        // 读出的值最低位在前面
        data = (data >> 1) | (flag << 7)
    }
    return data;
}

4.4 Rom 操作指令

  • a.0xCC
    功能:Skip ROM( 跳过 ROM ),在单点总线系统中,此命令通过允许总线。
    不给主机提供 64 位 ROM 编码而访问存储器操作来节省时间。如果在总线上
    存在多于一个的从属器件而且在 Skip ROM 命令之后发出读命令,那么由于
    多个从片同时发送数据,会在总线上发生数据冲突(漏极开路下拉会产生线
    与的效果)。
  • b.0x44
    功能:Convert T(温度变换),这条命令启动一次温度转换而无需其他数据。
    温度转换命令被执行,而后 DS18B20 保持等待状态。如果总线控制器在这
    条命令之后跟着发出读时间隙,而 DS18B20 又忙于做时间转换的话,
    DS18B20 将在总线上输出“0”,若温度转换完成,则输出“1”。如果使用寄生
    电源,总线控制器必须在发出这条命令后立即起动强上拉,并保持 500ms。

4.5 存储器操作命令

  • a.0xBE:Read Scratchpad(读暂存存储器)
    读取将从字节 0 开始,一直进行下去,直到第 9(字节 8,CRC)字节读完。如果不
    想读完所有字节,控制器可以在任何时间发出复位命令来中止读取。读取的前两
    个字节为存储了温度值。

4.6 处理数据

  • a. 温度传感器初始化;

  • b. 写入字节 0xCC,跳过 ROM;

  • c. 写入字节 0x44,启动一次温度转换

  • d. 温度传感器初始化;

  • e. 写入字节 0xCC,跳过 ROM;

  • f. 写入字节 0xBE,发送读温度命令

  • g. 读取返回的前两个字节,并转化为温度值。

    // 处理数据获取温度
    unsigned int GetT_18B20(void)
    {
    	unsigned char Temp_L, Temp_H;
    	unsigned int Temp;
      	InitDS18B20();
    	WrByte_18B20(0xCC);
    	WrByte_18B20(0x44);
      	InitDS18B20();
       	WrByte_18B20(0xCC);
       	WrByte_18B20(0xBE);
    	// 读一个8位
    	Temp_L = RdByte_18B20();
    	// 读第二个8位
    	Temp_H = RdByte_18B20();
    	// 两个8位组合成16位
    	Temp = ((unsigned int)Temp_H << 8) + Temp_L;
    	return Temp;
    }
    

4.5 把以上函数写成.h 库供后面调用

image.png

4.5.1 1602.c

/*
 * @Author: cuihaonan
 * @Email: devcui@outlook.com
 * @Date: 2021-03-29 22:39:41
 * @LastEditTime: 2021-03-30 15:57:33
 * @LastEditors: cuihaonan
 * @Description: Basic description
 * @FilePath: /sdcc-include/src/temperature/include/1602.c
 * @LICENSE: NONE
 */
#include "../../../include/STC89xx.h"
#include "./1602.h"

SBIT(RS_1602, _P3, 6);
SBIT(RW_1602, _P3, 5);
SBIT(EN_1602, _P3, 4);

unsigned char RD_sta() //读状态函数
{
    unsigned char sta;
    RS_1602 = 0;
    RW_1602 = 1; //进入读 1602 状态模式
    EN_1602 = 1; //拉高使能信号
    sta = P2;    //将 1602 状态数据读取
    EN_1602 = 0; //拉低使能,完成读操作
    return sta;  //将状态值返
}

void Ready() //空闲检测函数
{
    P2 = 0xFF;
    while (RD_sta() & 0x80)
        ; //bit7 等于 1 表示忙,一直检测到 0 为止
}

void WR_Cmd(unsigned char cmd) //写指令函数
{
    Ready(); //检测 1602 是否处于空闲状态
    RS_1602 = 0;
    RW_1602 = 0; //进入写指令模式
    P2 = cmd;    //将指令数据输出
    EN_1602 = 1; //拉高使能信号
    EN_1602 = 0; //拉低使能,完成写操作
}

void WR_Dat(unsigned char dat) //写数据函数
{
    Ready(); //检测 1602 是否处于空闲状态
    RS_1602 = 1;
    RW_1602 = 0; //进入写数据模式

    P2 = dat; //将数据输出

    EN_1602 = 1; //拉高使能信号
    EN_1602 = 0; //拉低使能,完成写操作
}

void Init_1602() //1602 初始化函
{
    WR_Cmd(0x38); //设置 16x2 显示,5x7 点阵,8 位数据接口
    WR_Cmd(0x0C); //开显示,关闭光标
    WR_Cmd(0x06); //读或写完一个字符后,地址指针、光标均加 1
    WR_Cmd(0x01); //数据指针清零、所示显示清零
}

void Disp_1602_str(unsigned char row, unsigned char column, char *str)
{
    unsigned char addr;

    addr = (row - 1) * 0x40 + (column - 1); //组合成地址
    WR_Cmd(0x80 + addr);                    //写地址命令

    while (*str) //判断 str 字符串是否已结束
    {
        WR_Dat(*str++); //将 str 字符串数据依次写入
    }
}

4.5.2 1602.h

/*
 * @Author: cuihaonan
 * @Email: devcui@outlook.com
 * @Date: 2021-03-29 22:34:24
 * @LastEditTime: 2021-03-30 15:57:25
 * @LastEditors: cuihaonan
 * @Description: Basic description
 * @FilePath: /sdcc-include/src/temperature/include/1602.h
 * @LICENSE: NONE
 */


#ifndef __1602_H__
#define __1602_H__
extern unsigned char RD_sta();
extern void Ready();
extern void WR_Cmd(unsigned char cmd);
extern void WR_Dat(unsigned char dat);
extern void Init_1602();
extern void Disp_1602_str(unsigned char row, unsigned char column, char *str);
#endif

4.5.3 DS18B20.c

/*
 * @Author: cuihaonan
 * @Email: devcui@outlook.com
 * @Date: 2021-03-30 15:52:42
 * @LastEditTime: 2021-03-31 22:35:09
 * @LastEditors: cuihaonan
 * @Description: Basic description
 * @FilePath: /sdcc-include/src/temperature/include/DS18B20.c
 * @LICENSE: NONE
 */

#include "./DS18B20.h"
#include "../../../include/STC89xx.h"
#include "../../../include/mcs51/compiler.h"

SBIT(DS18B20, _P3, 7);

void DelayT_10us(unsigned char count)
{
    while (count--)
    {
        NOP();
        NOP();
        NOP();
        NOP();

        NOP();
        NOP();
        NOP();
        NOP();
    }
}

void InitDS18B20(void)
{
    // 拉低
    DS18B20 = 0;
    // 持续600us
    DelayT_10us(50);
    // 拉高
    DS18B20 = 1;
    // 持续60us
    DelayT_10us(6);
    // 直到DS18B20高阻抗被拉高
    while (DS18B20 != 1)
        ;
    // 持续600us
    DelayT_10us(50);
}

void WrByte_18B20(unsigned char dat)
{
    unsigned char flag;
    for (int j = 1; j <= 8; j++)
    {
        // 拿到数据位
        flag = dat & 0x01;
        // 右移1位
        dat >>= 1;
        // 拉低
        DS18B20 = 0;
        // >1us
        NOP();
        NOP();
        // 给数据位
        DS18B20 = flag;
        // 延时60us
        DelayT_10us(6);
        // 写入完成后拉高
        DS18B20 = 1;
    }
}

unsigned char RdByte_18B20(void)
{
    unsigned char data, flag;
    for (unsigned char j = 1; j <= 8; j++)
    {
        // 主机将DS18B20拉低
        DS18B20 = 0;
        // 延时2us
        NOP();
        NOP();
        // 主机将DS18B20拉高
        // 这里和温度计并无关系,是RYMCU 51单片机设计
        // 读取数据时需要拉高
        DS18B20 = 1;
        // 延时2us
        NOP();
        NOP();
        // 回到正常时序图,读取数据给flag
        flag = DS18B20;
        // 延时60us结束周期
        DelayT_10us(6);
        // 读出的值最低位在前面
        data = (data >> 1) | (flag << 7);
    }
    return data;
}

// 处理数据获取温度
unsigned int GetT_18B20(void)
{
    unsigned char Temp_L, Temp_H;
    unsigned int Temp;
    InitDS18B20();
    WrByte_18B20(0xCC);
    WrByte_18B20(0x44);
    InitDS18B20();
    WrByte_18B20(0xCC);
    WrByte_18B20(0xBE);
    // 读一个8位
    Temp_L = RdByte_18B20();
    // 读第二个8位
    Temp_H = RdByte_18B20();
    // 两个8位组合成16位
    Temp = ((unsigned int)Temp_H << 8) + Temp_L;
    return Temp;
}

4.5.4 DS18B20.h

/*
 * @Author: cuihaonan
 * @Email: devcui@outlook.com
 * @Date: 2021-03-30 15:52:36
 * @LastEditTime: 2021-03-31 10:13:05
 * @LastEditors: cuihaonan
 * @Description: Basic description
 * @FilePath: /sdcc-include/src/temperature/include/DS18B20.h
 * @LICENSE: NONE
 */

#ifndef __DS18B20_H__
#define __DS18B20_H__
// 模拟10us延时
extern void DelayT_10us(unsigned char count);
// 初始化
extern void InitDS18B20(void);
// 写入1字节
extern void WrByte_18B20(unsigned char dat);
// 读取1字节
extern unsigned char RdByte_18B20(void);
// 处理数据获取温度
extern unsigned int GetT_18B20(void);
#endif

5 结合液晶显示器应用

/*
 * @Author: cuihaonan
 * @Email: devcui@outlook.com
 * @Date: 2021-03-29 22:30:39
 * @LastEditTime: 2021-03-31 22:31:35
 * @LastEditors: cuihaonan
 * @Description: 单片机每秒采集一次温 度值,并将温度值显示到液晶显示模块上。用定时器中断来时实现 1s 的定时
 * @FilePath: /sdcc-include/src/temperature/temperature.c
 * @LICENSE: NONE
 */

#include "./include/1602.h"
#include "./include/DS18B20.h"
#include "../../include/STC89xx.h"
#include "../../include/mcs51/lint.h"

// 晶振频率
#define FOSC 11059200
// 定时器初始值计算
#define T_1ms (65536 - FOSC / 12 / 1000)
unsigned int Temp;
unsigned char str[10] = {0};
unsigned int count = 0;

void main()
{
    // 定时器模式
    TMOD = 0x01;
    // 装载初始值
    TL0 = T_1ms;
    TH0 = T_1ms >> 8;
    // 启动定时
    TR0 = 1;
    // 定时器中断
    ET0 = 1;
    // 总中断
    EA = 1;

    // 1602初始化
    Init_1602();
    // 第一行第三列开始显示
    Disp_1602_str(1, 3, "temperature");

    while (1)
    {
        // 1s进行一次温度采集显示
        if (count >= 1000)
        {
            // 先关闭中断,防止定时器中断影响温度传感器的读写
            EA = 0;
            // 重新计时
            count = 0;
            // 采集16位温度信息
            Temp = GetT_18B20();
            // 假如拿到的16位为  0000 0111 1101 0000 >> 4 =  0000 0111 1101  = 125
            // 125/10 = 12
            str[0] = (Temp >> 4) / 10 + '0'; // 右移4位,获得温度整数部分
            // 125%10 = 5
            str[1] = (Temp >> 4) % 10 + '0';
            str[2] = '.';
            // 0000 0111 1101 1000 >>3 = 0000 0111 1101 1 =  125.5
            // 125.5%10 = 5.5
            // 有余数直接按0.5算
            if ((Temp >> 3) % 10)
            {
                str[3] = '5';
            }
            // 否则按0算
            else
            {
                str[3] = '0';
            }
            str[4] = '0';
            Disp_1602_str(2, 3, str);
            // 处理完毕开启中断
            EA = 1;
        }
    }
}

// 定时器进入中断后
void timer0() __interrupt(1)
{
    // 重装初始值
    TL0 = T_1ms;
    TH0 = T_1ms >> 8;
    count++;
}

6.过程中我出现了什么问题?

由于使用的不是专业单片机程序编辑器,所以涉及到编译需要手动 sdcc 编译以后在使用 stcgal 刷程序。

当写到 第 14 章 DS18B20 模块时,需要封装 1602DS18B20 的 C 语言头文件,封装完成后无法使用 sdcc 进行编译的问题。

6.1 原因是什么?

sdcc 无法使用链接去链接头文件进行编译,所以尝试了一次编译多个文件,也无法编译,所以最终一个个编译出来

6.2 如何解决?

image.png

image.png

  • 1.使用 sdcc -c 将头文件全部编译到 lib 目录下,生成对应的 rel 文件
  • 2.使用 sdcc 将全部 rel 文件拼接 生成 ihx 文件

image.png

如图所示,执行最后一条链接命令时,sdcc 生成了 lk 文件,里面有链接信息,并输出了全部的 ihx

最后使用 stcgal 直接刷程序到板子里

image.png

所属作品集

后发布评论
作者

代码在 https://github.com/devcui/sdcc-include