2317848_zh-CN

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

2317848_zh-CN

2317848_zh-CN

如何实现非阻塞 API GetStatus

大家@@ 好 , 从我们的实验中,我们观察到 MSTATUS 仍然停留在 SLVREQ 状态 ,这导致 MCU 将 I3C 总线视为繁忙 状态。

Bruce_Teng_1-1770985476588.pngBruce_Teng_1-1770985476588.png

我们发现,某些情况下,调用阻塞GetStatus API 会触发此问题。我们认为,根本原因是混用阻塞和非阻塞 API,这会导致 IBI 处理不正确。我们想实现一个非阻塞 GetStatus 来解决这个问题。你能帮忙实施吗?

MCU: MCXN556 和 MCXN236


通信& 控制(I3C | I2C | SPI | FlexCAN | 以太网 | FlexIO)Re: How to implement nonblocking API GetStatus
bool i3c_write(uint8_t address, uint8_t* data, size_t length) {
    i3c_master_transfer_t xfer = {};
    xfer.slaveAddress  = address;
    xfer.data         = data;
    xfer.dataSize     = length;
    xfer.direction    = kI3C_Write;
    xfer.busType      = kI3C_TypeI3CSdr;
    xfer.flags        = kI3C_TransferDefaultFlag;
    xfer.ibiResponse  = kI3C_IbiRespAckMandatory;
    
    status_t status = I3C_MasterTransferEDMA(I3C0, &i3c_handle, &xfer);
    return (status == kStatus_Success);
}
```

### Read Operation

```cpp
bool i3c_read(uint8_t address, uint8_t* data, size_t length) {
    i3c_master_transfer_t xfer = {};
    xfer.slaveAddress  = address;
    xfer.data          = data;
    xfer.dataSize      = length;
    xfer.direction     = kI3C_Read;
    xfer.busType       = kI3C_TypeI3CSdr;
    xfer.flags         = kI3C_TransferDefaultFlag;
    xfer.ibiResponse   = kI3C_IbiRespAckMandatory;
    
    status_t status = I3C_MasterTransferEDMA(I3C0, &i3c_handle, &xfer);
    return (status == kStatus_Success);
}



如果我们
使用这个 blocking API get_status来实现恢复或私有读/写 NACK似乎似乎会导致似乎会导致 MCU进入错误状态(如我所述)。
了恢复方法,并进行了压力测试这个版本;它不会再发生再次发生。


Re: How to implement nonblocking API GetStatus

你好@Bruce_Teng

感谢您的来信,并对延迟回复表示歉意。

能否请您提供有关测试内容的更多详细信息(测试步骤、预期行为与观察到的行为,以及任何日志或跟踪记录)?

我假定 MCU 配置为主设备,因为您检查的是 MSTATUS 而不是 SSTATUS。如果不是这样,请告诉我。

根据参考手册,该条件下的接口行为描述如下:

carlos_o_0-1771349361781.pngcarlos_o_0-1771349361781.png


你可以尝试启用自动发射 IBI,这样总线就不会保持该状态。

请做一些说明,以帮助我们重现您的设置:

您指的是哪个 GetStatus API(完整的函数名称和模块/驱动程序)?

您使用的 SDK 版本(确切的版本字符串)。

角色和配置:主控与从属,以及任何相关寄存器设置(如先进先出阈值、中断掩码)。

硬件:MCU/主板部件号和总线上的任何外部元器件。

定时:总线频率、IBI 配置以及是否启用时钟扩展或重试。

展示如何配置和调用应用程序接口的简短代码片段。

Re: How to implement nonblocking API GetStatus

