示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更

示波器观察时序不是因为好奇,是因为遇到了问题

STM32F4探索者开发板板载24C02EEPROM,自制了一块开发板,需要存储容量更大的24C08A芯片,焊接后发现开发板上能成功运行的程序,在自己的板子上无法运行,24C0824C02贴片封装一样,制板时也没仔细看24Cxx系列数据手册,照着ExploerSTM32F4_V2.2_SCH设计了电路,发现问题后详细读了24Cxx系列手册,发现电路可以用,万幸!!
写的程序是根据例程实验24 IIC实验改的,用24C08不行,试一下24C02,飞线把24C02换上,下载程序,发现还是不能运行(这并不是24C02的问题,因为用到了LCD显示,有些参数是从24C02中读取显示的,读取有整型浮点型,用的是全新的24C02芯片,读出来转换会有点问题。这其实后来才明白的)。没办法,只能从头开始调,从例程开始,先实现能读写24C02,再用LCD显示。
把例程写进去,可以运行。再试试自己的程序,不行。多次实验突然有了点想法,把24C02先全部写0,再试试能不能运行。结果证明问题就出在这里,从24C02中读出数据转换成int会有问题,导致程序跑飞。24C02调成功后,不甘心24C08不能用,于是把24C02换成了24C08。
认认真真把24Cxx手册读了几遍,在网上搜了搜24C08读写程序,把24Cxx系列的随机读写时序弄明白了,看了看例程中的读写程序,也应该没错,把例程下进去,发现还是有问题,读出数据串口打印总是显示’?’,而且LCD显示不出来,心态快崩了呀。无意间看到有分享用逻辑分析仪观察IIC读写时序的,没有逻辑分析仪,示波器倒是有,又找了找用示波器观察IIC时序的文章。很久没用示波器了,找了找视频学习了下,终于把时序图弄出来了。通过观察IIC读写时序,还真的找出了问题,本来想的是通过观察比较24C02读写时序和24C08读写时序,看看24C08问题到底出在哪。在观察24C02读写时序时,发现产生停止信号时,SCL和SDA几乎是同时变为高,IIC协议中停止信号是SCL为高时,SDA由低变为高,恰当的做法是SCL拉高后,延时一段时间再将SDA拉高,回到例程,找到IIC_Stop()函数,发现延时SDA=1放到了延时之前,改了之后,下载运行,成功读写!!!把24C08全写为0,再把自己的程序下到里边,完美运行!!!
这次经历虽然有点糟心,但问题解决的那刻,心情还是很舒爽的。通过观察时序图,加深了对IIC的理解,用示波器进行故障排除真的是一种很好的方法。下面我就具体介绍24C08读写时序以及用示波器观察IIC时序的操作方法,如有错误还请大家指正。

1. IIC协议

IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。它是由数据线 SDA 和时钟 SCL 构成的串行总线,可发送和接收数据。在 CPU 与被控 IC 之间、 IC 与 IC 之间进行双向传送, 高速 IIC 总线一般可达 400kbps 以上。

1.1 IIC三种类型信号

  1. 开始信号:SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。
  2. 结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
  3. 应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。 CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号, CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,则受控单元可能出现故障。
  4. IIC总线时序图:
    示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第1张图片
    开始结束信号时序图:
    示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第2张图片

信号有效性:IIC在传数据时需要满足条件,SCL高电平期间,SDA电平必须稳定,SCL低电平期间,SDA电平可以变化。如图所示:
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第3张图片
IIC写时序图:发送方每传完8bit数据等待接收应答信号,图中红线区域即为有效应答。
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第4张图片

1.2 IIC程序代码

  1. 头文件相关定义
//IO方向设置
#define SDA_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}	//PB9输入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式
//IO操作函数	 
#define IIC_SCL    PBout(8) //SCL
#define IIC_SDA    PBout(9) //SDA	 
#define READ_SDA   PBin(9)  //输入SDA 
  1. 初始化IIC
 void IIC_Init(void)
{			
  GPIO_InitTypeDef  GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟

  //GPIOB8,B9初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
	IIC_SCL=1;
	IIC_SDA=1;
}
  1. 开始、结束、应答(IIC_Stop(void)函数改过):
//产生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; 
	delay_us(4);		
	IIC_SDA=1;//发送I2C总线结束信号
}
//等待应答信号到来
//返回值: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停止信号
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);							   	
}

更改后结束:

//产生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; 
	delay_us(4);		
	IIC_SDA=1;//发送I2C总线结束信号
}

更改后的才是与时序图对应,也是24C08不能读的问题所在。
4. 发送接收一个字节:

//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;
}

IIC用到的基本读写函数写完了,和24C02/08通信,实现特定地址的读写还需要按照芯片的要求写具体函数。

2. AT24Cxx

2.1 AT24Cxx内存地址

2.1.1

