STM32 IIC通信总结
IIC串行通信协议,是一种同步、半双工的通信方式。用于连接微控制器和外部设备,如传感器、存储器、显示器等,是一种简单灵活的通信协议。
IIC的基本原理
1、双线制:串行时钟线(SCL)和串行数据线(SDA)。SDA上的数据在SCL的时钟信号下进行同步传输。
2、主从结构:IIC通信中,设备分为主设备(通常是微控制器)和从设备(如传感器、显示器等),主设备发送通信和控制通信的时序,从设备响应主设备的指令。
IIC通信基本过程
1、开始信号:SDA 线从高电平转为低电平,而SCL线保持高电平。
2、发送地址和读写位:主设备发送目标设备的地址,并指定是读取数据还是写入数据。
3、接收应答:从设备在接收到正确的地址后,会在下一个时钟脉冲期间将SDA拉低,发送一个应答(ACK)信号。
4、数据传输:主设备和从设备根据读写位的设置进行数据传输。在写操作中,主设备发送数据;在读操作中,从设备发送数据。
5、发送应答:在每次字节传输后,接收方(无论是主设备还是从设备)需要发送一个应答信号。如果接收方准备好接收下一个字节,它会发送ACK;如果传输应该停止,它会发送NACK
6、重复开始条件:如果主设备想要在不释放SDA和SCL线的情况下改变传输方向(从写变读或从读变写),它会发送一个重复开始条件。和开始信号类似。如可以在写入操作后立即发送重复开始条件,随后发送从设备的地址和读操作的位。
7、停止信号:主设备通过在SCL为高电平时将SDA从低电平拉高到高电平,结束当前的I2C通信。这表示通信的结束。
IIC时序
开始和停止信号
1、当SCL线是高电平时,SDA线从高电平向低电平切换,表示通讯的开始。
2、当SCL线是高电平时,SDA线从低电平向高电平切换,表示通讯的停止。
开始和停止信号一般由主机产生。
应答信号
IIC的数据和地址传输都带响应,响应包扣“应答(ACK)”和“非应答(NACK)"两种信号。
作为数据接收端时,当设备(无论住从机)接收到IIC传输的一个字节数据或地址后,如果希望对方继续发送数据,则需要对方发送一个应答“ACK”信号,发送方就会继续发送下一个数据。
若接收方希望结束数据传输,则需要对方发送非应答信号“NACK”,发送方接收到该信号后就会产生一个停止信号,结束信号传输。
传输时主机产生时钟,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。
数据有效性
IIC使用SDA信号来传输数据,使用SCL信号线进行数据同步,SDA数据线在SCL的每个时钟周期传输一位数据。
传输时,
当SCL为高电平时,SDA表示的数据有效,即在此时的SDA为高电平时表示数据1,低电平时表示数据0。
当SCL为低电平时,SDA表示的数据无效,一般在这个时候进行SDA的电平切换,为下一次的表示数据做好准备。
每次数据传输都以字节为单位,每次传输的字节数不受限制。
软件模拟IIC协议
软件IIC优点:1、灵活性 2、适用性 3、移植性
iic.h
#ifndef __OLED_IIC_H
#define __OLED_IIC_H
#define OLED_RCC_IIC_SCL RCC_AHB1Periph_GPIOE //端口时钟
#define OLED_IIC_SCL_PORT GPIOE //端口号
#define OLED_IIC_SCL GPIO_Pin_3 //引脚
#define OLED_RCC_IIC_SDA RCC_AHB1Periph_GPIOE
#define OLED_IIC_SDA_PORT GPIOE
#define OLED_IIC_SDA GPIO_Pin_4
//io操作
#define OLED_IIC_SCL_H GPIO_SetBits(OLED_IIC_SCL_PORT,OLED_IIC_SCL); //SCL置1
#define OLED_IIC_SCL_L GPIO_ResetBits(OLED_IIC_SCL_PORT,OLED_IIC_SCL);//SCL置0
#define OLED_IIC_SDA_H GPIO_SetBits(OLED_IIC_SDA_PORT,OLED_IIC_SDA); //SDA置1
#define OLED_IIC_SDA_L GPIO_ResetBits(OLED_IIC_SDA_PORT,OLED_IIC_SDA);//SDA置0
#define OLED_READ_SDA GPIO_ReadInputDataBit(OLED_IIC_SDA_PORT,OLED_IIC_SDA)//读取SDA输入引脚电平
void OLED_IIC_GPIO_Init(void);
void OLED_IIC_SDA_OUT(void);
void OLED_IIC_SDA_IN(void);
void OLED_IIC_Start(void);
void OLED_IIC_Stop(void);
void OLED_IIC_ACK(void);
void OLED_IIC_NACK(void);
void OLED_IIC_SendByte(uint8_t data);
uint8_t OLED_IIC_ReadByte(uint8_t ack);
uint8_t OLED_IIC_WaitACK(void);
iic.c
#include "iic.h"
void OLED_IIC_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO时钟
RCC_AHB1PeriphClockCmd(OLED_RCC_IIC_SCL, ENABLE);
RCC_AHB1PeriphClockCmd(OLED_RCC_IIC_SDA, ENABLE);
//SCL GPIO初始化
GPIO_InitStructure.GPIO_Pin=OLED_IIC_SCL;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_OD; //开漏输出
GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
GPIO_Init(OLED_IIC_SCL_PORT, &GPIO_InitStructure);
//SDA GPIO初始化
GPIO_InitStructure.GPIO_Pin=OLED_IIC_SDA;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_OD; //开漏输出
GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
GPIO_Init(OLED_IIC_SDA_PORT, &GPIO_InitStructure);
OLED_IIC_SCL_H;
OLED_IIC_SDA_H;
}
//配置SDA数据线为输出
void OLED_IIC_SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//SDA GPIO初始化
GPIO_InitStructure.GPIO_Pin=OLED_IIC_SDA;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_OD; //开漏输出
GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
GPIO_Init(OLED_IIC_SDA_PORT, &GPIO_InitStructure);
}
//配置SDA数据线为输入
void OLED_IIC_SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//SDA GPIO初始化
GPIO_InitStructure.GPIO_Pin=OLED_IIC_SDA;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉,仅对输入有效
GPIO_Init(OLED_IIC_SDA_PORT, &GPIO_InitStructure);
}
//IIC时序信号
//IIC开始信号
void OLED_IIC_Start(void)
{
OLED_IIC_SDA_OUT();
OLED_IIC_SCL_H;
OLED_IIC_SDA_H;
Delay_us(4);//让高电平保持稳定
OLED_IIC_SDA_L; // 拉低SDA
Delay_us(4);
OLED_IIC_SCL_L; //钳住IIC总线,准备发送或者接收数据
}
//IIC停止信号
void OLED_IIC_Stop(void)
{
OLED_IIC_SDA_OUT();
OLED_IIC_SCL_H;
OLED_IIC_SDA_L; //SAD低
Delay_us(4);//等待时序稳定
OLED_IIC_SDA_H; //SDA高
}
//IIC应答
void OLED_IIC_ACK(void)
{
OLED_IIC_SDA_OUT();
OLED_IIC_SCL_L; //在SCL低电平的时候,SDA可以进行数据切换(1和0切换)
OLED_IIC_SDA_L; //SDA低电平表示应答型号
Delay_us(1);//让电平稳定
OLED_IIC_SCL_H; //拉高SCL,表示此时SDA的数据有效
Delay_us(1);
OLED_IIC_SCL_L; //拉低SCL,表示SCL一个周期结束
}
//IIC非应答
void OLED_IIC_NACK(void)
{
OLED_IIC_SDA_OUT();
OLED_IIC_SCL_L; //在SCL低电平的时候,SDA可以进行数据切换(1和0切换)
OLED_IIC_SDA_H; //SDA高电平表示非应答型号
Delay_us(1);//让电平稳定
OLED_IIC_SCL_H; //拉高SCL,表示此时SDA的数据有效
Delay_us(1);
OLED_IIC_SCL_L; //拉低SCL,表示SCL一个周期结束
}
//IIC等待应答 ,返回0表示应答,返回1表示非应答
uint8_t OLED_IIC_WaitACK(void)
{
OLED_IIC_SDA_IN();
uint8_t temp=0;
OLED_IIC_SDA_H;
Delay_us(1);
OLED_IIC_SCL_H;
Delay_us(1);
while(OLED_READ_SDA)
{
temp++;
if(temp>250)
{
OLED_IIC_Stop();
return 1;
}
}
OLED_IIC_SCL_L;
return 0;
}
//IIC发送一个字节
void OLED_IIC_SendByte(uint8_t data)
{
OLED_IIC_SDA_OUT();
OLED_IIC_SCL_L; //在SCL低电平的时候,SDA可以进行数据切换(1和0切换)
uint8_t i=0;
for(i=0;i<8;i++)
{
if((data&0x80)>0) //0x80 1000 0000
{
OLED_IIC_SDA_H;
}
else
{
OLED_IIC_SDA_L;
}
OLED_IIC_SCL_H; //拉高SCL ,数据有效
Delay_us(1); //延时,将数据发送出去
OLED_IIC_SCL_L;
Delay_us(1);
data<<=1;
}
}
//IIC读取一个字节
uint8_t OLED_IIC_ReadByte(uint8_t ack) //ack 1 应答 0 非应答
{
OLED_IIC_SDA_IN();
uint8_t i=0;
uint8_t receive=0;
for(i=0;i<8;i++)
{
OLED_IIC_SCL_L;
Delay_us(1);
OLED_IIC_SCL_H;
receive<<=1;
if(OLED_READ_SDA)
{
receive++;
}
Delay_us(1);
}
if(ack)
OLED_IIC_ACK();
else
OLED_IIC_NACK();
return receive;
}
作者:成700