前言
外设我觉得只算是嵌入式的边边角,通信才是重头戏,串口、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 通讯设备,支持多个通讯主机及多个通讯从机。
(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协议可以作为芯片间通信的一种方式,实现数据传输。