糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > 示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更

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

时间:2020-01-30 21:36:52

相关推荐

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

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

STM32F4探索者开发板板载24C02EEPROM,自制了一块开发板,需要存储容量更大的24C08A芯片,焊接后发现开发板上能成功运行的程序,在自己的板子上无法运行,24C08和24C02贴片封装一样,制板时也没仔细看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三种类型信号

开始信号:SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。 CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号, CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,则受控单元可能出现故障。IIC总线时序图:

开始结束信号时序图:

信号有效性:IIC在传数据时需要满足条件,SCL高电平期间,SDA电平必须稳定,SCL低电平期间,SDA电平可以变化。如图所示:

IIC写时序图:发送方每传完8bit数据等待接收应答信号,图中红线区域即为有效应答。

1.2 IIC程序代码

头文件相关定义

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

初始化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;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化IIC_SCL=1;IIC_SDA=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 highdelay_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 highdelay_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 highdelay_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();//发送nACKelseIIC_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地址组成才能实现指定地址的读写。封装引脚图:

需要注意的是A2、A1、A0三个脚,手册中是这样描述的:

对于24C02,大小为256字节,8位寻址,A0、A1、A2作为硬件寻址;而对于24C08A,1024字节,需要10位才能寻址,A0、A1用来页寻址,A2作为硬件选址,也就是说一个设备可以搭载2块24C08A,8块24C02。下图为设备地址表:

设备地址高四位固定为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 时序图

单字节写时序图:

以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); }

单字节读时序图:

单字节读过程:发送开始信号,发送设备地址(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基本设置

示波器实物图:

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盘中,会有更好的观察效果。

3.2 波形图:

3.2.1 单字节写

主程序中通过按键控制往24C08地址0写入/读出‘E’,波形图如下:

如图所示,SCL为蓝色,SDA为黄色,红色区域表示开始信号(SCL为高,SDA由高变低),之后在SCL为高时发送一位数据,发送8位后等待产生应答信号,右图中可以看出发送的数据一次为1010 0000,也就是设备地址0xA0,绿色区域为应答信号,参考IIC_Wait_Ack(),收到应答后就可以发送下一数据了。

第二个要发送的数据是字节地址,发送的是00000000,等待应答后发送要写的‘E’,波形图应该是发送:01000101。

单字节写的波形图到此结束,跟程序代码是相对应的,可以对比代码看波形图 。

3.2.2 单字节读

单字节写弄明白后,单字节读波形图也就很容易理解了。

开始信号,发送设备地址写(最低位为0),等应答。

收到应答后发送字节地址,再次发送开始信号。

发送设备地址读(最低位为1),进入接受模式。

读出来的数据是:01000101,是‘E’的ASCII码,非应答信号是24C08发出的,接收完数据后,结束信号是STM32发送的,一次完整的字节读到此结束。

IIC时序图、24Cxx读写时序图、程序代码、波形图是相对应的,仔细对比学习后,相信大家会对IIC读写过程会有更加深入的了解。

如果觉得《示波器观察IIC通讯协议-STM32F4读写24C08EEPROM时序图-新人首更》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。