矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 4593|回复: 33

52单片机的I2C读写24C08程序波形问题

[复制链接]
     
发表于 2020-11-25 10:29:42 | 显示全部楼层 |阅读模式
52单片机I2c读写at24c08芯片,程序下载没反应,排查用示波器测量发现单片机连接at24c08芯片SDA引脚有方波,SCL引脚波形没有方波,再查SCL引脚和P2^1连接没问题,通过简单led点亮熄灭命令测量单片机2^1指令1或0测量都是5v电压,其他口没问题就换了SCL引脚连接P2^2,更换I/O口示波器测量SCL引脚还是没有方波, 对调 SDA=P2^0和 SCL=P2^2        为SDA=P2^2和 SCL=P2^0,都是SDA口有方波 问题在哪,直接用坛友提供测试没问题的程序还是不行。
     
发表于 2020-11-25 10:51:21 | 显示全部楼层
你倒是把波形和程序发上来啊。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-11-25 11:23:41 | 显示全部楼层
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();
}
void  write_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);
}
示波器测量
DS1Z_QuickPrint1.png
回复 支持 反对

使用道具 举报

     
发表于 2020-11-25 11:37:13 | 显示全部楼层
探头用直流档啊,你是测的是电平,触发位置调一下啊。
另外把
把void delay()   //微秒级延时函数
{
        ;;  //用两个空语句实现短时间延时,当晶振为11.0592MHz时,约4~5微秒
}
改成这样,你这个可能被编译器直接优化了。压根就没延时。

#include  "intrins.h"
void delay()   //微秒级延时函数
{
   _nop_();
   _nop_();
   _nop_();
   _nop_();
}
回复 支持 反对

使用道具 举报

     
发表于 2020-11-25 12:10:24 | 显示全部楼层
建议买台逻辑分析仪,协议的各项之间的时序关系很重要,用示波器很不好捕捉到,逻辑分析仪就方便多了
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-11-25 12:16:11 | 显示全部楼层
isoimg2130 发表于 2020-11-25 11:37
探头用直流档啊,你是测的是电平,触发位置调一下啊。
另外把
把void delay()   //微秒级延时函数

谢谢,示波器我还不怎么会用,我下班回去示波器测量和延时按您说的设置调整,看示波器有正反向变化符合您说的可能是void delay()编译器直接优化了没延时,方波应该是delay_10ms()形成的吧间隔很长。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-11-25 12:17:57 | 显示全部楼层
chhds 发表于 2020-11-25 12:10
建议买台逻辑分析仪,协议的各项之间的时序关系很重要,用示波器很不好捕捉到,逻辑分析仪就方便多了

谢谢,关注过逻辑分析仪不怎么知道怎么用,原来是在这里有用。
回复 支持 反对

使用道具 举报

     
发表于 2020-11-25 12:54:42 | 显示全部楼层
pdshyh 发表于 2020-11-25 12:17
谢谢,关注过逻辑分析仪不怎么知道怎么用,原来是在这里有用。

模拟信号用示波器,数字信号用逻辑分析仪,各司其职。
回复 支持 反对

使用道具 举报

     
发表于 2020-11-25 13:45:09 | 显示全部楼层
pdshyh 发表于 2020-11-25 12:16
谢谢,示波器我还不怎么会用,我下班回去示波器测量和延时按您说的设置调整,看示波器有正反向变化符合您 ...


就算没延时,他高低电平应该还是有的,毕竟51那么慢。波形还是会有的,你没测到只能是测量方法不对。
但是如果读出来数据不正常就是程序问题了。
这个程序没仔细深究,但是看起来总感觉怪怪的,你可以试着写255个数据进去,再隔个500毫秒读一个显示,如果LED是1.2.3.....255显示的就是没问题。如果不是就是程序有问题。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-11-25 14:45:46 | 显示全部楼层
chhds 发表于 2020-11-25 12:54
模拟信号用示波器,数字信号用逻辑分析仪,各司其职。

