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

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在传数据时需要满足条件,SCL高电平期间,SDA电平必须稳定,SCL低电平期间,SDA电平可以变化。如图所示:

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

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;//100MHzGPIO_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 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 时序图

  1. 单字节写时序图:

    以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. 单字节读时序图:

    单字节读过程:发送开始信号,发送设备地址(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时序图-新人首更相关推荐

  1. 示波器解析IIC通讯

    通过示波器解析IIC通讯 可以通过示波器捕获IIC SDA数据线,SCL时钟线波形,结合协议规则,加深我对IIC协议的学习和理解 文章目录 通过示波器解析IIC通讯 一.IIC数据协议简要介绍 二.I ...

  2. ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议

    IIC通讯协议 协议简介 IIC(inter-integrated Circuit集成电路总线)总线支持设备之间的短距离通信,用于处理器和一些外围设备之间的接口,它需要两根信号线来完成信息交换,它是由 ...

  3. 【蓝桥杯单片机】IIC通讯协议与EEPROM(AT24C02)(官方驱动源码改写)

    实验开发板为CT107D蓝桥官方板,编译环境为MDK5 由蓝桥官方驱动改写 AT24C02: AT24C02为地址可编程器件(CT107D可编程位均为0),因此可于总线上挂载多个相同的器件. 最后一位 ...

  4. STM32使用IIC总线通讯协议在OLED屏幕上显示字符串、汉字、图像(硬件IIC)

    参考:基于STM32-Oled(IIC)的使用 作者:奋斗的小殷 发布时间: 2021-05-07 13:09:26 网址:https://blog.csdn.net/boybs/article/de ...

  5. 示波器分析IIC波形图

    测试设备: 1.MCU:STM32G0 2.IIC设备:lis2dh12(ST的三轴加速度计) 3.示波器:普源RIGOL DS1054Z 知识点: 1.开始信号:SCL在高电平期间,SDA由高变低即 ...

  6. STM32之IIC通讯-SHT30温湿度采集

    一.实验目的:通过单片机普通IO口,模拟IIC时序,掌握IIC通讯协议. 二.实验用到的主控芯片:STM32F103RCT6,IIC通讯引脚IIC_SDA:PC11,IIC_SCL:PC12 三.SH ...

  7. ppi协议源码 c语言,西门子PPI通讯协议

    S7-200 PLC之PPI协议 通过硬件和软件侦听的方法,分析PLC内部固有的PPI通讯协议,然后上位机采用VB编程,遵循PPI通讯协议,读写PLC数据,实现人机操作任务.这种通讯方法,与一般的自由 ...

  8. spi协议时序图和四种模式实际应用详解

    大家好,我是无际. 上个章节我们讲解了spi接口定义,今天我们更加深入讲解下spi协议时序图和spi四种模式的用法. 刚开始接触单片机开发时,最怕就是看时序图,对于我来说就是奇怪的知识. 特别是SPI ...

  9. 常见的通讯协议总结(USART、IIC、SPI、485、CAN)

    目录 一.通讯的基本概念 1.串行通讯 2.并行通讯 3.串行通讯与并行通讯对比 4.传输模式(单工.半双工.全双工) 二.USART-串口通讯 1.物理层 2.协议层 (1)波特率 (2)起始和停止 ...

最新文章

  1. response.setContentType()方法浅析
  2. 几种数据库两列字段的拼接方法
  3. 认识Backbone (四)
  4. 【python】队列——用顺序表实现队列操作
  5. SpringBoot2.x 整合websocket 消息推送,单独发送信息,群发信息
  6. python用outlook自动发邮件_python使用两种发邮件的方式smtp和outlook示例
  7. Python_排序算法实现
  8. 让你的原创设计作品展示给世界|设计师的舞台
  9. Java基础小常识-final,抽象类,接口-(11)
  10. 如何通过看书来学习技术
  11. Python入门学习三
  12. microsoftonenote_OneNote2017官方下载
  13. 什么是光纤?光纤的原理是什么?你能想象没有光纤通讯的世界么?
  14. layui中实现动态的cols表头字段
  15. 什么是四种七和弦 和三种转位
  16. UEFI下的Gmac驱动实现
  17. 阿德莱德大学计算机科学学士学分,澳洲阿德莱德大学软件工程计算机科学学士专业就业方向简析...
  18. 基于对抗训练的轨迹预测半监督算法(美国西北大学和加州尔湾分校)
  19. Console and Shells的介绍,大家都看过吗
  20. 略谈狄利克雷卷积和数论函数基础

热门文章

  1. nginx禁止某些指定的浏览器标识来爬我们网站
  2. 奇技淫巧玄妙无穷| M1 mac os(苹果/AppleSilicon)系统的基本操作和设置
  3. 【算法上车②】编译打包华为摄像头yolov3 狗识别demo
  4. 3D 游戏之父卡马克再创业:“我自己出得起 2000 万美元,但花投资人的钱会更有责任心”...
  5. 编写程序实现乐手弹奏乐器。乐手可以弹奏不同的乐器从而发出不同的声音。 可以弹奏的乐器包括二胡、钢琴和小提琴
  6. android蓝牙开启后会尝试自动连接,Android 自动连接蓝牙
  7. linux服务器自动断开,解决centos7服务器用ssh连接老是自动断开问题
  8. 通过网格拆分高德地图
  9. DDR3及DDR4的传输带宽
  10. 页面访问控制的3种方法