发新帖我要提问
12
返回列表
打印
[MCU]

软件i2c读多字节只能读出第一个字节

[复制链接]
楼主: qinchxiong
手机看帖
扫描二维码
随时随地手机跟帖
21
qinchxiong|  楼主 | 2020-6-11 14:22 | 只看该作者 回帖奖励 |倒序浏览
ayb_ice 发表于 2020-6-11 14:11
可以忽略这个应答的结果,直接往下读试试

读完一个字节,我发出ack(0),但下一个字节读到的是0,实际是slave没有再送出下一个字节,波形图上最后8个clk,SDA都是低

使用特权

评论回复
22
ayb_ice| | 2020-6-11 14:29 | 只看该作者
qinchxiong 发表于 2020-6-11 14:22
读完一个字节,我发出ack(0),但下一个字节读到的是0,实际是slave没有再送出下一个字节,波形图上最后8 ...

我改下,100% OK,

#include "i2c_master.h"

#define         SCL_H         I2C1_SCL_Write(1)
#define         SCL_L         I2C1_SCL_Write(0)
#define         SDA_H         I2C1_SDA_Write(1)
#define         SDA_L         I2C1_SDA_Write(0)
#define I2C_SDA_READ  I2C1_SDA_Read()

#define     IO_DELAY        3
#define     IO_DELAY_2X     4
#define     IO_DELAY_3X     8
#define     IO_DELAY_4X     10
#define     IO_DELAY_5X     12

#define SDA_INPUT()                SDA_H

uint8_t IC7211_WriteI2C_Byte(uint8_t reg,uint8_t data)
{
      return i2c_write_byte(IC7211_I2C_ADDR,reg,&data,1);
}

uint8_t IC7211_WriteI2C_NByte(uint8_t reg,uint8_t data,uint8_t size)
{
      return i2c_write_byte(IC7211_I2C_ADDR,reg,&data,size);
}

uint8_t IC7211_ReadI2C_Byte(uint8_t reg)
{
      uint8_t data;
          
      i2c_read_byte(IC7211_I2C_ADDR,reg,&data,1);
      return data;
}

uint8_t IC7211_ReadI2C_NByte(uint8_t reg,uint8_t *buffer, uint32_t size)
{           
      i2c_read_byte(IC7211_I2C_ADDR,reg, buffer, size);
      return 1;
}

uint8_t ic7211_check_id(void)
{
        uint32_t nChipID;
        uint32_t fw_byte_count;
        char output[50];
        uint8_t buffer[2]={0x0};
        uint32_t ID;


        IC7211_WriteI2C_Byte(0xFF, 0x80);
        IC7211_WriteI2C_Byte(0xEE, 0x01);//IIC Enable

        IC7211_WriteI2C_Byte(0xff, 0xa0);
//        uint8_t bA000 = IC7211_ReadI2C_Byte(0x00);
//        uint8_t bA001 = IC7211_ReadI2C_Byte(0x01);
//        nChipID = ((bA000 << 8) | bA001);

        IC7211_ReadI2C_NByte(0x00,buffer,2);

        memset(output,0,sizeof(output));
        sprintf(output,  "\r\n ChipID=0x%x|buffer=0x%x 0x%x|ID=0x%x  \r\n", nChipID,buffer[0],buffer[1],(buffer[0]<<8)|buffer[1]);
        UART_UartPutString((const char*)output);       
        return nChipID == 0x1605;
}

static void start_i2c(void)
{
        SDA_H;   
        CyDelayUs(IO_DELAY);
        SCL_H;
        CyDelayUs(IO_DELAY_5X);
        SDA_L;          
        CyDelayUs(IO_DELAY_3X);        
        SCL_L;   
        CyDelayUs(IO_DELAY_2X);
}

static void stop_i2c(void)
{
        SDA_L;   
        CyDelayUs(IO_DELAY);
        SCL_H;
        CyDelayUs(IO_DELAY_5X);
        SDA_H;  
        CyDelayUs(IO_DELAY_2X);
}

static uint8_t read_ack(void)                        //接收应答信号函数
{
        uint8_t ack;                                    //定义一个位变量,来暂存应答状态。
        
                SDA_INPUT();
        CyDelayUs(IO_DELAY);
        SCL_H;                                            //拉高时钟线。
        CyDelayUs(IO_DELAY);
        ack = I2C_SDA_READ;                                //读取应答信号的状态。
        CyDelayUs(IO_DELAY);
        SCL_L;                                            //拉低时钟线。
        CyDelayUs(IO_DELAY);
        return ack;                                        //返回应答信号的状态,0表示应答,1表示非应答。
}

static uint8_t wait_read_ack(void)
{
        if (!read_ack())
        {
                return 1;
        }
        return 0;
}

static uint8_t  i2c_send_byte(uint8_t data)
{
        uint8_t bit_cnt;
            uint8_t size = 0;

        for(bit_cnt = 0; bit_cnt < 8; bit_cnt++)
        {
                if((data<<bit_cnt) & 0x80)
                        SDA_H;   
                else
                        SDA_L;               
                CyDelayUs(IO_DELAY);
                SCL_H;            
                CyDelayUs(IO_DELAY_2X);         
                SCL_L;
                CyDelayUs(IO_DELAY);
        }
   
    //释放SDA总线,等待从设备应答
        if(wait_read_ack() != 0)
                size = 0;
        else
                size = 1;

            return size;
}

