STM32进阶学习(4)-以IIC通信例程看IIC传输机制

前言

外设我觉得只算是嵌入式的边边角,通信才是重头戏,串口、IIC、SPI、CAN、RS485、RS232、NBIOT、WIFI、bluetooth等等,这些我慢慢写博客逐渐学习。这里以MPU6050驱动例程看IIC传输机制。

一、IIC是什么?

1.IIC简介

IIC,即I²C,全称 Inter-Integrated Circuit,是I²C Bus简称,中文叫集成电路总线 ,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。

2. I2C通信线路连接

I2C 在硬件上的接法如下所示,主控芯片引出两条线 SCL(SCK)时钟线,SDA 数据线,在一条I2C 总线上可以接很多 I2C 设备,SDA和SCL这两根线必须要接一个上拉电阻,常用是4.7K,一般在4.7k-10k之间。
总线空闲的时候SCL和SDA处于高电平。
在这里插入图片描述

IIC总线标准模式下速度可以达到100Kb/s,快速模式下可以达到400Kb/s,高速模式可达3.4Mb/s。

3.I2C最重要的功能包括:

只需要两条总线
没有严格的波特率要求,例如使用RS232,主设备生成总线时钟
所有组件之间都存在简单的主/从关系,连接到总线的每个设备均可通过唯一的地址进行软件寻址
I2C是真正的多主设备总线,可提供仲裁和冲突检测
传输速度
标准模式:100kbit/s
快速模式:400kbit/s
高速模式:3.4Mbit/s
最大主设备数:无限制
最大从机数:理论上是127

使用I2C,可以将多个从机(Slave)连接到单个主设备(Master),并且还可以有多个主设备(Master)控制一个或者多个从机(Slave).
假如希望有多个微控制器(MCU)将数据记录到单个存储卡或将文本显示到单个LCD时,这个功能就非常有用。

4.I2C主要特点:

通常我们为了方便把I2C设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。
I2C主设备功能:主要产生时钟,产生起始信号和停止信号
I2C从设备功能:可编程的I2C地址检测,停止位检测
I2C的一个优点是它支持多主控(multi mastering),其中任何一个能够进行发送和接受的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
支持不同速率的通讯速度,标准速度为100kbit/s,快速为400kbit/s,最大为3.4Mbit/s
SCL和SDA都需要借上拉电阻(大小由速度和容性负载决定,一般在3.3K到10K之间)保证数据的稳定性,减少干扰。
I2C是半双工,而不是全双工,同一时间只可以单向通信
为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或者集电极开路(OC)输出,这一点在下面会进行讲解。

5.硬件IIC和软件IIC的区别

硬件IIC和软件IIC都是I2C总线通信的方式,但它们实现的方式有所不同:

硬件IIC:硬件IIC是通过专门的硬件模块实现的,通常是通过I2C控制器或者外部I2C芯片来实现的,具有高可靠性、传输速度快、占用CPU资源少等优点。硬件IIC协议实现比较简单,只需要在芯片上添加I2C控制器就可以实现I2C通信,适用于传输数据量较大、传输速度要求较高的场景。

软件IIC:软件IIC是通过软件模拟实现的,通常是在单片机的GPIO上实现的,具有低成本、可扩展性强等优点。软件IIC的实现需要占用CPU资源,因此传输速度相对较慢,可靠性也不如硬件IIC。软件IIC适用于传输数据量较小、传输速度要求不高的场景。

二、IIC的物理层

在这里插入图片描述
(1) 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机

扫描二维码关注公众号,回复: 16082854 查看本文章

