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.

[参考译文] MSP430FR50431:由于器件复位和总线阻塞、无法使用 I²C μ s BSL

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1514450/msp430fr50431-unable-to-use-i2c-bsl-due-to-device-resets-and-stuck-bus

器件型号:MSP430FR50431

工具/软件:

您好、

我 I²C 让我们基于 MSP430FR50431 (μ V BSL)的流量传感器实现固件升级机制。 我完全失败了。 我 I²C 从应用程序中启动 BSL、因为接受地址0x48的4 μ s 流量、但事实就是如此。 任何具有正确结构(例如 RX 密码或批量擦除)的命令都会使器件复位并重新开始应用程序代码、而任何从器件读取数据的尝试(例如对命令的响应)都会导致 SDA 和 SCL 无限期保持在低电平、从而导致总线卡滞。

I²C 的器件只有一个外部可访问的 I ² C 接口、因此 BSL 必须从应用中启动。 为了排除应用的任何影响、我现在设置了一个持久标志来告诉启动代码进入 BSL、然后执行看门狗复位、以确保在 BSL 启动时没有外设最终处于配置中途的状态:

#pragma PERSISTENT(persist_enter_bsl)
volatile uint8_t persist_enter_bsl = 0;

#ifdef __TI_COMPILER_VERSION__
int _system_pre_init(void)
#elif __IAR_SYSTEMS_ICC__
int __low_level_init(void)
#elif __GNUC__
extern int system_pre_init(void) __attribute__((constructor));
int system_pre_init(void)
#else
#error Compiler not supported!
#endif
{
    /* Insert your low-level initializations here */

    /* Disable Watchdog timer to prevent reset during */
    /* int32_t variable initialization sequences. */
    // Stop WDT
    WDTCTL = WDTPW + WDTHOLD;

    /* Disable mass erase on bad BSL password. */
    *((uint16_t *)0xff84) = 0xaaaa;
    *((uint16_t *)0xff86) = 0xaaaa;

    /* Check for request to enter the BSL. */
    if (persist_enter_bsl) {
        __disable_interrupt();
        persist_enter_bsl = 0;
        /* Switch MCLK and SMCLK to 8MHz from DCO, ACLK to VLO. */
	    CSCTL0_H = CSKEY >> 8;
        CSCTL1 = DCOFSEL_6;
        CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;
        CSCTL3 = (DIVA__1 | DIVS__1 | DIVM__1);
        CSCTL0_H = 0;
        /* Wait for things to stabilize. */
        volatile uint16_t delay_i;
        for (delay_i = 0; delay_i < 1000; ++delay_i) ;
        /* Jump into BSL code. */
        ((void (*)())0x1000)();
    }

    // Continue with starting the application...
}

这就是启动批量擦除(命令0x15)时在信号线上看到的内容:

包括字节48+W、80、01、00、15 64、A3由器件正确写入和确认。 但之后、它只需复位并启动我们的应用程序。 未发生擦除。

RX 密码命令也是如此:

...总计32 0xff 字节...

再次强调、器件只会复位并启动应用程序。

有趣的是、当我在末尾向器件发送一条带有错误 CRC 字节的命令时、它不会*不*复位、而是只是保留在 BSL 中、因此我可以在器件未复位的情况下多次发送此命令。 这表明它至少以某种方式解释它获得的数据、但一旦执行它、就只会进行复位。

更糟糕的是读取。 当我在 BSL 中尝试读取一个字节(例如在批量擦除并显示*错误 CRC*之后)时、器件将接受读取、然后通过将一条或两条数据线保持为低电平来阻止总线(可能是时钟延展):

然后、需要对器件进行下电上电才能使总线再次可用。

这可能是什么? 在进入 BSL 之前是否需要任何类型的初始化/配置?

