今天用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