This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] CC430F5137:与 SGP30 CO2传感器的 I2C 通信失败

Guru**** 2534260 points
Other Parts Discussed in Thread: CC430F5137

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/761381/cc430f5137-i2c-communication-fails-with-sgp30-co2-sensor

器件型号:CC430F5137
主题中讨论的其他器件: MSP430WARE

我正在尝试与 sgp30建立通信、并从传感器读取 CO2和 VOC 值、但似乎失败了。 我正在使用由 Alex 提供的 i2c 库(https://github.com/amykyta3/msp430_modules.I正在使用此库与 si7020、mpl3115a2进行 i2c 通信、读取这些值时没有问题、但 sgp30似乎出现故障。首先、我将 reg_id 传递为0x2003用于 init、然后传递0x2008以读取测量值。 初始化时似乎失败。  


i2C_PACKAGE_t i2c_req; i2C_req.slave_addr = slave_addr; i2C_req.addr[0]= reg_id >> 8;//reg_id 是16位 uint i2C_req.addr[1]= reg_id & 0xFF; i2C_req.addr_len = addr_len;// 2. i2C_req.data_len = destSize;//6 I2C_req.data = dest;// uint8_t 测量值 I2C_req.read =读取;// true I2C_IN_PROGRESS = true; i2C_TRANSFER_START (&i2c_req、i2c_callback); timeout_handle_init (i2c_cmd、2);

它似乎完成了 SEND_addr、SEND_RESTART 并将下一个状态指定为 READ_DATA、但 I2C_IV = USCI_I2C_UCNACKIFG 时失败。

有什么建议是错误的?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Kumar、

    由于 I2C 通信与 SI7020和 MPL3115A2器件一起工作、因此我想首先查看 SGP30器件。 我希望您仔细检查一下我的情况:

    您是否遵循了 SGP30数据表第6节中概述的所有步骤?
    具体而言:
    -以 I2C 快速模式波特率(400kHz)运行?
    -是否将传感器的电源电压设置为所需的电源电压?
    -在发送 I2C 命令之前等待所需的上电时间(看起来是0.6ms)?

    SGP30数据表链接供参考:
    cdn-learn.adafruit.com/.../Sensirion_Gas_Sensors_SGP30_Datasheet_EN.pdf

    谢谢、

    Mitch
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Mitch、

    之前的 I2C 波特率等效于60kHz,现在我已将其设置为400kHz。 工作电压为3.0V、在访问 I2C 寄存器之前的加电时间为2秒、这就足够了。 我将 Adafruit 板与 sgp30配合使用

    现在、通过时钟更改、我可以通过读取0x2003寄存 器来初始化 sgp30、I2C 返回空闲状态、我可以读取功能集寄存器0x202f、它返回{0x0、0x20、0x7}、这看起来也是正确的、但当我读取测量寄存器时、I2C 通信失败。

    在 INIT 命令和测量命令之间有20秒的延迟。 我在测量序列中缺少的任何东西、我 已经阅读了数据表、但不确定缺少什么。

    我是否需要在库内的 i2c 函数(i2c_transfer_start)内有任何延迟?

    谢谢你

    库马尔

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Kumar、

    很棒! 我们现在知道、我们可以初始化通信、get_feature_set_version 命令有效。

    您能否再次检查您是否看到此行为、以及是否尝试按如下所述读取测量结果?

    为了安全起见、我们提供了一些适用于 CC430F5137的 I2C 示例代码。 您可能需要根据我们的示例检查 I2C 代码、以确保功能相同。 这些示例可在 CCS 中找到、方法是导航至:

    View > Resource Explorer > Software > MSP430Ware > Devices > CC430FXX > CC430F5137 > Register Level

    我还找到了一个介绍如何使用 SGP30驱动程序软件的文档。 此处可能提供一些有用的信息:

    如果您仍然遇到问题、能否向我发送问题的逻辑分析仪屏幕截图?  

    谢谢、

    Mitch

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Mitch、

    感谢您的帮助,我没有逻辑分析仪可以看到 i2c 的运行,因为我们是一个小启动。 我附上了我使用的代码、如果您能帮助我了解问题的发生、我将不胜感激。  

    ISR (I2C_ISR_Vector){
    if (I2C_IV = USCI_I2C_UCNACKIFG){
    I2C_IFG = 0;
    I2C_IE = 0;
    transfer.status = I2C_FAILED;
    I2C_CTL1 |= UCTXSTP;//设置停止条件
    if (transfer.callback){
    transfer.callback(I2C_FAILED);
    }
    
    返回;
    }
    
    I2C_IFG = 0;
    
    switch (transfer.next 状态){
    案例 SM_SEND_ADDR://(TX 寄存器地址)
    // STT 开始后到达此处。 可以安全地写入 TXBUF
    
    I2C_TXBUF = transfer.pkg->addr[transfer.idx];
    transfer.idx++;
    if (transfer.idx =transfer.pkg->addr_len){
    //这是最后一个地址字节。
    
    //更新下一状态
    if (transfer.pkg->data_len!= 0){
    transfer.idx = 0;
    if (transfer.pkg->read){
    transfer.next 状态= sm_send_restart;
    }否则{
    transfer.next 状态= sm_write_data;
    }
    }否则{
    transfer.next 状态= SM_DONE;
    }
    }
    中断;
    案例 SM_WRITE_DATA:
    I2C_TXBUF = transfer.pkg->data[transfer.idx];
    transfer.idx++;
    if (transfer.idx =transfer.pkg->data_len){
    //这是要发送的最后一个数据字节。
    
    //更新下一状态
    transfer.next 状态= SM_DONE;
    }
    
    中断;
    案例 SM_SEND_RESTART:
    I2C_CTL1 &=~UCTR;//设置为接收器模式
    I2C_CTL1 |= UCTXSTT;//写入(重新)启动条件
    
    if (transfer.pkg->data_len = 1){
    //等待 STT 位丢弃
    while (I2C_CTL1 & UCTXSTT);
    I2C_CTL1 |= UCTXSTP;//计划停止条件
    }
    //更新下一状态
    transfer.next 状态= SM_READ_DATA;
    中断;
    案例 SM_READ_DATA:
    transfer.pkg->data[transfer.idx]= I2C_RXBUF;
    transfer.idx++;
    if (transfer.idx = transfer.pkg->data_len - 1){
    //下一个传入字节是最后一个字节。
    I2C_CTL1 |= UCTXSTP;//计划停止条件
    中断;
    }如果(transfer.idx < transfer.pkg->data_len){
    //仍有更多需要恢复的内容。
    中断;
    }
    //否则,这是要重新生成的最后一个数据字节。
    //掉下来
    案例 SM_DONE:
    I2C_IE = 0;
    if (transfer.pkg->read =false){
    //如果写入完成,安排一个停止条件
    I2C_CTL1 |= UCTXSTP;
    }
    transfer.status = I2C_IDLE;
    if (transfer.callback){
    transfer.callback(I2C_IDLE);
    }
    中断;
    }
    }//------------------
    
    
    void i2c_init (void){
    
    I2C_CTL0 |= UCSWRST;
    //初始化 UCSWRST=1时的所有 USCI 寄存器(包括 UCxCTL1)。
    I2C_CTL0 = UCMST|UCMODE_3|UCSYNC;
    I2C_CTL1 = UCTR|UCSWRST|(I2C_CLK_SRC << 6);
    I2C_BR = I2C_CLK_DIV;
    
    I2C_CTL1 &=~UCSWRST;//清除复位
    
    transfer.status = I2C_IDLE;
    }
    
    //-----------------
    void i2c_uninit (void){
    I2C_CTL0 = UCSYNC;
    I2C_CTL1 = UCSWRST;
    I2C_SA = 0;
    I2C_IE = 0;
    }//-----------------
    
    
    void i2c_transfer_start (const i2c_package_t * pkg、void (*回调)(i2c_status_t 结果)){
    
    if (i2c_transfer_status ()=I2C_BUSY) return;
    
    transfer.pkg =(i2c_package_t*) pkg;
    transfer.idx = 0;
    transfer.callback =回调;
    transfer.status = I2C_BUSY;
    
    //更新下一状态
    if (pkg->addr_len!= 0){
    transfer.next 状态= SM_SEND_ADDR;
    }else if (pkg->data_len!= 0){
    if (pkg->read){
    transfer.next 状态= sm_send_restart;
    }否则{
    transfer.next 状态= sm_write_data;
    }
    }否则{
    transfer.next 状态= SM_DONE;
    }
    
    I2C_IFG = 0;
    I2C_IE = UCNACKIE | UCTXIE | UCRXIE;
    I2C_SA = pkg->slave_addr;
    I2C_CTL1 |= UCTR;//设置为发送器模式
    I2C_CTL1 |= UCTXSTT;//开始条件(发送从器件地址)
    
    }
    //下面的函数从应用程序代码
    int8_t read (uint8_t slave_addr、uint16_t reg_id、uint8_t addr_len、void* dest、uint8_t destSize、bool read){调用
    i2C_PACKAGE_t i2c_dev;
    I2C_DEV.SLAVE_ADDR = SLAVE_ADDR;
    I2C_DEV.addr[0]= reg_id >> 8;
    I2C_DEV.addr[1]= reg_id & 0xFF;
    i2C_dev.addr_len = addr_len;
    i2C_dev.data_len = destSize;
    I2C_DEV.data =目的;
    I2C_DEV.Read =读取;
    
    I2C_IN_PROGRESS = true;
    i2C_TRANSFER_START (&i2c_dev、i2c_callback);
    timeout_handle_init (i2c_cmd、2);
    
    while (i2c_in_progress &&!is_handle_timeout (i2c_cmd));
    if (is_handle_timeout (i2c_cmd)|| i2c_failed)
    返回1;
    返回0;//确定
    } 
    //主要
    ....
    uint8_t read[6];
    
    uint8_t init[2];
    
    read (0x58、0x200、2、init、sizeof (init)、true);
    
    delay (20000);//发送 init 命令
    
    read (0x58、0x2008、2、read、sizeof (read)、true)后的20秒延迟;
    ... 

    当我将 i2c_transfer_start 与来自 cc430f5137的 i2c 示例(单字节 RX/TX 和多字节 RX/TX)进行比较时、它看起来是相同的、除非我错过了一些东西。  

    我能够得到一个乘以 zeroplus 的数字 osc。 我连接了三个屏幕抓图,一个用于初始化、读取测量和读取功能。 我仍然不知道为什么会出现 NACK。

    下面的屏幕截图用于初始化:

    下面的屏幕截图用于读取测量值

    下面的屏幕截图针对 Get 功能-这似乎是根据数据表返回正确的值0x0020、但我仍然看到 NACK。

    有没有关于我缺少什么的建议?

    注:请忽略屏幕截图中的数据包3和数据包4,我还有两个 i2c 设备(0x490x7F),由于它已断开连接,因此可以从外部连接同一总线,这将是一个 NACK。

    谢谢你  

    库马尔

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗨、Kumar、

    我认为您的 I2C 通信正常。 I2C 规范规定、在主器件在第一个字节后立即读取从器件的情况下、主器件将在停止位之前发送一个 NACK:

    此处是它参考的图12:

    "get_feature_set_version"命令的屏幕截图遵循 I2C 规范文档描述的相同格式。 NACK 是预期值。

    此外、再次查看 SGP30数据表后、可以在以下几个位置解决该问题:

    图9如下:

    您看到的是黑色。 下面是 I2C 规范文档的链接、供参考

    谢谢、

    Mitch

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Mitch、

    感谢您提供有关功能集的信息、

    但是、您是否有任何建议、说明在尝试读取测量值的同时读取操作期间失败的原因。  

    当写入测量命令时、我似乎正在获取一个 ACK、而在尝试读取时、它会发送 addr NACK。 我不知道为什么会发生这种情况。

    是否有任何输入?。

    库马尔

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Mitch、

    我现在的理解是、在执行测量读取操作时、根据数据表、一旦发出测量命令(0x2008)、传感器需要102ms 才能完成并准备好将数据传输到 MCU。   

    缺少延迟是它尝试读取数据时失败的原因吗? 我也不确定下面的库代码是否处理传感器所需的延迟、我还修改了库并让 sgp30与 CC430通信、但您是否有任何建议/权变措施而不更改库。

    非_工作_代码

    switch (transfer.next 状态){
    案例 SM_SEND_ADDR://(TX 寄存器地址)
    // STT 开始后到达此处。 可以安全地写入 TXBUF
    
    I2C_TXBUF = transfer.pkg->addr[transfer.idx];
    transfer.idx++;
    if (transfer.idx =transfer.pkg->addr_len){
    //这是最后一个地址字节。
    
    //更新下一状态
    if (transfer.pkg->data_len!= 0){
    transfer.idx = 0;
    if (transfer.pkg->read){
    transfer.next 状态= sm_send_restart;
    }否则{
    transfer.next 状态= sm_write_data;
    }
    }否则{
    transfer.next 状态= SM_DONE;
    }
    }
    中断;
    
    案例 SM_SEND_RESTART: I2C_CTL1 &=~UCTR;//设置为接收器模式 I2C_CTL1 |= UCTXSTT;//写入(重新)启动条件 if (transfer.pkg->data_len = 1){ //等待 STT 位丢弃 while (I2C_CTL1 & UCTXSTT); I2C_CTL1 |= UCTXSTP;//计划停止条件 } //更新下一状态 transfer.next 状态= SM_READ_DATA; 中断;

    MODFIED_working_code:

    案例 SM_SEND_RESTART:
    I2C_CTL1 &=~UCTR;//设置为接收器模式
    UCB0IE |= UCRXIE;
    执行{
    UCB0CTL1 |= UCTXSTT;//重新启动
    while (UCB0CTL1&UCTXSTT);//再次等待 STT 清零
    } while (UCB0IFG 和 UCNACKIFG);
    if (transfer.pkg->data_len = 1){
    //等待 STT 位丢弃
    while (I2C_CTL1 & UCTXSTT);
    I2C_CTL1 |= UCTXSTP;//计划停止条件
    }
    //更新下一状态
    transfer.next 状态= SM_READ_DATA;
    中断; 

    库马尔

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Kumar、

    是的、这是有道理的。 这说明了在这种情况下获取 A-NACK 的原因、但在发出 get_feature_set_version 命令时不会这样做。 我可以想到的一种权变措施是在发送 measure_air_quality 命令后触发计时器计数到12ms。 一旦该计时器计数到12ms、发送读取命令。 您可以在看到 FIT (使用标志变量等)的情况下实现此目的。 这将需要对应用代码进行软件更改、但您不必在 I2C 库中进行更改。

    谢谢、

    Mitch
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Mitch、

    非常感谢您对此的支持、必须在驱动程序内等待12ms、或者不要使用驱动程序、并在应用程序代码中实现相同的等待时间。 在写入/发送测量命令后,我们需要等待或轮询12ms,然后再发送重新启动命令,很遗憾这在驱动程序中没有完成。 我猜是在编写库时,开发人员没有遇到任何需要等待才能读取测量值的设备。

    然而,我们遇到了另一个完全不相关的问题,需要每秒轮询一次 sgp30以获得精确的测量,我们需要设置湿度和其他基线功能,不幸的是,我们有如此多的模拟和数字外设, 实施 sgp30的所有功能会导致 SRAM 溢出。 我们的下一个行动方案是使用 Arduino mini pro 3.3 V,在上面安装 sgp30,并将其连接到我们的传感器产品。

    更新了:我选择 Adafruit trinket M0作为 sgp30的配套板、它将使用串行端口连接到 cc430f5137以发送数据、它看起来非常紧凑、只有2.7cm x 1.5cm。 就点了


    库马尔