(2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线SDA(Serial Data Line ),一条串行时钟线SCL(Serial Data Line )。数据线即用来表示数据,时钟线用于数据收发同步

(3) 总线通过上拉电阻接到电源。当 I2C 设备空闲时会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。

(4) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线,也就是设备在发送数据之前会检测I2C总线是否忙碌(忙碌总线应该为低电平)。

(5)I2C 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式。

每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问的,地址也是一个数据,主机可以同过SDA发送这个地址出去,则挂载在总线上的设备会自行匹配,匹配成功之后就可以互相通信了

(6)IIC为什么要加上拉电阻?
因为IIC具体来说,I2C总线的信号线是双向传输的,意味着既能输出数据,也能接收数据。因此,在I2C总线通信中,当SDA和SCL线上没有数据传输时,这两根线上的电平状态就变得不确定,如果没有拉电阻,就会导致电平状态不稳定,可能会出现误判的情况,影响通信的可靠性。
IIC芯片的SCL和SDA是开漏输出的,当NMOS管导通时为低电平,当截止时却是开漏状态,即使总线接入电源VCC也无法确定是否为高电平,因此:
加上拉电阻的作用是将SDA和SCL线上的电平状态拉高到正常电平,保证了电平状态的稳定性,防止了误判的情况。通常,I2C总线上的拉电阻大小为4.7kΩ左右,可以有效地保持电平状态的稳定。

三、IIC的软件协议层

1.IIC的信号时序

I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答
信号。
开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,
表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接
收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为
受控单元出现故障。
这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。IIC 总线时序图如
图:在这里插入图片描述
其实这个信号机制我一开始没有理解,现在觉得还得看代码比较好理解。

2.IIC启动、关闭、等待应答函数

//产生IIC起始信号
void IIC_Start(void)
{
    
    
	SDA_OUT();     //sda线输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void IIC_Stop(void)
{
    
    
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//发送I2C总线结束信号
	delay_us(4);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
    
    
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
    
    
		ucErrTime++;
		if(ucErrTime>250)
		{
    
    
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
    
    
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
    
    
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				 

在IIC通信中,起始和停止信号是非常重要的信号,用来标志一次完整的数据传输的开始和结束。可以看到,当我们需要开启IIC时,

记忆IIC的开启、停止和接收应答的SCL和SDA变化可以采用“一点一线,一点两线,两点两线”的方式。

具体来说,可以将IIC的开启、停止和接收应答分别拆分成三个部分,每个部分都对应着SDA和SCL的变化。例如:

1.开启一个IIC总线传输的过程:
SDA从高电平变为低电平,SCL保持高电平;
等待一段时间(如4us);
SCL从高电平变为低电平,SDA保持低电平。

2.停止一个IIC总线传输的过程:
SDA从低电平变为高电平,SCL保持高电平;
等待一段时间(如4us);
SCL从高电平变为低电平,SDA保持高电平。

3.接收应答信号的过程:
SDA从高电平变为低电平,SCL保持高电平;
等待一段时间(如4us);
SCL从高电平变为低电平,SDA保持低电平;
等待一段时间(如4us);
SCL从低电平变为高电平,SDA可以变为高电平或者低电平,用于发送应答信号。

通过这种方式,可以将IIC总线的开启、停止和接收应答的SDA和SCL的变化规律记忆下来,便于在实际的开发中应用。

3.IIC读取和发送一个字节的函数

//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{
    
                            
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {
    
                  
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
    
    
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
    
    
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

①发送一个字节的逻辑:

这段代码是一个基于单片机的I2C总线发送函数,用于发送8位数据。具体来说,它通过循环8次的方式,逐位发送数据到I2C总线上。下面对代码的每一行进行解释:

u8 t;
定义一个无符号8位整型变量t,用于循环计数。

SDA_OUT();
将SDA线设置为输出模式。

IIC_SCL=0;
将I2C总线的时钟信号SCL拉低,准备发送数据。

for(t=0;t<8;t++)
循环8次,每次发送一位数据。

IIC_SDA=(txd&0x80)>>7;
将要发送的数据的最高位取出,放到SDA线上,然后将其左移7位,为发送下一位数据做准备。

txd<<=1;
将要发送的数据左移一位,为发送下一位数据做准备。

delay_us(2);
延时2微秒,等待数据稳定。

IIC_SCL=1;
将I2C总线的时钟信号SCL拉高,发送数据。

delay_us(2);
延时2微秒,等待数据稳定。

IIC_SCL=0;
将I2C总线的时钟信号SCL拉低,准备发送下一位数据。

delay_us(2);
延时2微秒,等待数据稳定。

通过以上几步操作,每次循环都可以向SDA线上发送一位数据。循环8次后,8位数据都被发送到了I2C总线上。需要注意的是,在使用该函数时,需要先将SDA线拉高,以确保总线处于空闲状态,否则可能会出现通信错误。此外,代码中的3个延时都是必要的,因为I2C总线的时序比较严格,需要满足一定的时序要求才能正常通信。

②接收一个字节的逻辑:

这段代码是一个基于单片机的I2C总线接收函数,用于接收8位数据。具体来说,它通过循环8次的方式,逐位读取I2C总线上的数据,并将其存储到变量receive中。下面对代码的每一行进行解释:

for(i=0;i<8;i++ )
这是一个for循环语句,用于循环8次,每次读取一位数据。

IIC_SCL=0;
将I2C总线的时钟信号SCL拉低,准备接收数据。

delay_us(2);
延时2微秒,等待数据稳定。

IIC_SCL=1;
将I2C总线的时钟信号SCL拉高,开始读取数据。

receive<<=1;
将变量receive左移一位,为接收下一位数据做准备。

if(READ_SDA)receive++;
如果当前SDA线为高电平,则将receive的最低位设为1,否则为0。

delay_us(1);
延时1微秒,等待数据稳定。

通过以上几步操作,每次循环都可以接收到一位数据,并将其存储到变量receive中。循环8次后,变量receive中就存储了8位数据,可以用于后续的处理。需要注意的是,在使用该函数时,需要先将SDA线拉高,以确保总线处于空闲状态,否则可能会出现通信错误。

四、以正点原子IIC例程解析IIC传输作用

1.实验目的:利用 STM32F1 的普通 IO 口模拟 IIC 时序,并实现和 24C02 之间的双向通信。

2.24C02介绍

24C02是一种2K位(256字节)串行I2C EEPROM存储器芯片。它支持I2C总线协议,可以使用两个引脚(SCL和SDA)进行通信控制。它具有低功耗、高可靠性、容易集成等特点,广泛应用于计算机、通讯、消费电子、汽车电子等领域。

24C02的引脚功能描述如下:
A0/A1/A2:地址输入引脚,用于设置芯片的I2C从机地址,可以通过将它们连接到VSS或VCC来选择不同的地址。
SDA:串行数据输入输出引脚,用于双向传输数据。
SCL:串行时钟输入引脚,用于同步数据传输。
VSS:电源地引脚。
VCC:电源正引脚。
24C02的通信协议是基于I2C总线协议的,需要通过SCL和SDA两个引脚进行控制。具体的通信步骤包括:
1.发送起始信号(Start);
2.发送I2C从机地址和读写方向(Write或Read);
3.发送寄存器地址;
4.发送数据;
5.发送停止信号(Stop)。
需要注意的是,在每个字节的传输过程中,都需要等待从机的ACK信号,以确保数据传输的正确性。

3.原始例程展示

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "usmart.h"	 
#include "24cxx.h"	 

			 	
//要写入到24c02的字符串数组
const u8 TEXT_Buffer[]={
    
    "Elite STM32 IIC TEST"};
#define SIZE sizeof(TEXT_Buffer)	
	
 int main(void)
 {
    
    	 
	u8 key;
	u16 i=0;
	u8 datatemp[SIZE];
	delay_init();	    	 //延时函数初始化	  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200
	LED_Init();		  		//初始化与LED连接的硬件接口
	LCD_Init();			   	//初始化LCD 	
	KEY_Init();				//按键初始化		 	 	
	AT24CXX_Init();			//IIC初始化 

 	POINT_COLOR=RED;//设置字体为红色 
	LCD_ShowString(30,50,200,16,16,"Elite STM32");	
	LCD_ShowString(30,70,200,16,16,"IIC TEST");	
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2015/1/15");	
	LCD_ShowString(30,130,200,16,16,"KEY1:Write  KEY0:Read");	//显示提示信息		
 	while(AT24CXX_Check())//检测不到24c02
	{
    
    
		LCD_ShowString(30,150,200,16,16,"24C02 Check Failed!");
		delay_ms(500);
		LCD_ShowString(30,150,200,16,16,"Please Check!      ");
		delay_ms(500);
		LED0=!LED0;//DS0闪烁
	}
	LCD_ShowString(30,150,200,16,16,"24C02 Ready!");    
 	POINT_COLOR=BLUE;//设置字体为蓝色	  
	while(1)
	{
    
    
		key=KEY_Scan(0);
		if(key==KEY1_PRES)//KEY_UP按下,写入24C02
		{
    
    
			LCD_Fill(0,170,239,319,WHITE);//清除半屏    
 			LCD_ShowString(30,170,200,16,16,"Start Write 24C02....");
			AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
			LCD_ShowString(30,170,200,16,16,"24C02 Write Finished!");//提示传送完成
		}
		if(key==KEY0_PRES)//KEY1按下,读取字符串并显示
		{
    
    
 			LCD_ShowString(30,170,200,16,16,"Start Read 24C02.... ");
			AT24CXX_Read(0,datatemp,SIZE);
			LCD_ShowString(30,170,200,16,16,"The Data Readed Is:  ");//提示传送完成
			LCD_ShowString(30,190,200,16,16,datatemp);//显示读到的字符串
		}
		i++;
		delay_ms(10);
		if(i==20)
		{
    
    
			LED0=!LED0;//提示系统正在运行	
			i=0;
		}		   
	}
}

4.逐步分析(main.c函数中的非while部分)

int main(void)
 {
    
    	
    u8 key;
	u16 i=0;
	u8 datatemp[SIZE];
	delay_init();	    	 //延时函数初始化	  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200
	LED_Init();		  		//初始化与LED连接的硬件接口
	LCD_Init();			   	//初始化LCD 	
	KEY_Init();				//按键初始化		 	 	
	AT24CXX_Init();			//IIC初始化 

 	POINT_COLOR=RED;//设置字体为红色 
	LCD_ShowString(30,50,200,16,16,"Elite STM32");	
	LCD_ShowString(30,70,200,16,16,"IIC TEST");	
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2015/1/15");	
	LCD_ShowString(30,130,200,16,16,"KEY1:Write  KEY0:Read");	//显示提示信息		
 	while(AT24CXX_Check())//检测不到24c02
	{
    
    
		LCD_ShowString(30,150,200,16,16,"24C02 Check Failed!");
		delay_ms(500);
		LCD_ShowString(30,150,200,16,16,"Please Check!      ");
		delay_ms(500);
		LED0=!LED0;//DS0闪烁
	}
	LCD_ShowString(30,150,200,16,16,"24C02 Ready!");    
 	POINT_COLOR=BLUE;//设置字体为蓝色	 		

这段代码是一个STM32的程序,主要初始化相关硬件接口和外设,然后通过按键控制AT24C02进行读写操作。来看IIC初始化函数。

(1)AT24CXX_Init(); 检查IIC通道是否初始化

void IIC_Init(void)
{
    
    					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//使能GPIOB时钟
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 输出高
}

就是简单的使用GPIO口模拟IIC的时序,称作软件IIC,这里让PB6/PB7作为SCL和SDA。

(2)AT24CXX_Check(void)-检查AT24C是否正常

//检查AT24CXX是否正常
//这里用了24XX的最后一个地址(255)来存储标志字.
//如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
u8 AT24CXX_Check(void)
{
    
    
	u8 temp;
	temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX			   
	if(temp==0X55)return 0;		   
	else//排除第一次初始化的情况
	{
    
    
		AT24CXX_WriteOneByte(255,0X55);
	    temp=AT24CXX_ReadOneByte(255);	  
		if(temp==0X55)return 0;
	}
	return 1;											  
}

这段代码是AT24C02 EEPROM存储器的检测函数。主要功能是检测AT24C02是否正常工作,具体的实现方法是在AT24C02的最后一个地址(地址为255)写入一个固定的数据(0x55),然后再读出来进行比对。如果读出来的数据和写入的数据相同,则表示AT24C02正常,函数返回0;否则,表示AT24C02异常,函数返回1。那么,这个读数据与写数据的过程就利用到了IIC的读写机制以及C语言的位操作

①AT24CXX_ReadOneByte(u16 ReadAddr)

//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址  
//返回值  :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
    
    				  
	u8 temp=0;		  	    																 
    IIC_Start();  //开启IIC
	if(EE_TYPE>AT24C16)
	{
    
    
		IIC_Send_Byte(0XA0);	   //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(ReadAddr>>8);//发送高地址
		IIC_Wait_Ack();		 
	}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据 	 

	IIC_Wait_Ack(); 
    IIC_Send_Byte(ReadAddr%256);   //发送低地址
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte(0XA1);           //进入接收模式			   
	IIC_Wait_Ack();	 
    temp=IIC_Read_Byte(0);		   
    IIC_Stop();//产生一个停止条件	    
	return temp;
}

②AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)

//在AT24CXX指定地址写入一个数据
//WriteAddr  :写入数据的目的地址    
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
    
    				   	  	    																 
    IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
    
    
		IIC_Send_Byte(0XA0);	    //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(WriteAddr>>8);//发送高地址
 	}else
	{
    
    
		IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据 
	}	 
	IIC_Wait_Ack();	   
    IIC_Send_Byte(WriteAddr%256);   //发送低地址
	IIC_Wait_Ack(); 	 										  		   
	IIC_Send_Byte(DataToWrite);     //发送字节							   
	IIC_Wait_Ack();  		    	   
    IIC_Stop();//产生一个停止条件 
	delay_ms(10);	 
}

分析:在实际应用中,I2C设备通常会有一个地址寄存器,用于存储设备的地址信息。在进行I2C通信时,需要先向设备发送一个地址,然后才能开始进行数据传输。由于地址通常会占用多个字节,因此需要将地址分成高位和低位,分别发送到设备上
在这段代码中,

IIC_Send_Byte(WriteAddr>>8);//发送高地址

使用了一个右移运算符(>>)将地址WriteAddr向右移动8位,这样就得到了地址的高8位。然后,使用IIC_Send_Byte函数将地址的高8位发送到I2C设备上。

IIC_Send_Byte(WriteAddr%256); //发送低地址

使用了一个取模运算符(%)将地址WriteAddr除以256取余数,这样就得到了地址的低8位

5.分析while循环中的内容

while(1)
	{
    
    
		key=KEY_Scan(0);
		if(key==KEY1_PRES)//KEY_UP按下,写入24C02
		{
    
    
			LCD_Fill(0,170,239,319,WHITE);//清除半屏    
 			LCD_ShowString(30,170,200,16,16,"Start Write 24C02....");
			AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
			LCD_ShowString(30,170,200,16,16,"24C02 Write Finished!");//提示传送完成
		}
		if(key==KEY0_PRES)//KEY1按下,读取字符串并显示
		{
    
    
 			LCD_ShowString(30,170,200,16,16,"Start Read 24C02.... ");
			AT24CXX_Read(0,datatemp,SIZE);
			LCD_ShowString(30,170,200,16,16,"The Data Readed Is:  ");//提示传送完成
			LCD_ShowString(30,190,200,16,16,datatemp);//显示读到的字符串
		}
		i++;
		delay_ms(10);
		if(i==20)
		{
    
    
			LED0=!LED0;//提示系统正在运行	
			i=0;
		}		   
	}

首先定义一个存储在flash里面的数组:

//要写入到24c02的字符串数组
const u8 TEXT_Buffer[]={
    
    "Elite STM32 IIC TEST"};
#define SIZE sizeof(TEXT_Buffer)	

核心是这两个函数:

①AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);

//在AT24CXX里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为0~255
//pBuffer   :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
    
    
	while(NumToWrite--)
	{
    
    
		AT24CXX_WriteOneByte(WriteAddr,*pBuffer);//写入的地址和写入的数据
		WriteAddr++;
		pBuffer++;
	}
}
AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);

