1. DS1302实时时钟
一、DS1302介绍

DS1302 是一款低功耗的实时时钟(RTC)芯片 ,你可以把它理解成一个自带 "小电池" 的电子日历 + 秒表。
- 它的核心任务:一直精准走时间,就算设备断电也不停止计时。
- 生产方:美国 DALLAS 公司,是单片机 / 嵌入式开发里最常用的时钟芯片之一。
二、它能做什么?

1. 基础计时功能
可以完整记录并输出:
- 年、月、日、星期
- 时、分、秒
- 自带闰年补偿,不用你手动处理 2 月 29 号这种情况
2. 断电也能走的关键特性
- 它有两个电源接口:主电源 + 备用电池(一般是 CR2032 纽扣电池)
- 设备断电时,会自动切换到备用电池供电,用极低的功耗维持时钟运行,下次开机时间不会重置。
- 支持涓细电流充电:部分电路设计下,主电源可以给备用电池慢慢充电,延长电池寿命。
三、它和普通定时器有啥区别?
| 特性 | 普通单片机定时器 | DS1302 实时时钟 |
|---|---|---|
| 断电后 | 停止计时,时间重置 | 靠备用电池继续走,时间不丢 |
| 占用资源 | 需要单片机一直运行计数 | 独立运行,不占用 CPU 资源 |
| 精度 | 易受系统时钟、中断影响 | 靠晶振校准,长期走时更准 |
四、生活 / 开发里的用处
- 单片机电子钟、万年历
- 设备开机记录时间戳(比如日志的时间)
- 定时开关控制(比如定时开灯、定时采集数据)
- 智能家居、工业设备里的时间基准
五、内部信息解析

通过串行的通信协议来实现数据交互 与DS1302内的寄存器实现数据的交换 实现时间的访问和读写


两个任务(由地址命令字来完成)

-
在哪(秒分还是时) 写入什么
-
在哪(秒分还是时) 读出什么(DS1302输出的)


CE置为1 开始写入 这里的写入时由低字节到高字节的 SCLK置1 代表一个上升沿 然后置0 重复操作 先把命令字给写入 然后把实际数据写入
先发送命令字判断是读还是写(对应下面那行),命令字中发送过去包含要写或者读的位置(秒 分 时);然后如果是写的话通过MCU向时钟写入,读的话通过时钟向单片机传递相应的信息

