嵌入式学习笔记DAY42(DS18B20、ARM)

一、DS18B20单总线数字温度计简介

1. 概念

比如在工厂环境温度测量,工厂里有储罐装的可燃性物体和气体需要检测温度,就可以使用该温度计,一般是封装起来去进行监测。

2. 特性

3. 引脚排列

4. 补充 (上拉电阻)

串行通信:只有一条数据请求线;

半双工通信:当ds18b20连接板子。板子为主机,温度计为从机,当传感器接收到主机发来的检测温度的请求就测量温度,测好的数据先放着,等接收到了需要反馈数据的指令后才反馈,所以数据传输是双向的,但同一时刻只能一个一个传。(有点类似于modbud,主从应答式)。
补充:

图中的P37引脚经过了一个10K的电阻,该电阻一端接的是P37引脚,另一端接了VCC电源,这个电阻对这段电路来说是必须的,通常称为------上拉电阻。其目的在于:

两个引脚连接起来数据是双向传输的,这根线上能够通过的信号是TTL信号,电压值取决于芯片的电源,51和18b20高电平均为5V,假如现在51要传输一个"1"到18b20,51会把引脚由低电平拉为高电平,但如果此时18b20也把引脚拉为高电平,这根线就呈现出高电平(5v)。

问题在于如果51拉高,18420要拉低?

答:在内部为摩斯管,将信号拉低的原理,其实是把这根引脚接地,拉到GND上,拉高就是到VCC上,现在51单片机把引脚拉高,18b20要把引脚拉低,意味着一个高电平直接就跟地连接起来了,短路了。但由于51单片机内部的摩斯管,本身输出电流是有限的。

所以,只有两个芯片同时都拉为高电平时,最终这条线上才呈现的是高电平。

拉高总线的不能最终决定电平状态,这个拉高总线过程称为释放总线;

任何接收方都应该释放总线;

为了保证释放总线时,这根信号线上能够呈现最终的高电平,需要在这根公共总线上连接上拉电阻。

上拉电阻(4.7K~10K):一端接电源(VCC),另一端接目标引脚。作用是保证双方在释放总线后,总线能呈现高电平。

二、DS18B20通讯时序

1. 存储器操作

我们需要去解决的问题:

  • 怎么把数据发过去
  • 怎么复位
  • 最终返回的数据,怎么读出来

此处的通信不是串口通信,这里是由达拉斯公司自己设计的通信方式。

2. 通讯时序

(1)初始化

初始化过程:

  • 在正式进行通信之前,双方都处于释放总线状态 ,总线处于高电平状态;
  • 主机(51)给18b20发送复位指令,将引脚电平从高电平拉至低电平(480us~960us)
  • 主机释放总线,恢复为高电平;
  • 18b20等待15-60us之后会拉低总线,并且保持60-240us;(这步发生就可以说明18b20是好的);
  • 之后,18b20释放总线,总线恢复至高电平;

代码实现:

复制代码
#define DQ_SET (P3 |= (1 << 7))
#define DQ_CLEAR (P3 &= ~(1 << 7))
#define DQ_TST ((P3 &= ~(1 << 7)) != 0)


/**
 * DS18B20复位函数 - 初始化通信并检测设备存在
 * 
 * 功能描述:
 * 1. 发送复位脉冲(480-960us低电平)
 * 2. 释放总线并等待DS18B20响应(存在脉冲)
 * 3. 检测存在脉冲并返回设备状态(1=存在,0=不存在)
 * 
 * 时序参考:
 * - 复位脉冲:主机拉低DQ线至少480us
 * - 存在脉冲:DS18B20在15-60us后拉低DQ线60-240us
 */
int ds18b20_reset(void)
{
    int t;
    
    // 步骤1:发送复位脉冲
    DQ_CLEAR;           // 拉低DQ线(P3.7置0)
    Delay10us(70);      // 延时700us(70*10us) - 满足复位脉冲时长要求
    
    // 步骤2:释放总线并准备接收存在脉冲
    DQ_SET;             // 拉高DQ线(P3.7置1)
    Delay10us(5);       // 延时50us - 等待DS18B20响应
    
    // 步骤3:检测DS18B20的存在脉冲(低电平)
    t = 0;
    while(DQ_TST && t < 30)  // 循环等待DQ线变低(存在脉冲)
    {
        Delay10us(1);        // 每次循环延时10us
        ++t;                 // 计数延时次数
    }
    if(t >= 30)              // 如果超时(300us)仍未检测到低电平
    {
        return 0;            // 返回0表示设备不存在或通信异常
    }
    
    // 步骤4:检测存在脉冲的释放(高电平)
    t = 0;
    while(!DQ_TST && t < 30) // 循环等待DQ线变高(存在脉冲结束)
    {
        Delay10us(1);        // 每次循环延时10us
        ++t;                 // 计数延时次数
    }
    if(t >= 30)              // 如果超时(300us)仍未检测到高电平
    {
        return 0;            // 返回0表示设备响应异常
    }
    
    return 1;                // 成功检测到完整的存在脉冲,返回1表示设备正常
}

(2) 读写操作

主机向 DS18B20 发送数据(命令或温度寄存器值),分为写 0和写 1两种时序:

  • 写 0 时序:
    1. 主机拉低 DQ 线60~120μs。
    2. 主机释放 DQ 线(拉高)。
  • 写 1 时序:
    1. 主机拉低 DQ 线1~15μs。
    2. 主机释放 DQ 线(拉高),DS18B20 在 15μs 内采样总线电平。

代码实现:

复制代码
/**
 * 向DS18B20写入一个字节数据
 * 
 * 功能描述:
 * 1. 逐位发送数据(低位在前)
 * 2. 根据数据位值(0/1)产生相应的写时序
 * 
 * 时序参考:
 * - 写0时序:主机拉低DQ线60-120us
 * - 写1时序:主机拉低DQ线1-15us后释放
 * - 数据采样点:DS18B20在写时序开始后的15us内采样
 */
void write_ds18b20(unsigned char dat)
{
    int i;
    for(i = 0; i < 8; ++i)  // 循环8次,逐位发送数据
    {
        if(dat & 1)  // 判断当前位是否为1(最低位)
        {
            // 写1时序:拉低短暂时间后释放总线
            DQ_CLEAR;          // 拉低DQ线(启动时序)
            _nop_();           // 延时2个机器周期(约2us @12MHz)
            _nop_();
            DQ_SET;            // 释放总线(拉高)
            Delay10us(5);      // 保持50us,完成写1时序
        }
        else  // 当前位为0
        {
            // 写0时序:拉低总线并保持较长时间
            DQ_CLEAR;          // 拉低DQ线
            Delay10us(5);      // 保持50us(满足写0时序要求)
            DQ_SET;            // 释放总线
        }
        dat >>= 1;  // 数据右移一位,准备发送下一位(低位在前)
    }
}

主机从 DS18B20 读取数据,时序如下:

  • 主机发起读时序:主机拉低 DQ 线1~15μs,然后释放总线(拉高);
  • DS18B20 输出数据:DS18B20 在主机释放总线后15μs内将数据位发送到总线(若为 0 则拉低,若为 1 则保持高);
  • 主机采样数据:主机在释放总线后15μs内采样 DQ 线电平,获取数据位;
  • 时序结束:读时序持续至少60μs。

代码实现:

复制代码
/**
 * 从DS18B20读取一个字节数据
 * 
 * 功能描述:
 * 1. 逐位读取DS18B20返回的数据(低位在前)
 * 2. 产生读时序并在有效采样点获取数据位
 * 
 * 时序参考:
 * - 读时序:主机拉低DQ线1-15us后释放
 * - 数据采样点:DS18B20在主机释放总线后的15us内驱动DQ线
 * - 读周期:每个读时序至少持续60us
 */
unsigned char read_ds18b20(void)
{
    unsigned char ret = 0;  // 存储读取结果(初始化为0)
    int i;
    
    for(i = 0; i < 8; ++i)  // 循环8次,逐位读取数据
    {
        // 步骤1:发起读时序
        DQ_CLEAR;           // 拉低DQ线(启动读时序)
        _nop_();            // 延时约2us(2个机器周期@12MHz)
        _nop_();
        DQ_SET;             // 释放总线(拉高)
        
        // 步骤2:等待DS18B20输出数据(约15us内)
        _nop_();            // 延时约6us(确保进入有效采样窗口)
        _nop_();
        _nop_();
        
        // 步骤3:在有效采样点读取数据位
        if(DQ_TST)          // 检测DQ线电平(DQ_TST应正确读取P3.7)
        {
            ret |= (1 << i); // 将当前位(1)写入ret的对应位(低位在前)
        }
        
        // 步骤4:完成当前读周期(总时长≥60us)
        Delay10us(5);       // 延时50us,确保读周期完整
    }
    
    return ret;             // 返回读取的8位数据
}

