超详细!新手必看!STM32基础-IIC串行通信协议-IO口模拟IIC操作AT24C02


  
  IIC(Inter-Integrated Circuit)是一种常用的串行通信协议,通常用于连接微控制器和各种外部设备(例如传感器、存储器、显示器等)。

●IIC、UART、SPI的比较:

通信协议UARTIICSPI
通信特征异步串行全双工同步串行半双工同步串行全双工
接口TX、RXSCL、SDAMOSI、MISO、SCL、CS/NSS
速度多种波特率100Khz、400Khz、3.4Mhz
数据帧格式起始位+数据位+校验位+停止位起始条件+位传输+应答+停止条件四种模式:MODE0~MODE3
主从设备通信没有主从有主从有主从
总线结构一对一一对多一对多

一、知识点

在这里插入图片描述
上图中上拉电阻的作用:使IIC总线空闲时,SDA与SCL都为高电平状态。


  1. 12C总线两线制包括: 串行数据SDA (Serial Data)、串行时钟SCL(Serial Clock)。

  2. I2C总线上有主机和从机之分,可以有多个主机和多个从机。IIC总线上的电容不超过400pf,只要不超过这个电容量,任意挂多少个从机都可以。但是在通信的时刻,只能有一个作为主机,其他的都为从机。

  3. 器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态(都可以作为发送器和接收器)。从机永远不会主动给主机发送数据

  4. 时钟线SCL必须由主机控制主机控制时钟线SCL产生跳变沿,从而控制主机是接收数据还是发送数据。 谁控制SCL时钟线,谁就是主机!

  5. 每个IIC设备都有一个唯一的地址。在通信开始之前,你需要知道要与主机进行通信的设备的地址。每个设备都有一个唯一的7位地址(4位固定地址+3位可编程地址),用于在总线上区分不同的设备。注:有些从设备的7位地址商家已经给固定好了,所以就不需要自行设计,查看商家给的手册就可以知道该地址。
在这里插入图片描述
如:AT24C02芯片中的A0、A1、A2就是可编程地址位,可以自行设计该位。下图中A0、A1、A2接地,则都为0。
在这里插入图片描述

  6. 数据的有效性: 时钟线产生下降沿,发送方准备/发送数据。时钟线产生上升沿,接收方采集数据。
   SDA数据线在 SCL 的每个时钟周期传输一位数据。传输时:
  (1)SCL 为高电平的时候 SDA 表示的数据有效,即此时的 SDA 为高电平时表示数据 “1”,为低电平时表示数据“ 0”。
  (2)当 SCL为低电平时,SDA 的数据无效,一般在这个时候 SDA 进行电平切换,为下一次表示数据做好准备。
在这里插入图片描述

二、IIC通信全流程

1. 硬件连接

  • 确保正确连接了I2C总线的两根线(SCL - 时钟线,SDA - 数据线)到目标设备。
  • 确保目标设备有正确的供电。

2. 初始化I2C模块

  • 首先,初始化I2C模块,SDA\SCL模式,GPIO等功能参数。

  • 引脚模式设置:

  • 方案一
    SCL–推挽输出模式。
    主机写数据: SDA–推挽输出模式。
    主机读数据: SDA–上拉输入模式。

void SDA_OUT(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	//GPIOB9初始化设置
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_Out_PP;//推挽输出模式  
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
	GPIO_SetBits(GPIOB, GPIO_Pin_7);	//上拉
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
}
void SDA_IN(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	//GPIOB9初始化设置
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入模式
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
}
  • 方案二
    SCL、SDA–开漏输出模式。

这个文章我们使用方案一进行演示。

3. 选择目标设备地址(制造商预先分配,查看手册获取)

  • 每个I2C设备都有一个唯一的地址。在通信开始之前,你需要知道你要与之通信的设备的地址。每个设备都有一个唯一的7位地址,用于在总线上区分不同的设备。

