概述
在单片机应用系统中,串行通信总线威廉希尔官方网站 是非常重要的通信手段。常用的串行总线通信方式包括异步串行通信 UART、I2C(Inter IC BUS)、单总线(One WIRE BUS)以及 SPI 总线(Serial Peripheral Interface BUS)等。单片机的串口通信为 UART 的一种,DS18B20 的通信方式为单总线。采用 I2C 总线通信方式的常用器件包括 E2PROM 存储器件 AT24C01 以及 AD/DA 器件 PCF8951,这章内容主要讲解 I2C 总线通信工作原理并结合 AT24C01 进行应用试验。
13.1 I2C 总线通信原理
I2C(Inter-Integrated Circuit)总线是由 PHILIP S 公司开发的两线式串行通信总线,由于连接主机以及外围设备。两根数据线一个为时钟线 SCL,另一根为数据线 SDA,可实现数据的发送或接收。通常将 I2C 通信速率分为:低速模式 100Kbit/s、快速模式 400Kbit/s 以及高速模式 3.4Mbit/s,I2C 器件为向下兼容模式,一般所用 I2C 器件均支持低速模式。I2C 通信器件典型电路如下图所示:
如上图所示,在 I2C 总线上挂载多个外围器件,总线与电源之间配置了上拉电阻,使所有器件之间形成了“线与”的逻辑关系,任何一个器件将总线拉低,总线将保持低电平,因此任意一个器件都可以当成主设备或者从设备。
I2C 通信最底层的时序操作包含四种类型的信号,所用基于 I2C 总线的外围器件都是在这五种底层信号的基础上进行数据的读写,这五种信号分别是:
1) 起始信号;
2. 停止信号;
3. 写字节信号;
4. 读字节并发送应答信号;
5. 读字节并发送非应答信号。
13.1.1 I2C 通信起始、停止信号
起始信号,功能为通知 I2C 器件可以开始进行数据操作,操作时序为:当 SCL 为高电平时,SDA 由高电平向低电平跳变。停止信号,功能为通知 I2C 器件数据操作已结束,操作时序为:当 SCL 为高电平时,SDA 由低电平向高电平跳变。时序如下图所示:
I2C 起始信号、停止信号时序 C 语言函数如下所示:
void Delay_I2C(void)
{//延时函数,设置传输速率
_nop_();
_nop_();
_nop_();
_nop_();
}
//总线起始信号
void Start_I2C(void)
{
//SCL高电平期间,拉低SDA
SCL_I2C = 0;
SDA_I2C = 1;//在SCL低电平期间先将SDA拉高,为起始信号做准备
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SDA_I2C = 0;//拉低SDA,发送起始信号
Delay_I2C();
SCL_I2C = 0;
}
//总线停止信号
void Stop_I2C(void)
{
//SCL高电平期间,拉高SDA
SCL_I2C = 0;
SDA_I2C = 0;//在SCL低电平期间先将SDA拉低,为停止信号做准备
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SDA_I2C = 1;//拉高SDA,发送停止信号
Delay_I2C();
SCL_I2C = 0;
}
13.1.2 I2C 写字节信号
I2C 写字节信号,功能为向总线写入 1 字节的数据,操作时序下图所示:
在写入数据的过程中,数据顺序为从高位到低位,最先写入的数据为 bit7,依次到 bit0 共 8 位数据。如果接收器件收到了上述 1 字节的数据,会在 SCL 的第 9 个周期的高电平期间将 SDA 拉低为“0”,这个第 9 位数据称为应答位 ACK,作用为通知主机已经收到了 1 字节的数据。因此,在主机程序中通过 ACK 位判断 1 字节数据是否写入成功。在写数据的过程中要求,数据在 SCL 高电平期间要保持 SDA 数据稳定,在 SCL 低电平期间,SDA 可由高电平变为低电平或者低电平变为高电平,如下图所示。
I2C 写字节信号 C 语言函数代码如下图所示:
//I2C写入字节dat,返回应答信号
bit Wr_I2C(unsigned char dat)
{
bit ack; //存储应答位
unsigned char mask; //探测字节内某一位值的掩码变量
for(mask=0x80;mask!=0;mask >>=1)//从高位依次到低位
{
if((mask & dat)==0) SDA_I2C=0;
else SDA_I2C=1;
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SCL_I2C = 0; //完成一位的传送
}
SDA_I2C=1; //主机释放总线
Delay_I2C();
SCL_I2C = 1;
ack = SDA_I2C;//获取应答位
Delay_I2C();
SCL_I2C = 0;
return ack; //返回0写入成功,返回1写入失败
}
13.1.3 I2C 读字节并发送应答信号
I2C 读字节并发送应答信号时序与图 13-4 基本相同,只不过 bit7-bit0 由 I2C 从器件给出,在 SCL 高电平期间主机将数据读取,第 9 位应答信号 ACK 由主机给出,ACK 为“0”表示主机后续还要继续读取数据,为“1”时主机不再读取后续数据,可以结束通信。C 语言函数如下图所示:
//I2C读操作,并发送应答信号
unsigned char RdACK_I2C(void)
{
unsigned char mask; //探测字节内某一位值的掩码变量
unsigned char dat;
SDA_I2C=1;//确保主机释放SDA
for(mask=0x80;mask!=0;mask >>=1)//从高位依次到低位
{
Delay_I2C();
SCL_I2C = 1;
if(SDA_I2C==0) dat &= ~mask;//为0时,dat对应位清零
else dat |= mask;//否则置1
Delay_I2C();
SCL_I2C = 0;
}
SDA_I2C=0; //8位数据传送完后,拉低SDA发送应答信号
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SCL_I2C = 0;
return dat;
}
13.1.4 I2C 读字节并发送非应答信号
与读字节并发送应答信号相同,唯一的区别为主机发出非应答信号,即 ACK=1,主机不再读取后续数据,可以结束通信。C 语言函数如下图所示:
//I2C读操作,并发送非应答信号
unsigned char RdNAK_I2C(void)
{
unsigned char mask; //探测字节内某一位值的掩码变量
unsigned char dat;
SDA_I2C=1;//确保主机释放SDA
for(mask=0x80;mask!=0;mask >>=1)//从高位依次到低位
{
Delay_I2C();
SCL_I2C = 1;
if(SDA_I2C==0) dat &= ~mask;//为0时,dat对应位清零
else dat |= mask;//否则置1
Delay_I2C();
SCL_I2C = 0;
}
SDA_I2C=1; //8位数据传送完后,拉高SDA发送非应答信号
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SCL_I2C = 0;
return dat;
}
将上述 5 个底层 I2C 总线操作函数放到文件“Drive_I2C.c”以及“Drive_I2C.h”。
“Drive_I2C.h”完整代码如下:
#ifndef __I2C_H__
#define __I2C_H__
extern void Start_I2C(void); //起始信号
extern void Stop_I2C(void); //停止信号
extern unsigned char RdACK_I2C(void); //读字节并发送应答信号
extern unsigned char RdNAK_I2C(void); //读字节并发送非应答信号
extern bit Wr_I2C(unsigned char dat); //读字节信号
#endif
“Drive_I2C.c”完整代码如下:
#include< reg52.h >
#include< intrins.h >
sbit SCL_I2C = P2^0;//总线管脚定义
sbit SDA_I2C = P2^1;
void Delay_I2C(void)
{//延时函数,设置传输速率
_nop_();
_nop_();
_nop_();
_nop_();
}
//总线起始信号
void Start_I2C(void)
{
//SCL高电平期间,拉低SDA
SCL_I2C = 0;
SDA_I2C = 1;//在SCL低电平期间先将SDA拉高,为起始信号做准备
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SDA_I2C = 0;//拉低SDA,发送起始信号
Delay_I2C();
SCL_I2C = 0;
}
//总线停止信号
void Stop_I2C(void)
{
//SCL高电平期间,拉高SDA
SCL_I2C = 0;
SDA_I2C = 0;//在SCL低电平期间先将SDA拉低,为停止信号做准备
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SDA_I2C = 1;//拉高SDA,发送停止信号
Delay_I2C();
SCL_I2C = 0;
}
//I2C写入字节dat,返回应答信号
bit Wr_I2C(unsigned char dat)
{
bit ack; //存储应答位
unsigned char mask; //探测字节内某一位值的掩码变量
for(mask=0x80;mask!=0;mask >>=1)//从高位依次到低位
{
if((mask & dat)==0) SDA_I2C=0;
else SDA_I2C=1;
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SCL_I2C = 0; //完成一位的传送
}
SDA_I2C=1; //主机释放总线
Delay_I2C();
SCL_I2C = 1;
ack = SDA_I2C;//获取应答位
Delay_I2C();
SCL_I2C = 0;
return ack; //返回0写入成功,返回1写入失败
}
//I2C读操作,并发送非应答信号
unsigned char RdNAK_I2C(void)
{
unsigned char mask; //探测字节内某一位值的掩码变量
unsigned char dat;
SDA_I2C=1;//确保主机释放SDA
for(mask=0x80;mask!=0;mask >>=1)//从高位依次到低位
{
Delay_I2C();
SCL_I2C = 1;
if(SDA_I2C==0) dat &= ~mask;//为0时,dat对应位清零
else dat |= mask;//否则置1
Delay_I2C();
SCL_I2C = 0;
}
SDA_I2C=1; //8位数据传送完后,拉高SDA发送非应答信号
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SCL_I2C = 0;
return dat;
}
//I2C读操作,并发送应答信号
unsigned char RdACK_I2C(void)
{
unsigned char mask; //探测字节内某一位值的掩码变量
unsigned char dat;
SDA_I2C=1;//确保主机释放SDA
for(mask=0x80;mask!=0;mask >>=1)//从高位依次到低位
{
Delay_I2C();
SCL_I2C = 1;
if(SDA_I2C==0) dat &= ~mask;//为0时,dat对应位清零
else dat |= mask;//否则置1
Delay_I2C();
SCL_I2C = 0;
}
SDA_I2C=0; //8位数据传送完后,拉低SDA发送应答信号
Delay_I2C();
SCL_I2C = 1;
Delay_I2C();
SCL_I2C = 0;
return dat;
}
13.1.5 I2C 一次通信时序
所有基于 I2C 总线通信设备都是以上面 5 条最底层操作为基础的,完成一次完整的 I2C 通信时序如下图所示:
如图所示,一次完整的 I2C 总线通信至少包含起始信号 、一次字节读或写,或者多次读或写,以及停止信号。在起始信号与停止信号之间读或写的具体内容与 I2C 器件本身的上层通信协议有关。接下来我们将讲解基于 I2C 总线通信威廉希尔官方网站 的 E2PROM 存储器 AT24C01 的上层通信协议以及具体使用实例。
13.2 E2PROM 存储器 AT24C01 应用
AT24C01 是 Atmel 公司生产的一款 E2PROM 数据存储器,容量为 128 字节,具有掉电不丢失的功能。在单片机中也有存储器,一种为数据存储器 RAM,一种为程序存储器,在掉电的情况下 RAM 内的数据会丢失,而程序存储器一般不支持在线编程。然而在很多应用场合我们希望把运行过程中重要的数据存储下来,而在掉电的情况下数据不丢失。AT24C01 能满足这样的要求,它与单片之间通过 I2C 总线通信实现信息的交换。
下面介绍一下芯片管脚,A0~A2 为地址输入引脚,SDA、SCL 为 I2C 总线接口,VCC,GND 分别为电源和地,WP 为写保护管脚,当 WP 接高电平时,禁止外部对它进行写数据,只能读取它的数据。如上图所示,将 WP 接地,单片机即可对它进行读也可以写。
13.2.1 AT24C01 单字节写通信
AT24C01 写字节如下图所示:
如上图所示,AT24C01 的写字节时序步骤如下:
1)起始信号;
2. 写器件地址;
3. 写存储地址;
4. 写存储数据;
5. 停止信号。
当 I2C 总线上挂载多个从器件时,单片机通过器件地址来区别器件,那在我们开发板上的 AT24C01 的器件地址是多少呢?AT24C01 器件地址如下所示:
如上图所示,地址的高 4 位为“1010”固定值,即 0x5,后四位分别由 A2-A0、R/W 决定,在 RY-51 开发板上将器件的 A2-A0 都接地,如原理图所示,因此为“000”,最后一位为读写方向位,当 R/W=0 时,表示我们接下来写数据,当 R/W=1 时,表示我们接下来要读数据。很显然我们这里是要写数据,因此 R/W=0。合并起来,要写的地址为 0x50。第 3 步为写存储器地址,AT24C01 总共有 128 字节的存储器,它的地址分别为 0x00~0x80,因此可以选择任一地址存入数据。第 4 步为写存储数据,即为你写存储的 8 位数据。总结上述,往 AT24C01 写入一个字节数据函数如下所示:
//往AT24C01地址addr写入单字节数据dat
void WrByte_AT24C01(unsigned char addr,unsigned char dat)
{
Start_I2C();
Wr_I2C(0x50< < 1);//通知地址50的器件,接下来写操作
Wr_I2C(addr); //写入要操作的地址addr
Wr_I2C(dat); //向addr写入数据dat
Stop_I2C();
}
13.2.2 AT24C01 单字节读通信
随机读取 AT24C01 单字节通信如下图所示:
随机读取单字节数据通信协议如上图所示,步骤如下:
1) 起始信号
2. 写器件地址,方向为写;
3. 写存储器地址 addr;
4. 起始信号;
5. 写器件地址,方向为读;
6. 读单字节数据,并发送非应答信号
7. 停止信号。
第 1 步至第 3 步为告诉 AT24C01 我将从地址 addr 处读取数据,第 4 步到第 6 步为读取存储器地址 addr 处的数据,并告诉 AT24C01 后面不再继续读数据了,第 7 步结束本次通信。具体函数代码如下图所示:
//读取AT24C01存储地址addr处的数据
unsigned char RdByte_AT24C01(unsigned char addr)
{
unsigned char dat;
Start_I2C();
Wr_I2C(0x50< < 1);//通知地址50的器件,接下来写操作
Wr_I2C(addr); //写入要操作的地址addr
Start_I2C();
Wr_I2C((0x50< < 1)|0x01);//通知地址50的器件,接下来读操作
dat = RdNAK_I2C();//从地址addr读出数据,读出数据后不应答E2Prom
Stop_I2C();
return dat;
}
到这里我们便完成了对 AT24C01 单字节的读、写通信函数,按照惯例我们将函数封装到“Drive_AT 24C01.c”、“Drive_AT 24C01.h”,如下。
Drive_AT 24C01.h 代码:
#ifndef __AT24C01_H__
#define __AT24C02_H__
extern void WrByte_AT24C01(unsigned char addr,unsigned char dat);//写单字节
extern unsigned char RdByte_AT24C01(unsigned char addr); //读单字节
extern void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len);//写多字节
extern void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len);//读多字节
#endif
Drive_AT 24C01.c 代码:
#include< reg52.h >
#include"Drive_I2C.h"
//往AT24C01地址addr写入单字节数据dat
void WrByte_AT24C01(unsigned char addr,unsigned char dat)
{
Start_I2C();
Wr_I2C(0x50< < 1);//通知地址50的器件,接下来写操作
Wr_I2C(addr); //写入要操作的地址addr
Wr_I2C(dat); //向addr写入数据dat
Stop_I2C();
}
//读取AT24C01存储地址addr处的数据
unsigned char RdByte_AT24C01(unsigned char addr)
{
unsigned char dat;
Start_I2C();
Wr_I2C(0x50< < 1);//通知地址50的器件,接下来写操作
Wr_I2C(addr); //写入要操作的地址addr
Start_I2C();
Wr_I2C((0x50< < 1)|0x01);//通知地址50的器件,接下来读操作
dat = RdNAK_I2C();//从地址addr读出数据,读出数据后不应答E2Prom
Stop_I2C();
return dat;
}
//多字节写
void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
while(len > 0)//检测上一次是否完成所以数据写操作
{
while(1)
{//循环检测器件应答信号
Start_I2C();
if(0 == Wr_I2C(0x50< < 1)) break;//收到应答,跳出循环
Stop_I2C();//没收到应答,发送停止信号,继续循环检测
}
Wr_I2C(addr); //写入要操作的初始地址addr
while(len > 0)
{
Wr_I2C(*str++);//写入一个字节,并将字符串指针指向下一个字符
len--;//字符数减1
addr++;//存储地址加1
if(0 == (addr & 0x07))//检测是否到达了下一页的起始地址,
break; //即上一个字节已经写到页的最后边界了
//跳出停止继续写,每页的起始地址后3位为0
//因此判断addr后3为是否为0即可
}
Stop_I2C();
}
}
//多字节读
void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
while(1)
{//循环检测器件应答信号
Start_I2C();
if(0 == Wr_I2C(0x50< < 1)) break;//收到应答,跳出循环
Stop_I2C();//没收到应答,发送停止信号,继续循环检测
}
Wr_I2C(addr); //写入要操作的初始地址addr
Start_I2C();//再次发送起始信号
Wr_I2C((0x50< < 1)|0x01);//通知地址50的器件,接下来读操作
while(len > 1)
{
*str++ = RdACK_I2C();//读字节并应答
len--;
}
*str = RdNAK_I2C();//最后一个字节,读字节并非应答
Stop_I2C();
}
//寻址AT24C01
bit Addressing_AT24C01(unsigned char addr)
{
bit ack;
Start_I2C();
ack = Wr_I2C(addr< < 1);
Stop_I2C();
return ack;
}
如上所示,所有的代码都是以 I2C 通信的 5 个底层函数为基础的,因此我们需要将“Drive_I2C.h”文件包含到代码中。
13.2.3 AT24C01 单字节读写应用
下面我们建立一个工程,写一个实例来展示单片机对 AT24C01 的读写应用。应用的功能为首先往 AT24C01 存储器地址 0x08 处写入数据 110,然后从该处把数据读出来显示在 1602 液晶上,以此来验证读写操作的正确性,主文件“MainAT24C01.c”代码如下所示:
#include < reg52.h >
#include"Drive_AT24C01.h" //包含AT24C01头文件
#include"Drive_1602.h"
#define uchar unsigned char
#define uint unsigned int
sbit DU = P2^7;//数码管段选、位选引脚定义
sbit WE = P2^6;
uchar str[10]=0;
void main()
{
uchar dat=0;
Init_1602();
P0 = 0xff;//关闭所有数码管
WE = 1;
WE = 0;
//往AT24C01存储器地址0x08处写入数字110
WrByte_AT24C01(0x08,110);
Disp_1602_str(1,2,"AT24C02 test!");
//读取AT24C01存储器地址0x08处的数据
dat = RdByte_AT24C01(0x08);
str[0]=dat/100+'0';
str[1]=dat%100/10+'0';
str[2]=dat%10+'0';
//将数据显示在1602的第2行第6列处
Disp_1602_str(2,6,str);
while(1);
}
将工程编译后,下载到开发板验证功能的正确性。
AT24C01 对写时序有一个特殊的要求,当完成一次数据通信后,需要延迟 tWR 才能开始下一次起始信号如下图所示,tWR 为 AT24C01 内部处理数据时间,查询 AT24C01 数据手册可知为 10ms。因此,我们在主程序代码第 19 行与第 22 行之间插入了第 20 行代码达到延时的目的,我们可以将第 20 行代码挪到后面测试一下效果。
13.2.4 AT24C01 多字节写通信
根据上面的介绍,大家很容易发现每隔 10ms 才能进行一次正常的写数据操作是非常浪费时间的,尤其是在进行多个字节写操作的时候。AT24C01 提供了另外一种多字节的写模式,为页操作模式。首先介绍一下 AT24C01 的内部存储器的分页结构。AT24C01 总共有 128 个字节的存储空间,总共分为 16 页,每一页总有 8 各字节。第一页的地址范围为 0x00~0x07,依次往下均分为 16 页。页操作模式通信时序如下图所示:
如上如所示,首先发送起始信号、写器件地址、写起始地址,紧接着写入多个字节,写完一个字节,器件内部会将地址自动加 1,最后结束信号。这里需要注意的是,连续写的多个字节必须在同一页内,不能进行跨页连续读写,因此一次通信周期内最多可以写入 8 个字节的数据。如果我们需要写的数据很多,而一页写不下怎么办?首先,启动页写通信,在写的过程中判断是否要写到页的边界了,当到达页边界后停止该次页写通信,再重新发起页写通信将剩余的数据写入,这样便实现了任意个字节的写入,具体函数如下图所示。其中,str 为需要写入的字符串,addr 为写入 AT24C01 的起始地址,len 为写入字符个数。多字节写数据代码如下:
//多字节写
void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
while(len > 0)//检测上一次是否完成所以数据写操作
{
while(1)
{//循环检测器件应答信号
Start_I2C();
if(0 == Wr_I2C(0x50< < 1)) break;//收到应答,跳出循环
Stop_I2C();//没收到应答,发送停止信号,继续循环检测
}
Wr_I2C(addr); //写入要操作的初始地址addr
while(len > 0)
{
Wr_I2C(*str++);//写入一个字节,并将字符串指针指向下一个字符
len--;//字符数减1
addr++;//存储地址加1
if(0 == (addr & 0x07))//检测是否到达了下一页的起始地址,
break; //即上一个字节已经写到页的最后边界了
//跳出停止继续写,每页的起始地址后3位为0
//因此判断addr后3为是否为0即可
}
Stop_I2C();
}
}
13.2.5 AT24C01 多字节读通信
多字节读时序如下图所示,与单字节类似,只是在第一个数据紧接着着读取多个数据,这里要注意的是只有最后一个字节的数据发送非应答信号,前面的数据均发送应答信号,这个也很好理解,应为我们都到最后一个字节时要告诉 AT24C01 我们不再继续读数据了,因此发送非应答信号。
多字节读取函数代码如下图所示:
//多字节读
void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
while(1)
{//循环检测器件应答信号
Start_I2C();
if(0 == Wr_I2C(0x50< < 1)) break;//收到应答,跳出循环
Stop_I2C();//没收到应答,发送停止信号,继续循环检测
}
Wr_I2C(addr); //写入要操作的初始地址addr
Start_I2C();//再次发送起始信号
Wr_I2C((0x50< < 1)|0x01);//通知地址50的器件,接下来读操作
while(len > 1)
{
*str++ = RdACK_I2C();//读字节并应答
len--;
}
*str = RdNAK_I2C();//最后一个字节,读字节并非应答
Stop_I2C();
}
将上述的多字节写、多字节读取函数添加到驱动文件“Drive_AT 24C01.c”、“Drive_AT 24C01.h”中,后续应用中只需要将文件添加到项目中,调用相关的函数就可以了。
13.2.6 AT24C01 多字节读写应用
本小节实现 AT24C01 多字节的读写应用,先向 AT24C01 连续写入多字节数据,然后将数据读出显示在 1602 液晶显示模块上,验证读写的正确性。主程序代码如下所示:
#include < reg52.h >
#include"Drive_AT24C01.h" //包含AT24C01头文件
#include"Drive_1602.h"
#define uchar unsigned char
#define uint unsigned int
sbit DU = P2^7;//数码管段选、位选引脚定义
sbit WE = P2^6;
uchar str1[]="AT24c01 Wr Str!";
uchar str2[20];
void main()
{
uchar dat=0;
P0 = 0;//关闭所有数码管
WE = 1;
WE = 0;
Init_1602();//1602初始化
WrStr_AT24C01(str1,0x05,16);//写入16个字节
RdStr_AT24C01(str2,0x05,16);//读取16个字节
Disp_1602_str(1,1,str2); //将数据从第一行第一列开始显示
while(1);
}
如代码所示,从地址 0x05 开始写入 16 个字节的数据,由于 AT24C01 为 8 个字节为一页,因此本次为横跨了 3 页的写操作,有效的验证的多字节的写操作。
13.3 本章小结
本章详细介绍了I2C总线的通信原理,以及驱动函数的编写,AT24C01芯片的使用。
-
单片机
+关注
关注
6036文章
44556浏览量
634966 -
uart
+关注
关注
22文章
1235浏览量
101377 -
I2C总线
+关注
关注
8文章
390浏览量
60923 -
总线通信
+关注
关注
0文章
49浏览量
11830 -
AT24C01
+关注
关注
0文章
3浏览量
5252
发布评论请先 登录
相关推荐
评论