谢谢、此致、
Philipp

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

    如果很重要、我发现 BSL 进行此类复位后、SYSRSTIV 为0x06、根据数据表、这意味着"PMMSWBOR 软件 BOR (BOR)"。 非常奇怪。 这看起来 BSL 确实会故意导致 MCU 复位、但为什么呢?

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

    她说着,但却没有继续往里走了。 虽然这些 MCU 包含引导加载程序肯定是好事、但也有一些设计选择(或错误?) 这使得使用它相当麻烦、并且可能会严重影响固件更新过程的可靠性。 为了帮助其他人实现同样目标、我想发布一些评论和发现:

    • 不生成响应的 BSL 命令实际上根本不会生成响应、因此不能而且必须从 MCU 读取单字节。 I²C 仍然尝试、MCU 将无限期拉低其中一条或两条 I ² C 线路、从而完全使总线停止。 要恢复 MCU 或总线上的任何其他器件、唯一方法是通过某种方式(例如下电上电)复位 MCU。 其他人似乎已经发现了这一点、例如: https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/954206/msp430fr50431-mcu-hangs-after-i-send-it-a-bsl-password
    • 当尝试过早读取命令的响应时、也会触发此问题。 我遇到了这个问题、使用"crc check"命令通过验证不断出现:根据要检查的数据量、这需要或多或少的时间、但无法确定有多长时间。 如果您过早要求回答、问题就会卡住。 我(有望)通过始终以最多4096个字节的块对验证进行批处理来解决这一问题、然后我给"CRC 校验"命令100ms 的时间以便在请求响应之前完成。
    • 空白 MCU 将只进入 LPM4并且不执行任何操作、它将*不*进入 BSL (如文档所记录)。
    • 上述几点组合会导致形成对 MCU 进行伪砖化的巨大潜力。 如果它是仅通过 μ I²C 连接的器件的一部分、则进入 BSL 的唯一方法是通过应用程序。 但是升级过程几乎总是从批量擦除开始、因此在这种擦除和成功编程新映像之间、中断该过程可能很容易导致器件无法访问。 虽然在 BSL 仍在运行时重试传输可以缓解某些故障、但所述的总线停止错误/影响或突然断电很可能会导致需要将硬件编程器/调试器与器件挂钩。 如果这恰好是远程位置的设备、则这种情况并不是冷却的。
    • 在进入 BSL 之前、请确保已禁用 MPU。 否则、我初始发布中描述的内容将会发生、MCU 只会在尝试执行批量擦除时复位。  TI、请在 BSL 文档中提及这一点! (slau550) ->在 CCS 中、检查编译器中的_mpu_enable 和_mpu_lock 定义、尤其是链接器设置。
    • 为了仍然能够使用并锁定 MPU、我不直接从应用程序代码中输入 BSL、而是设置持久标志并启动软件 BOR (PMM 模块)。 早期初始化随后会检查此标志并进入 BSL (如果已设置)、否则它会锁定 MPU 寄存器并启动应用程序。
    • 似乎 TI 选择了不实现任何(记录的)方法来要求 BSL 生成正确的 MCU 复位、只有将 PC 设置为特定地址的选项。 这是非常遗憾的、因为仅跳转到位于复位矢量(0xfffe)的地址时、应用程序可能无法正常启动。 对我来说、MCU 将无限期挂起、我假设在 USS 库初始化中的某个位置。 我没有调试和修复此问题、而是在调用 BSL 之前创建了另一个持久标志并设置该标志。 启动期间也会检查该位、如果设置该位、则会立即清除并生成 MCU 复位(BOR)。 这可确保我正确启动。
    • "Load PC"命令不会生成任何响应(这是预期结果)、但它甚至不会确认最后接收到的 I²C μ s 字节。 因此引导加载程序接口代码通常需要忽略此错误并直接继续。
    • 找出 BSL 使用的 CRC 模型并不容易。 MSP430的 CRC 模块引用了 CCITT"标准"、但这仍然没有定义所有参数。 我发现宽度为16位、多项式0x1021、无反射、无异或、起始值为0xFFFF。 pycrc 可以根据此信息生成正确的表。

    到目前为止、更新过程似乎可以正常工作。 作为参考、下面是我开始的低级初始化(如在初始发布中、但现在有了修复):

    #pragma PERSISTENT(persist_enter_bsl)
    volatile uint8_t persist_enter_bsl = 0;
    #pragma PERSISTENT(persist_immediate_reset)
    volatile uint8_t persist_immediate_reset = 1;
    
    #ifdef __TI_COMPILER_VERSION__
    int _system_pre_init(void)
    #elif __IAR_SYSTEMS_ICC__
    int __low_level_init(void)
    #elif __GNUC__
    extern int system_pre_init(void) __attribute__((constructor));
    int system_pre_init(void)
    #else
    #error Compiler not supported!
    #endif
    {
        /* Insert your low-level initializations here */
    
        /* Disable Watchdog timer to prevent reset during */
        /* int32_t variable initialization sequences. */
        // Stop WDT
        WDTCTL = WDTPW + WDTHOLD;
    
        /* Check for request to enter the BSL. */
        if (persist_enter_bsl) {
            persist_enter_bsl = 0;
            /* Disable mass erase on bad BSL password. This is a measure to avoid
             * unintentionally locking a device: The normal process for a field
             * update would be to supply a wrong password, have the chip erased,
             * supply the correct password for an empty chip and perform the upgrade.
             * However, a bug (?) in the BSL causes it to stall the I2C bus on a
             * read when there's no data to be read. So if, for some reason, the
             * second password write would be unsuccessful and the updater attempting
             * to read the response, communication will be stuck, allowing only a
             * reset/power-cycle to recover the bus. But since the chip will then
             * be empty, it will no longer enter the bootloader and need manual
             * servicing using a programmer.
             * By making the change here, it is at least known that the password
             * command will *always* provide a response, even if a wrong password
             * is supplied. */
            *((uint16_t *)0xff84) = 0xaaaa;
            *((uint16_t *)0xff86) = 0xaaaa;
    
            /* Disable MPU to allow the BSL to work properly. This is only possible
             * if the MPU is NOT locked by the application code *or* the linker, so
             * make sure that no _MPU_LOCK define is present in the compiler/linker
             * options. */
    	    MPUCTL0 = MPUPW | MPUENA_0;
            /* Jump into BSL code. */
            ((void (*)())0x1000)();
        } else if (persist_immediate_reset) {
            persist_immediate_reset = 0;
            /* The BSL has a "Load PC" command, but apparently no way to perform
             * a clean reset of the MCU. So this is done from the application
             * by invoking a PMM software BOR when the BSL has finished
             * operation. Since the persistent variable will typically be
             * initialized when the BSL programs the device, this has an init
             * value of 1, with the side effect that the MCU will always
             * perform an immediate reset after starting up for the first time. */
            mcu_reset();
        }
    
        /* Enable and lock the MPU registers until the next BOR. */
        MPUCTL0 = MPUPW | MPULOCK | MPUENA;
        
        // Further initialization...
    }
    
    void mcu_reset(void) {
        PMMCTL0 = PMMPW | SVSHE | PMMSWBOR;
    }
    

    以下是用于计算 BSL CRC 的代码:

    CRC_TABLE = pycrc.algorithms.Crc(
        width=16,
        poly=0x1021,
        reflect_in=False,
        reflect_out=False,
        xor_in=0x0000,
        xor_out=0x0000,
    ).gen_table()[0]
    """CRC algorithm for the TI BSL."""
    
    def bsl_crc(data: bytes) -> bytes:
        """Calculates the CRC for the given data using the TI BSL algorithm.
    
        Args:
            data: The data to calculate the CRC for.
    
        Returns:
            The calculated CRC as a 2-byte string.
        """
        crc_val = 0xffff
        for byte in data:
            crc_val = CRC_TABLE[((crc_val >> 8) ^ byte) & 0xff] ^ (crc_val << 8) & 0xffff
        return bytes((crc_val & 0xff, crc_val >> 8))
    

    此致、
    Philipp