六、DS1302时钟实现
需要用到LCD1602.c LCD1602h
DS1302.c
cpp
#include <REGX52.H>
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
//将这三个引脚(P3^6,4,5)定义为DS1302_...这样调用的时候就用
//DS1302_..更加直观
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_WP 0x8E
//年月日时分秒星期
unsigned char DS1302_Time[]={26,5,30,11,24,10,6};
//1.初始化 CE和SCLK要置为0
void DS1302_Init(void)
{
DS1302_CE=0;
DS1302_SCLK=0;
}
//2.写字节函数 两个返回值命令字和数据
void DS1302_WriteByte(unsigned char Command,Data)//命令字和数据
{
unsigned char i;
DS1302_CE=1;//CE置为1
for(i=0;i<8;i++)//写入八位命令字上升沿部分
{
DS1302_IO=Command&(0x01<<i);//取出第0位
DS1302_SCLK=1;//置为1
DS1302_SCLK=0;//手动置0
}
for(i=0;i<8;i++)//写入八个数据上升沿部分
{
DS1302_IO=Data&(0x01<<i);//取出第0位
DS1302_SCLK=1;//置为1
DS1302_SCLK=0;//手动置0
}
DS1302_CE=0;//数据写完了清零
}
//3.读字节函数
unsigned char DS1302_ReadByter(unsigned char Command)
{
unsigned char i,Data=0x00;
Command|=0x01;//最低位控制读和写 1就是读
DS1302_CE=1;
for(i=0;i<8;i++)//写入八位命令字上升沿部分
{
DS1302_IO=Command&(0x01<<i);//取出第0位
DS1302_SCLK=0;//这里要调整一下顺序 因为读的时序
DS1302_SCLK=1;//中有一个上升沿和下降沿共用了
}
for(i=0;i<8;i++)//将8个命令数读取到Data中
{
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO) Data|=(0x01<<i);//读取IO上的数据到Data
//如果IO上的数为1则Data为1 如果IO为0 则Data就是0
}
DS1302_IO=0;
DS1302_CE=0;
return Data;
}
void DS1302_SetTime(void) //DS1302 内部存的是【BCD 码】
{
DS1302_WriteByte(DS1302_WP,0x00);//写保护
//把十进制转成BCD码
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
DS1302_WriteByte(DS1302_WP,0x80);//关闭写保护
}
void DS1302_ReadTime(void)//显示只能显示十进制
{
//把BCD码转成十进制
unsigned char Temp;
Temp=DS1302_ReadByter(DS1302_YEAR);
DS1302_Time[0]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByter(DS1302_MONTH);
DS1302_Time[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByter(DS1302_DATE);
DS1302_Time[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByter(DS1302_HOUR);
DS1302_Time[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByter(DS1302_MINUTE);
DS1302_Time[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByter(DS1302_SECOND);
DS1302_Time[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByter(DS1302_DAY);
DS1302_Time[6]=Temp/16*10+Temp%16;
}
DS1302.h
cpp
#ifndef __DS1302_H__
#define __DS1302_H__
extern unsigned char DS1302_Time[];
void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByter(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);
#endif
main.c
cpp
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Delay.h"
void main()
{
LCD_Init();
DS1302_Init();
DS1302_SetTime();
while(1)
{
DS1302_ReadTime();
//Second=DS1302_ReadByter(0x81);//读出秒的数据
//Minute=DS1302_ReadByter(0x83);//读出分的数据
//LCD_ShowHexNum(2,1,Second,3);//这里写成数字形式显示会转成BCD码 所以要用16进制显示
//或者写成BCD码转换成10进制
LCD_ShowNum(1,1,DS1302_Time[0],2);
LCD_ShowNum(1,4,DS1302_Time[1],2);
LCD_ShowNum(1,7,DS1302_Time[2],2);
LCD_ShowNum(2,1,DS1302_Time[3],2);
LCD_ShowNum(2,4,DS1302_Time[4],2);
LCD_ShowNum(2,7,DS1302_Time[5],2);
}
}
七、DS1302可调时钟
需要用到前面学的LCD1602 以及定时器Timer0 还有DS1302
main.c主函数内有详细注释仅供参考
cpp
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"
unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;//MODE是模式 让两个函数交替运行
//TimeSetSelect六个挡位年月日时分秒 TimeSetFlashFlag时间设置闪烁标志位
void TimeShow(void)
{
DS1302_ReadTime();
LCD_ShowNum(1,1,DS1302_Time[0],2);
LCD_ShowNum(1,4,DS1302_Time[1],2);
LCD_ShowNum(1,7,DS1302_Time[2],2);
LCD_ShowNum(2,1,DS1302_Time[3],2);
LCD_ShowNum(2,4,DS1302_Time[4],2);
LCD_ShowNum(2,7,DS1302_Time[5],2);
}
void TimeSet(void)//第一个独立按键进行修改操作
{
if(KeyNum==2)//第二个独立按键设置修改时间的挡位
{
TimeSetSelect++;//这代表六个挡位的调节
TimeSetSelect%=6;//年月日时分秒 越界清零
}
if(KeyNum==3)//第三个独立按键对时间进行加
{
DS1302_Time[TimeSetSelect]++;
if(DS1302_Time[0]>99){DS1302_Time[0]=0;}
if(DS1302_Time[1]>12){DS1302_Time[1]=1;}
if(DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)
{
if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
}
else if( DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9|| DS1302_Time[1]==11 )
{
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]=2)
{
if((DS1302_Time[0]%4==0 && DS1302_Time[0]%100 !=0 ) || DS1302_Time[0]%400==0 )
{
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
}
if(DS1302_Time[3]>23){DS1302_Time[3]=0;}
if(DS1302_Time[4]>59){DS1302_Time[4]=0;}
if(DS1302_Time[5]>59){DS1302_Time[5]=0;}
}
if(KeyNum==4)//第四个独立按键对时间进行减
{
DS1302_Time[TimeSetSelect]--;
if(DS1302_Time[0]<0){DS1302_Time[0]=99;}
if(DS1302_Time[1]<1){DS1302_Time[1]=12;}
if(DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=31;}
if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
}
else if( DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9|| DS1302_Time[1]==11 )
{
if(DS1302_Time[2]<1){DS1302_Time[2]=30;}
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]=2)
{
if((DS1302_Time[0]%4==0 && DS1302_Time[0]%100 !=0 ) || DS1302_Time[0]%400==0 )
{
if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
}
if(DS1302_Time[3]<0){DS1302_Time[3]=23;}
if(DS1302_Time[4]<0){DS1302_Time[4]=59;}
if(DS1302_Time[5]<0){DS1302_Time[5]=59;}
}
//修改完成后进行更新显示
//只有选中并且TimeSetFlashFlag等于0的时候才会清空
//其他就显示 这就会实现闪烁
if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1," ");}
else{LCD_ShowNum(1,1,DS1302_Time[0],2);}
if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4," ");}
else{LCD_ShowNum(1,4,DS1302_Time[1],2);}
if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7," ");}
else{LCD_ShowNum(1,7,DS1302_Time[2],2);}
if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1," ");}
else{LCD_ShowNum(2,1,DS1302_Time[3],2);}
if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4," ");}
else{LCD_ShowNum(2,4,DS1302_Time[4],2);}
if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7," ");}
else{LCD_ShowNum(2,7,DS1302_Time[5],2);}
/*LCD_ShowNum(2,10,TimeSetSelect,2);
LCD_ShowNum(2,13,TimeSetFlashFlag,2);
/*LCD_ShowNum(1,1,DS1302_Time[0],2);
LCD_ShowNum(1,4,DS1302_Time[1],2);
LCD_ShowNum(1,7,DS1302_Time[2],2);
LCD_ShowNum(2,1,DS1302_Time[3],2);
LCD_ShowNum(2,4,DS1302_Time[4],2);
LCD_ShowNum(2,7,DS1302_Time[5],2);
*/
}
void main()
{
LCD_Init();
DS1302_Init();
Timer0Init();
LCD_ShowString(1,1," - - ");
LCD_ShowString(2,1," : : ");
DS1302_SetTime();
while(1)
{
KeyNum=Key();
if(KeyNum==1)
{
if(MODE==0){MODE=1;}
else if(MODE==1){MODE=0;DS1302_SetTime();}
}
switch(MODE)
{
case 0:TimeShow();break;
case 1:TimeSet();break;
}
}
}
//
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;//内部静态局部变量
TH0=0xfc;//中断之后值会溢出 溢出就变成0
TL0=0X66;//所以我们要重新赋初值
T0Count++;
if(T0Count>=500)
{
T0Count=0;
TimeSetFlashFlag=!TimeSetFlashFlag;//这是逻辑取反! 不是按位取反~
//相当于把0变成1 1变成0
}
}
2.蜂鸣器

🔍 核心概念
蜂鸣器是一种电信号转声音信号 的器件,在电子设备中常用来发出按键音、报警音、提示音等信号,是嵌入式开发中最常见的输出元件之一。
📊 有源 vs 无源蜂鸣器对比
这里的 "源" 指的是振荡源,而非电源,这是两者最核心的区别:
| 特性 | 有源蜂鸣器 | 无源蜂鸣器 |
|---|---|---|
| 内部结构 | 自带振荡电路 | 仅发声组件,无振荡电路 |
| 驱动方式 | 直接接直流电压即可发声 | 需要控制器提供 2K~5KHz 的方波 / PWM 信号 |
| 声音特性 | 频率固定,音调不可调 | 可通过调整信号频率发出不同音调 |
| 控制难度 | 简单,仅需 GPIO 高低电平控制 | 稍复杂,需要定时器 / PWM 支持 |
| 典型应用 | 固定报警音、提示音 | 播放简单音乐 |
51单片机上的是无源蜂鸣器(用的是集成电路驱动 也是就是 五线四项步进电机)
注意 不能用无源蜂鸣器始终通电!