(3)通信流程(测温)

代码实现:

复制代码
/**
 * 从DS18B20读取温度值并转换为浮点数
 * 
 * 工作流程:
 * 1. 初始化DS18B20
 * 2. 发送跳过ROM命令(单设备场景)
 * 3. 启动温度转换
 * 4. 等待转换完成(典型750ms@12位精度)
 * 5. 读取温度寄存器数据
 * 6. 组合高低字节并转换为实际温度值
 * 
 * 温度数据格式:
 * - 16位二进制补码(高5位为符号位,低11位为温度值)
 * - 分辨率默认12位(0.0625°C/LSB)
 */
float get_temperature(void)
{
    unsigned char tl;  // 温度低字节
    unsigned char th;  // 温度高字节
    short ret;         // 组合后的16位温度值
    
    // 步骤1:初始化并启动温度转换
    ds18b20_reset();          // 发送复位脉冲
    write_ds18b20(0xCC);      // 跳过ROM命令(单设备无需匹配ROM)
    write_ds18b20(0x44);      // 启动温度转换命令
    Delaylms(1000);           // 等待转换完成(12位精度最大750ms,此处延时1秒确保完成)
    
    // 步骤2:读取温度寄存器
    ds18b20_reset();          // 再次复位,准备读取数据
    write_ds18b20(0xCC);      // 跳过ROM命令
    write_ds18b20(0xBE);      // 发送读取暂存器命令
    
    // 步骤3:读取温度数据(低位在前)
    tl = read_ds18b20();      // 读取低8位
    th = read_ds18b20();      // 读取高8位
    
    // 步骤4:组合16位温度值(补码形式)
    ret = th << 8;            // 高字节左移8位
    ret |= tl;                // 组合低字节
    
    // 步骤5:转换为实际温度值(分辨率0.0625°C/LSB)
    return ret * 0.0625;      // 返回浮点温度值(带符号扩展)
}

补充:

这样接收到的数据是最原始的数据,如何转换为真实温度:

要给读出来的数据×0.0625,原因如下:

三、ARM架构

1. soc

soc(system on chip):是一种将计算机或其他电子系统的所有组件集成到单个芯片上的集成电路。通常包含中央处理器(CPU)、图形处理器(GPU)、内存、输入 / 输出端口和辅助存储等,通过先进的半导体制造技术,将这些组件集成在一个半导体衬底上,实现高效的性能和低功耗。

  • 在soc里存在大量的外设,外设是听从Kernel操作的,所以外设和kernal之间就要建立通信;
  • 建立通信的方式即连线,这根线就称作------总线(BUS);
  • 如果kernal和各外设之间的总线只有一根------单总线;多根------多总线;

一般是2根总线(AHB、APB)

AHB(先进的高速总线):专为高带宽、高速率场景设计,支持多主设备、流水线操作及突发传输,用于连接 CPU、内存控制器等高性能组件;

APB(先进外设总线):面向低速、低功耗外设,采用单主结构和简单协议,连接 GPIO、串口等低速模块。

我们写程序写的是驱动的程序,为C语言编写,C语言的运行条件是要有足够大的内存(栈、堆、代码区等),soc外接的RAM意味着内存是可以人为去控制的,但是程序运行起来,它是不知道RAM在什么位置,所以首先我们先去研究内核。

后续写代码时必须先告诉内核,RAM在哪儿;且内核当中也有一些需要进行配置的。

2. 架构

一般有这三种架构:8051、x86、ARM

CISC(复杂指令集计算机):

  • 一般包含 200 多条不同的指令,例如 Intel 的 x86 架构。指令功能复杂,不仅有简单的算术运算、逻辑运算指令,还有像字符串处理、数据转换等功能复杂的指令。
  • 为了实现复杂的指令功能,CISC 架构的处理器需要配备大量的硬件电路,导致体积大、功耗高,不利于便携式使用。