谢谢,原来是这样使用的,知道了。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-11-25 14:51:04 | 显示全部楼层
isoimg2130 发表于 2020-11-25 13:45
就算没延时,他高低电平应该还是有的,毕竟51那么慢。波形还是会有的,你没测到只能是测量方法不对。
...

谢谢给出测试方法,这个程序就是写入0xaa在读出来在led显示。刚刚跟着系统学单片机就碰到问题了。
回复 支持 反对

使用道具 举报

     
发表于 2020-11-25 16:38:50 | 显示全部楼层
pdshyh 发表于 2020-11-25 14:51
谢谢给出测试方法,这个程序就是写入0xaa在读出来在led显示。刚刚跟着系统学单片机就碰到问题了。
  1. #include "reg52.h"
  2. #include  "intrins.h"

  3. typedef unsigned char u8;

  4. sbit SCL=P2^1;                                             //I2C  时钟
  5. sbit SDA=P2^2;                                             //I2C  数据

  6. void delay()                                                   //4微秒延时函数
  7. {
  8.    _nop_();
  9.    _nop_();
  10.    _nop_();
  11.    _nop_();
  12. }

  13. void yanshi(unsigned int hm)                  //延时毫秒
  14. {
  15.         unsigned int i;
  16.         do
  17.         {
  18.                 i = 12000000 / 97560;
  19.                 while(--i)        ;                          
  20.         }
  21.         while(--hm);
  22. }

  23. void Start_I2c()
  24. {
  25.         SDA=1;                                                 //发送起始条件的数据信号
  26.         SCL=1;
  27.         delay();
  28.         SDA=0;                                                 //发送起始信号
  29.         delay();      
  30.         SCL=0;                                               //钳住I2C总线,准备发送或接收数据
  31. }

  32. void Stop_I2c()
  33. {
  34.         SCL=0;
  35.         SDA=0;                                              //发送结束条件的数据信号
  36.         delay();
  37.         SCL=1;                                              //结束条件建立时间大于4μs
  38.         SDA=1;                                                      //发送I2C总线结束信号
  39.         delay();
  40. }
  41.       
  42. u8 I2c_wait_ack(void)                                  //等待应答信号到来         1,接收应答失败 0,接收应答成功
  43. {
  44.         u8 Time=0;
  45.         SDA=1;                                        //准备接收应答位
  46.         _nop_();
  47.         SCL=1;                 
  48.         _nop_();
  49.         while(SDA)                          
  50.         {
  51.                 Time++;
  52.                 if(Time>250)
  53.                 {
  54.                         Stop_I2c();
  55.                         return 1;                                 //无应答返回1
  56.                 }
  57.         }
  58.         SCL=0;                                                        //时钟输出0
  59.         return 0;                                                   //有应答返回0
  60. }

  61. void  SendByte(u8  c)                                //字节数据发送函数   
  62. {
  63.         u8  BitCnt;                                                   //条件 一定要开启总线 保持SCL处于0状态 才能进行写入
  64.         for(BitCnt=0;BitCnt<8;BitCnt++) //要传送的数据长度为8位
  65.         {   
  66.                 SDA=c<<BitCnt&0x80?1:0;         //判断发送位  发送是由高位开始发送               
  67.                 _nop_();
  68.                 SCL=1;                               //置时钟线为高,通知被控器开始接收数据位
  69.                 _nop_();   
  70.                 SCL=0;
  71.                 _nop_();          
  72.         }     
  73. }

  74. void I2C_Ack(void)                                          //产生ACK应答
  75. {
  76.         SCL=0;
  77.         delay();                   
  78.         SDA=1;
  79.         delay();
  80.         SDA=0;
  81.         delay();
  82.         SCL=1;
  83.         delay();
  84.         SCL=0;
  85.         delay();
  86.         SDA=1;
  87.         delay();
  88. }
  89.             
  90. void I2C_NAck(void)                                           //不产生ACK应答       
  91. {
  92.         SCL=0;
  93.         delay();
  94.         SDA=1;
  95.         delay();
  96.         SCL=1;
  97.         delay();
  98.         SCL=0;
  99.         delay();
  100.         SDA=0;
  101.         delay();
  102. }
  103.       

  104. u8 RcvByte(u8 ack)                                        //字节数据接收函数     
  105. {                                                                         //ack  1 发送应答  0 不发送应答
  106.         u8 retc=0,i;
  107.         SDA=1;
  108.         delay();          
  109.         for(i=0;i<8;i++)
  110.         {         
  111.                 SCL=0;                          //置时钟线为低,准备接收数据位
  112.                 delay();
  113.                 SCL=1;                          //置时钟线为高使数据线上数据有效
  114.                 _nop_();
  115.                 retc<<=1;
  116.                 if(SDA)retc++;                      //读数据位,接收的数据位放入retc中
  117.                 _nop_();
  118.         }
  119.         if (!ack)
  120.         I2C_NAck();                                                //发送nACK
  121.         else
  122.         I2C_Ack();                                                 //发送ACK                  
  123.         return retc;                                       
  124. }

  125. u8 AT24C_Rcvone(u8 Addr)                           //在AT24CXX指定地址读出一个数据
  126. {
  127.    u8 temp=0;
  128.    Start_I2c();                                //启动总线
  129.    SendByte(0xa0);                             //发送写命令
  130.    I2c_wait_ack();                                           //等待应答
  131.    SendByte(Addr);                             //发送地址
  132.    I2c_wait_ack();                                           //等待应答

  133.    Start_I2c();                                //重新启动总线
  134.    SendByte(0xa1);                                       //设置为读操作
  135.    I2c_wait_ack();                                           //等待应答;

  136.    temp=RcvByte(0);                                           //读字节        非应答

  137.    Stop_I2c();                                 //结束总线
  138.    return temp;
  139. }

  140. void AT24C_Sendone(u8 Addr,u8 Data)        //在AT24CXX指定地址写入一个数据         此函数只限于 c02-c16
  141. {
  142.    Start_I2c();                               //启动总线
  143.    SendByte(0xa0);                            //发送写命令
  144.    I2c_wait_ack();                                          //等待应答
  145.    SendByte(Addr);                            //发送地址
  146.    I2c_wait_ack();                                          //等待应答
  147.    SendByte(Data);                                          //发送字节数据
  148.    I2c_wait_ack();                                          //等待应答
  149.    Stop_I2c();                                //结束总线
  150.    yanshi(10);                                                  //如果是连续发送字节的时候这个延时很重要 否则将回传错
  151. }

  152. void main()
  153. {
  154.    AT24C_Sendone(10,0xaa);
  155.    P1=AT24C_Rcvone(10);        
  156.    while(1);
  157. }




