打印
[研电赛技术支持]

GD32L233通过I2C驱动sensirionSCD4X传感器

[复制链接]
460|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
在使用GD32L233KBT6通过sensirion官方驱动调试SCD4X二氧化碳传感器时,遇到软件模拟IIC通信正常,硬件IIC无法读写的问题,折腾了好久终于调试通过!记录如下:

1.遇到的问题
基于上一篇《GD32L233通过I2C总线驱动AHT20温湿度传感器》的硬件I2C代码,无法正常驱动SCD4X,现象是地址发完后就触发了STOP信号,导致无法正常读写。

2.原因
先看原来的iic_write代码:

uint8_t aht20_interface_iic_write_cmd(uint8_t addr, uint8_t *buf, uint16_t len)
{
    uint16_t count=len;

     // 配置为发送模式
    i2c_master_addressing(I2C1, addr, I2C_MASTER_TRANSMIT);
    /* wait until I2C bus is idle */
    while(i2c_flag_get(I2C1, I2C_FLAG_I2CBSY));
    // 配置要发送的字节数
    i2c_transfer_byte_number_config(I2C1, count);
    // 生成开始条件
    i2c_start_on_bus(I2C1);

     /* wait until the transmit data buffer is empty */
    I2C_STAT(I2C1) |= I2C_STAT_TBE;
    while(!i2c_flag_get(I2C1, I2C_FLAG_TBE));

    // 发送数据
    while(count--) {

        i2c_data_transmit(I2C1, *buf++);
        // 等待可以发送数据
        while(i2c_flag_get(I2C1, I2C_FLAG_TBE) == RESET) {
        }
    }
    // 等待传输完成
    while(i2c_flag_get(I2C1, I2C_FLAG_TC) == RESET) {
    }
    // 生成停止条件
    i2c_stop_on_bus(I2C1);
      /* wait until stop condition generate */
    while(!i2c_flag_get(I2C1, I2C_FLAG_STPDET));
    /* clear the STPDET bit */
    i2c_flag_clear(I2C1, I2C_FLAG_STPDET);

    return 0;
}



分析
在产生START后,GD32自动触发address发送

// 生成开始条件
i2c_start_on_bus(I2C1);

此时,紧跟着代码状态置位TBE,这里是不对的,问题就出在I2C_STAT(I2C1) |= I2C_STAT_TBE;这句,如果有这句,下面的while等待直接就过了不会等待。为什么驱动AHT20时没有出现问题,暂时不明。但是驱动SCD4X,这里必须去掉 I2C_STAT(I2C1) |= I2C_STAT_TBE;否则后面等待I2C_FLAG_TBE无效。

/* wait until the transmit data buffer is empty */
I2C_STAT(I2C1) |= I2C_STAT_TBE;
while(!i2c_flag_get(I2C1, I2C_FLAG_TBE));


修改后的iic_write底层实现代码:

int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data,
                               uint16_t count) {
     // 配置为发送模式
    i2c_master_addressing(I2C1, address, I2C_MASTER_TRANSMIT);
    /* wait until I2C bus is idle */
    while(i2c_flag_get(I2C1, I2C_FLAG_I2CBSY));
    // 配置要发送的字节数
    i2c_transfer_byte_number_config(I2C1, count);
    // 生成开始条件
    i2c_start_on_bus(I2C1);

     /* wait until the transmit data buffer is empty */
     //这里等待address发送完毕,gd32在start信号后自动发送地址
    while(!i2c_flag_get(I2C1, I2C_FLAG_TBE));

    // 发送数据
    while(count--) {

        i2c_data_transmit(I2C1, *data++);

        // 等待可以发送数据
        while(!i2c_flag_get(I2C1, I2C_FLAG_TBE));
    }
        // 等待传输完成
    while(!i2c_flag_get(I2C1, I2C_FLAG_TC));
    // 生成停止条件
    i2c_stop_on_bus(I2C1);
      /* wait until stop condition generate */
    while(!i2c_flag_get(I2C1, I2C_FLAG_STPDET));
    /* clear the STPDET bit */
    i2c_flag_clear(I2C1, I2C_FLAG_STPDET);
    return 0;
}



同理,修改后的iic_read函数如下:

int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) {

     /* wait until I2C bus is idle */
    while(i2c_flag_get(I2C1, I2C_FLAG_I2CBSY));
     // 配置为接收模式
    i2c_master_addressing(I2C1, address, I2C_MASTER_RECEIVE);
    // 配置要接收的字节数
    i2c_transfer_byte_number_config(I2C1, count);
    // 生成开始条件
    i2c_start_on_bus(I2C1);

    //等待地址发送完成
    while(!i2c_flag_get(I2C1, I2C_FLAG_TBE));

    // 读取数据
    while(count--) {
        while(i2c_flag_get(I2C1, I2C_FLAG_RBNE) == RESET) {
        }
        *data++ = i2c_data_receive(I2C1);
    }
    // 等待传输完成
    while(!i2c_flag_get(I2C1, I2C_FLAG_TC));
    // 生成停止条件
    i2c_stop_on_bus(I2C1);
      /* wait until stop condition generate */
    while(!i2c_flag_get(I2C1, I2C_FLAG_STPDET));
    /* clear the STPDET bit */
    i2c_flag_clear(I2C1, I2C_FLAG_STPDET);
    return 0;
}



传感器官方驱动文件github下载地址:embedded-i2c-scd4x

拷贝如下几个文件及对应头文件到工程:

scd4x_i2c.c
sensirion_common.c
sensirion_i2c_hal.c
sensirion_i2c.c
重点修改文件:sensirion_i2c_hal.c,该文件实现了i2c底层的读写接口。
scd4x_i2c_example_usage.c中有测试示例代码:
int main(void) {
    int16_t error = 0;

    sensirion_i2c_hal_init();

    // Clean up potential SCD40 states
    scd4x_wake_up();
    scd4x_stop_periodic_measurement();
    scd4x_reinit();

    uint16_t serial_0;
    uint16_t serial_1;
    uint16_t serial_2;
    error = scd4x_get_serial_number(&serial_0, &serial_1, &serial_2);
    if (error) {
        printf("Error executing scd4x_get_serial_number(): %i\n", error);
    } else {
        printf("serial: 0x%04x%04x%04x\n", serial_0, serial_1, serial_2);
    }

    // Start Measurement

    error = scd4x_start_periodic_measurement();
    if (error) {
        printf("Error executing scd4x_start_periodic_measurement(): %i\n",
               error);
    }

    printf("Waiting for first measurement... (5 sec)\n");

    for (;;) {
        // Read Measurement
        sensirion_i2c_hal_sleep_usec(100000);
        bool data_ready_flag = false;
        error = scd4x_get_data_ready_flag(&data_ready_flag);
        if (error) {
            printf("Error executing scd4x_get_data_ready_flag(): %i\n", error);
            continue;
        }
        if (!data_ready_flag) {
            continue;
        }

        uint16_t co2;
        int32_t temperature;
        int32_t humidity;
        error = scd4x_read_measurement(&co2, &temperature, &humidity);
        if (error) {
            printf("Error executing scd4x_read_measurement(): %i\n", error);
        } else if (co2 == 0) {
            printf("Invalid sample detected, skipping.\n");
        } else {
            printf("CO2: %u\n", co2);
            printf("Temperature: %d m°C\n", temperature);
            printf("Humidity: %d mRH\n", humidity);
        }
    }

    return 0;
}



3.运行效果
示例代码中温度和湿度都是1000倍值,我除1000后打印效果如下:



logic抓包写指令:



logic抓包读序列:



4.总结
gd32l233这个i2c驱动不如STM32好用,甚至都没有iic速度配置,需要自己去换算i2c_sda和i2c_scl的保持时间来实现速度控制。
建议先调试write接口,再调试read接口。比如调用scd4x_wake_up();函数,就只会发2字节指令下去。通过抓包看下实际波形确保硬件没问题。
走了很多弯路,包括加延时之类的,其实不需要。本质是对gd32这个硬件i2c驱动理解不深刻。
gd32的硬件i2c在i2c_start_on_bus(I2C1);后会自动发送address字节,这里一定要等待发送完成,因此要跟一句while(!i2c_flag_get(I2C1, I2C_FLAG_TBE));
不管是read还是write,在读写字节完成后,一定要等待传输完成-> while(!i2c_flag_get(I2C1, I2C_FLAG_TC));
如果遇到stop信号或data无法发送,抓包只有address字节,基本可以确定就是硬件上还没发送完成造成的,必须增加相应的TC/TBE等待。上述代码是调试通过的,可以直接使用。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/kingnike/article/details/143596601

使用特权

评论回复
沙发
AloneKaven| | 2024-12-10 14:36 | 只看该作者
为啥会直接发送停止信号啊?

使用特权

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

本版积分规则

154

主题

4128

帖子

5

粉丝