4. 主机发送起始信号

  • 主机发送一个启动信号来开始I2C通信(唤醒所有的从机设备)。起始信号的生成标志着I2C通信的开始,接着就可以发送目标设备的地址和读写位来开始通信。

  • 步骤:SCL为高电平时,SDA维持超过4us的高电平时间,SDA再变为低电平(起始信号),再维持超过4.7us的低电平时间。这就是启动信号的完整步骤。在这里插入图片描述

void IIC_Start()
{
	SCL=1;
	SDA=1;
	delay_us(5); //至少为4us,所以5us也可以
	
	SDA=0;
	delay_us(5); //至少为4。7us,所以5us也可以
	SCL=0;  //发送方准备数据
}

5. 主机发送目标设备地址和读/写位

  • 发送目标设备的地址和指定读写方向。
    注:跟哪个从机通讯,把从机的地址发出去。发送数据是8个bit,这8个bit位中前7个bit位是从机的地址,最后1个bit位是用来表示读或者写的。1表示读,0表示写;这个过程相当于主机往SDA上发了8个bit的数据。主要是为了寻找与其通信的从机。
    ●详细过程看过程7!!

6. 是否应答信号

  • 当主机找到需要通信的从机后,从机需要回复应答信号。应答信号:0,非应答信号:1。

(1)主机给从机发送应答信号

void IIC_Send_Ack(u8 ack) //主机给从机发送应答信号
{
    SDA_OUT(); //SDA数据线为输出模式
	SCL=0;    //发送方准备数据
	if(ack) SDA=1; //非应答
	else SDA=0;   //应答信号
	delay_us(5);
    
	SCL = 1;
	delay_us(5);
	SCL = 0;
}

(2)主机接收(等待)从机的应答信号

在这里插入图片描述
   从上图中可以看出,在保持SCL高电平的状态下,通过读取SDA线的电平状态来判断从机是否应答,由于SDA默认是为高信号(即非应答),所以通过从机操作SDA线,将SDA线拉低则视为应答,而不动则视为非应答。实现代码如下:

#define 	READ_SDA   	PBin(7)  //读取SDA的状态 与GPIO_ReadInputDataBit()函数功能相同

uint8_t IIC_Receive_Ack(void) //主机接收从机的应答信号
{
    uint8_t ucErrorTime = 0;
    SDA_IN(); //SDA数据线为输入模式
    
    SDA=1;   //释放数据线
    delay_us(5); //低电平保持时间
	SCL=1;
	delay_us(5); //低电平保持时间
	
    while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7) == 1){   //SDA为高电平,一直为非应答状态, 
        ucErrorTime++;                                      //上行代码可替换为  while(READ_SDA){}  同理
        if(ucErrorTime > 250){ //当出错时间超过时
            IIC_Stop();    //停止信号
            return 1;//未接收到应答
        }
    }
	SCL = 0;
	return 0;   //接收到应答
}

7. 读取/写入数据

时钟线SCL产生下降沿,发送方准备/发送数据。时钟线SCL产生上升沿,接收方采集数据。

  • 如果是写入操作,发送要写入的数据到目标设备。由主机来控制SDA的电平变化
  • 如果是读取操作,从目标设备读取数据。那就由从机来控制SDA的电平变化;
  • 每次8bit的数据传输完成,都要有个应答信号,谁接收数据,谁来应答。
    最后一位:读:1 , 写:0

  ●如果你已知从机的七位地址(0111 100)。
  要进行写操作:Write_IIC_Byte (0x78); // (0111 1000)
  要进行读操作:Write_IIC_Byte (0x79); // (0111 1001)

(1)主机写(发送)数据给从机

时钟线SCL产生下降沿,发送方准备/发送数据。注:SDA设置为推挽输出模式,且电平拉高。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(1)首先将时钟线 SCL 拉低,准备传输数据。
(2)循环发送8位数据。从数据的最高位开始发送。
(3)每发送完一个bit位的数据就要产生一个时钟脉冲。如:SCL先拉高再拉低。