RISC(精简指令集计算机):

  • 通常只包含几十条最基本、最常用的指令,例如 ARM 架构,主要包括数据处理指令(如加、减、乘等 )、数据传输指令等。
  • 由于 RISC 处理器具有低功耗、体积小、成本低等特点,在嵌入式系统领域得到了广泛应用,比如手机、平板电脑、智能家居设备等,这些设备中的处理器大多采用 ARM 架构(RISC) 。

3. kernel内核

上述c语言程序,i 和 j 是存储在RAM里的,在进行运算时,其实是把数据传给kernal中的通用寄存器,再调用ALU进行运算,计算出来的结果先写入通用寄存器,再传回RAM。

扩: 计算机更高一级的架构方法

  • 冯诺依曼架构:数据和指令都放在同一个存储器,这种方式的计算机结构简单,但处理起来数据吞吐量不行
  • 哈佛架构:数据和指令分开存放

(1) ALU:算数逻辑单元

(2) 通用寄存器

不等同于外设寄存器,最大的区别是外设寄存器可寻址,而通用寄存器不可寻址,直接拿它的名字过来用即可。

  • R0~R12:
  • SP(栈指针寄存器):即在物理层实实在在的做了一个寄存器去管理栈区。kernel根本不知道RAM在哪儿,所以要设置让SP指向RAM中的固定位置,这些变量才能获得正确的内存;
  • PC(程序计数器):指向当前正在执行指令的下一条指令(ARM架构中,当设备被复位后,PC会自动清零,意味着PC一定会从地址零的位置取出来一条指令开始执行,PC、SP均为4字节,执行一条+4);
  • LR(链接寄存器):

在函数走完了之后,如何返回到之前的位置继续运行呢,LR链接寄存器即用来存储之前的地址,直接把LR的值拿出来交给PC即可。

**(3) MMU(内存管理单元):**负责虚拟地址到物理地址的转换及内存访问权限控制。

并不是说管理RAM的,有专门的RAM管理器。一个应用程序有4GB的内存空间(3GB的用户空间、1GB的内核空间),但实际外接的内存可能都没有4GB,所以程序走起来并不是真的有4GB的,而是虚拟的,在程序中取变量的地址和真正写入到RAM里的地址是不同的。为了提高物理内存的使用效率,假装分配一个内存,当真的需要时才会去划分内存。MMU即负责把虚拟内存转为虚拟内存,也能将物理内存迅速计算出需要的虚拟内存。

对于裸机程序,默认将MMU关闭

**(4)Cache(高速缓存):**Cache(高速缓存)是计算机系统中位于 CPU 与主内存之间的高速存储组件,通过存储高频访问数据的副本来减少 CPU 对主存的访问延迟,提升系统整体性能。

icache:打开

dcache:关闭

(5)CPSR(当前程序状态寄存器)、SPSR(程序状态保存寄存器)

  • 程序发生溢出的情况,溢出的状态就会被写进CPSR;
  • CPU响应中断,要不要响应也存在CPSR中,要响应就得让CPSR不能屏蔽中断;
  • CPSR保护工作由SPSR执行,SPSR起到保护、备份的作用;

4. ARM发展

ARM Cortex A(用于消费类电子产品)

R(用于微控制器MAU方向,如单片机)

M(用于实时性方向,如军事、通信)

ARM的指令集版本:

ARM V4

ARM V5

...

两者不一样,cortex指硬件上的版本,而V是指令集版本

5. 编译流程

相关推荐
sealaugh3219 分钟前
aws(学习笔记第四十八课) appsync-graphql-dynamodb
笔记·学习·aws
水木兰亭1 小时前
数据结构之——树及树的存储
数据结构·c++·学习·算法
鱼摆摆拜拜1 小时前
第 3 章:神经网络如何学习
人工智能·神经网络·学习
aha-凯心1 小时前
vben 之 axios 封装
前端·javascript·学习
freexyn3 小时前
Matlab自学笔记六十一:快速上手解方程
数据结构·笔记·matlab
很小心的小新3 小时前
12、jvm运行期优化
java·开发语言·jvm·笔记
ytttr8735 小时前
matlab通过Q学习算法解决房间路径规划问题
学习·算法·matlab
寻丶幽风6 小时前
论文阅读笔记——NoPoSplat
论文阅读·笔记·三维重建·3dgs·相机位姿·dustr
听风ツ8 小时前
固高运动控制
学习
西岭千秋雪_8 小时前
Redis缓存架构实战
java·redis·笔记·学习·缓存·架构