一个周期需要翻转两次也就是翻转频率是音符频率的两倍,所以周期是二分之一
如果板子是11.0592的调音不准的,自己搞重载装值的时候高位不要四舍五入,这个会导致较大音
11.0592的:周期/2*11.0592/12,再取整求重装载值差
3.AT24C02(I2C总线---这里的2是平方)
一、存储器分类详解
存储器整体分为易失性存储器 (RAM) 、** 非易失性存储器 (ROM)** 两大分支,核心区别:断电后 RAM 数据丢失,ROM 数据永久保存✅
易失性存储器(RAM 随机存取存储器,断电丢数据)
1. SRAM(静态随机存储器)
- 原理:由触发器电路存储数据,无需刷新电路
- 特点:读写速度极快、功耗高、集成度低、成本贵
- 典型应用:CPU 内部高速缓存 L1/L2 Cache
2. DRAM(动态随机存储器)
- 原理:电容存储电荷保存数据,电容漏电,需周期性刷新
- 特点:容量大、成本低、速度低于 SRAM
- 典型应用:电脑内存条、手机运行内存
非易失性存储器(ROM 类,断电数据保留)
1. Mask ROM(掩膜 ROM)
出厂时工厂光刻固化程序,用户无法改写 ,多用于大批量固定固件设备。
2. PROM(一次性可编程 ROM)
用户可通过专用设备单次烧录,烧录后不能修改,出错即报废。
3. EPROM(紫外线擦除 ROM)
芯片带石英窗口,紫外线照射全片擦除,可反复编程,拆装擦除不便,现已淘汰。
4. E²PROM(电可擦除 ROM)
电信号按字节擦写 ,无需拆芯片,可在线修改少量数据,多用于设备配置参数存储(如单片机小配置)。
5. Flash(闪存)
块擦写结构、大容量、低成本,是现在主流非易失芯片:
- NAND Flash:U 盘、固态硬盘 SSD、手机机身存储
- NOR Flash:单片机固件、BIOS 芯片
6. 硬盘 / 软盘 / 光盘
机械式大容量存储介质:
- 机械硬盘 HDD:磁盘磁头结构;光盘:光学读写;软盘已基本淘汰。
💡核心区分速记
| 类型 | 断电数据 | 读写特性 |
|---|---|---|
| SRAM/DRAM(RAM) | 丢失 | 任意地址随机读写 |
| ROM 全系列 / 闪存 / 硬盘 | 保留 | ROM 早期只读,Flash 可擦写 |

这里理解成,竖线在横线的上面,有数据的地方就在横线和竖线的那个点接一个二极管
二极管是单向的,可以从地址总线上来,但是不能从数据总线下去,这样数据总线的高电平就不会影响到其他行了
二、AT24C02 芯片解析
基础参数信息
- 存储类型 :\(E^2PROM\)(电可擦除 ROM,非易失存储器,断电数据永久保存,和上一张思维导图的\(E^2PROM\)对应)
- 存储容量 :256Byte(2Kbit,命名中
02代表 2K 比特,\(2048÷8=256\)字节) - 通信协议 :标准I2C 串行总线,仅 SDA、SCL 两根信号线即可和单片机通信
- 封装:常用 DIP-8 直插、SOP-8 贴片两种封装(图中两种实物)


8 个引脚功能详解
| 引脚号 | 引脚名 | 功能说明 |
|---|---|---|
| 1 | A0 | 设备地址引脚,I2C 从地址配置端 |
| 2 | A1 | 设备地址引脚,搭配 A0/A2 可挂载最多 8 片同型号芯片到同一条 I2C 总线 |
| 3 | A2 | 设备地址引脚 |
| 4 | GND | 电源地 |
| 5 | SDA | I2C 串行数据线,双向收发数据 |
| 6 | SCL | I2C 串行时钟线,由主控单片机提供时钟信号 |
| 7 | WP | 写保护引脚:接 VCC 时芯片只读(禁止写入),接 GND 时可正常读写 |
| 8 | VCC | 电源正极,工作电压常见 2.7V~5.5V,适配 51/STM32 单片机 |
核心用途💡
- 单片机参数掉电保存:保存设备配置参数、计数值、密码、校准数据(如温控阈值、遥控器配对码)
- 小容量数据存储:容量小,不存大量文件,是嵌入式最常用的小容量掉电存储器件
- 拓展:同系列还有 AT24C04/08/16...,后缀数字越大容量越高,通信协议完全兼容 I2C
三、I2C 总线 + 电路规范
I2C 总线基础概述

1. 来源与硬件组成
I2C(Inter IC BUS)由飞利浦公司研发,仅 SCL(时钟线)、SDA(数据线)两根信号线即可完成多设备通信。
- SCL:串行时钟,由主机(单片机)输出时序,管控传输节奏
- SDA:串行数据,双向半双工传输数据
2. 三大通信特性
- 同步通信:依靠 SCL 时钟电平同步收发时序;
- 半双工:同一时刻 SDA 只能单向收发数据;
- ACK 应答:每传输 1 字节数据,接收方回复应答信号,保障传输可靠。
3. 协议优势
- 芯片厂商:统一通信标准,省去自定义协议开发,缩短芯片设计周期、提升稳定性;
- 开发人员:一套 I2C 驱动兼容多款外设,不用学习零散私有协议,降低开发难度。
4. 图中三款典型 I2C 外设
- OLED 显示屏:I2C 收发显示数据,输出文字 / 数字;
- DS3231 时钟模块:存储年月日时分秒,I2C 读写实时时间;
- MPU6050 姿态传感器:采集加速度、陀螺仪数据。
I2C 硬件电路规范

1. 总线接线规则
- 总线并联拓扑:所有挂载 I2C 外设的 SCL 引脚全部接在一起、SDA 引脚全部接在一起,共用主机 SCL/SDA;
- 端口模式 :全部 I2C 设备的 SCL、SDA 引脚配置为开漏输出(对应右侧 MOS 管结构图,输出管下端接地);
- 上拉电阻配置 :SCL、SDA 总线分别外接4.7KΩ 上拉电阻到电源 VDD。
2. 开漏 + 上拉电阻的原理💡
- 线与逻辑:开漏结构只能拉低总线电平,无法主动拉高;空闲时依靠上拉电阻把 SCL/SDA 拉为高电平;
- 多设备防干扰:任意设备拉低总线,整条总线电平变低;所有设备释放总线,上拉电阻统一拉高电平,完美解决多器件总线竞争、互相干扰的问题。
四、I2C时序结构 重点
I2C 起始 (S)、停止 (P) 时序

起始信号 S(左图)
- 时序规则 :
SCL保持高电平时,SDA 电平从高 → 低跳变 - 作用:主机发起通信,通知总线上所有从设备准备接收数据,标志一次 I2C 传输正式开始
- 时序要点:SCL 不能变低,只有 SCL 高电平期间的 SDA 下降沿才是合法起始
终止信号 P(右图)
- 时序规则 :
SCL保持高电平时,SDA 电平从低 → 高跳变 - 作用:主机结束本次通信,释放 I2C 总线,总线回到空闲高电平状态
- 时序要点:同样必须在 SCL 高电平下完成 SDA 上升沿
五、I2C 字节收发时序📌
主机发送 1 字节

