一、IIC 简介
I2C 是内部整合电路的称呼, 是一种串行通讯总线, 使用多主从架构, 由飞利浦公司在1980年为了让主板、 嵌入式系统或手机用以连接低速周边装置而发展。 I2C的正确读法为"I-squared-C" , 而"I-two-C"则是另一种错误但被广泛使用的读法, 在中国则多以"I方C"称之。 I2C 总线支持任何IC 生产过程(NMOS CMOS、 双极性) 。 两线――串行数据(SDA) 和串行时钟 (SCL) 线在连接到总线的器件间传递信息。 每个器件都有一个唯一的地址识别(无论是微控制器——MCU、 LCD 驱动器、 存储器或键盘接口) , 而且都可以作为一个发送器或接收器。
分为开始信号,结束信号,应答信号
1、 SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。
2、 SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
3、接收数据的 IC 在接收到 8bit 数
从机地址+R/W | 描述 |
---|---|
0000 0000 | 呼叫地址 |
0000 0001 | 起始字节 |
0000 001X | CBUS地址 |
0000 010X | 保留供不同的总线格式 |
0000 011X | 保留将来用 |
0000 1XXX | HS模式主机码 |
1111 0XXX | 10位从机地址 |
1111 1XXX | 保留将来用 |
IIC是双向通信的,那主机肯定也是需要从外设读取数据的,那这个读取的过程又是怎么实现的呢?毕竟外设对于我们而言是不能直接操作的,我们能操作的只有stm32。我们知道,一个IIC总线上,可以挂载多个设备,那么stm32如何确定是哪个外设正在跟我进行通信呢。对于此,那些生产外设模块的厂商们就约定,要是这个设备使用IIC协议进行通信,那么就要给这个设备指定一个器件地址,以供芯片访问。这个器件地址会在你购买其模块的时候在使用手册上注明。所以,要跟哪个模块通信,就一定要通过查阅其使用手册,找到它的器件地址。
在上文所述的最开始的一个字节的数据传输过程中,这一个数据往往是器件地址。这样,对应的外设才知道,是要跟我进行通信。读取数据,也是同理,要想从外设中读取到数据,主机要明确三点:从哪个外设中的哪个地方读取数据,读取到的数据要存到哪里。
在开始读数据之前,主机必须要先给外设发器件地址,数据所在的地址,外设才会知道你要从该地址读取数据,从而把数据通过SDA线传出来。至于具体的每个字节的传输过程,和上面所讲的从主机到外设的过程差不多,只不过反了一个反向而已,并且主机的等待应答变成了主动应答。
void IIC_setSDAMode_In()
{
GPIO_InitTypeDef GPIO_IIC;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
GPIO_IIC.GPIO_Mode = GPIO_Mode_IN; //输出
GPIO_IIC.GPIO_Pin = GPIO_Pin_15; //引脚
GPIO_IIC.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOE, &GPIO_IIC);
}
void IIC_Start()
{
IIC_setSDAMode_Out();
IIC_SDA_OUT(1); //总线释放状态
IIC_SCL_OUT(1);
delay_us(5);
IIC_SDA_OUT(0); //SDA跳变为低电平
delay_us(5);
IIC_SCL_OUT(0);
delay_us(5);
}
void IIC_Stop()
{
IIC_setSDAMode_Out();
IIC_SDA_OUT(0);
IIC_SCL_OUT(0);
delay_us(5);
IIC_SCL_OUT(1); //SDA跳变为高电平
delay_us(5);
IIC_SDA_OUT(1);
delay_us(5);
}
/*
主机写入数据到外设中
参数值:
data 要写入的一个字节
返回值:NULL
*/
void IIC_writeByte(u8 data)
{
IIC_setSDAMode_Out();
IIC_SCL_OUT(0); //只有时钟线拉低,SDA上的数据才允许写入
delay_us(5);
//将数据一位一位的发出去
for(int i =0;i<8;i++)
{
if(data&(0x1<<(7-i))) //高位先入
{
IIC_SDA_OUT(1);
}
else
{
IIC_SDA_OUT(0);
}
IIC_SCL_OUT(1); //让外设读取数据
delay_us(5);
IIC_SCL_OUT(0); //重新拉低,准备写入下一位数据
delay_us(5);
}
}
u8 IIC_readByte()
{
u8 data = 0;
IIC_setSDAMode_In();
IIC_SCL_OUT(0); //先拉低,为读取数据做准备
delay_us(5);
for(int i=0;i<8;i++)
{
IIC_SCL_OUT(1); // SCL为高期间才可以读取数据
delay_us(5);
if(IIC_SDA_IN)
{
data|=(0x01<<(7-i));
}else{
data &= ~(0x1<<(7-i));
}
IIC_SCL_OUT(0);
delay_us(5);
}
return data;
}
u8 IIC_waitAck()
{
u8 ack =0;
IIC_setSDAMode_In();
IIC_SCL_OUT(0); //准备时序
delay_us(5);
IIC_SCL_OUT(1);
delay_us(5);
if(IIC_SDA_IN)
{
ack =1;
}
else
{
ack =0;
}
IIC_SCL_OUT(0); //拉低,表示应答完成
delay_us(5);
return ack;
}