AT24C01A/02/04/08A/16A大小分别为128/256/512/1024/2048字节,掌握24Cxx地址组成才能实现指定地址的读写。封装引脚图:
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第5张图片
需要注意的是A2、A1、A0三个脚,手册中是这样描述的:
在这里插入图片描述
在这里插入图片描述
对于24C02,大小为256字节,8位寻址,A0、A1、A2作为硬件寻址;而对于24C08A,1024字节,需要10位才能寻址,A0、A1用来页寻址,A2作为硬件选址,也就是说一个设备可以搭载2块24C08A,8块24C02。下图为设备地址表:
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第6张图片
设备地址高四位固定为1010,A2、A1、A0与硬件电路连接有关,P0、P1、P2是页地址,最后一位代表读写操作,1表示读,0表示写。只有确定了设备地址(DEVICE ADDRESS)和字节地址(WORD ADDRESS),才能往指定地址读写。

2.1.2. 设备地址(DEVICE ADDRESS)的确定

以24C08为例,1K字节寻址需要10位,要写的地址位Addr,AddrH=(Addr/256),AddrL=(Addr%256),AddrH与设备地址里的A1、A0对应,假设A2位为0表示选中该片24C08(可以搭载2片24C08),高四位为1010,最低位为0表示写。则DEVICE ADDRESS可由此计算得来:0XA0+((WriteAddr/256)<<1)
字节地址:WORD ADDRESS=Addr%256

2.2 时序图

  1. 单字节写时序图:
    示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第7张图片
    以24C08为例,根据时序图,字节写过程如下:发送开始信号,发送设备地址,等待应答,发送字节地址,等待应答,发送数据,等待应答,发送停止信号。
//在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(20);	 
}
  1. 单字节读时序图:
    示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第8张图片
    单字节读过程:发送开始信号,发送设备地址(0XA0+((WriteAddr/256)<<1)),等待应答,发送字节地址,等待应答;发送开始信号,发送设备地址读(0XA1+((WriteAddr/256)<<1)),等待应答,读数据,发送停止信号。代码如下:
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址  
//返回值  :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=0;		  	    																 
    IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		IIC_Send_Byte(0XA0);	   //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(ReadAddr>>8);//发送高地址	    
	}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+((ReadAddr/256)<<1));
//	IIC_Send_Byte(0XA1);           //进入接收模式			   
	IIC_Wait_Ack();	 
    temp=IIC_Read_Byte(0);		   
    IIC_Stop();//产生一个停止条件	    
	return temp;
}

3. 示波器观察IIC波形

用的是RIGOL DS1102D示波器,基本忘了怎么用了,在网上找视频学了学。

3.1 RIGOL DS1102D基本设置

示波器实物图:
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第9张图片
CH1接SDA,CH2接SCL,CHI电压幅度调节为2V,CH2电压幅度调节为2V,时间调整为100或200us(根据IIC频率设置,设置不当可能观察不到波形或观察不到完整的波形)。触发模式选择边沿触发,边沿类型选下降沿触发,触发信源选择CH1(CH1接SDA,IIC通讯时会先发开始信号,、即SCL为高时,SDA由高变为低,最先变化的时SDA,且第一个边沿为下降沿),触发方式为单次触发,边沿触发需要设定TRIGGER值(电压越过该值认为是出现边沿),值在1.5-3.0V之间即可。设置完成后,连接SCL、SDA就可以进行观察了。
可以将每次的波形存储到U盘中,用WFMReader软件(下载地址)如下图,打开保存的wfm文件观察时序。波形显示后调节时间分度为50us,再保存到U盘中,会有更好的观察效果。
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第10张图片

3.2 波形图:

3.2.1 单字节写

主程序中通过按键控制往24C08地址0写入/读出‘E’,波形图如下:
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第11张图片
如图所示,SCL为蓝色,SDA为黄色,红色区域表示开始信号(SCL为高,SDA由高变低),之后在SCL为高时发送一位数据,发送8位后等待产生应答信号,右图中可以看出发送的数据一次为1010 0000,也就是设备地址0xA0,绿色区域为应答信号,参考IIC_Wait_Ack(),收到应答后就可以发送下一数据了。
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第12张图片
第二个要发送的数据是字节地址,发送的是00000000,等待应答后发送要写的‘E’,波形图应该是发送:01000101。
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第13张图片
单字节写的波形图到此结束,跟程序代码是相对应的,可以对比代码看波形图 。

3.2.2 单字节读

单字节写弄明白后,单字节读波形图也就很容易理解了。
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第14张图片
开始信号,发送设备地址写(最低位为0),等应答。
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第15张图片
收到应答后发送字节地址,再次发送开始信号。
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第16张图片
发送设备地址读(最低位为1),进入接受模式。
示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更_第17张图片
读出来的数据是:01000101,是‘E’的ASCII码,非应答信号是24C08发出的,接收完数据后,结束信号是STM32发送的,一次完整的字节读到此结束。
IIC时序图、24Cxx读写时序图、程序代码、波形图是相对应的,仔细对比学习后,相信大家会对IIC读写过程会有更加深入的了解。

你可能感兴趣的:(STM32F4)