时序规则
- SCL 低电平阶段 :主机更改 SDA 电平,把数据位放到数据线,高位 B7 先发、低位 B0 最后发;
- SCL 拉高电平阶段 :SDA 电平必须保持不变,从机在 SCL 高电平时采样读取当前 SDA 数据;
- 重复以上 8 次,依次送出 B7~B0,完整传输 1Byte(8 位数据)。
核心约束:SCL = 高时 SDA 禁止跳变,防止从机采样出错。
主机接收 1 字节

时序规则
- 接收前置操作:主机提前释放 SDA 引脚(开漏模式,交出 SDA 控制权,由从机驱动电平);
- SCL 低电平阶段 :从机修改 SDA 电平,逐位输出数据,依旧高位 B7 优先输出;
- SCL 拉高电平阶段:SDA 电平锁定不变,主机在高电平读取 SDA 上的数据;
- 循环 8 次,主机读完 B7~B0,完成单字节接收。
收发共同点 & 关键区别
| 项目 | 主机发数据 | 主机收数据 |
|---|---|---|
| 数据驱动方 | 主机控制 SDA 电平 | 从机控制 SDA 电平 |
| SDA 控制权 | 主机占用 | 主机提前释放 |
| 传输顺序 | 固定高位 (B7)→低位 (B0) | 固定高位 (B7)→低位 (B0) |
| 采样时刻 | 从机在 SCL 高电平读 | 主机在 SCL 高电平读 |
| 电平约束 | SCL 高电平 SDA 不许变化 | SCL 高电平 SDA 不许变化 |
六、I2C 应答(ACK/NACK)时序

SA:主机发送应答(左图,主机收完 1 字节后执行)
使用场景:主机接收从机发来的 1Byte 数据之后,由主机发出 1 位应答位
- 时序规则:SCL 时钟脉冲期间,主机主动控制 SDA 电平
- SDA 拉低 = 0 → ACK 应答:告诉从机,本机已收到数据,请继续发送下一字节
- SDA 拉高 = 1 → NACK 非应答:告诉从机,不再接收后续数据,准备结束通信
- 时序特征:SCL 高电平时 SDA 保持既定电平,和字节收发电平约束一致。
RA:主机接收应答(右图,主机发完 1 字节后执行)
使用场景:主机向从机发送 1Byte 数据之后,主机读取从机回复的应答
- 前置操作:主机提前释放 SDA 引脚(让出总线控制权,由从机驱动 SDA 电平)
- 时序规则:SCL 拉高采样 SDA
- SDA=0 → 从机 ACK:从机正常收下数据,可继续下发数据
- SDA=1 → 从机 NACK:从机未收到 / 无法接收,通信异常或终止
- 电平特征:SDA 电平由从机驱动,主机只做读取。
两种应答区别汇总
| 标识 | 操作方 | 时机 | SDA 控制权 | 电平含义 |
|---|---|---|---|---|
| SA 发送应答 | 主机 | 主机收完数据后 | 主机掌控 SDA | 0=ACK 继续读;1=NACK 停止读 |
| RA 接收应答 | 从机 | 主机发完数据后 | 从机掌控 SDA | 0 = 从机收下;1 = 从机拒收 |
结合 AT24C02 实例
- AT24C02 写流程应答 起始→主机发器件地址 (RA 读从机 ACK)→发存储地址 (RA 读 ACK)→发写入数据 (RA 读 ACK)→停止 P
每次主机发完字节,都 RA 接收从机应答,确认芯片正常就绪。
- AT24C02 读流程应答 重起始→器件地址 + 读→主机逐个接收存储数据 (SA 发应答:多字节读时 ACK,最后 1 字节 NACK)→停止 P
主机读完一个数据,SA 发 ACK = 继续读下一个;读完最后 1 字节 SA 发 NACK = 终止读取。
七、I2C【主机写入数据帧】笔记📝
整帧传输顺序(从左→右)
{S → 从机地址+W → RA(0) → BYTE1 → RA(0) → BYTE2 → RA(0) ...→ BYTEn → RA(0) → P}

1. S:起始信号
SCL 高电平,SDA高→低,开启本次 I2C 通信。
2. SLAVE ADDRESS+W(8 位从机寻址字节)
- (A6~A0)(高 7 位):从机硬件地址,用来选中总线上指定外设(AT24C02 靠 A0/A1/A2 引脚修改该地址)
- (R/W)(最低位):写标志位,代表主机要向从设备写入数据
功能:确定「要写给哪个芯片」
3. RA:接收应答(主机接收从机应答)
主机发完 1 字节数据后释放 SDA,读取从机电平:
- RA=0 →ACK 应答:从机拉低 SDA,正常收到数据,继续传输(图中全为 0,代表设备正常就绪)
- RA=1→ NACK 非应答:从机无响应,主机可直接发停止 P 结束通信
4. S:BYTE1~BYTEn:主机连续发送数据字节
主机占用 SDA 逐字节下发数据,每发送完 1 个字节,必须等待 1 次从机 RA 应答,收到 ACK 才继续发送下一字节;可无限拼接多字节。
AT24C02 场景:第 1 个 BYTE = 芯片内部存储地址,后续 BYTE = 需要存入的有效数据。
5. P:停止信号
全部数据发送完成,SCL 高电平,SDA低→高 ,释放 I2C 总线,本次写传输结束。
一句话总结
S + 从机地址 (W):选定目标设备;后续 BYTE:下发写入的数据,实现「指定从机 + 批量写入」。
八、I2C接收一帧(读帧)
🔹整帧链路(接收一帧完整流程)
S → 从机地址 + R → RA (0) → BYTE1 + SA (0) → BYTE2 + SA (0) ... → BYTEn + SA (1) → P
一句话任务:向指定从机,读取一串数据