复制代码
QQ截图20200512141157.png
QQ截图20200512141213.png
回复 支持 反对

使用道具 举报

     
发表于 2020-11-25 16:45:31 | 显示全部楼层
pdshyh 发表于 2020-11-25 14:51
谢谢给出测试方法,这个程序就是写入0xaa在读出来在led显示。刚刚跟着系统学单片机就碰到问题了。

掉个头,这样直观点。改成这样。流水显示。这样能判断是否出错。。按二进制看。
  1. void main(void)
  2. {
  3.         u8 a;
  4.         for(a=0;a<255;a++)
  5.            AT24C_Sendone(a,a);
  6.         for(a=0;a<255;a++)
  7.         {
  8.                    P1=AT24C_Rcvone(a);  
  9.                 yanshi(200);
  10.         }      
  11.            while(1);
  12. }
复制代码
QQ截图20200512141536.png
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-11-25 17:28:12 | 显示全部楼层

谢谢,图文并茂还有模拟还有测量万分感谢,逻辑分析仪测量的?

补充内容 (2020-11-26 08:03):
还有仿真
回复 支持 反对

使用道具 举报

     
发表于 2020-11-25 20:41:31 来自手机 | 显示全部楼层
还是用我那个分享的程序吧!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2024-5-24 20:16

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表