//==============================================================================================*/
void Write_IIC_Byte(unsigned char IIC_Byte)
{
	unsigned char i;  //用于循环
	unsigned char maxbit;
	unsigned char date;  //要发送的数据
	date = IIC_Byte;
	SCL=0;     //发送方准备发送数据
	for(i=0;i<8;i++)		
	{
		maxbit=date&0x80;  //0x80:1000 0000   高位开始发送
		if(maxbit==0x80) SDA = 1;   //写入数据1
		else        SDA = 0;        //写入数据0
		
		date=date<<1;   //从最高位依次发送出去
	   //发送时钟脉冲	
		SCL = 1;
		SCL = 0;
	}
}

  

(2)主机从“从机”读取数据

时钟线SCL产生上升沿,接收方采集(读取)数据。注:SDA:设置为上拉输入模式!
(1)首先将时钟线 SCL 拉高,准备读取数据。
(2)循环接收8位数据。从数据的最高位开始接收。
(3)每接收完一个bit位的数据就要产生一个时钟脉冲。如:SCL先拉低再拉高。

在这里插入图片描述

在这里插入图片描述

unsigned char Read_IIC_Byte(void)
{
    unsigned char i;
    unsigned char received_byte = 0;
    
    SCL = 0; // 发送方准备发送数据
    
    for(i = 0; i < 8; i++)		
    {
        received_byte =received_byte << 1; // 左移一位准备接收数据
        
        SCL = 1; // 拉高SCL,接收方准备接收数据
        if(SDA)   // 读取SDA线的状态,如果为高则表示接收到了1
        {
            received_byte |= 0x01; // 设置最低位为1
        }
        SCL = 0; // 拉低SCL,准备接收下一个数据位
    } 
    return received_byte;
}

8. 发送停止信号

  • 发送停止信号以结束I2C通信。
  • SCL(时钟线)为高电平时,SDA(数据线)由低电平变为高电平,表示通信结束。发送完停止信号后,SDA、SCL都为高电平状态(初始状态由于上拉电阻也为高电平状态)。
    **加粗样式**
void IIC_Stop()
{
    SCL=1;
    SDA=0;
    delay_us(5); //至少为4.7us
	SDA=1;	
	delay_us(5); //至少为4.7us
}

9. 处理接收的数据

  • 如果你进行了读取操作,确保处理接收到的数据。
    此外,确保在进行I2C通信时,你了解所连接设备的I2C协议和通信规范,包括设备的地址和数据传输格式等。

三、实验(向AT24C02中写入一个字节与读取一个字节)

  操作前提:要对AT24C02进行操作,需要查看AT24C02的芯片手册再进行下面的操作。下图就是手册中通信方式的内容。

  如果已知从机AT24C02的七位地址(1010 000)
  要进行写操作:Write_IIC_Byte (0xA0); // (1010 0000)
  要进行读操作:Write_IIC_Byte (0xA1); // (1010 0001)

在这里插入图片描述
在这里插入图片描述


void Write_IIC_Data(uint8_t addr, uint8_t IIC_Data)
{
	IIC_Start();     //发送起始信号
	
	Write_IIC_Byte(0xA0);  //写操作--从机的地址(包含读写位)
	IIC_Receive_Ack();	       //等待应答
	
	Write_IIC_Byte(addr);	//从机要写入数据的字地址,这里不包含读写位。
	IIC_Receive_Ack();	        //等待应答
	
	Write_IIC_Byte(IIC_Data);  //将数据写入
	IIC_Receive_Ack();	
	
	IIC_Stop();
}


在这里插入图片描述
在这里插入图片描述