🔹逐段拆解
-
S 起始:开启整帧读取 SCL 高、SDA 高→低,开启一次 I2C 通信。
-
从机地址 + R(寻址字节) A6~A0=7 位设备地址(选定要读的芯片) R/W=1 = 读指令 :通知从机,主机接下来要读你数据 → 完成:确定「向谁读」
-
RA (0) 接收应答(从机回复) 主机发完地址,松开 SDA,从机拉低 SDA=RA=0 (ACK) 含义:从机收到地址,准备往外发数据;无应答直接终止帧。
-
BYTE1~BYTEn + SA 主机接收 + 主动应答【读帧核心】
- 数据方向:从机发数据、主机收数据
- SA 是主机发送应答(读帧独有) ✅ 中间 BYTE:SA=0 (ACK) → 主机:数据收到,麻烦继续发下一个 ✅ 最后 1 个 BYTEn:SA=1 (NACK) → 主机:全部收完,不要再发了
- P 停止:结束本帧读取 SCL 高、SDA 低→高,整帧接收完毕、释放总线。
🔹关键区分(写 / 读帧)
✅ 写帧(发送一帧):W=0,应答全 RA(主机收从机信号),任务:给谁发数据 ✅ 读帧(接收一帧):R=1,应答全 SA(主机发信号给从机),任务:向谁收数据
🔹AT24C02 读一帧实操
- 先写寻址(不发 P):S + 器件 W 地址 + ACK + 内存地址 + ACK
- 重启 S + 读帧:S + 器件 R 地址 + ACK→依次读数据→最后 NACK→P,完整读完一帧数据
九、I2C 复合帧(先发后收|AT24C02 随机读专用)笔记📝
🔹整帧分为上下两段:【上段写帧 + 下段读帧】,中间不停 P,下段重新发起始 S
第一段:写入帧(用来指定要读取的内存地址)
链路:S → 从机地址+W → RA(0) → BYTE1 → RA(0) ...→ BYTEn → RA(0)

S:起始信号,开启通信从机地址+W:7 位硬件地址 +\(\boldsymbol{R/\overline{W}=0}\)写标志,选定芯片、告知接下来下发地址RA=0:从机 ACK 应答,每发完 1 字节主机都接收从机应答- BYTE 内容:BYTE1 = AT24C02 片内存储地址(核心:把要读的位置写给芯片,实现寻址)
✅ 本段结束不发停止 P,总线保持占用
第二段:读取帧(重起始,读取指定地址的数据)
链路:S → 从机地址+R → RA(0) → BYTE1+SA(0) ...→ BYTEn+SA(1) → P
- 新
S:重复起始信号(重起始),不用释放总线,直接切换读写方向 从机地址+R:7 位地址不变、\(\boldsymbol{R/\overline{W}=1}\)读标志,通知从机准备往外发数据RA=0:从机应答,准备输出数据- 数据交互:从机发数据、主机收数据
- 中间字节:
SA=0(ACK)→ 主机:继续发下一个数据 - 末尾字节:
SA=1(NACK)→ 主机:数据收完,停止发送
- 中间字节:
P:停止信号,整帧复合传输结束
🔹一句话功能
先发地址定位存储位置,再回头读该地址数据 = AT24C02 随机读 任务概括:确定向哪个芯片、读取哪个指定地址的数据
十、AT24C02 字节写 + 随机读笔记📝
字节写(单字节存入指定地址)
传输帧顺序
S → 从机地址 + W → RA (0) → WORD ADDRESS (片内地址) → RA (0) → DATA (写入数据) → RA (0) → P

- S:起始信号,开启 I2C 通信
- SLAVE ADDRESS+W:器件地址 + 写位 (W=0),本板硬件地址:固定 1010+A0A1A2=000 → 二进制
1010000,加写 0 → 0xA0 - RA (0):从机 ACK 应答,代表收到数据
- WORD ADDRESS:AT24C02 内部存储地址(0~255,共 256 字节空间)
- DATA:要存入该地址的有效数据
- P:停止信号,本次写入结束
功能:在【指定片内地址】存入 1 字节数据
随机读(读取指定地址里的数据,复合帧结构)
两段式帧结构(上段写地址,下段重读数据,中间不发 P,重发起始 S)
第一段(伪写,只发地址不存数据): S → 从机地址 + W → RA (0) → WORD ADDRESS → RA (0) 第二段(重读): S (重起始) → 从机地址 + R → RA (0) → DATA → SA (1) → P
- 第一段作用:把要读取的内部地址发给 AT24C02,指针定位到目标位置
- 重起始 S:不释放总线,直接切换读模式
- SLAVE ADDRESS+R:R=1 读标志,地址 0xA1
- RA (0):从机应答,准备输出数据
- DATA:从机传回目标地址存储的数据
- SA (1):主机 NACK 非应答,告诉芯片读完了
- P:停止,读取结束
功能:定位指定地址,读出该地址存储的数据
I2C 四类数据帧拟人化笔记(小明 = 主机 (单片机)、老师 = 从机 (AT24C02))
1、第一类:【主机写帧|小明给老师递纸条(单字节写入)】
流程:S→老师编号 + W→老师回话 (RA=0)→纸条页码→老师回话→纸条内容→老师回话→P
-
S (起始):小明敲桌,示意「老师我要递消息」
-
设备地址 + W:小明报老师专属编号 +"我要写给你",点名锁定这位老师
-
RA=0:老师点头(ACK):收到编号,等着收内容
-
WORD ADDRESS:小明先说本子页码(要写在哪一页),老师再次点头确认
-
DATA:小明写上要存的文字内容,老师点头收下
-
P (停止):小明收笔,本次投递结束。
任务:小明在老师笔记本【指定页码】写下内容
2、第二类:【主机读帧|小明找老师要单页内容(纯读)】
流程:S→老师编号 + R→RA=0→老师发数据→小明回话 SA→末尾 SA=1→P
- S:小明敲桌,发起问话
- 地址 + R:报编号 +"我要向你借书里内容"
- RA=0:老师点头就绪
- BYTE:老师翻页读出一页文字、逐条念给小明
- SA=0:小明听完一句点头:继续往下读(中间数据)
- SA=1:小明听完最后一句摆手:不用再读了(末尾 NACK)
- P:谈话结束
任务:小明向老师索要现成数据(需要提前已经定好页码)
3、第三类:【复合随机读帧|小明先报页码、再找老师要内容(AT24C02 随机读)】
两段对话:先发地址 (问页码) + 重起始再读内容 (要正文) ① 上段(伪写,只报页码不写字) S→编号 + W→RA=0→页码→RA=0:小明:老师,我想看 XX 页码;老师点头记下页码,不结束对话、不离开座位(无 P) ② 下段(重起始,重新问话借书) S (再次敲桌)→编号 + R→RA=0→老师读内容→SA=1→P:小明:现在把这页内容读给我;老师读完,小明摆手结束聊天。
任务:先指定页码,再读取该页码内容(AT24C02 最常用随机读)
4、补充总规则(应答区分)
✅ RA(接收应答):老师说话 (小明发完信息,老师点头 / 摇头回复),写帧全程用 RA ✅ SA(发送应答):小明说话(老师发完信息,小明点头 / 摆手回复),读帧全程用 SA
- ACK=0 = 点头:收到、继续
- NACK=1 = 摆手:结束、别发了
精简口诀
写是小明递纸条,全程老师来回话; 读是老师念课文,全程小明定收尾; 随机读要先报页,重读之后再借书。
AT24C02 字节写、随机读笔记(对标两张时序图)

