您尚未登录。

楼主 # 2022-06-21 11:39:52

SwordofMorning
会员
注册时间: 2022-05-20
已发帖子: 6
积分: 11

【I2C】当ADC输入大于1v时读取错误

小弟今天用Licheepi Nano读取ADS1110,当输入数据小于等于1V时,能得到正确的数据,但是当输入大于1v时,得到的数据则是不正确的。代码如下:

#define NUMS 10
char readbuf[NUMS];

int main(int argc, char** argv)
{
    int file = open("/dev/i2c-0", O_RDWR);

    // addr = 1001 xxx
    int addr = 0b1001000;

    if (file < 0)
    {
        printf("openfile error!\n");
        exit(1);
    }

    if (ioctl(file, I2C_SLAVE, addr) < 0)
    {
        printf("ioctl error!\n");
        exit(1);
    }

    // Debug Gain 1
    readbuf[0] = 0x0C;

    if (write(file, readbuf, 1) != 1)
    {
        printf("write error!\n");
        exit(1);
    }

    if (read(file, readbuf, 3) != 3)
    {
        printf("read error!\n");
        exit(1);
    }

    printf("%x %x %x\n", readbuf[0] & 0xff, readbuf[1] & 0xff, readbuf[2] & 0xff);

    close(file);

    exit(EXIT_SUCCESS);
}