这段代码是一个写入24C02芯片的函数,用于将一段数据写入到24C02芯片的指定地址处。具体来说,该函数接收三个参数:WriteAddr表示要写入的起始地址,pBuffer表示要写入的数据缓冲区的指针,NumToWrite表示要写入的数据长度。下面对函数的每一行进行解释:

while(NumToWrite–)
使用while循环,从要写入的数据长度中依次取出每个字节,直到所有数据都被写入。
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
调用AT24CXX_WriteOneByte函数,将pBuffer指针所指向的数据写入到24C02芯片的WriteAddr地址处。
WriteAddr++;
将写入地址加1,以便将下一个字节写入到24C02芯片的下一个地址处。
pBuffer++;
将pBuffer指针向后移动一个字节,以便将下一个字节写入到24C02芯片中。

AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
调用AT24CXX_Write函数,将TEXT_Buffer数组中的数据写入到24C02芯片的0地址处。

这样的话,就把TEXT_Buffer字符串数组写入了24C02的从地址0开始的空间去。

②AT24CXX_Read(0,datatemp,SIZE);

//在AT24CXX里面的指定地址开始读出指定个数的数据
//ReadAddr :开始读出的地址 对24c02为0~255
//pBuffer  :数据数组首地址
//NumToRead:要读出数据的个数
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
    
    
	while(NumToRead)
	{
    
    
		*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);	
		NumToRead--;
	}
}  