一、字节写(定点写入 1 字节)
完整时序链路
起始条件 → 器件地址 (写 W=0) → 从机 ACK → 字地址 → 从机 ACK → 写入数据 → 从机 ACK → 停止条件
- 起始:SCL 高、SDA 高→低,开启通信
- 器件地址:7 位芯片地址 + 最低位 0 (写指令),选中 AT24C02
- LACK(RA 应答,从机发):AT24C02 收到数据,拉低 SDA=ACK
- 字地址:8 位地址,选定芯片内部要写入的存储位置 (0~255)
- 写入数据:主机发送要保存的 1 字节数据
- 停止:SCL 高、SDA 低→高,结束本次写操作
图缩写释义
MB = 主机输出 (SDA);LACK = 从机应答;全程从机回复 ACK
二、随机读(指定地址读取 1 字节,复合帧)
整段分前段伪写寻址 + 后段重读数据,中间无停止 P、重发起始
① 前段(只发地址,不存数据)
起始 → 器件地址 (W=0) → ACK → 字地址 → ACK 作用:把要读的地址发给 AT24C02,内部存储指针跳到目标位置
② 后段(重复起始,切换读模式)
重复起始 → 器件地址 (R=1) → ACK → 从机出数据 → 主机 NACK → 停止
- 重复起始:不释放总线,直接二次起始信号
- 器件地址 R=1:读指令,通知芯片向外发数据
- 数据:AT24C02 输出目标地址存储内容
- NACK:主机拉高 SDA (SA=1),告诉从机读完、不用继续发
- 停止:结束读通信
关键特点
- 前段全是从机 ACK (RA) ,后段末尾主机 NACK (SA)
- 随机读核心:先写地址定位置,重读拿数据
三、读写规律总结
- 写操作:主机发全部数据,每次由从机回复 ACK
- 读操作:从机发数据,最后主机发 NACK 终止传输
- AT24C02 器件地址:固定 1010+A2A1A0,写 0xA0、读 0xA1
- !!!!将I2C_ReceiveACK();ACK=I2C_ReceiveACK();合并为ACK=I2C_ReceiveACK();!!!!!
4.DS18B02(温度传感器)
4.1 概念
一、基础概述
DS18B20 是 Maxim(原 Dallas)出品的单总线数字温度传感器,区别于热敏电阻、热电偶这类模拟传感器,数据与指令全部以数字信号传输,硬件接线精简、抗干扰优异,是嵌入式测温最常用器件之一。

二、核心参数
| 项目 | 参数详情 |
|---|---|
| 测温区间 | -55℃ ~ +125℃;常温(-10~85℃)精度 ±0.5℃ |
| 通信协议 | 1-Wire 单总线,单根数据线完成双向收发 |
| 分辨率 | 可编程 9~12 位(12 位时最小分度 0.0625℃) |
| 供电方式 | ①外部供电 3.0~5.5V;②寄生供电(仅 DQ+GND 两根线) |
4.2 引脚
一、引脚功能汇总

| 引脚 | 额定电压 | 功能说明 |
|---|---|---|
| VDD | 3.0~5.5V | 器件工作电源正极,外部供电模式接入电源,寄生供电可悬空 |
| GND | 0V | 电源参考地,必须和主控系统共地 |
| DQ | 单总线电平 | 双向数据通信引脚,单片机读写温度、指令交互的唯一信号线 |

二、图中外部供电电路解析
1. 接线逻辑
- 引脚 1(GND):直接连接系统地 GND;
- 引脚 3(VDD):直接接系统电源 VCC,为芯片提供工作电压;
- 引脚 2(DQ):一路引出作为总线端口
DQ接单片机 IO 口,同时通过 4.7KΩ 上拉电阻 R1 接到 VCC。
2. 4.7K 上拉电阻作用💡
- 空闲电平钳位:总线无数据传输时,电阻把 DQ 拉至高电平,保证总线默认处于空闲;
- 驱动能力补充:DS18B20 只能拉低总线、无法主动拉高,单片机释放引脚后由电阻拉高电平,满足单总线时序电平规范;
- 多器件挂载:同一根 DQ 总线上挂多个传感器时,统一依靠单个上拉电阻完成电平恢复。
4.3 内部结构
一、左侧供电电路模块(寄生电源电路 PARASITE POWER CIRCUIT)

