文章目录
- 前言
- 一、IIC协议
- 二、IIC的控制
- 2.1 IIC控制相关寄存器
- 2.2 IIC控制中的特征信号
- 2.3 IIC的数据传输格式
- 2.3.1 ACK信号
- 2.3.2 数据读写操作
- 2.3.3 中止通信
- 三、 IIC编程
- 3.1 IIC编程的相关寄存器
- 3.2 IIC代码实现
- 总结
前言
本期和大家主要分享的是一种非常常见并且非常常用的的一种协议------IIC通信协议;既然能称为一种协议,那么一定是通信中人们需要去遵守的一种通信标准,接下来让我们细细来了解一下!
一、IIC协议
IIC有两条串行线,一条是专用串行数据线(SDA),另一条是专用串行时钟线(SCL),并且这两根线都是双向的;
同步串行半双工,多主的总线协议;
二、IIC的控制
2.1 IIC控制相关寄存器
S3C2440要是想控制IIC总线,那么必须写入值到以下寄存器中:
多主控 IIC 总线控制寄存器,IICCON
多主控 IIC 总线控制/状态寄存器,IICSTAT
多主控 IIC 总线 Tx/Rx 数据移位寄存器,IICDS
多主控 IIC 总线地址寄存器,IICADD
IIC的总线框图如下:
2.2 IIC控制中的特征信号
要想合理的控制IIC总线,必须清楚几个重要的信号:
(1)当IIC总线空闲时,SCL和SDA两个串行线都为高电平;
(2)IIC总线的起始信号:SCL为高时(总线空闲),SDA由高变低的这个信号,定义其为一个起始信号(start);
(3)IIC总线的停止信号:SCL为高时(总线使用时),SDA由低变高的这个信号,定义其为一个停止信号(stop);
这里需要注意的是start和end只能由主机发出;当产生了stop信号后,iic总线变为空闲状态;
2.3 IIC的数据传输格式
当start信号被发送后,要想进行通信,必须指出通信目标的地址,之后再进行数据发送;发送完毕后再发送stop信号;按字节发送,高位先发 ,每8位发送必须有一个位应答;
start发送后,紧接着的1个字节允许用户写入从机地址和读写标志,这一个字节中前七位为从机地址,最后一位是读写标志(0为写标志,1为读标志);
start + 1字节 + ack + 数据
SCL=1 前七位为从机地址
SDA=0 最后一位为读写标志
2.3.1 ACK信号
(1)为了完成一次单字节传输操作,接收器应该发送一个 ACK 位给发送器。ACK 脉冲应该发生在 SCL 线的第 9个时钟。前 8 个时钟是提供给单字节传输的。主机需要应该产生时钟脉冲来发送 ACK 位。
(2)当发送器收到 ACK 时钟脉冲时,应该通过拉高 SDA 线(stop)来释放 SDA 线。当接收器在 ACK 时钟脉冲期间也应该驱动 SDA 线为低,在第 9 个脉冲的高电平时期期间保持 SDA 为低。
(3)ACK 位发送功能可以由软件(IICSTAT)使能或禁止。然而,需要 SCL 的第 9 个时钟上的 ACK 脉冲来完成单字节的传输操作。
2.3.2 数据读写操作
(1)发送模式中当发送了数据时,在 IIC 总线数据移位(IICDS)寄存器收到新数据之前 IIC 总线接口将会一直等待。在新数据写入到寄存器之前,SCL 线将会保持为低,然后在其写入后释放。S3C2440A 应该等待中断来确定当前数据发送的完成。在 CPU 收到中断请求后,需要再次写一个新数据到 IICDS 寄存器中。
(2)接收模式中当收到了数据时,在读取 IICDS 寄存器前 IIC 接口将会一直等待。在新数据读出前,SCL 线将会保持为低,然后在其读取后释放。S3C2440A 应该等待中断来确定当前数据接收的完成。在 CPU 收到中断请求后,需要从 IICDS 寄存器中读取数据。
这里当主机发送了一个数据后并且ACK信号产生之后,SCL会变为低电平,这个时候从机可以读取数据;
2.3.3 中止通信
(1)如果从接收器不能应答从地址的确认,其应该保持 SDA 线的电平为高。这种情况中,主机应该产生一个停止条件并且中止传输。
(2)如果主机接收器受到了传输中止的影响,其应该通过取消来自从机收到的最后数据字节后 ACK(nack)的产生来指示从发送操作的结束。从发送器应该随后释放 SDA 来允许主机产生停止条件。
三、 IIC编程
3.1 IIC编程的相关寄存器
-
start stop ack noack
IICCON 第7位 1使能ack 0禁止ack
IICSTAT 第5位 写操作 1 start信号 0 stop信号 读操作 1 总线忙碌 0 总线空闲
IICSTAT 第4位 1使能电路
-
数据
IICDS发送和接收的缓存区
IICADDIIC 总线地址寄存器
-
数据操作时刻
IICCON第5位使能中断(发完8位 接够8位产生中断)
IICCON第4位 中断标志(读操作 1 产生中断 0 没有中断 写0清除中断)
IICSTAT 第0位 0 接收到ack 1没有ack -
时钟 IICCON 第6位 一级分频 第3-0位二级分频值
3.2 IIC代码实现
void iic_init()
{
GPECON &= ~(0xf<<28);
GPECON |= 0xa<<28; //设置GPE14,15为IIC功能
IICCON = (1<<7) | (1<<5) | (1<<6); //使能ack,使能中断,时钟=50M/512/1 = 97k;
IICSTAT = 1<<4; //使能输出
}
int wait_pend()
{
int cnt = 0xffff;
while (cnt--)
{
if(IICCON & (1<<4)) //标志位是否置位,置位代表发完了
{
return 0;
}
}
return -1;
}
void clear()
{
IICCON &= ~(1<<4); //清除中断标志位
}
void stop()
{
int num = 1000;
IICSTAT = 0xd0; //发送stop信号
clear();
while(num--); //电路作用需要时间
}
int iic_write(char s_addr, char d_addr, char data) //总流程是写入从机地址,写入数据地址,最后写入数据
{
int ret = -1;
IICDS = s_addr<<1; //写入从机地址和写标志(最后一位是写标志)
IICSTAT = 0xf0; //发送start信号
if(wait_pend() < 0) //(如果中断标志位置位)判断发送完毕
{
stop();
return ret; //没发完,发的有问题
}
if(IICSTAT & 1) //没有收到ack
{
stop();
return ret;
} //上面两个if的作用是条件不成立时退出
IICDS = d_addr;
clear(); //开始发送数据地址
if(wait_pend() < 0) //判断发送完毕
{
stop();
return ret;
}
if(IICSTAT & 1) //有没有收到ack
{
stop();
return ret;
} //上面两个if的作用是条件不成立时退出
IICDS = data;
clear(); //开始发送数据
if(wait_pend() < 0) //判断发送完毕
{
stop();
return ret;
}
if(IICSTAT & 1) //有没有收到ack
{
stop();
return ret;
} //上面两个if的作用是条件不成立时退出
stop();
ret = 0;
return ret;
}
int iic_read(char s_addr, char d_addr, char *data)
{
int ret = -1;
IICDS = s_addr<<1; //写入从机地址和写标志(最后一位是写标志)
IICSTAT = 0xf0; //发送start信号
if(wait_pend() < 0) //判断发送完毕
{
stop();
return ret;
}
if(IICSTAT & 1) //没有收到ack
{
stop();
return ret;
} //上面两个if的作用是条件不成立时退出
IICDS = d_addr;
clear(); //开始发送数据地址
if(wait_pend() < 0) //判断发送完毕
{
stop();
return ret;
}
if(IICSTAT & 1) //没有收到ack
{
stop();
return ret;
}
stop(); //发送start信号的stop(一个start一个stop)
IICDS = (s_addr<<1) | 1; //写入从机地址和读标志(最后一位是写标志)
IICSTAT = 0xb0; //发送start信号
if(wait_pend() < 0) //判断发送完毕
{
stop();
return ret;
}
if(IICSTAT & 1) //没有收到ack
{
stop();
return ret;
}
IICCON &= ~(1<<7); //禁止ack
clear(); //清除标志位,也就是开始接收
if(wait_pend() < 0) //判断是否接收完毕
{
stop();
return ret;
}
*data = IICDS; //一个字节收到
stop();
ret = 0;
return ret;
}
测试代码:
void main(void)
{
uart_init();
iic_init();
uprintf("ni hao pc!\r\n");
// while(iic_write(0x50, 0x23, 0x45) < 0); //返回值为0跳出循环,写入成功
while(iic_read(0x50, 0x23, &info) < 0);
uprintf("info = %c\r\n", info);
while(1)
{
}
}
总结
本期和大家分享的是IIC协议以及IIC编程,将数据写入板载的EEPROM中,再从EEPROM中读出来,实现一个IIC协议的demo,希望通过这个demo各位小伙伴们能够掌握IIC通信的全过程。个人观点:S3C2440中的IIC机制相对比较完善,如果想要更深一步学习IIC,大家可以使用IO口模拟IIC,以此提升自己对IIC协议的理解!最后,各位小伙伴们如果喜欢我的分享可以点赞收藏哦,你们的认可是我创作的动力,一起加油!