52单片机的I2C读写24C08程序波形问题
52单片机I2c读写at24c08芯片,程序下载没反应,排查用示波器测量发现单片机连接at24c08芯片SDA引脚有方波,SCL引脚波形没有方波,再查SCL引脚和P2^1连接没问题,通过简单led点亮熄灭命令测量单片机2^1指令1或0测量都是5v电压,其他口没问题就换了SCL引脚连接P2^2,更换I/O口示波器测量SCL引脚还是没有方波,:L 对调 SDA=P2^0和 SCL=P2^2 为SDA=P2^2和 SCL=P2^0,都是SDA口有方波:L 问题在哪,直接用坛友提供测试没问题的程序还是不行。 你倒是把波形和程序发上来啊。 isoimg2130 发表于 2020-11-25 10:51你倒是把波形和程序发上来啊。
谢谢,程序:
I2C.H
#ifndef __I2C_H__ //文件名全部都大写,首尾各添加2个下划线”__”
#define __I2C_H__
#include<reg52.h>
#define uchar unsigned char
sbit SDA=P2^0;//24C02芯片SDA引脚位定义
sbit SCL=P2^2; //24C02芯片SCL引脚位定义
void delay(); //分别对各函数声明
void start();
void stop();
void ack();
void nack();
void write_byte(uchar date);
uchar read_byte();
void write_at24c02(uchar address ,uchar date);
uchar read_at24c02(uchar address);
#endif
I2C.c
#include "i2c.h"//包含i2c.h头文件,注意自建的头文件是用双引号
void delay() //微秒级延时函数
{
;;//用两个空语句实现短时间延时,当晶振为11.0592MHz时,约4~5微秒
}
void start() //起始信号
{
SDA=1;
SCL=1;
delay();
SDA=0;
delay();
}
void stop() //终止信号
{
SDA=0;
SCL=1;
delay();
SDA=1;
delay();
}
void ack() //应答信号
{
uchar i;
SCL=1;
delay();
while((SDA==1)&&i<250)i++;
SCL=0;
delay();
}
void nack() //无应答信号
{
SCL=1;
delay();
SDA=1;
SCL=0;
delay();
}
voidwrite_byte(uchar date) //写入一个字节到I2C总线
{
uchar i,temp;
temp=date;
for(i=0;i<8;i++)
{
temp=temp<<1;
SCL=0;
delay();
SDA=CY;
delay();
SCL=1;
}
SCL=0;
delay();
SDA=1;
delay();
}
uchar read_byte() //从I2C读一个字节
{
uchar i,j,k;
SCL=0;
delay();
for(i=0;i<8;i++)
{
SCL=1;
delay();
j=SDA;
k=(k<<1)|j;
SCL=0;
delay();
}
return k;
}
void wrte_at2402(uchar address,uchar date)//at24c02按字节写入函数
{
start();
write_byte(0xa0);
ack();
write_byte(address);
ack();
write_byte(date);
ack();
stop();
}
uchar read_dat24c02(uchar address) //对at24c02随机读函数
{
uchar date;
start();
write_byte(0xa0);
ack();
write_byte(address);
ack();
start();
write_byte(0xa1);
ack();
date=read_byte();
nack();
stop();
return date;
}
main.c
#include<reg52.h>
#include"I2C.h"
#include<reg52.h>
void delay_10ms()
{
uchar a,b;
for(a=50;a>0;a--)
for(b=200;b>0;b--) ;
}
void main()
{
start();
write_at24c02(10,0xaa);
delay_10ms();
P1=read_at24c02(10);
while(1);
}
示波器测量
探头用直流档啊,你是测的是电平,触发位置调一下啊。
另外把
把void delay() //微秒级延时函数
{
;;//用两个空语句实现短时间延时,当晶振为11.0592MHz时,约4~5微秒
}
改成这样,你这个可能被编译器直接优化了。压根就没延时。
#include"intrins.h"
void delay() //微秒级延时函数
{
_nop_();
_nop_();
_nop_();
_nop_();
} 建议买台逻辑分析仪,协议的各项之间的时序关系很重要,用示波器很不好捕捉到,逻辑分析仪就方便多了 isoimg2130 发表于 2020-11-25 11:37
探头用直流档啊,你是测的是电平,触发位置调一下啊。
另外把
把void delay() //微秒级延时函数
谢谢,示波器我还不怎么会用,我下班回去示波器测量和延时按您说的设置调整,看示波器有正反向变化符合您说的可能是void delay()编译器直接优化了没延时,方波应该是delay_10ms()形成的吧间隔很长。:handshake chhds 发表于 2020-11-25 12:10
建议买台逻辑分析仪,协议的各项之间的时序关系很重要,用示波器很不好捕捉到,逻辑分析仪就方便多了
谢谢,关注过逻辑分析仪不怎么知道怎么用,原来是在这里有用。:handshake pdshyh 发表于 2020-11-25 12:17
谢谢,关注过逻辑分析仪不怎么知道怎么用,原来是在这里有用。
模拟信号用示波器,数字信号用逻辑分析仪,各司其职。 pdshyh 发表于 2020-11-25 12:16
谢谢,示波器我还不怎么会用,我下班回去示波器测量和延时按您说的设置调整,看示波器有正反向变化符合您 ...
就算没延时,他高低电平应该还是有的,毕竟51那么慢。波形还是会有的,你没测到只能是测量方法不对。
但是如果读出来数据不正常就是程序问题了。
这个程序没仔细深究,但是看起来总感觉怪怪的,你可以试着写255个数据进去,再隔个500毫秒读一个显示,如果LED是1.2.3.....255显示的就是没问题。如果不是就是程序有问题。 chhds 发表于 2020-11-25 12:54
模拟信号用示波器,数字信号用逻辑分析仪,各司其职。
谢谢,原来是这样使用的,知道了。 isoimg2130 发表于 2020-11-25 13:45
就算没延时,他高低电平应该还是有的,毕竟51那么慢。波形还是会有的,你没测到只能是测量方法不对。
...
谢谢给出测试方法,这个程序就是写入0xaa在读出来在led显示。刚刚跟着系统学单片机就碰到问题了。 pdshyh 发表于 2020-11-25 14:51
谢谢给出测试方法,这个程序就是写入0xaa在读出来在led显示。刚刚跟着系统学单片机就碰到问题了。
#include "reg52.h"
#include"intrins.h"
typedef unsigned char u8;
sbit SCL=P2^1; //I2C时钟
sbit SDA=P2^2; //I2C数据
void delay() //4微秒延时函数
{
_nop_();
_nop_();
_nop_();
_nop_();
}
void yanshi(unsigned int hm) //延时毫秒
{
unsigned int i;
do
{
i = 12000000 / 97560;
while(--i) ;
}
while(--hm);
}
void Start_I2c()
{
SDA=1; //发送起始条件的数据信号
SCL=1;
delay();
SDA=0; //发送起始信号
delay();
SCL=0; //钳住I2C总线,准备发送或接收数据
}
void Stop_I2c()
{
SCL=0;
SDA=0; //发送结束条件的数据信号
delay();
SCL=1; //结束条件建立时间大于4μs
SDA=1; //发送I2C总线结束信号
delay();
}
u8 I2c_wait_ack(void) //等待应答信号到来 1,接收应答失败 0,接收应答成功
{
u8 Time=0;
SDA=1; //准备接收应答位
_nop_();
SCL=1;
_nop_();
while(SDA)
{
Time++;
if(Time>250)
{
Stop_I2c();
return 1; //无应答返回1
}
}
SCL=0; //时钟输出0
return 0; //有应答返回0
}
voidSendByte(u8c) //字节数据发送函数
{
u8BitCnt; //条件 一定要开启总线 保持SCL处于0状态 才能进行写入
for(BitCnt=0;BitCnt<8;BitCnt++) //要传送的数据长度为8位
{
SDA=c<<BitCnt&0x80?1:0; //判断发送位发送是由高位开始发送
_nop_();
SCL=1; //置时钟线为高,通知被控器开始接收数据位
_nop_();
SCL=0;
_nop_();
}
}
void I2C_Ack(void) //产生ACK应答
{
SCL=0;
delay();
SDA=1;
delay();
SDA=0;
delay();
SCL=1;
delay();
SCL=0;
delay();
SDA=1;
delay();
}
void I2C_NAck(void) //不产生ACK应答
{
SCL=0;
delay();
SDA=1;
delay();
SCL=1;
delay();
SCL=0;
delay();
SDA=0;
delay();
}
u8 RcvByte(u8 ack) //字节数据接收函数
{ //ack1 发送应答0 不发送应答
u8 retc=0,i;
SDA=1;
delay();
for(i=0;i<8;i++)
{
SCL=0; //置时钟线为低,准备接收数据位
delay();
SCL=1; //置时钟线为高使数据线上数据有效
_nop_();
retc<<=1;
if(SDA)retc++; //读数据位,接收的数据位放入retc中
_nop_();
}
if (!ack)
I2C_NAck(); //发送nACK
else
I2C_Ack(); //发送ACK
return retc;
}
u8 AT24C_Rcvone(u8 Addr) //在AT24CXX指定地址读出一个数据
{
u8 temp=0;
Start_I2c(); //启动总线
SendByte(0xa0); //发送写命令
I2c_wait_ack(); //等待应答
SendByte(Addr); //发送地址
I2c_wait_ack(); //等待应答
Start_I2c(); //重新启动总线
SendByte(0xa1); //设置为读操作
I2c_wait_ack(); //等待应答;
temp=RcvByte(0); //读字节 非应答
Stop_I2c(); //结束总线
return temp;
}
void AT24C_Sendone(u8 Addr,u8 Data) //在AT24CXX指定地址写入一个数据 此函数只限于 c02-c16
{
Start_I2c(); //启动总线
SendByte(0xa0); //发送写命令
I2c_wait_ack(); //等待应答
SendByte(Addr); //发送地址
I2c_wait_ack(); //等待应答
SendByte(Data); //发送字节数据
I2c_wait_ack(); //等待应答
Stop_I2c(); //结束总线
yanshi(10); //如果是连续发送字节的时候这个延时很重要 否则将回传错
}
void main()
{
AT24C_Sendone(10,0xaa);
P1=AT24C_Rcvone(10);
while(1);
}
pdshyh 发表于 2020-11-25 14:51
谢谢给出测试方法,这个程序就是写入0xaa在读出来在led显示。刚刚跟着系统学单片机就碰到问题了。
掉个头,这样直观点。改成这样。流水显示。这样能判断是否出错。。按二进制看。
void main(void)
{
u8 a;
for(a=0;a<255;a++)
AT24C_Sendone(a,a);
for(a=0;a<255;a++)
{
P1=AT24C_Rcvone(a);
yanshi(200);
}
while(1);
}
isoimg2130 发表于 2020-11-25 16:38
谢谢,图文并茂还有模拟还有测量万分感谢,逻辑分析仪测量的?
补充内容 (2020-11-26 08:03):
还有仿真 还是用我那个分享的程序吧!