STC89C52RC I2C多字节读AT24C02问题

今天用51单片机驱动AT24C02,I2C是用I/O口模拟的,出现了个问题:I2C多字节读取24C02时,只有读出的第一个数正确,后面的都为0。找了一天终于定位到问题,问题在于我的__i2c_sendOneByte函数一开始没有把SDA拉高,因为I2C总线是“线与”关系,当主机将SDA拉低时,从机不能将SDA拉高,而我的I2C读函数应该将SDA的控制权交给从机,所以主机应该主动释放SDA(即将SDA拉高)。
这也正好解释了为什么我读出的第一个字节正确,后面的都为0。因为主机读完第一个字节数据后会发送一个应答信号,表示还要接着读取数据,而这个应答信号主机会将SDA拉低,导致读第二个字节时,从机不能控制SDA。
为了以后不再把时间浪费在I2C时序上,将今天调好的程序备份一下:

/************* my_iic.c ***************/
#include "my_iic.h"

/**
 * \brief us延时函数
 */
static void __delay_us (uint16_t us) 
{
    while (us--) {
        _nop_();
    }
}

/**
 * \brief IIC起始信号
 */
static void __i2c_start (void)
{
    I2C_SCL = 1; 
	I2C_SDA = 1;	  	  	
	__delay_us(1);
 	I2C_SDA = 0;
	__delay_us(1);
	I2C_SCL = 0;     /* 每个子函数后将SCl拉低,子函数开头直接操作SDA */
}	 

/**
 * \brief IIC结束信号
 */
static void __i2c_stop (void)
{
	I2C_SDA = 0; 
	I2C_SCL = 1;
    __delay_us(1);
    I2C_SDA = 1;
    __delay_us(1); 
    I2C_SCL = 0;    
}

/**
 * \brief IIC等待应答
 *
 * \retval IIC_OK     : 应答成功
 * \retval IIC_ENOACK : 无应答 
 */
static int8_t __i2c_wati_ack (void)
{
	uint8_t ucErrTime=0;
       
	I2C_SDA = 1;
    __delay_us(1);	   
	I2C_SCL = 1;
    __delay_us(1);	 
	while (I2C_SDA == 1)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			__i2c_stop();
			return I2C_ENOACK;
		}
	}
	I2C_SCL = 0; 	   
	return I2C_OK;  
} 

/**
 * \brief IIC发送应答信号
 */
static void __i2c_ack (void)
{
	I2C_SDA = 0;
	__delay_us(1);
	I2C_SCL = 1;
	__delay_us(1);
	I2C_SCL = 0;
}

/**
 * \brief IIC发送非应答信号
 */
static void __i2c_nack (void)
{
	I2C_SDA = 1;
	__delay_us(1);
	I2C_SCL = 1;
	__delay_us(1);
	I2C_SCL = 0;
}					 				     

/**
 * \brief IIC发送一个字节
 *
 * \param[in] data : 待发送的字节
 */
static void __i2c_sendOneByte (uint8_t dat)
{                        
    uint8_t i = 0;
        
    for (i = 0; i < 8; i++) {              
		if ((dat & 0x80))
			I2C_SDA = 1;
		else
			I2C_SDA = 0;
		dat <<= 1; 	  
		__delay_us(1);   
		I2C_SCL = 1;
		__delay_us(1); 
		I2C_SCL = 0;	
    }	 
} 	    

/**
 * \brief IIC读取一个字节
 *
 * \param[in] ack : 是否发送ACK(0 - nACK, 1 - ACK)
 *
 * \return 读到的数据 
 */
static uint8_t __i2c_readOneByte (uint8_t ack)
{
    uint8_t i    = 0;
    uint8_t dat  = 0;
    
    I2C_SDA = 1;    /* 这里非将SDA拉高不可!!!*/
    for (i = 0; i < 8; i++)
	{ 
        __delay_us(1);
		I2C_SCL = 1;
        dat <<= 1;
        if (I2C_SDA == 1) {
            dat++;
        }            
		__delay_us(1);
        I2C_SCL = 0;
    }	
    if (!ack)
        __i2c_nack();
    else
        __i2c_ack();   
    return dat;
}

/******************************************************************************/
int8_t my_i2c_writeOneByte (uint8_t addr, uint8_t dat)
{
    __i2c_start();
    __i2c_sendOneByte((DIVICE_ADDR << 1) | 0x00);   /* 器件地址 + W */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    __i2c_sendOneByte(addr);                        /* 发送寄存器地址 */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    __i2c_sendOneByte(dat);                         /* 发送数据 */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    __i2c_stop();
    return I2C_OK;
}

/******************************************************************************/
uint8_t my_i2c_readOneByte (uint8_t addr)
{
    uint8_t dat = 0;
    
    __i2c_start();
    __i2c_sendOneByte((DIVICE_ADDR << 1) | 0x00);   /* 器件地址 + W */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    __i2c_sendOneByte(addr);                        /* 发送寄存器地址 */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    __i2c_start();                                  /* 重启IIC */
    __i2c_sendOneByte((DIVICE_ADDR << 1) | 0x01);   /* 器件地址 + R */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    dat  = __i2c_readOneByte(0);                    /* 读取数据 + NACK */ 
    __i2c_stop();
    return dat;
}

