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.

[参考译文] MSPM0L1306:控制器实现的 I2C ACK/NACK 问题

Guru**** 2783285 points

Other Parts Discussed in Thread: LP-MSPM0L1306

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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1612061/mspm0l1306-i2c-ack-nack-issue-with-controller-implementation

器件型号: MSPM0L1306
主题中讨论的其他部件: ASH

我在 I2C 控制器上遇到了一点麻烦、因为我有一个外设始终是 ACK 的传输字节。 我无法改变这种行为,这不是我的芯片 — 具体来说,它是 SOIC RTC。 数据表很清楚、它不会否定确认最后一个字节、我可以理解为什么。 它不知道我将要发送多少个字节、直到我发送它们。 有*多*个 I2C 外设以这种方式工作。

TI 数据表中包含很多词语、但功能句子并不多。

对于 NACK 业务、TI 数据表相当明确。 它不会发出一个停止,除非有一个否定,但它永远不会来。

因此、唯一的问题是如何损坏硬件状态机、以便发出“仅停止“信号。 事情是,即使我请求一个没有停止的事务,它仍然在等待 NACK。

我可以看到、某个地方可能有人认为主实现应该与从器件对称、但它不是这样工作的、我已经在上面解释了原因。

让我不太自信的是、TI 数据表中的命名约定不是自行一致的。 文本中以不同的方式将 I2C 总线的驱动元件作为主器件和控制器、这使得使用的首字母缩略词非常令人困惑。

数据表确实说明了如何在环回模式下成功地使用 MSPM0 I2C 主器件/控制器与 I2C 片上相关的目标。 我不怀疑这可以正常工作、但问题在于 I2C 端口看起来非常有限、原因令人沮丧。

什么是我的缺失? 如何使其正常工作?

非常感谢、

Andy Ash。

 

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

    实际上、我对文档还有另一个抱怨。 它包含有关如何使用“run"位“位的描述。

    问题在于 I2C 片没有“run"位“位。

    事实上、在 1800 页的多美语中只有一个略显含糊的句子、它松散地将“run"和“和“BURSTRUN “联系起来、后者实际上存在。 它的工作方式并不像这样。 BURSTRUN 是否会自行重置? 设置寄存器是边缘还是电平?  这就像 somoene 从 MSP430 进行了剪切和粘贴,但它所代表的切片是新的/不同的。

    在机器繁忙时、R/W 寄存器看起来确实受到保护。

    … 但是、当受害者忙于等待永远不会到来的事情时、他们会怎么做?

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

    我不确定您指的是什么陈述、 但主发送器不需要从从器件发送 NACK 即可发送停止。 为了从从器件检索对 SDA 的控制、主接收器需要否定确认最后一个字节。

    观察到的行为是 BURSTRUN 不会自动清除、而是在该位置写入值 1、导致事务开始。 您需要警惕在该寄存器上使用 RMW。

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

    我正在使用 TI 驱动程序库  2.09.00.01

    从其工作方式的角度来看、它是 RMW、但您没有选择、因为它们对寄存器中的每个控制位都有单独的函数。 您认为使用 TI 驱动程序库是错误的吗?

    从我的角度来看、我认为我理解你的意思、我同意--这就是为什么我清除 BURSTRUN、修改控制登记册、然后设置 BURSTRUN 来调用它。 这正是我对文件感到遗憾的原因。 两年期更新报告的运作显然至关重要、但却含糊不清。

    尽管我清除了 BURSTRUN、但我可以看到忙旗仍然被设置、而主旗仍然没有响应。

    您是否建议我不能使用 TI 函数打破等待时间、而我需要直接访问寄存器? 也就是说,设置停止,清除启动和设置 BURSTRUN 一次写入? 我不知道我能用我的驱动程序 fnctions 来实现这一点。

    但是我看着它,似乎相当清楚,没有停止产生,我不能让它产生一个。

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

    另外、感谢您的观看。

    :-)

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

    我很确定清除 BURSTRUN 不会导致传输停止。 I2C 单元将继续运行、直到达到 MBLEN。

    更一般而言、您可能不想对事务进行微观管理。 只需将 Tx 数据放入 FIFO、调用 StartTransfer、然后等待完成。 要读取寄存器、请使用 RD_ON_TXEMPTY

    【我在这里没有我的材料,因此我可能会有一些名字被误入歧途】

    [编辑:但也请参阅  

     MSPM0G3507:DL_I2C_enableControllerReadOnTXEmpty () 中的危险 

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

    您好 Bruce、

    我要给你的“解决方案“,因为你很善良,可以尝试。

    我已经设法使它正常工作。 我不是完全确定原因是什么,但我有一个想法,我将解释,以防它帮助别人。

    为了使其正常工作、我放弃了“Read on TX empty“。 然后,我实现了代码,简单地从两个单独的事务“整体读取操作“。 显然、这是完全标准的、应该始终有效。

    问题与 REPEAT_START 有关。 我将示波器放在 I2C 总线上、可以看到、正如我预期的那样、没有产生停止条件、控制器正忙于 SDA 为高电平、SCL 为低电平。 把交易分开、我意识到还有其他事情发生了。

    最终失败的事务是主器件发送。 传输已正确完成、但错误地后跟 REPEAT_START、并由主器件发出器件地址、读取/写入设置为写入。 在 器件地址之后、主器件卡住、因为它已经开始一个从未请求的重新启动事务。 发送 FIFO 已正确为空。

    我注意到、 在正确完成“在 TX 为空时读取“、REPEAT_START — 主器件接收事务后,第一次主器件发送时会发生这种情况。 我使用调试器观察主器件状态寄存器的 IDLE 位、验证了前面的事务是否正确完成。 我还从从器件收到了几个字节的数据、经验证这些字节是正确的。

    我更改了代码、以便在没有前面的 “读取 TX 空“事务的情况下执行相同的主器件发送。 这项工作完美无缺。 我唯一改变的是交易的整体顺序。

    数据表 slau847e 在表 22-55 中明确表示;“如果 DIR 在 MSA 中未设置为读取、则忽略该位。“

    无论如何、我尝试了  设置并清除了“Read on TX empty“的错误传输。

    我认为 “在 TX 为空时读取“机制以某种方式诱发了以下传输。 状态机会被记住、并且只有通过复位才能使其被忘记。

    没有 “Read on TX empty“的一切都是“Lubbly Jubbly “!

    我确实 WSH TI 文档更好、但我承认它可能*很多*更糟。

    非常感谢、

    Andy Ash。

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

    您好 Bruce、

    我阅读了您链接到的“DL_I2C 中的危害...“ “这与我遇到的问题非常接近、但我确信在更改 RD_ON_TXEMPTY 之前、我已经将 BURSTRUN 设置为零。 当我编码它时、我无法确定如何解释文档、因此我只选择了我认为安全的路线。

    我不是说你错了。 这些事情可能是相当难以捉摸。

    我只会注意到你说你选择了一种不同的方法来解决这个问题。 我假设这意味着未使用 RD_ON_TXEMPTY。

    如果您最终没有使用 RD_ON_TXEMPTY,这肯定是我所做的 — 也许您不能完全确定您提出的解决方案是否有效。 我只是说,因为我认为我最初是在不知情的情况下使用的其中一个,我仍然有一个问题。

    再次感谢。

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

    我使用 RD_ON_TXEMPTY 没有特别问题。 我的权变措施是始终执行写入(而不是 RMW)、将所有其他位设置为 0。 我的 CMSIS 驱动程序一次性写入所有位(driverlib 中不可用)。

    为了告诉真相,我没有完全理解你的顺序或你的症状。 可能有一个我没有遇到的临界情况。

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

    您好 Bruce、

    我想您建议、一种解决方案是在使用 RMW TI 驱动程序库函数更改同一寄存器中的任何其他控制位之前单独清除 BURSTRUN。 我就是这么做的。 我还在做这个。

    也许有一些看不见的好处、但我不明白为什么需要单独的库来进行直接访问。 我只看到它的缺点,所以它不会是首选,除非有具体的原因。 我想你说有,但我不明白。

    对我来说,这必须是一个硬件问题,因为如果我是一个杂乱的,并要求在整个写的双重事务,这将不是我有一个事先读,或者没有关系。 如果我是杂乱的、那么我总是会得到 REPEAT_START。

    实际上、该行为仅在传输完成后发生、具体而言是 RD_ON_TXEMPTY REPEAT_START 整体读取。 如果它在没有 RD_ON_TXEMPTY REPEAT_START 的情况下进行正常读取、则发送行为正确。 在我请求 RD_ON_EMPTY 的位置或方式没有任何区别、这是中断传输的选项。 根据数据、我应该能够 在机器初始化时调用 RD_ON_TXEMPTY 一次、它不会影响任何整体写入事务。 我也尝试过、它的行为仍然是一样的。

    我甚至验证了 在 I2C 主器件空闲时、RD_ON_TXEMPTY 位正在更改、并且 BURSTRUN 清零。

    我认为 RD_ON_TXEMPTY 相对于 REPEAT_START 出现了问题。 不过、我不会再花时间讨论它了。

    感谢你能抽出时间。

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

    只是跟进【也许只是为考古学家】:我想你描述的是一个不利的影响有 rd_on_TXEMPTY 设置写入 (TX) 操作。

    我发现了一个简单的程序,它写入和读回寄存器(正如它发生的)MPR121(阿达夫鲁伊特中断)。 这些操作会交替进行(读取/写入/读取/写入/...)、因此每次写入之前都要进行读取。 根据编译时设置 (rs_once)、它可以为每次读取打开/关闭 RD_ON_TXEMPTY、也可以始终保持开启状态。 我看到两个设置之间的行为没有区别。

    您似乎在执行一些不同的操作、下面是程序[在 PA0/1 上具有 I2C 的 L1306 Launchpad 上测试]:

    #include "ti_msp_dl_config.h"
    #include <stdint.h>
    
    #define MPR_ADDR (0x5A)     // MPR121
    #define RS_WA    1          // Workaround for RD_ON_TXEMPTY (driverlib) hazard
    #define RS_ONCE  1          // Keep RD_ON_TXEMPTY all the time
    
    ///
    //  rs_onoff()
    //
    inline void
    rs_onoff(I2C_Regs *i2c, uint32_t onoff)
    {
        if (onoff == 0)     // Off?
        {
    #if RS_WA       // Hazard workaround
            i2c->MASTER.MCTR = 0*I2C_MCTR_RD_ON_TXEMPTY_ENABLE;// Clear repeat-start, clear others
    #else // RS_WA
            DL_I2C_diusableControllerReadOnTXEmpty(i2c);   // Write then read
    #endif // RS_WA
        }
        else {      // On
    #if RS_WA       // Hazard workaround
            i2c->MASTER.MCTR = I2C_MCTR_RD_ON_TXEMPTY_ENABLE;// Set repeat-start, clear others
    #else // RS_WA
            DL_I2C_enableControllerReadOnTXEmpty(i2c);   // Write then read
    #endif // RS_WA
        }
        return;
    }
    
    ///
    //  readreg()
    //
    uint16_t 
    readreg(I2C_Regs *i2c, uint8_t i2caddr, uint8_t reg)
    {
        uint32_t stat;
        uint8_t val[1];
        uint16_t r;
        
        while (!(DL_I2C_getControllerStatus(i2c) & DL_I2C_CONTROLLER_STATUS_IDLE)); // Just in case
    #if !RS_ONCE
        rs_onoff(i2c, 1);        // RD_ON_TXEMPTY=1
    #endif // RS_ONCE
        DL_I2C_clearInterruptStatus(i2c, (DL_I2C_INTERRUPT_CONTROLLER_RX_DONE|DL_I2C_INTERRUPT_CONTROLLER_NACK));// Clear stale
    
        val[0] = reg;
        DL_I2C_fillControllerTXFIFO(i2c, &val[0], sizeof(val)); // One byte fits
        DL_I2C_startControllerTransfer(i2c, i2caddr, DL_I2C_CONTROLLER_DIRECTION_RX, 1); // Write reg number then read 1 byte
        do {
            stat = DL_I2C_getRawInterruptStatus(i2c, (DL_I2C_INTERRUPT_CONTROLLER_RX_DONE|DL_I2C_INTERRUPT_CONTROLLER_NACK));
        }  while(!stat);
        if (stat & DL_I2C_INTERRUPT_CONTROLLER_NACK )
        {
            DL_I2C_flushControllerTXFIFO(i2c);       // Keep it neat
            r = (uint16_t)-1;
        }
        else
        {
            r = DL_I2C_receiveControllerData(i2c);  // We "know" there's only 1
        }
    #if !RS_ONCE
        rs_onoff(i2c, 0);        // RD_ON_TXEMPTY=0
    #endif // RS_ONCE
        return(r);
    }
    
    ///
    //  writereg()
    //
    uint16_t 
    writereg(I2C_Regs *i2c, uint8_t i2caddr, uint8_t reg, uint8_t regval)
    {
        uint32_t stat;
        uint8_t val[2]; 
        uint16_t r;
        
        while (!(DL_I2C_getControllerStatus(i2c) & DL_I2C_CONTROLLER_STATUS_IDLE)); // Just in case
    
        DL_I2C_clearInterruptStatus(i2c, (DL_I2C_INTERRUPT_CONTROLLER_TX_DONE|DL_I2C_INTERRUPT_CONTROLLER_NACK));// Clear stale
    
        val[0] = reg;
        val[1] = regval;
        DL_I2C_fillControllerTXFIFO(i2c, &val[0], sizeof(val)); // Two bytes fit
        DL_I2C_startControllerTransfer(i2c, i2caddr, DL_I2C_CONTROLLER_DIRECTION_TX, sizeof(val)); // Write 2 bytes
        do {
            stat = DL_I2C_getRawInterruptStatus(i2c, (DL_I2C_INTERRUPT_CONTROLLER_TX_DONE|DL_I2C_INTERRUPT_CONTROLLER_NACK));
        }  while(!stat);
        if (stat & DL_I2C_INTERRUPT_CONTROLLER_NACK )
        {
            DL_I2C_flushControllerTXFIFO(i2c);       
            r = (uint16_t)-1;
        }
        else
        {
            r = 0;
        }
        return(r);
    }
    
    //  Some history
    #define VALMAX (50)
    uint8_t val_read[VALMAX];
    uint32_t val_i;
    
    ///
    //  main()
    //
    int 
    main(void)
    {
        uint8_t reg;
        uint8_t regval;
        SYSCFG_DL_init();
    #if RS_ONCE                         // Set it and leave it
        rs_onoff(I2C_INST, 1);          // RD_ON_TXEMPTY=1
    #endif // RS_ONCE
    
        DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN); // Make 'em think we're doing something
        reg = 0x5B;                     // MPR121 Debounce register
        regval = 0;
        while (1) {
            val_read[val_i] = readreg(I2C_INST, MPR_ADDR, reg); // Fetch current
            if (++val_i >= VALMAX)
                val_i = 0;
            if (++regval >= VALMAX)
                regval = 1;
            writereg(I2C_INST, MPR_ADDR, reg, regval);          // Set new
    
            DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_TEST_PIN);
            delay_cycles(1000);         // Pause for the scope
        }
    }

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

    您好 Bruce、

    我看了 MPR121 Adafruit 的东西,这似乎是一个电容式触摸传感器,用于将苹果和香蕉变成一个用户界面。 宣传材料表明、IC 可能具有适合 M0L1306 的正确封装、但封装上的标识不适合 M0L1306。

    我  所做的一切都使用了 TI LaunchPad LP-MSPM0L1306、因此也许这有所帮助。

    老实说,我现在就走了。 我没有时间讨论这个问题。

    非常感谢、

    Andy Ash。