这段代码是一个从24C02芯片中读取数据的函数,用于将24C02芯片中的一段数据读取到指定的缓冲区中。具体来说,该函数接收三个参数:ReadAddr表示要读取的起始地址,pBuffer表示要读取数据的缓冲区的指针,NumToRead表示要读取的数据长度。下面对函数的每一行进行解释:

while(NumToRead)
使用while循环,从要读取的数据长度中依次取出每个字节,直到所有数据都被读取。

*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
调用AT24CXX_ReadOneByte函数,从24C02芯片的ReadAddr地址处读取一个字节,并将读取的数据保存到pBuffer指针所指向的缓冲区中。同时,将pBuffer指针向后移动一个字节,以便将下一个字节读取到缓冲区中。

NumToRead–;
将要读取的数据长度减1,以便下一次循环读取下一个字节。

总结

1.STM32利用IIC传输数据到从机,再利用IIC从从机读取数据的过程

STM32利用IIC传输数据到从机,再利用IIC从从机读取数据的过程一般可以分为以下几个步骤:

初始化IIC总线
在STM32中,需要先初始化IIC总线,包括设置IIC的时钟速度、GPIO引脚的模式等等。具体来说,需要配置IIC的GPIO引脚为开漏输出模式、使能IIC外设、设置IIC的时钟速度等等。

发送写命令到从机
在STM32中,需要将从机的地址和写命令发送到IIC总线上,以便从机准备接收数据。具体来说,需要调用IIC发送函数,将从机地址和写命令发送到IIC总线上。

发送数据到从机
在STM32中,需要将要写入的数据发送到IIC总线上,以便从机接收数据。具体来说,需要调用IIC发送函数,将要写入的数据逐个字节地发送到IIC总线上。

发送停止信号
在STM32中,需要发送停止信号,以表示数据传输完成。具体来说,需要调用IIC发送停止信号的函数,以便从机知道数据传输已经完成。

发送读命令到从机
在STM32中,需要将从机的地址和读命令发送到IIC总线上,以便从机准备将数据发送到IIC总线上。具体来说,需要调用IIC发送函数,将从机地址和读命令发送到IIC总线上。

从从机读取数据
在STM32中,需要从IIC总线上读取从机发送的数据。具体来说,需要调用IIC接收函数,逐个字节地从IIC总线上读取从机发送的数据,将其保存到缓冲区中。

发送停止信号
在STM32中,需要发送停止信号,以表示数据传输完成。具体来说,需要调用IIC发送停止信号的函数,以便从机知道数据传输已经完成。

2.IIC的具体应用

IIC协议在嵌入式开发中有着广泛的应用,以下列举几个常见的应用:

传感器数据采集:很多传感器(如温度、湿度、气压等)都支持IIC接口,可以通过IIC协议将传感器采集到的数据传输到嵌入式系统中。

存储器读写:很多存储器(如EEPROM、Flash等)也支持IIC接口,可以通过IIC协议将嵌入式系统中的数据写入到存储器中,或者从存储器中读取数据。

触摸屏:很多触摸屏控制器也支持IIC接口,可以通过IIC协议将触摸屏采集到的数据传输到嵌入式系统中,实现触摸屏的操作。

显示屏控制:很多液晶显示屏的控制器也支持IIC接口,可以通过IIC协议将需要显示的数据传输到液晶显示屏控制器中,实现液晶显示屏的显示。

时钟芯片读写:很多实时时钟芯片也支持IIC接口,可以通过IIC协议读取或写入实时时钟的时间信息。

芯片间通信:在一些多芯片嵌入式系统中,不同的芯片之间需要进行通信,IIC协议可以作为芯片间通信的一种方式,实现数据传输。

猜你喜欢

转载自blog.csdn.net/qq_53092944/article/details/131756652