矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 3807|回复: 20

如何52单片机的I2C读写24C08程序问题排查修改

[复制链接]
     
发表于 2020-12-2 16:21:42 | 显示全部楼层 |阅读模式
      52单片机的I2C读写24C08程序,跟着书本示例一直不过

在坛友老师帮助下已另一个版本的示例可以正常的读写程序示波器也捕捉到了图形,
DS1Z_QuickPrint4.png
(52单片机的I2C读写24C08程序波形问题 http://www.crystalradio.cn/forum ... &fromuid=228929
对书本示例有视频也是可以正常,参照论坛老师正确的示例对--启动/停止/延时--等函数进行修改,问题没有解决,两个示例差别在读写和ack应答部分,看下面波形感觉是不是写有问题(SDA=CY; 有问题)也就是 芯片PSW状态寄存器CY(Carry)进位标志溢出没有变化,写不上。
DS1Z_QuickPrint3.png ,想请教各位老师,如何对书本示例进行检查纠错,使其正常运行谢谢。
     
发表于 2020-12-2 16:48:51 | 显示全部楼层
学会用仿真器跟踪,一步步调试,不怕找不到问题所在。
另外,有通过的程序,对比一下差别,每次改一个语句,再执行,也能找到问题。
回复 支持 反对

使用道具 举报

     
发表于 2020-12-2 17:07:49 | 显示全部楼层
不贴最新代码,是个坏习惯
回复 支持 反对

使用道具 举报

     
发表于 2020-12-2 17:53:06 | 显示全部楼层
20年前,用VB带控件,通过RS232口加电平转换芯片,读写无障碍
回复 支持 反对

使用道具 举报

     
发表于 2020-12-3 07:47:43 | 显示全部楼层
最好把你使用的单片机型号全称写齐全,因为52单片机是一个总称,不同厂家的产品是不同的,同一厂家,不同系列也不相同,没有齐全型号,只能瞎蒙
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-12-3 08:28:27 | 显示全部楼层
glory 发表于 2020-12-2 16:48
学会用仿真器跟踪,一步步调试,不怕找不到问题所在。
另外,有通过的程序,对比一下差别,每次改一个语句 ...

谢谢指教,仿真器跟踪是调试程序,知道了,买的是最小系统单片机是贴片不能更换 ,跟着做相关实验是在洞洞板上扩展后做的很麻烦,正在做两个程序对比,应答和读写架构不一样,想对不一样部分分别运行看看波形对不对。再次感谢!
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-12-3 08:33:47 | 显示全部楼层
t3486784401 发表于 2020-12-2 17:07
不贴最新代码,是个坏习惯

O(∩_∩)O哈哈~谢谢提醒,另一个帖子也是老师提醒把波形和程序发上来 ------波形在一楼
isoimg2130老师提供在单片机正常运行的程序:
#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
}

void  SendByte(u8  c)                                //字节数据发送函数   
{
        u8  BitCnt;                                                   //条件 一定要开启总线 保持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)                                        //字节数据接收函数     
{                                                                         //ack  1 发送应答  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);
}

下面是书本示例运行没有读出数据led灯不亮:
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);
}


回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-12-3 08:44:06 | 显示全部楼层
未成之佛 发表于 2020-12-2 17:53
20年前,用VB带控件,通过RS232口加电平转换芯片,读写无障碍

高手,是电脑通过RS232口加电平转换芯片+24C0*读写?
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-12-3 09:06:26 | 显示全部楼层
本帖最后由 pdshyh 于 2020-12-3 09:13 编辑
chhds 发表于 2020-12-3 07:47
最好把你使用的单片机型号全称写齐全,因为52单片机是一个总称,不同厂家的产品是不同的,同一厂家,不同系 ...


谢谢提醒查了购买网站信息咨询了卖家用的是STC89C52RC-401芯片/单片机LOFP44工业级,这个单片机P2^1脚有问题发0/1都是高电平,因此更换用了P2^2脚,另外读写24C08用另一个程序没问题
没问题程序写部分没有用到PSW状态寄存器CY
SDA=c<<BitCnt&0x80?1:0;         //判断发送位  发送是由高位开始发送               
                _nop_();
                SCL=1;                               //置时钟线为高,通知被控器开始接收数据位
                _nop_();   
                SCL=0;
                _nop_();     
有问题部分程序写部分
            temp=temp<<1;
                SCL=0;
                delay();
                SDA=CY;        
                delay();
                SCL=1;
    所以怀疑是不是PSW状态寄存器CY(Carry)进位标志溢出没有变化,写不上。这个系统有P2^1脚坏的前科因此才这样怀疑。
回复 支持 反对

使用道具 举报

     
发表于 2020-12-3 09:30:45 | 显示全部楼层
把有问题的部分替换成这个试试.
        temp<<= 1;              //移出数据的最高位
        SDA = CY;               //送数据口
        SCL = 1;                //拉高时钟线
        delay();             //延时
        SCL = 0;                //拉低时钟线
        delay();             //延时
回复 支持 反对

使用道具 举报

     
发表于 2020-12-3 09:39:14 | 显示全部楼层
本帖最后由 iffi123 于 2020-12-3 09:57 编辑

还没搞定吗, 看着真闹心
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-12-3 10:03:36 | 显示全部楼层
haisens 发表于 2020-12-3 09:30
把有问题的部分替换成这个试试.
        temp

谢谢,下班回家改改看如何。
回复 支持 反对

使用道具 举报

     
发表于 2020-12-3 10:16:10 | 显示全部楼层
看了一下程序,未通过函数uchar read_byte(),和通过函数u8 RcvByte(u8 ack),有明显差别。
通过函数,开始有
        SDA=1;
        delay();
读位数据后有延迟
_nop_();  
而未通过函数都没有。        
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-12-3 10:19:35 | 显示全部楼层
iffi123 发表于 2020-12-3 09:39
还没搞定吗, 看着真闹心

谢谢,书本示例先跟着书本和视频学习的一直没过,先入为主放不下,想看看哪有问题。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-12-3 10:33:30 | 显示全部楼层
glory 发表于 2020-12-3 10:16
看了一下程序,未通过函数uchar read_byte(),和通过函数u8 RcvByte(u8 ack),有明显差别。
通过函数,开 ...

谢谢,在启动停止部分注意对比差异了这部分对比没仔细看到谢谢,回家把这部分按照通过的修改看看变化如何。
回复 支持 反对

使用道具 举报

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

本版积分规则

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

蒙公网安备 15040402000005号

GMT+8, 2025-4-28 09:09

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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