IIC总线概述
IIC总线介绍
I2C (Inter-Integrated Circuit)总线产生于在80年代, 由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备, 最初为音频和视频设备开发。I2C总线两线制包括:串行数据SDA(Serial Data)、串行时钟SCL(Serial Clock)。时钟线必须由主机(通常为微控制器)控制,主机产生串行时钟(SCL)控制总线的传输方向,并产生起始条件和停止条件。I2C总线上有主机(MCU)和从机(片外外设,如AT24C02)之分,可以有多个主机和多个从机。从机永远不会主动给主机发送数据。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态(半双工),即IIC总线通信为同步串行半双工通信。
I2C总线的特征
- 只要求两条总线线路 一条串行数据线 SDA 一条串行时钟线 SCL。
- 每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机 从机关系软件设定地址 主机可以作为主机发送器或主机接收器。
- 它是一个真正的多主机总线 如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁防止数据被破坏。
- 串行的 8 位双向数据传输位速率在标准模式下可达 100kbit/s 快速模式下可达 400kbit/s 高速模式下可达 3.4Mbit/s。
- 片上的滤波器可以滤去总线数据线上的毛刺波 保证数据完整。
- 连接到相同总线的 IC 数量只受到总线的最大电容 400pF 限制。
标准模式 I2C 总线规范的扩展
- 快速模式 位速率高达 400kbit/s
- 高速模式 Hs 模式 位速率高达 3.4Mbit/s
- 10 位寻址 允许使用高达 1024 个额外的从机地址
扩展 I2C 总线规范主要的两个原因:
- 现在很多应用需要传输大量的串行数据,要求的位速率远远超过100kbit/s 标准模式,或者甚至是 400kbit/s快速模式,半导体技术持续改进的结果使 I2C总线器件现在可以使用高达3.4Mbit/sHs模式的位速率,而且接口电路的生产成本没有任何明显的提高
- 由于使用 7 位寻址策略的大多数 112 地址能被立即分配,很显然要求更多地址结合来防止为新器件分配从机地址的问题,这个问题可以用新的 10 位寻址策略解决 它允许可使用的地址成 10 倍增长。
IIC总线主从设备通信
主从机: 控制时钟线的是主机(通常是微控制器),从机通常是外围设备,支持一主多从。
从机不会主动发信息给主机的、主机先发送信息(起始信号…)给从机、从机才会进行响应,主从机之间的通信都有严格的时序图、必须按照时序图进行通信。
发送器: 发送信息的设备
接收器: 接收信息的设备
主从机之间的通信需要依赖唯一的地址(从机地址)进行通信
IIC总线与UART比较
通讯协议 | IIC | UART | SPI |
---|---|---|---|
特征 | 同步串行半双工 | 异步串行全双工 | 同步串行全双工 |
接口 | SDA SCL | TX RX | MOSI/MISO/CS/SCK |
速度 | 波特率 | 100Khz / 400Khz / 3.4Mhz | Mhz级以上 |
数据帧格式 | 起始条件+位传输+应答+停止条件 | 起始位+数据位+校验位+停止位 | 四种MODE0 - 3 |
主从设备通讯 | 有主从之分 | 没有主从之分 | 有主从之分 |
总线结构 | 一对多 | 一对一 | 一对多 |
IIC总线数据帧
时序
上图响应的参数对应下表
每次传输八位数据、传输速率(100Kbit/s 400Kbit/s 3.4Mbit/s),同步通信(主机 从机) 主从机需要同步时间周期
例:
100Kbit/s---->发送一位的时间=10us
400Kbit/s---->发送一位的时间=2.5us
即响应的SCL高电平周期与低电平周期都需大于对应的发送一位的时间,确保在周期时间内,数据能发送完成。
IIC可以如何进行配置????
首先IIC协议芯片有集成、可以直接配置芯片集成的控制器(一般不这样用)主频较快方波不正规(容易产生正弦波等不正规的波形)
不用集成的控制器可以使用IO进行模拟。
IO模拟的优点
1) 正弦波正常
2) 通用性强(可移植性强)
IIC数据帧的格式
SDA 线上的数据必须在时钟的高电平周期保持稳定,数据线的高或低电平状态只有在 SCL 线的时钟信号是低电平时才能改变,即在时钟线的高电平期间能接收数据、在时钟线的低电平期间能发送数据。(在时钟线SCL和数据线SDA上通常接有上拉电阻,默认状态为高电平)
起始条件:
1.SCL和SDA初始状态高电平
2.一段时间后(>4.7us,该处可适当选择大于IIC传输速率的时间就可),拉低数据线SDA
3.延时一段时间,需要发送数据时,拉低时钟线SCL
/*
函 数 名:BSIIC_Start
函数功能:IIC起始条件
返 回 值:无
形 参:无
备 注:
*/
void BSIIC_Start(void)
{
BSIIC_SCL = 0; //BS8116A器件原因,需要先拉低一下时钟线
TIM5_Delay_us(4);
BSIIC_SCL = 1;
BSIIC_SDA_OUT = 1;
TIM5_Delay_us(4);
BSIIC_SDA_OUT = 0;
TIM5_Delay_us(4);
BSIIC_SCL = 0;
}
从设备地址:
I2C 总线的寻址过程是通常在起始条件后的第一个字节决定了主机选择哪一个从机。例外的情况是可以寻址所有器件的广播呼叫地址。
第一个字节的头 7 位组成了从机地址,最低位 LSB 是第 8 位 它决定了报文的方向,第一个字节的最低位是 0 表示主机会写信息到被选中的从机,1表示主机会向从机读信息。
当发送了一个地址后,系统中的每个器件都在起始条件后将头 7 位与它自己的地址比较。如果一样,器件会任务它被主机寻址 至于是从机接收器还是从机发送器都由 R/ W 位决定。
停止条件:
1.时钟线SCL初始拉高,SDA初始拉低
2.延时一段时间后,拉高数据线SDA,延时等待SDA拉高
3.最后可不用拉低数据线(保持数据线初始状态,方便之后数据的传输)
/*
函 数 名:BSIIC_End
函数功能:IIC停止条件
返 回 值:无
形 参:无
备 注:
*/
void BSIIC_End(void)
{
BSIIC_SCL = 1;
BSIIC_SDA_OUT = 0;
TIM5_Delay_us(4);
BSIIC_SDA_OUT = 1;
TIM5_Delay_us(4);
}
发送数据帧:
1.拉低时钟线SCL
2.在SCL低电平周期内发送1字节数据
3.延时一段时间(SCL周期)
4.拉高时钟线SCL(恢复到默认状态,产生一个完整的SCK低电平周期)
/*
函 数 名:BSI2C_SendByte
函数功能:IIC发送字节数据
返 回 值:ack 接受到的响应
形 参:data 待发送的数据
备 注:
*/
u8 BSI2C_SendByte(u8 data)
{
u8 i ,ack = 0;
for(i = 0;i<8;i++)
{
BSIIC_SCL = 0;
TIM5_Delay_us(4);
if(data & (0x80>>i))
{
BSIIC_SDA_OUT = 1;
}
else
{
BSIIC_SDA_OUT = 0;
}
TIM5_Delay_us(4);
BSIIC_SCL = 1;
TIM5_Delay_us(4);
}
ack=BSI2C_RevAck();
return ack;
}
接收数据帧:
1.SDA引脚输出信号拉高,恢复至默认状态,关闭发送数据
2.先将SCL引脚拉低一段时间,再拉高SCL(产生一个完整接收数据的高电平周期)
3.接收1字节的数据(从高位开始收,注意处理接收数据)
4.延时一个高电平周期。
/*
函 数 名:BSI2C_RevByte
函数功能:IIC接收字节数据
返 回 值:接收到的数据
形 参:ack 待发送的响应
备 注:
*/
u8 BSI2C_RevByte(u8 ack)
{
BSIIC_SDA_OUT=1;//断开发送
u8 data,i;
for(i = 0;i<8;i++)
{
BSIIC_SCL = 0;
TIM5_Delay_us(4);
BSIIC_SCL = 1;
data <<= 1;
if(BSIIC_SDA_IN)
{
data |= 1;
}
TIM5_Delay_us(4);
}
BSI2C_SendAck(ack);
return data;
}
发送应答: 原理与发送数据帧一致
当需要页写操作时,随机读等操作时,可在应答函数末尾多加一个拉低时钟线的操作,可防止下次应答未及时拉低时钟线。
/*
函 数 名:BSI2C_SendAck
函数功能:IIC发送响应
返 回 值:无
形 参:ack 对应响应
备 注:
ack: 0 响应
1 未响应
*/
void BSI2C_SendAck(u8 ack)
{
BSIIC_SCL = 0; //低电平周期能发送数据
if(ack)
{
BSIIC_SDA_OUT = 1;
}
else
{
BSIIC_SDA_OUT = 0;
}
TIM5_Delay_us(4);
BSIIC_SCL = 1; //完整周期,恢复到默认状态
TIM5_Delay_us(4);
}
接收应答: 与接收数据帧一致
/*
函 数 名:BSI2C_RevAck
函数功能:IIC接受响应
返 回 值:无
形 参:ack 对应响应
备 注:
ack: 0 响应
1 未响应
*/
u8 BSI2C_RevAck(void)
{
BSIIC_SDA_OUT=1;//断开发送
u8 ack = 0;
BSIIC_SCL = 0;
TIM5_Delay_us(4);
BSIIC_SCL = 1;
if(BSIIC_SDA_IN)
{
ack = 1;
}
TIM5_Delay_us(4);
return ack;
}
发送数据格式:
1.主机发送起始条件
2.主机发送从机地址(7bit)+读写位(0为写,1为读,从机地址和读写位合成8位发送),主机接收应答
3.主机发送数据(8bit),主机接收应答
4.发送完毕,主机发送停止条件。
接收数据格式:
1.主机发送起始条件
2.主机发送从设备地址+读写位,主机接收应答
3.主机接收数据,主机发送应答
4.接收完毕,主机发送停止条件
注: 在配置数据帧,应答等相关的发送使用时,期间的延时用定时器延时更好一些,因为在单片机上,IIC所使用的时钟与定时器使用的时钟频率更加接近,关联性更强,产生的时间误差较小(该处理解不太到位,可自行查找相关解释)。
BS8116A-3电容按键实验![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/032b17b356a5dff09b04950f5194b6e5.png)
此处KEY8的位和KEY16的位初始默认为1,其余默认为0,按键按下置1。单片机板子上需观察电容按键对应的按键码是多少。
起始条件(Start) 后 发 送 7bit 从 机 地 址,BS81x-3 从机地址是 0x50,其与读写位合成,则器件写地址为:0xA0,读地址为:0xA1。
电容按键实现代码:
u8 BS8116_Read_Key(void)
{
u16 Key = 0;
BSIIC_Start();
if(BSI2C_SendByte(0xA0))
{
return 1;
}
if(BSI2C_SendByte(0x08))
{
return 2;
}
BSIIC_Start();
if(BSI2C_SendByte(0xA1))
{
return 3;
}
Key = BSI2C_RevByte(0);
Key = BSI2C_RevByte(1)<<8|Key;
BSIIC_End();
switch(Key)
{
case 0x8081:return '1';
case 0x8480:return '2';
case 0x8080:return '3';
case 0x8082:return '4';
case 0x8880:return '5';
case 0x80c0:return '6';
case 0x8088:return '7';
case 0x8180:return '8';
case 0x80a0:return '9';
case 0x8084:return '*';
case 0x8280:return '0';
case 0x8090:return '#';
}
return 0;
}
AT24Cxx(EEPEOM存储)
引脚说明:
引脚号 | 引脚说明 | 功能说明 |
---|---|---|
1 | A0 | 地址输入。A2、A1和A0是器件地址输入引脚。 24C02/32/64使用A2、A1和A0输入引脚作为硬件地址,总线上可同时级联8个24C02/32/64器件 24C04使用A2和A1输入引脚作为硬件地址,总线上可同时级联2个24C08器件,A0为空脚(备用),可接地。 24C08使用A2输入引脚作为硬件地址,总线上可同时级联2个24C08器件,A0和A1为空脚,可接地。 24C16未使用器件地址引脚,总线上最多只可连接一个16K器件,A2、A1和A0为空脚,可接地。 |
2 | A1 | |
3 | A2 | |
5 | SDA | 串行地址和数据输入/输出。SDA是双向串行数据传输引脚,漏极开路,需外接上拉电阻到Vcc(典型值10kΩ) |
6 | SCL | 串行时钟输入。SCL同步数据传输,上升沿数据写入,下降沿数据读出。 |
7 | WP | 写保护。WP引脚提供硬件数据保护。当WP接地时,允许数据正常读写操作,当WP接Vcc时,写保护,只读。 |
8 | GND | 地 |
9 | Vcc | 正电源 |
存储结构
AT24Cxx芯片后两位代表的是可存储容量的大小(单位为位)。
器件寻址:
- 器件地址信息的LSB为读/写操作选择位,高为读操作,低位写操作。
- 若比较器件地址一致,EEPROM将输出应答“0”。如果不一致,则返回待机状态。
Ps: 待机模式,EEPROM具有低功耗待机的特点,条件为:1、电源上电;2、接收停止条件及完成任务内部操作后。
写操作
字节写:
页写:
/*
函 数 名:AT24C04_Write
函数功能:页写
返 回 值:正确返回0,错误返回对应值
形 参:u8 addr,u8 length,u8 *send_buff
备 注:
在末尾需注意加个延时等待数据写入完成
*/
u8 AT24C04_Page_Write(u8 addr,u8 length,u8 *send_buff)
{
ATIIC_Start(); //起始条件
if(ATIIC_Send_Data(0xa0)) //器件读地址
{
return 1;
}
if(ATIIC_Send_Data(addr)) //发送存储的首地址
{
return 2;
}
while(length--)
{
if(ATIIC_Send_Data(*send_buff++))
{
return 3;
}
}
ATIIC_End(); //停止条件
Delay_ms(5); //等待数据发送完成
return 0;
}
跨页写:
执行页写操作时,没写入一个数据后,字地址的相应低位内部自动加1,维持在当前页,当产生的字地址达到该页边界地址时,随后的数据将写入该页的页首,所以当我们要一次行写入多个数据时,需要使用跨页写操作。
/*
函 数 名:AT24C04_Stride_Pagewrite
函数功能:跨页写
返 回 值:正确返回0,错误返回对应值
形 参:u16 addr,u16 length,u8 *send_buff
备 注:
*/
u8 AT24C04_Stride_Pagewrite(u16 addr,u16 length,u8 *send_buff)
{
u8 n = 16-addr%16;
if(length<=n)
{
n = length;
}
do{
AT24C04_Page_Write(addr,n,send_buff);
addr = addr+n;
send_buff+=n;
length-=n;
if(addr>512)
{
addr = 0;
}
if(length>16)
{
n =16;
}
else{
n = length;
}
}while(length);
return 0;
}
读操作
当前地址读:
内部地址计数器保存着上次访问时最后一个地址加1的值。只要芯片有电,该地址就一致保存。当读到最后页的最后字节,地址会回转到0;当写道某页尾的最后一个字节,地址会回到该页的首地址
随机读:
随机读能够读取整个存储芯片的数据。
/*
函 数 名:AT24C04_Random_Read
函数功能:随机读
返 回 值:正确返回0,错误返回对应值
形 参:u8 addr,u8 length,u8 *send_buff
备 注:
*/
u8 AT24C04_Random_Read(u8 addr,u8 length,u8 *rev_buff)
{
ATIIC_Start();
if(ATIIC_Send_Data(0xa0)) //器件写地址
{
return 1;
}
if(ATIIC_Send_Data(addr)) //字地址
{
return 2;
}
ATIIC_Start();
if(ATIIC_Send_Data(0xa1)) //器件读地址
{
return 3;
}
while(length--)
{
*rev_buff++ = ATIIC_Rev_Data(0);
}
*rev_buff = ATIIC_Rev_Data(1);
ATIIC_End(); //停止条件
return 0;
}
顺序读: