一、初识IIC
IIC(Inter Interface Circuit,芯片间总线)是一种两线式串行总线,由PHILIPS公司开发,用于连接微控制器及其外围设备。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。I2C总线由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可接收数据,是一种半双工通信协议。总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。
(一)IIC的三种传输速率:
- 标准速率:100K bit/s
- 快速速率:400K bit/s
- 高速速率:3.4M bit/s
(二)IIC的硬件特性:
IIC是一种一主多从的的硬件连接方式,具体连接可参照下图;
(三)IIC时序
起始信号:在SCL高电平的时候SDA线上有高到低的跳变
停止信号:在SCL高电平的时候SDA线上有低到高的跳变
应答信号:当从设备数据接收完成并且需要产生一个应答信号时,主设备需要主动产生一个时钟信号同时释放SDA,让从设备将SDA拉低则为一个应答信号、
数据传输:SDA线上的数据需要在SCL为高电平的时候保持不变,在SCL为低电平的时候发生数据翻转
IIC读写:IIC通讯的第一个字节用于确定主机选择的通讯的从机,最后一位代表的写入数据还是读取数据(0:写入;1:读取)
IIC数据写入:IIC数据写入流程(应答信号由从机产生)
IIC数据读取:IIC数据读取流程(灰色的数据由从设备产生)
二、MCU模拟IIC驱动代码
(一)通用部分
IIC延时:
void delay_us(unsigned char Time)
{
unsigned char a;
for (a = 0; a < Time; a++) {
NOP();
}
}
(二)GPIO初始化
当mcu模拟IIC通信时候需要将SDA以及SDL脚初始化为推挽输出(若无可初始化为上拉输出),以下以一个8位单片机初始化为例:
#define IIC_SCL_1() {PB7 = 1;}
#define IIC_SCL_0() {PB7 = 0;}
#define IIC_SDA_1() {PC1 = 1;}
#define IIC_SDA_0() {PC1 = 0;}
#define IIC_SDA_READ() PC1
#define IIC_SDA_IN() {TRISC1 = 1;}
#define IIC_SDA_OUT() {TRISC1 = 0;}
void soc_iic_gpio_init(void)
{
// SDA init 输出上拉
TRISC1 = 0; // 设置PC1为输出
WPDC1 = 0;
WPUC1 = 1;
// SCK init 输出上拉
TRISB7 = 0; // 设置PB7为输出
WPDB7 = 0;
WPUB7 = 1;
}
(三)IIC起始信号
void soc_iic_start(void)
{
// IIC 开始信号在SCL高电平的时候SDA下降沿
IIC_SDA_OUT();
IIC_SDA_1();
IIC_SCL_1();
delay_us(1);
IIC_SDA_0();
delay_us(1);
IIC_SCL_0();
delay_us(1);
}
(四)IIC停止信号
void soc_iic_stop(void)
{
// IIC 结束信号在SCL高电平的时候SDA上升沿
IIC_SDA_OUT();
IIC_SCL_0();
IIC_SDA_0();
delay_us(1);
IIC_SCL_1();
delay_us(1);
IIC_SDA_1();
delay_us(1);
}
(五)IIC应答/无需应答
void soc_iic_ack(void)
{
IIC_SDA_OUT();
IIC_SDA_0(); /* CPU驱动SDA = 0 */
delay_us(1);
IIC_SCL_1(); /* CPU产生1个时钟 */
delay_us(1);
IIC_SCL_0();
delay_us(1);
IIC_SDA_1(); /* CPU释放SDA总线 */
}
void soc_iic_no_ack(void)
{
IIC_SDA_1(); /* CPU驱动SDA = 1 */
delay_us(1);
IIC_SCL_1(); /* CPU产生1个时钟 */
delay_us(1);
IIC_SCL_0();
delay_us(1);
}
(六)IIC等待应答
unsigned char soc_iic_wait_ack(void)
{
unsigned char re;
IIC_SDA_1(); /* CPU释放SDA总线 */
IIC_SDA_IN();
delay_us(1);
IIC_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
delay_us(1);
if (IIC_SDA_READ()) { /* CPU读取SDA口线状态 */
re = 1;
} else {
re = 0;
}
IIC_SCL_0();
delay_us(1);
return re;
}
(七)IIC发生一个字节数据
void soc_iic_send_byte(unsigned char _ucByte)
{
unsigned char i;
/* 先发送字节的高位bit7 */
IIC_SDA_OUT();
for (i = 0; i < 8; i++) {
if (_ucByte & 0x80) {
IIC_SDA_1();
} else {
IIC_SDA_0();
}
delay_us(1);
IIC_SCL_1();
delay_us(1);
IIC_SCL_0();
_ucByte <<= 1; /* 左移一个bit */
delay_us(1);
}
IIC_SDA_1(); // 释放总线
}
(八)IIC读取一个字节数据
unsigned char soc_iic_read_byte(void)
{
unsigned char i;
unsigned char value;
/* 读到第1个bit为数据的bit7 */
value = 0;
IIC_SDA_IN();
for (i = 0; i < 8; i++) {
value <<= 1;
IIC_SCL_1();
delay_us(1);
if (IIC_SDA_READ()) {
value++;
}
IIC_SCL_0();
delay_us(1);
}
return value;
}