组成:二极管、储能电容(C_{PP})、电源检测(POWER-SUPPLY SENSE)
- 寄生供电模式:VDD 悬空,依靠 DQ 总线高电平通过二极管给\(C_{PP}\)充电储能,测温转换时电容放电给芯片供电,实现仅两根线(DQ+GND)工作。
- 外部供电模式:VDD 接 3.3~5V 电源,电源检测电路识别外部供电后关闭寄生取电回路,工作稳定性更高。
- DQ 外接 4.7K 上拉电阻((V_{PU})),是单总线电平基准。
二、64-BIT ROM + 1-WIRE PORT(单总线 ROM 模块)
- 每个传感器内置全球唯一 64 位固化 ROM 编码:前 8 位家族码(固定 0x28)+48 位器件序列号 + 8 位 CRC 校验码。
- 作用:一条总线挂载多片 DS18B20 时,单片机通过 ROM 寻址命令选中指定传感器,实现多点测温。
三、暂存器 SCRATCHPAD + 后端功能寄存器(核心数据区)
由存储器控制逻辑(MEMORY CONTROL LOGIC)统筹读写,分为高速暂存 RAM 与非易失 EEPROM两部分
| 寄存器 | 存储介质 | 功能说明 |
|---|---|---|
| 温度传感器 TEMPERATURE SENSOR | 模拟测温单元 | 实时采集环境温度,转换后存入暂存器高低字节 |
| (T_H)高温报警阈值 | EEPROM | 断电数据不丢失,超限触发报警标记 |
| (T_L)低温报警阈值 | EEPROM | 同上,自定义低温警戒值 |
| 配置寄存器 CONFIG | EEPROM | 设置测温分辨率(9~12bit,对应精度 0.5~0.0625℃) |
| 8 位 CRC 生成器 | 硬件逻辑 | 对读取的温度数据做校验,防止总线传输出错 |
💡 关键逻辑:EEPROM 参数需要手动下发拷贝指令,才会加载到 SCRATCHPAD 暂存器参与工作;修改阈值后写入 EEPROM,掉电不会丢失配置。
四、数据流简要流程
- DQ 下发单片机指令 →1-WIRE 端口解析 →存储器控制逻辑;
- 温度传感器完成 A/D 转换→数据存入 SCRATCHPAD;
- 单片机读取暂存温度 + CRC 校验,同时可读写(T_H/T_L)、分辨率配置;
- 温度超出(T_H/T_L)时芯片内置报警标记,主机可批量巡检总线上所有异常传感器。
4.4 单总线
1-Wire 单总线协议
一、基础定义
单总线(1-Wire BUS)由原 Dallas(现并入 Maxim)公司推出的串行通信总线,仅一根 DQ 信号线完成双向数据交互,是 DS18B20、DS2431 等器件的标准通信协议。

核心特性
- 物理接线
- 常规外部供电:**
DQ + VDD + GND**三线; - 寄生供电模式 :省略 VDD,仅
DQ+GND两根线,DQ 既传输数据又分时给从机供电,布线最简。
- 常规外部供电:**
- 通信制式 :异步半双工通信,主从架构(单片机为主机,DS18B20 为从机),单线分时收发数据,依靠严格电平延时区分 0、1、复位时序。
- 总线拓扑 :单根 DQ 总线可并联数十个挂载带唯一 64 位 ROM 的从设备,依靠 ROM 地址点对点寻址。
二、时序分段(主机 + 从机动作拆分)

1. 主机发送复位脉冲(黑色波形:主机拉低总线)
主机主动将 DQ 总线拉低电平,持续时间 ≥480μs,完成总线复位,清空总线状态。
2. 释放总线,从机等待窗口期
主机释放 DQ 引脚(总线依靠 4.7K 上拉电阻回到高电平),之后等待 15~60μs; 挂载在总线上的 DS18B20 从机在此区间内检测复位信号,准备应答。
3. 从机发送存在应答脉冲(灰色波形:从机拉低总线)
满足等待时间后,在线的 DS18B20 把 DQ 总线拉低,持续 60~240μs,用来通知主机:设备在线、通信就绪。
4. 从机释放总线
应答结束后从机释放 DQ,上拉电阻重新拉高总线,一次初始化时序完成,主机即可下发读写指令。
图例释义:
- 黑线:主机拉低总线 ;灰线:DS18B20 从机拉低总线 ;细线:上拉电阻维持总线高电平
三、发送一位bit位

一、写 0 时序(MASTER WRITE "0" SLOT)
- 主机动作 :起始瞬间将 DQ 总线拉低,低电平保持 60~120μs后释放总线,由上拉电阻恢复高电平。
- 从机采样 :从机在总线被拉低后约 30μs(典型采样点)读取电平 ,检测为低电平即判定主机下发数据
0。 - 时隙总要求 :单次完整写时隙时长必须>60μs,相邻两位指令间隔无强制下限。
二、写 1 时序(MASTER WRITE "1" SLOT)
- 主机动作 :起始瞬间拉低 DQ,仅保持 1~15μs 就立刻释放总线,总线快速被上拉电阻抬为高电平。
- 从机采样 :同样在拉低后 30μs 左右采样 ,此时总线已经变回高电平,判定下发数据
1。 - 时隙总要求:和写 0 一致,整段时隙时长>60μs。
三、关键细节
| 类型 | 主机拉低时长 | 从机采样时刻 | 电平判定结果 |
|---|---|---|---|
| 写 0 | 60~120μs | 拉低后 30μs | 低电平→0 |
| 写 1 | 1~15μs | 拉低后 30μs | 高电平→1 |
💡 核心原理:从机统一在下降沿后 30μs采样,依靠主机拉低的长短区分 0/1;全时隙 > 60μs 是为保证从机完成采样、电平稳定。
四、编程实操要点⚠️
- 单片机 IO 输出低电平后,严格卡准延时,写 0 不能短于 60μs、写 1 不能超过 15μs;
- 写完 1bit 后剩余时间补足至 60μs 以上,保证时序合规;
- 一字节(8bit)从最低位 LSB 开始逐位发送。
四、接收一位bit位

一、统一起始动作
主机拉低 DQ 总线 1~15μs 后立刻释放,作为读时隙起始触发信号;从机收到下降沿后,在接下来很短时间内把待发送的 0/1 电平驱动到总线上。
二、读 0、读 1 电平区分
- 读 0(MASTER READ "0" SLOT) 从机持续拉低总线 ,在主机采样窗口内总线保持低电平,主机读取到低电平 → 收到数据
0。 - 读 1(MASTER READ "1" SLOT) 从机不主动拉低总线 ,总线依靠 4.7K 上拉电阻恢复高电平,主机采样时为高电平 → 收到数据
1。
三、采样关键规则💡
- 采样窗口:总线拉低后的 15μs 之内(尽量靠近 15μs 末端),是主机读取电平的最佳时间点;
- 时隙总长:单个读位完整周期必须大于 60μs,剩余时长用来等待总线电平稳定,再开启下一位读写。
四、时序参数表格
| 项目 | 参数要求 |
|---|---|
| 主机拉低总线 | 1~15μs |
| 主机采样区间 | 下降沿起 0~15μs(推荐 12~15μs) |
| 单时隙总时长 | >60μs |
五、编程注意事项⚠️
- 主机拉低引脚后,需要精准延时再读 IO,过早 / 过晚采样都会读错电平;
- 读取一字节时,从最低位 (LSB) 开始逐位读取 8 次,最后拼接成完整字节;
- 读时序和写时序不能混用延时参数。
五、发送一个字节 接受一个字节