/* 往从机AT24C02中读取一个字节 */
uint8_t at24c02_Read_One_Byte(uint8_t addr)
{
    uint8_t read_data = 0;
      
    IIC_Start();             // 1、发送起始信号 
    
    Write_IIC_Byte(0xA0);   // 2、发送通讯地址(写操作地址) 
    IIC_Receive_Ack();        // 3、等待应答信号 
    
    Write_IIC_Byte(addr);   // 4、发送内存地址:0~255 
    IIC_Receive_Ack();        // 5、等待应答信号 
   
    IIC_Start();           // 6、发送起始信号 
    
    Write_IIC_Byte(0xA1);   // 7、发送通讯地址(读操作地址) 
    IIC_Receive_Ack();        // 8、等待应答信号 
    
    read_data = Read_IIC_Byte();    //9、接收数据 
    IIC_Send_Ack(1);    //发送非应答信号,0为应答
    
    IIC_Stop();    // 10、发送停止信号    
    return read;
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是STM32IIC模拟)读写AT24c02的代码: ``` #include "stm32f10x.h" #include "delay.h" #define IIC_SCL_H GPIOB->BSRR = GPIO_Pin_10 #define IIC_SCL_L GPIOB->BRR = GPIO_Pin_10 #define IIC_SDA_H GPIOB->BSRR = GPIO_Pin_11 #define IIC_SDA_L GPIOB->BRR = GPIO_Pin_11 #define IIC_SDA GPIOB->IDR & GPIO_Pin_11 void IIC_Start() { IIC_SDA_H; IIC_SCL_H; delay_us(5); IIC_SDA_L; delay_us(5); IIC_SCL_L; } void IIC_Stop() { IIC_SDA_L; IIC_SCL_H; delay_us(5); IIC_SDA_H; delay_us(5); } void IIC_Ack() { IIC_SDA_L; delay_us(5); IIC_SCL_H; delay_us(5); IIC_SCL_L; IIC_SDA_H; } void IIC_NoAck() { IIC_SDA_H; delay_us(5); IIC_SCL_H; delay_us(5); IIC_SCL_L; } uint8_t IIC_WaitAck() { uint8_t ucErrTime = 0; IIC_SDA_H; delay_us(1); IIC_SCL_H; delay_us(1); while (IIC_SDA) { ucErrTime++; if (ucErrTime > 250) { IIC_Stop(); return 1; } } IIC_SCL_L; return 0; } void IIC_SendByte(uint8_t ucByte) { uint8_t i; for (i = 0; i < 8; i++) { if ((ucByte << i) & 0x80) { IIC_SDA_H; } else { IIC_SDA_L; } delay_us(1); IIC_SCL_H; delay_us(1); IIC_SCL_L; } } uint8_t IIC_ReadByte() { uint8_t i, ucByte = 0; IIC_SDA_H; for (i = 0; i < 8; i++) { ucByte <<= 1; IIC_SCL_H; delay_us(1); if (IIC_SDA) { ucByte |= 0x01; } IIC_SCL_L; delay_us(1); } return ucByte; } void IIC_Init() { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); IIC_SDA_H; IIC_SCL_H; } void AT24C02_WriteByte(uint8_t ucAddr, uint8_t ucData) { IIC_Start(); IIC_SendByte(0xa0); IIC_WaitAck(); IIC_SendByte(ucAddr); IIC_WaitAck(); IIC_SendByte(ucData); IIC_WaitAck(); IIC_Stop(); delay_ms(10); } uint8_t AT24C02_ReadByte(uint8_t ucAddr) { uint8_t ucData; IIC_Start(); IIC_SendByte(0xa0); IIC_WaitAck(); IIC_SendByte(ucAddr); IIC_WaitAck(); IIC_Start(); IIC_SendByte(0xa1); IIC_WaitAck(); ucData = IIC_ReadByte(); IIC_NoAck(); IIC_Stop(); return ucData; } int main(void) { uint8_t ucData; IIC_Init(); AT24C02_WriteByte(0x00, 0x55); ucData = AT24C02_ReadByte(0x00); while (1); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值