/******************************************************************************/
int8_t my_i2c_write (uint8_t addr, uint8_t *p_buf, uint16_t length)
{
    uint16_t i = 0;
    
    __i2c_start();
    __i2c_sendOneByte((DIVICE_ADDR << 1) | 0x00);   /* 器件地址 + W */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    __i2c_sendOneByte(addr);                        /* 发送寄存器地址 */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    for (i = 0; i < length; i++) {
        
        __i2c_sendOneByte(*(p_buf + i));            /* 发送数据 */
        if (__i2c_wati_ack() == I2C_ENOACK) {
            return I2C_ENOACK;
        }
    }  
    __i2c_stop();
    return I2C_OK;   
}

/******************************************************************************/
int8_t my_i2c_read (uint8_t addr, uint8_t *p_buf, uint16_t length)
{
    uint16_t i = 0;
    
    __i2c_start();
    __i2c_sendOneByte((DIVICE_ADDR << 1) | 0x00);   /* 器件地址 + W */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    __i2c_sendOneByte(addr);                        /* 发送寄存器地址 */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    __i2c_start();                                  /* 重启IIC */
    __i2c_sendOneByte((DIVICE_ADDR << 1) | 0x01);   /* 器件地址 + R */
    if (__i2c_wati_ack() == I2C_ENOACK) {
        return I2C_ENOACK;
    }
    for (i = 0; i < length - 1; i++) {
    
        *(p_buf + i) = __i2c_readOneByte(1);        /* 读取数据 + ACK */
    }
    *(p_buf + i) = __i2c_readOneByte(0);            /* 读最后一个数据 + NACK */
    
    __i2c_stop();
    return I2C_OK;
}
/************** my_iic.h ****************/
#ifndef __MY_IIC_H
#define __MY_IIC_H
#include <STC89C5xRC.H>
#include "intrins.h"

#define uint8_t     unsigned char
#define uint16_t    unsigned int
#define int8_t      unsigned char

#define I2C_SCL             P21
#define I2C_SDA             P20

#define DIVICE_ADDR         0x50

#define I2C_OK              0       /**< 操作成功   */
#define I2C_ENOACK         -1       /**< 从机无应答 */

int8_t my_i2c_writeOneByte (uint8_t addr, uint8_t dat);
uint8_t my_i2c_readOneByte (uint8_t addr);

/**
 * \brief I2C 主机写数据
 *
 * \param[in] addr   : 从机的寄存器地址
 * \param[in] p_buf  : 待写的数据缓存地址
 * \param[in] length : 待写的字节长度
 *
 * \retval  0 : 操作成功
 * \retval -1 : 操作失败
 */
int8_t my_i2c_write (uint8_t addr, uint8_t *p_buf, uint16_t length);

/**
 * \brief I2C 主机读数据
 *
 * \param[in] addr   : 从机的寄存器地址
 * \param[in] p_buf  : 存放读取数据的缓存地址
 * \param[in] length : 待读的字节长度
 *
 * \retval  0 : 操作成功
 * \retval -1 : 操作失败
 */
int8_t my_i2c_read (uint8_t addr, uint8_t *p_buf, uint16_t length);

#endif
  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
void start 开始信号 void stop 停止信号 void Ack 发确认信号 void NoAck 发无确认信号 void init 初始化信号 拉高SDA和SCL两条总线 bit write byte uchar date 写一字节 将 date 写入AT24C02 中 uchar read byte 字节 从 AT24C02字节 bit busy 应答查询 stop 后 启动AT24C02内部写周期 启动查询 初始化EEPROM子程序内容为0XFF nPage 0 31 void Init Flash uchar nPage 8 bytes 1 page init 0xFF void write add uchar address uchar date 向 AT24C02 中写数据 从AT24C02中给定的地址nAddr起 将存放在以指针nContent开头的存储空间中的nLen个字节数据 连续写入AT24C02 void write flash uchar nContent uchar nAddr uchar nLen uchar read add uchar address 从 AT24C02出数据 从AT24C02中给定的地址nAddr起 取nLen个字节数据存放在以指针nContent开头的存储空间 void read flash uchar nContent uchar nAddr uchar nLen 单片机P2口接74HC138(三八译码器)P2 3 74HC138: EI P2 2 74HC138:A2 P2 1 74HC138:A1 P2 0 74HC138:A0 译码器输出 Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7均低电平有效 分别选通1 8个数码管 包括2个四位一体数码管LG3641BH 共2x4 8个数码管 数码管数据口为P0口 数码管为共阳4位一体数码管 功能: 译码器输出为1 8个数码管的段选信号 轮流选择1 8数码管 void display uchar nContent uchar nLen 功能:在8段数码管上显示nLen个字符 这些字符存储在指针nContent开头的往下的内容中 显示原理: 1 送出要显示的段数 2 P2译码 选择要显示的位 3 延时1 2ms 时间不能太长 否则会闪烁 也不能太短 否则会很暗 4 取消段选 消隐 若要显示多段 重复以上4步 ">void start 开始信号 void stop 停止信号 void Ack 发确认信号 void NoAck 发无确认信号 void init 初始化信号 拉高SDA和SCL两条总线 bit write byte uchar date 写一字节 将 date 写入AT24C02 中 uchar read byte 字节 从 AT24C02一 [更多]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值