一、发送一字节(S:BYTE)
- 实现逻辑 :循环连续调用 8 次写 1 位时序函数,完成 1 字节 8 位数据发送。
- 位序规则:低位优先 先发最低位B_0(bit0)→依次{B_1、B_2...B_7}\),最后发送最高位{B_7}(bit7)。 例:发送
0x05(00000101),发送顺序:1→0→1→0→0→0→0→0。
二、接收一字节(R:BYTE)
- 实现逻辑 :循环连续调用 8 次读 1 位时序函数,依次采集 8 个 bit 拼成 1 字节。
- 位序规则:低位优先 最先收到的电平存入{B_0},后续依次(B_1 ~ B_7\),最终组合为完整字节。
三、应用场景
DS18B20 完整通信流程:初始化复位→发送 ROM 指令→发送功能指令→读取温度字节,全部依靠字节收发函数完成指令与温度数据交互。
单总线四层函数汇总
- 初始化函数(复位 + 存在脉冲)
- 写 1 位函数
- 写 1 字节(循环 8 次写位)
- 读 1 位函数
- 读 1 字节(循环 8 次读位)
七、操作流程
整体固定流程:初始化复位 → ROM 指令 → 功能指令,单次测温必须严格遵照顺序执行。

一、三步基础流程
- 初始化 主机发送复位时序,等待从机存在应答脉冲;无应答代表器件未挂载 / 故障,终止后续通信。
- ROM 操作(寻址环节) 下发 5 类 ROM 指令,用来选中总线上目标传感器(单设备 / 多设备区分)。
- 功能操作(寄存器 / 测温环节) 下发功能指令,实现启动测温、读写暂存器、参数拷贝等实际功能。
二、ROM 指令(5 条,设备寻址用)
| 指令码 | 指令名称 | 使用场景 |
|---|---|---|
0x33 READ ROM |
读 ROM | 单总线仅 1 片 DS18B20,直接读取 64 位唯一 ROM 地址 |
0x55 MATCH ROM |
匹配 ROM | 多设备组网,后跟 64 位地址,精准选中指定传感器 |
0xCC SKIP ROM |
跳过 ROM | 单设备最简测温 ,跳过寻址直接下发功能指令(最常用) |
0xF0 SEARCH ROM |
搜索 ROM | 自动遍历总线,扫描总线上全部设备 ROM 编号 |
0xEC ALARM SEARCH |
告警搜索 | 只筛选温度超(T_H/T_L)报警阈值的传感器 |
💡 日常单点测温首选:
0xCC(跳过ROM),省去 64 位地址写入,代码最简。
三、功能指令(6 条,芯片功能控制)
| 指令码 | 指令名称 | 功能说明 |
|---|---|---|
0x44 CONVERT T |
启动温度转换 | 触发 AD 测温转换,转换完成后温度存入暂存器 Byte0、Byte1; 寄生供电时转换期间需持续拉高总线 |
0xBE READ SCRATCHPAD |
读暂存器 | 连续读取 9 字节暂存数据(温度 + 阈值 + 配置 + CRC) |
0x4E WRITE SCRATCHPAD |
写暂存器 | 写入(T_H)、(T_L)、分辨率配置 3 个参数(Byte2~4) |
0x48 COPY SCRATCHPAD |
暂存→EEPROM | 把(T_H/T_L)配置从RAM 拷贝进 EEPROM ,实现参数掉电保存 |
0xB8 RECALL E2 |
EEPROM→暂存 | 从 EEPROM 回读参数加载到暂存器(芯片上电自动执行) |
0xB4 READ POWER SUPPLY |
读供电模式 | 查询当前器件是外部供电 / 寄生供电模式 |
八、DS1802数据帧

适用:单总线只挂载 1 片 DS18B20 ,全程使用
SKIP ROM(0xCC)跳过 ROM 寻址
一、第一段:温度转换(AD 采样,启动测温)
指令时序:初始化 → 发送 0xCC → 发送 0x44
- 主机执行单总线复位初始化,等待从机应答;
- 主机发送一字节
0xCC(SKIP ROM),跳过设备地址匹配; - 主机发送一字节0x44(CONVERT T),芯片启动内部 A/D 温度转换;
⏱ 转换耗时:12bit 分辨率最长 750ms,程序需延时等待转换完成,温度结果存入暂存器 Byte0、Byte1。
二、第二段:读取温度数据(读 9 字节暂存区)
指令时序:初始化 → 发送 0xCC → 发送 0xBE → 连续读 9 个字节
- 再次总线初始化复位;
- 再次发送
0xCC跳过 ROM; - 发送**
0xBE(READ SCRATCHPAD)读暂存指令**; - 连续调用 9 次读字节函数,依次读出: Byte0(温度低LSB) → Byte1(温度高MSB)
→ TH → TL → 配置寄存器 → 3个保留字节 → CRC校验
三、温度数值换算(12 位分辨率默认)
- 拼接数据:
temp = (MSB << 8) | LSB - 正数(MSB 高 5 位全 0):
实际温度 = temp × 0.0625 ℃ - 负数(MSB 高 5 位全 1):补码换算
temp = temp - 65536,再 ×0.0625
例:上电默认
MSB=0x05,LSB=0x50→ temp=0x0550=1360 → 1360×0.0625=85℃
九、温度存储格式(以补码的形式存储数据)
一、16 位温度数据位定义(LSB 低字节 + MSB 高字节)
1. LSB(低 8 位,Byte0)
| Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
|---|---|---|---|---|---|---|---|
| (2^3) | (2^2) | (2^1) | (2^0) | (2^{-1}) | (2^{-2}) | (2^{-3}) | (2^{-4}) |
小数位:Bit3~Bit0 对应 (0.5℃、0.25℃、0.125℃、0.0625℃),最小分度值 = 0.0625℃
2. MSB(高 8 位,Byte1)
| Bit7~Bit3 (共 5 位) | Bit2 | Bit1 | Bit0 |
|---|---|---|---|
| S (符号位) | (2^6) | (2^5) | (2^4) |
- S = 符号位:全 0 = 正温度;全 1 = 负温度(补码存储)
- Bit2~Bit0:整数高位 (64、32、16)
通用换算公式: 拼接 16 位数据
temp = (MSB << 8) | LSB实际温度 (T = temp × 0.0625℃)
二、正负温度换算规则
① 正温(MSB 高 5 位 = 00000)
直接T = temp × 0.0625
② 负温(MSB 高 5 位 = 11111,补码)
真实数值 = temp - 65536,再 ×0.0625