static void ack_i2c(uint8_t a)
{
        if(a == 0)
                SDA_L;
        else
                SDA_H;

        CyDelayUs(IO_DELAY);     
        SCL_H;
        CyDelayUs(IO_DELAY_2X);  
        SCL_L;
        CyDelayUs(IO_DELAY_2X);
}

static uint8_t  recv_byte(void)
{
        uint8_t retc = 0;
        uint8_t bit_cnt;

//CyDelayUs(IO_DELAY_4X);   

        SDA_INPUT();
        for (bit_cnt = 0; bit_cnt < 8; bit_cnt++)
        {
                SCL_H;            
                CyDelayUs(IO_DELAY);
                retc = retc<<1;
                if (I2C_SDA_READ != 0)
                {
                        retc = retc | 0x1;
                }                                       
                CyDelayUs(IO_DELAY);
                SCL_L;
                CyDelayUs(IO_DELAY);
        }

//CyDelayUs(IO_DELAY_4X);               
        return retc;
}

uint8_t i2c_write_byte(uint8_t slave, uint8_t sub_addr, uint8_t *buffer, uint8_t len)
{
        uint8_t i;
           uint8_t size;
   
        if(buffer == NULL || len == 0)
        {
            return 0;
        }
   
        start_i2c();              
        size = i2c_send_byte(slave);            
        if(size == 0)
        {
             stop_i2c();
             return  size;
        }
        size = i2c_send_byte(sub_addr);            
        if(size == 0)
        {
             stop_i2c();
             return size;
        }

        for(i = 0; i < len; i++)
        {   
             size = i2c_send_byte(*buffer);         
             if(size == 0)
             {
                 stop_i2c();
                 return  size;
             }
             buffer++;
        }
   
        stop_i2c();            
        return len;
}

uint8_t i2c_read_byte(uint8_t slave, uint8_t sub_addr, uint8_t *buffer, uint8_t len)
{
        uint8_t i;
        uint8_t size;
        uint8_t output[50];
               
        if(buffer == NULL || len == 0)
        {
            return 0;
        }
               
        start_i2c();                 
        size = i2c_send_byte(slave);              
        if(size == 0)
        {
             stop_i2c();
             *buffer = 0x00;
             return size;
        }
   
        size = i2c_send_byte(sub_addr);            
        if (size == 0)
        {
             stop_i2c();
             *buffer = 0x00;
             return  size;
        }
   
        start_i2c();                       
        size = i2c_send_byte(slave+1);
        if (size == 0)
        {
              stop_i2c();
              *buffer = 0x00;
              return  size;
        }

        for (i = 0; i < len - 1; i++)
        {   
              *buffer = recv_byte();

              ack_i2c(0);

              buffer++;
        }

        *buffer = recv_byte();
       
        ack_i2c(1);
        stop_i2c();   

        return  len;
}

使用特权

评论回复
23
qinchxiong|  楼主 | 2020-6-11 14:38 | 只看该作者
本帖最后由 qinchxiong 于 2020-6-11 14:39 编辑
ayb_ice 发表于 2020-6-11 14:11
可以忽略这个应答的结果,直接往下读试试
我试一下你的代码

使用特权

评论回复
24
qinchxiong|  楼主 | 2020-6-11 15:17 | 只看该作者
本帖最后由 qinchxiong 于 2020-6-11 15:21 编辑
ayb_ice 发表于 2020-6-11 14:29
我改下,100% OK,

#include "i2c_master.h"

现在可以了,根本原因是那2句SDA_INPUT, 如你所说需要先输出1,问题解决了,感谢!准双向口要做输入口时,需要先写1

使用特权

评论回复
25
ayb_ice| | 2020-6-11 15:22 | 只看该作者
qinchxiong 发表于 2020-6-11 15:17
现在可以了,根本原因是那2句SDA_INPUT, 如你所说需要先输出1,问题解决了,感谢!准双向口要做输入口时, ...

我早提醒你了,你没听

使用特权

评论回复
26
qinchxiong|  楼主 | 2020-6-11 15:47 | 只看该作者
ayb_ice 发表于 2020-6-11 15:22
我早提醒你了,你没听

之前有点彷徨,我想到了这里,昨晚网上查资料动态修改io方向,看别人讨论要修改寄存器,所以在官网下载了文档研究,结果打不开,但实际上写1就可以设置为输入模式了,另外read 单字节没有问题,所以SDA的方向配置比较模糊,这个平台不是太熟悉。总之很感谢你!

使用特权

评论回复
27
红心J| | 2020-6-12 15:00 | 只看该作者
qinchxiong 发表于 2020-6-11 11:48
static uint8_t wait_read_ack(void)
{
       uint8_t ack = 1;

可定不可以多次发时钟;时钟高电平时,检测ACK,若未检测到,就连续检测,直到检测到,才将SCL拉低,完成ACK检测时序,或者规定个时延检测不到时认为出错,进行出错处理。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则