上述代码中我写入0x0C,使ADS1110以15sps、16bits输出,同时增益为1。当输入为1V时,输出为`3E 00 0C`,即,3E00 / 32767 * 2.048;当输入为1.5V时,输出大约应为`5E 00 0C'左右,但是我得到的数据大约为`1D 00`。使用调试器的时候能正确读取,请问我这里是哪里没写对吗,感谢大佬指出。

离线

楼主 #2 2022-07-05 10:38:09

SwordofMorning
会员
注册时间: 2022-05-20
已发帖子: 6
积分: 11

Re: 【I2C】当ADC输入大于1v时读取错误

可以看到D14的数据本来应该拉高的,但是Nano这边却在将SDA拉低。
libi2c.png
但是除了这一位和低八位的数据,接收的其他数据确是正常的。之后我又使用gpiod库来手动模拟了i2c,但是该问题仍然存在:
gpiod.png
可以看到,除了config八位的数据是正常的,data的数据均没有被拉起来。这三个字节使用同一段代码读取,不知道是哪里出的问题。下面是我模拟i2c的代码:

struct gpiod_chip* gpiochip0;
struct gpiod_line* line_SCL;
struct gpiod_line* line_SDA;

int PE11Num = (4 * 32) + 11;
int PE12Num = (4 * 32) + 12;

bool i2cInit()
{
    gpiochip0 = gpiod_chip_open("/dev/gpiochip0");
    line_SCL = gpiod_chip_get_line(gpiochip0, PE11Num);
    line_SDA = gpiod_chip_get_line(gpiochip0, PE12Num);

    int reqSCL = gpiod_line_request_output(line_SCL, "SCL", 1);
    int reqSDA = gpiod_line_request_output(line_SDA, "SDA", 1);

    return !(reqSCL | reqSDA);
}

bool i2cSDAConf(bool toOutput)
{
    int reqSDA;

    gpiod_line_release(line_SDA);

    if (toOutput)
    {
        int reqSDA = gpiod_line_request_output(line_SDA, "SDA", 1);
    }
    else
    {
        int reqSDA = gpiod_line_request_input(line_SDA, "SDA");
    }

    return !reqSDA;
}

void i2cSDALevel(bool toHigh)
{
    gpiod_line_set_value(line_SDA, toHigh);
}

void i2cSCLLevel(bool toHigh)
{
    gpiod_line_set_value(line_SCL, toHigh);
}

void i2cDelay()
{
    for (int i = 0; i < 1000; ++i) { /* do nothing */ }
}

void i2cStart()
{
    i2cSDALevel(1);
    i2cSCLLevel(1);
    i2cDelay();
    i2cSDALevel(0);
    i2cDelay();
    i2cSCLLevel(0);
    i2cDelay();
}

void i2cEnd()
{
    i2cSDALevel(0);
    i2cDelay();
    i2cSCLLevel(1);
    i2cDelay();
    i2cSDALevel(1);
}

void i2cACK()
{
    // make sure the SDA is output before send
    i2cSDAConf(1);

    i2cSDALevel(0);
    i2cDelay();
    i2cSCLLevel(1);
    i2cDelay();
    i2cSCLLevel(0);
    i2cDelay();
}

void i2cNACK()
{
    // make sure the SDA is output before send
    i2cSDAConf(1);

    i2cSDALevel(1);
    i2cDelay();
    i2cSCLLevel(1);
    i2cDelay();
    i2cSCLLevel(0);
    i2cDelay();
}

bool i2cSDARelease()
{
    // i2cSDALevel(0);
    return i2cSDAConf(0);
}

bool i2cWaitAck()
{
    return !i2cGetSDA();
}

void i2cSend(u8 data)
{
    // make sure the SDA is output before send
    i2cSDAConf(1);

    for (int i = 0; i < 8; ++i)
    {
        if (data & 0x80)
            i2cSDALevel(1);
        else
            i2cSDALevel(0);
        
        i2cDelay();
        i2cSCLLevel(1);
        i2cDelay();

        i2cSCLLevel(0);
        i2cDelay();

        data <<= 1;
    }

    // 释放SDA
    i2cSDARelease();
}

bool i2cGetSDA()
{
    i2cSCLLevel(1);
    i2cDelay();

    bool level = gpiod_line_get_value(line_SDA);

    i2cSCLLevel(0);
    i2cDelay();

    return level;
}

u8 i2cReceive()
{
    i2cSDARelease();

    u8 data;

    for (int i = 0; i < 8; ++i)
    {
        data <<= 1;
        data |= i2cGetSDA();
    }
    
    return data;
}

u8 i2cReceiveDebug(u32* biasDebug)
{
    i2cSDARelease();

    u8 data;

    for (int i = 0; i < 8; ++i)
    {
        data <<= 1;
        data |= i2cGetSDA();
        *biasDebug <<= 2;

        // 查看pull寄存器状态
        static int dev_fd;
        unsigned char *map_base;
        dev_fd = open("/dev/mem", O_RDWR);
        map_base = (unsigned char *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, GPIO_PAGE_OFFSET);
        *biasDebug |= (*(volatile u32*)(map_base + GPIO_BASE_OFFSET + rPE_PULL0)) >> 24 & 0b11;
        munmap(map_base, MAP_SIZE);
    }
    
    return data;
}

void i2cRelease()
{
    gpiod_chip_close(gpiochip0);
}

void i2cClear()
{
    i2cSDARelease();
    for (int i = 0; i < 32; ++i)
    {
        i2cSCLLevel(0);
        i2cSCLLevel(1);
    }
}

ADS1110读取,在start之前和end之后,我输出了一段scl的时钟,以期对清楚ADS1110的寄存器内容。

void ADS1110_ReadData()
{
    if(!i2cInit())
    {
        printf("I2C init error\n");
        i2cRelease();
        exit(1);
    }

    i2cClear();

    i2cSDAConf(1);

    i2cStart();
    
    u8 addr = 0b10010001;

    i2cSend(addr);

    if (!i2cWaitAck())
    {
        printf("none ACK received\n");
        printf("%d\n", gpiod_line_direction(line_SDA));
        i2cRelease();
        exit(1);
    }

    u8 data[3];

    data[0] = i2cReceive();
    i2cACK();
    data[0] = i2cReceive();
    i2cACK();
    data[0] = i2cReceive();
    i2cNACK();

    i2cEnd();

    i2cClear();

    i2cRelease();

    printf("%x %x %x\n", data[0] & 0xFF, data[1] & 0xFF, data[2] & 0xFF);
}

同时,总线上只有ADS1110一个设备:

# i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn