找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3602|回复: 9
收起左侧

对于51单片机I2C传送的问题

[复制链接]
ID:254333 发表于 2018-2-14 20:47 | 显示全部楼层 |阅读模式
//************向AT24C02写入数据*******************//
bit write_byte(uchar write_data)
{
        uchar i;
        bit ack_bit;        //定义应答信号
        for(i=0;i<8;i++)    // 循环移入8个位
        {
                SDA=(bit)(write_data & 0x80);
                _nop_();
                SCL = 1;
                _nop_();
                    _nop_();
                SCL = 0;
                write_data=write_data<<1;
        }
        SDA = 1;                        // 读取应答
        _nop_();
        _nop_();
        SCL = 1;
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        ack_bit=SDA;
        SCL = 0;
        return ack_bit;                        // 返回AT24Cxx应答位
}
这是原函数
1、SDA=(bit)(write_data & 0x80);我的这行代码有一些疑惑,不知道我的理解是否正确,我们写入的数据比如说是1000 0000&1000 0000那么结果就是1000 0000只取最高位,是这样吗?
2、SDA = 1;                        // 读取应答
        _nop_();
        _nop_();
        SCL = 1;
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        ack_bit=SDA;
        SCL = 0;
        return ack_bit;                        // 返回AT24Cxx应答位
主机和从机的SDA是同一根线吧?SDA=1是设置总线为空闲状态,SCL从1到0是为了传输传输信号。定义的应答信号是从机的吗?最后返回应答位给主调函数。还是不太明白这个ack_bit存在的意义。
求解

回复

使用道具 举报

ID:279646 发表于 2018-2-28 18:30 | 显示全部楼层
我来给你解答一下吧。
第一个问题 C51里面有bit这个关键字。这个关键字定义了一个位,我们在单片机里所说的置位和复位,一个是把位变成1,一个是把位变成0. 这里SDA=(bit)(write_data & 0x80);  bit加了小括号代表强制类型转换。也就是说 后面的(write_data & 0x80) 做了与运算以后, 如果不是0,就会被强制转化为1,如果是0那么就是0.你说的write_data=0x80的时候 运算以后是 0x80,强制类型转换就变成了1.
第二个问题的原因在于,你没有理解I2C总线的时序。不要只看代码,你要去深刻理解时序图。楼上给的图很好。你看看,不管是应答0 还是非应答1. 读取这个应答信号的时候SCL都要为高电平。在代码里面就是SCL=1,根据时序图先延时一下。然后再采样SDA线上的数据,于是4个延时以后ack_bit=SDA; 这个时候SDA上的状态就放到了这个ack_bit里。然后看这个变量是0还是1来判断从机是否有应答。一开始SDA=1,释放了总线。如果有应答,器件就会把SDA线拉低变成0.如果没有应答,那么SDA一直为高。

评分

参与人数 1黑币 +80 收起 理由
admin + 80 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:123289 发表于 2018-2-16 10:31 | 显示全部楼层

回帖奖励 +10

如果你想知道线上的数据是什么,当然是可以调用一个子程序来实现。
你想啊,调用结束后,线上的数据会在哪里呢?
回复

使用道具 举报

ID:254333 发表于 2018-2-16 22:07 | 显示全部楼层
yzwzfyz 发表于 2018-2-16 10:31
如果你想知道线上的数据是什么,当然是可以调用一个子程序来实现。
你想啊,调用结束后,线上的数据会在哪 ...

我把全文贴出来吧
#include<reg51.h>
#include<intrins.h>
#define uint unsigned int
#define uchar unsigned char
bit flag=0;   //写24C02的标志位类型bit可以定义一个位类型的变量,它的值是一个二进制位,只有一个0或者1
sbit SDA=P3^7;                    
sbit SCL=P3^6;
sbit shi=P3^0;
sbit ge=P3^1;
uchar sec,tcnt;//sec表示读出的数据,tcnt表示定时器中断次数
uchar code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
//******************** 延时函数 *********************//
void delayms(uint ms)
{
        uchar i;
        while(ms--)
        {
                for(i=0;i<120;i++);
        }
}
//***********起始信号 ********************//
void start()
{
        SDA=1;
        SCL=1;
        _nop_();
        _nop_();
        SDA=0;
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        SCL=0;
}

//***********停止信号 ********************//       
void stop()
{
        SDA=0;
        _nop_();
        _nop_();
        SCL=1;
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        SDA=1;               
}
//************从AT24C02读取数据*******************//
uchar read_byte( )
{
        uchar i,da;
        for(i=0; i<8;i++)
        {
                SCL=1;
                da=da<<1;
                da=da|(uchar)SDA;
                SCL=0;
        }
        return(da);
}
//************向AT24C02写入数据*******************//
bit write_byte(uchar write_data)
{
        uchar i;
        bit ack_bit;        //定义应答信号
        for(i=0;i<8;i++)    // 循环移入8个位
        {
                SDA=(bit)(write_data & 0x80);
                _nop_();
                SCL = 1;
                _nop_();
            _nop_();
                SCL = 0;
                write_data=write_data<<1;
        }
        SDA = 1;                        // 读取应答
        _nop_();
        _nop_();
        SCL = 1;
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        ack_bit=SDA;
        SCL = 0;
        return ack_bit;                        // 返回AT24Cxx应答位
}
//************向AT24C02指定地址写入数据************//
void write_byte_add(uchar addr, uchar write_data)
{
        start();
        write_byte(0xa0);//E2PROM的地址码,最后一位0写方向位
        write_byte(addr);
        write_byte(write_data);
        stop();
        delayms(10);               
}
//************从AT24C02当前地址读取数据************//
unsigned char read_current()
{
        uchar read_data;
        start();
        write_byte(0xa1);//同理为地址码,最后一位1读方向位
        read_data=read_byte();
        stop();
        return read_data;
}
//************从AT24C02指定地址读取数据************//
unsigned char read_random(uchar random_addr)
{
        start();
        write_byte(0xa0);
        write_byte(random_addr);
        return(read_current());
}

//*******************显示函数 **************//
void display(uchar bai_c,uchar sh_c)
{
        shi=1;
        P2=table[bai_c];  //显示第一位
        delayms(5);
        shi=0;
        P2=table[sh_c];  //显示第二位
        ge=1;
        delayms(5);
        ge=0;
}

//*******************主函数 **************//
void main()
{
        SDA = 1;//总线初始化
        SCL = 1;//总线初始化
        sec=read_random(0x02); //读出保存的数据赋予sec
        if(sec>100)                 //防止首次读出错误数据
                sec=0;
        TMOD=0x01;   //定时器T0工作在定时方式1
        ET0=1;                  //T0中断允许
        EA=1;                  //CPU中断允许
        TH0=(65536-50000)/256; //
        TL0=(65536-50000)%256; //TH0、TL0送初值
        TR0=1;  //启动T0
        while(1)
        {
                display(sec/10,sec%10);
                if(flag==1)   //判断计时器是否计时1秒
                {
                        flag=0;   //清0
                        write_byte_add(2,sec); //在24C02的地址02中写入数据sec
                }
        }
}
//*******************定时器T0中断函数**************//
void t0() interrupt 1
{
        TH0=(65536-50000)/256; //
        TL0=(65536-50000)%256; //重装TH0、TL0送初值
        tcnt++;  //50ms         tcnt加1
        if(tcnt==20)
        {
                tcnt=0;         //
                sec++;
                flag=1; //1秒写一次24C02
                if(sec==100)
                {
                        sec=0;
                }
        }
}
谢谢了,线上的数据是具有记忆功能秒表的时间,0~99
回复

使用道具 举报

ID:254333 发表于 2018-2-26 20:43 | 显示全部楼层
求助,有没有大佬可以回答一下我的问题。
回复

使用道具 举报

ID:280512 发表于 2018-2-26 22:37 | 显示全部楼层
(bit)是个转换类型符。只要write_data非零,(bit)(write_data & 0x80)就为“1”,然后,write_data左移,写入一位。ack_bit,是你定义的一位返回值的位,是这样吧?
回复

使用道具 举报

ID:280512 发表于 2018-2-26 22:40 | 显示全部楼层
你需要的是最高位,所以,要用0x80作为判断的条件
回复

使用道具 举报

ID:111634 发表于 2018-2-26 22:56 | 显示全部楼层

回帖奖励 +10

本帖最后由 zl2168 于 2018-2-26 23:03 编辑

能读懂IIC收发时序图就能理解。介绍你一本书,《80C51单片机实用教程——基于Keil C和Proteus》高等教育出版社ISBN 978-7-04-044532-9,内有IIC详细讲解。
I2C总线基本信号.jpg I2C总线数据传送时序.jpg
回复

使用道具 举报

ID:247040 发表于 2018-2-28 18:42 | 显示全部楼层
同求ack_bit存在的意义楼主解决没
回复

使用道具 举报

ID:304404 发表于 2018-4-24 08:59 | 显示全部楼层
楼主有没有仿真图  让我参考一下
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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