你好@carlos_o,
以下代码实现了阻塞 API GetStatus

 get_status(uint8_t address, uint16_t& status)
{
    constexpr uint8_t     BoradcastAddress = 0x7EU;
    constexpr uint8_t     CccGetStatus     = 0x90U;
    constexpr uint16_t    WordMask         = 0xFFFF;
    I3cBuffer             buffer{};
    i3c_master_transfer_t xfer{};
    xfer.slaveAddress   = BoradcastAddress;
    xfer.subaddress     = CccGetStatus;
    xfer.subaddressSize = 1U;
    xfer.direction      = kI3C_Write;
    xfer.busType        = kI3C_TypeI3CSdr;
    xfer.flags          = kI3C_TransferNoStopFlag;
    xfer.ibiResponse    = kI3C_IbiRespAckMandatory;
    auto result         = I3C_MasterTransferBlocking(_i3c_m_handle.base, &xfer);
    if (result != kStatus_Success) {
        I3C_MasterEmitRequest(_i3c_m_handle.base, kI3C_RequestForceExit);
        const logger::EventData data = {
            CccGetStatus,
            static_cast(result >> 0 & 0xFF),
            static_cast(result >> 8 & 0xFF),
            static_cast(result >> 16 & 0xFF),
            static_cast(result >> 24 & 0xFF),
        };
        logger::info(logger::Event::I3CCccError, data);
        auto driver_status = to_driver_status(static_cast(result));
        if (driver_status != Status::Success) {
            const auto& task = *static_cast<:i3c::task>(_task);
            task.record_error(static_cast(driver_status));
        }
        return false;
    }
    memset(&xfer, 0, sizeof(xfer));
    xfer.slaveAddress = address;
    xfer.data         = buffer.data();
    xfer.dataSize     = 2;
    xfer.direction    = kI3C_Read;
    xfer.busType      = kI3C_TypeI3CSdr;
    xfer.flags        = kI3C_TransferDefaultFlag;
    xfer.ibiResponse  = kI3C_IbiRespAckMandatory;
    result            = I3C_MasterTransferBlocking(_i3c_m_handle.base, &xfer);
    if (result != kStatus_Success) {
        I3C_MasterStop(_i3c_m_handle.base);
        const logger::EventData data = {
            CccGetStatus,
            static_cast(result >> 0 & 0xFF),
            static_cast(result >> 8 & 0xFF),
            static_cast(result >> 16 & 0xFF),
            static_cast(result >> 24 & 0xFF),
        };
        logger::info(logger::Event::I3CCccError, data);
        auto driver_status = to_driver_status(static_cast(result));
        if (driver_status != Status::Success) {
            const auto& task = *static_cast<:i3c::task>(_task);
            task.record_error(static_cast(driver_status));
        }
        return false;
    }
    status = (buffer[0] << 8 | buffer[1]) & WordMask;
    return true;
}

SDK 版本: SDK_25_09_00_MCXN556S
角色和配置:

主模式 -I3C 控制器被配置为主设备

I3C 主配置设置如下:
i3c_master_config_t_master_config;

// 配置设置:
_master_config.波特率.i2cBaud = 频率.i2c; // I2C 波特率(通常为 400 kHz)
_master_config.波特率.i3cPushPullBaud = freq.i3c_pp; // I3C 推挽波特率(通常为 12.5 MHz)
_master_config.波特率.i3cOpenDrainBaud = freq.i2c_od; // I3C 开漏波特率(通常为 2.5 MHz)
_master_config.启用 OpenDrainStop = ;
_master_config.disableTimeout = true; // 禁用超时
_master_config.启用 OpenDrainHigh =is_gpu ?true: false;//特定于 GPU:50 :50 占空比
_master_config.hKeep =kI3C_MasterHighKeeperNone;
```
传输模式:**基于 SmartDMA 和 EDMA(增强型 DMA)的传输

DMA配置
- **TX DMA 通道:**通道 0(DMA0 用于 I3C0,DMA1 用于 I3C1)
- **RX DMA 通道:**通道 1(DMA0 用于 I3C0,DMA1 用于 I3C1)
- **DMA 复用器:**为 I3C TX/RX 请求配置

硬件

MCU/主板部件号
MCXN556 (MCXN556SCDF 变体)

代码支持两个 I3C 端口:
- **I3C0:**基准地址 I3C0,使用 DMA0
- **I3C1:**基准地址 I3C1,使用 DMA1
I3C 总线用于与 GPU 设备通信
 
总线频率
- **主时钟:**25 MHz (时钟 = 25000000UL)
- **I3C推挽波特率:**12.5 MHz(典型值)
- **I3C 开路漏极波特率:**2.5 MHz(典型值)
- **I2C 波特率:**400 kHz(典型值)
 
### 时钟拉伸和重试
- **超时:**禁用 (禁用超时 = true)
- **重试机制:**在软件中实施,对失败的传输进行 5 次重试

 
Nack occurred when MCU read/write data to the slave.
MCU send GetStatus command to GPU to recover it.

    for (uint8_t i = 0; i < recover_retry; i++) {
        bool success = get_status(address, value);
        if (success && value == 0) {
            return true;
        }
        task->delay(10ms);
    }​

Re: How to implement nonblocking API GetStatus

在我们的系统中,MCU 是 I3C 主设备,GPU 是 I3C 从设备。
因此,看来我们无法修改寄存器设置来决定 GPU 如何检测总线可用状态。对不对?
如果是,我们是否应该关注 GPU 的行为?

Re: How to implement nonblocking API GetStatus

你好 @Bruce_Teng
根据 I3C 标准,总线必须保持IDLE状态至少 1 微秒,然后才能发布 IBI。为确保满足此定时要求,设备使用 CLK_SLOW。当 IBI 请求处于待处理状态时,由 CLK_SLOW 驱动的内部计数器必须完成一个或多个计数,然后才能将总线视为完全空闲并生成 IBI。如下图所示,该图像来自 RM (MCXN236):

image.pngimage.png

另外,为了更好地为您提供支持,您能否提供以下信息?
- 能否请您说明一下您以前是否获得过该特定部件 MCXN556 的支持?
- 我很难看到您在上一篇文章中分享的图片,您能否分享给我更高分辨率的图片?
- 您能否提供复制此行为的步骤?

BR
Habib

Re: How to implement nonblocking API GetStatus

你好,
,我还有一个问题。
如果 f 从站在 STOP 状态后立即将 SDA 拉至低电平,如图所示。
MCXN556/236 I3C IP 似乎无法检测到 SDA 失效边沿,因此不会切换 SCL 线以产生 START。
是否符合您的期望?
如果是,是不是因为从机违反了总线可用条件?

image.pngimage.png@Smartling Language Service

 

Re: How to implement nonblocking API GetStatus
 

你好@Habib_MS
没有,我还没有收到任何关于 MCXN556 这一特定部件的支持。

Screenshot 2026-02-25 131322.pngScreenshot 2026-02-25 131322.png
背景:在我们的系统
中,微控制器在USB和下游设备(GPU)之间起着桥梁的作用,
因此,微控制器负责将数据从USB转发到GPU或从GPU转发到USB。
一旦 GPU 需要向 USB 传递数据,GPU 就会发出 IBI 请求,然后 MCU 对其进行服务并从 GPU 读取数据,然后将其转发到 USB。

实验:
在每个事务(读/写)之前发送 GetStatus 命令。
我获取 GPU 信息或更新 GPU 固件以触发读/写交易。

注:GPU 的 I3C IP 不是恩智浦产品

Re: How to implement nonblocking API GetStatus

你好@Bruce_Teng

谢谢您的答复。根据您的评论,从机 (CPU) 不符合 I3C 的要求,即在发出 IBI 请求之前,总线至少保持 IDLE状态 1 µs。我的理解正确吗?如果是这样,这就可以解释为什么主站(MCU)无法处理 IBI。

另一方面,AN14434 确切描述了 MCU 在您的应用中的预期行为类型。我强烈建议查看该文件,以确保 MCU 正常工作。

如果您还有其他问题,请告诉我。

BR
Habib

Tags (1)
No ratings
Version history
Last update:
‎02-26-2026 05:36 AM
Updated by: