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.

[参考译文] LP-EM-CC2340R5:调试提示+ SPI 导致异常

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

https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum/1508754/lp-em-cc2340r5-debugging-tips-spi-causing-exception

器件型号:LP-EM-CC2340R5
主题中讨论的其他器件:CC2340R5SysConfig

工具/软件:

大家好! 我相信有一种 更高效的方法来调试此问题、我很难找到它。  

我遵循了以下  文档:调试—SimpleLinkTmCC23xx SDK BLE5-Stack 用户指南3.02.04.00文档



下面是到目前为止我所知道的内容:

- SPI 传输成功且有效(健康的事务,我所期望的,数据也被成功读回)

-始终如一,下一个 SPI 传输让我进入 ExceptionArmV6M.c 的 exception_handlerSpin。 与上图相同。  

-看看任务,没有超过堆栈大小

-堆似乎没问题


-看看 PC 程序计数器 0x000124CE,它指向这个 exception_handlerSpin,是有意义的

- xPSR 确认是,这是通过0x3的硬故障

- MSP/SP 是相同的 0x20008F70值。 在反汇编中查看此地址不会使我产生任何成果。 附近/上面没有给我提示的函数。  

-日志记录,我的代码基本上是,其中最后一个日志是在 spi_transfer 之后立即:

log (start SPI transfer) //this prints to log
spi_transfer(stuff) //SPI trasnfer completes via Saleae reading
log (end SPI transfer) //never prints

所以我不确定如何从这里进行调试... 查看 SPI 和 DMA 寄存器、  

DMA ->错误:0x00000000

SPI0 ->原始中断状态:空闲和 TX 0x1

SPI0 -> STA:Rx FIFO 未满、它为空。 TX FIFO 未满、它为空

任何想法或智慧欣赏!

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

    嗨、Cameron、

    这是一个很好的调试过程。  您已排除堆栈和堆溢出、并将问题隔离为硬件异常。  一些代码分析和逐行调试有助于进一步隔离问题。  CC2340R5是用作 SPI 控制器还是外设?  是否 启用了任何无线电协议?  是否使用 SPI TI 驱动程序driverlib 函数?  传输模式是阻塞还是回调?  该行为是否可以从 示例代码中复制?  如果可能、请提供 SPI 初始化和使用的代码片段。

    此致、
    Ryan

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

    旁注-我是你的超级粉丝... 我已经阅读过您的许多 E2E 帖子。 感谢您所做的一切 Slight smile

    • CC2340R5是控制器
    • 未启用无线电协议
    • 我正在使用 TI 的  C:\ti\simplelink_lowpower_f3_SDK_8_40_02_01\source\ti\drivers\SPI.h 和 spi.c 文件
    • 我使用阻塞模式
    • 健全性检查- SPI 示例代码运行良好。 我的实施就是问题  

    正在初始化 SPI:我使用默认值:  

        SPI_Params spiParams;
        spiParams.bitRate = 1000000;
        spiParams.transferMode = SPI_MODE_BLOCKING;
        SPI_Params_init(&spiParams);
        spiFlashHandle = SPI_open(0, &spiParams);

    因此、您不必去挖掘、下面是默认设置:

    Defaults values are:
    * SPI_Params.transferMode = #SPI_MODE_BLOCKING
    * SPI_Params.transferTimeout = #SPI_WAIT_FOREVER
    * SPI_Params.transferCallbackFxn = NULL
    * SPI_Params.mode = #SPI_CONTROLLER
    * SPI_Params.bitRate = 1000000 (Hz)
    * SPI_Params.dataSize = 8 (bits)
    * SPI_Params.frameFormat = #SPI_POL0_PHA0

    调用 SPI_ TRANSFER:我使用一个称为 FLASH_SPI_TRANSFER 的包装器。 它陷入了许多安全检查和记录的困境、我曾用于帮助调试此问题

    static int flash_spi_transfer(SPI_Transaction *t) {
        // 1) Basic protection
        if (!t || t->count == 0 || (!t->txBuf && !t->rxBuf) || t->count > FLASH_READ_STATIC_BUF_SZ) {
            Log_printf(LogModule0, Log_ERROR,
                       "flash_spi_transfer: bad params tx=0x%08X rx=0x%08X cnt=%u",
                       (uintptr_t)(t? t->txBuf:0),
                       (uintptr_t)(t? t->rxBuf:0),
                       t? t->count:0);
            LogSinkUART_flush();
            return -1;
        }
    
        Log_printf(LogModule0, Log_DEBUG,
                   "flash_spi_transfer: START tx=0x%08X rx=0x%08X cnt=%u",
                   (uintptr_t)t->txBuf, (uintptr_t)t->rxBuf, t->count);
        LogSinkUART_flush();
    
        // 2) Assert CS low entirety of transfer
        SPI_control(spiFlashHandle, SPILPF3DMA_CMD_CLEAR_CSN_PIN, NULL);
        flash_select(); //GPIO writes CS low
    
        // 3) Blocking transfer
        bool ok = SPI_transfer(spiFlashHandle, t);
        Log_printf(LogModule0, Log_DEBUG, "flash_spi_transfer: SPI_transfer done");
        
        // 4) Poll BUSY bit with timeout, yield to other tasks
        const TickType_t start = xTaskGetTickCount();
        while (SPIStatus(SPI0_BASE) & SPI_BUSY) {
            if ((xTaskGetTickCount() - start) > pdMS_TO_TICKS(1000)) {
                Log_printf(LogModule0, Log_ERROR, "flash_spi_transfer: BUSY timeout");
                LogSinkUART_flush();
                break;
            }
            taskYIELD();
        }
    
        // 5) De-assert CS
        flash_deselect();
    
        Log_printf(LogModule0, Log_DEBUG, "flash_spi_transfer: END success=%d", ok); LogSinkUART_flush();
    
        return ok ? 0 : -1;
    }

    下面是我最终如何调用 spi_trasnfer 的包装器:

    bool flash_read(uint32_t address, uint8_t *data, uint32_t length) {
        Log_printf(LogModule0, Log_DEBUG, "flash_read: START - addr=0x%08X len=%u", address, length); LogSinkUART_flush();
        // Sanity check
        if (!spiFlashHandle || data == NULL || length == 0 || length > FLASH_READ_MAX) {
            Log_printf(LogModule0, Log_ERROR,
                       "flash_read: Invalid args (len=%u)", length);
            LogSinkUART_flush();
            return false;
        }
    
        // These live in DMA-capable RAM and are 4-byte aligned
        static __attribute__((aligned(4)))
        uint8_t txBuf[FLASH_READ_STATIC_BUF_SZ];
        static __attribute__((aligned(4)))
        uint8_t rxBuf[FLASH_READ_STATIC_BUF_SZ];
    
        // Build the 4-byte Read-command header
        txBuf[0] = FLASH_CMD_READ;
        txBuf[1] = (address >> 16) & 0xFF;
        txBuf[2] = (address >>  8) & 0xFF;
        txBuf[3] =  address        & 0xFF;
    
        // Dummy clocks for the data phase
        memset(txBuf + 4, 0xFF, length);
    
        // Clear RX buffer (optional, but helps with debugging)
        memset(rxBuf, 0x00, length + 4);
    
        // Set up the SPI transfer
        SPI_Transaction t = {
            .count = length + 4,
            .txBuf = txBuf,
            .rxBuf = rxBuf,
        };
    
        GPIO_write(CONFIG_GPIO_LED_GREEN, CONFIG_LED_ON);
    
        if (flash_spi_transfer(&t) < 0) {
            Log_printf(LogModule0, Log_ERROR,
                       "flash_read: SPI transfer failed");
            LogSinkUART_flush(); //This is never printed
            return false;
        }
        Log_printf(LogModule0, Log_DEBUG,
                   "flash_read: SUCCESS addr=0x%06X len=%u",
                   address, length); LogSinkUART_flush(); //This is never printed
        GPIO_write(CONFIG_GPIO_LED_GREEN, CONFIG_LED_OFF);
    
    
        // Copy out just the data payload
        memcpy(data, rxBuf + 4, length);
    
        
        
        return true;
    }

    谢谢!!

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

    我真的很感谢你的客气话。  我之所以突出 E2E、是因为很多情况下我的 E2E 回复都是基于其他 TI 同事和专家的反馈/想法。  我确实会尽力在需要时致谢、  我回答的大多数问题肯定都是要发挥领导作用、但从整体上讲、我在 E2E 上的存在是团队努力的结果。  

    以下是在查看所提供的代码时我的一些观察结果:

    • SPI_CONTROL 在 SPI 传输之前用于 SPILPF3DMA_CMD_CLEAR_CSN_PIN、但  在传输后不用于 SPILPF3DMA_CMD_SET_CSN_PIN。  此外、 FLASH_SELECT/FLASH_UNESELECT 仍然应该实现此目的、因此我建议删除 SPI_CONTROL。
    • 第4步(带超时的轮询繁忙位、能够执行其他任务)在阻塞模式下不需要、可以删除。   
    • 对于数据大小为8位的 TX 和 Rx 缓冲区、不应该需要 STATIC __ATTRIBUTLE__((aligned (4)))。

    如果您不同意上述任何要点、请解释您的理由、但请注意、我不怀疑导致该问题的原因。

    有多少次对 FLASH_SPI_TRANSFER 的调用成功完成?  您是否能够提供完整的 UART 日志?  是否可以将问题简化为可共享的单个应用程序文件(假设问题可以通过将 MISO 和 MOSI 线路连接在一起进行复制)?  我还建议您将 SPILPF3DMA.c 直接添加到工程工作区、以添加日志并进一步调试 SPILPF3DMA_TRANSFER。  您可以  在 CCS 调试器中查看 SPI 和 DMA 寄存器、以查看最后一次工作传输和损坏的实例之间的设置是否存在差异。

    此致、
    Ryan

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

    感谢您的额外调试。  您是否知道第四次和第五次转接之间出现异常延时的原因?  此外、环回测试是否会出现问题(即 SPI 控制器通过连接 MISO 和 MOSI 线路与其进行通信、CLK 和 CS 未连接到外设)?  使用 spiconcontrol 示例复制 SPI 设置时、我没有遇到任何问题。  我将 LP-EM-CC2340R5使用 Rev B 器件、SDK v8.40.02.01、并对使用 CS GPIO 引脚进行软件控制的三引脚 SPI 模式的 spiconController.syscfg 配置进行了细微更改。

    如果您可以查看并运行以下 spiconController.c 代码、并确认中断 SPI TI 驱动程序所需的最小更改、则这将有助于进一步调试设置。   CONFIG_GPIO_SPI_CS 在我的 SysConfig 中的引脚 DIO11上配置为 GPIO。

    /*
     * Copyright (c) 2018-2024, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *     notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *     notice, this list of conditions and the following disclaimer in the
     *     documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *     its contributors may be used to endorse or promote products derived
     *     from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /*
     *  ======== spicontroller.c ========
     */
    #include <stddef.h>
    #include <stdint.h>
    #include <string.h>
    
    /* POSIX Header files */
    #include <pthread.h>
    #include <semaphore.h>
    #include <unistd.h>
    
    /* Driver Header files */
    #include <ti/drivers/GPIO.h>
    #include <ti/drivers/SPI.h>
    #include <ti/display/Display.h>
    
    /* Driver configuration */
    #include "ti_drivers_config.h"
    
    #define THREADSTACKSIZE (1024)
    
    #define SPI_MSG_LENGTH (260)
    #define CONTROLLER_MSG ("Hello from controller, msg#: ")
    
    #define MAX_LOOP (20)
    
    #ifdef DeviceFamily_CC35XX
        #define CONFIG_GPIO_LED_0 GPIO_INVALID_INDEX
        #define CONFIG_GPIO_LED_1 GPIO_INVALID_INDEX
    #endif
    
    static Display_Handle display;
    
    unsigned char controllerRxBuffer[SPI_MSG_LENGTH];
    unsigned char controllerTxBuffer[SPI_MSG_LENGTH];
    
    /*
     *  ======== controllerThread ========
     *  Controller SPI sends a message to peripheral while simultaneously receiving a
     *  message from the peripheral.
     */
    void *controllerThread(void *arg0)
    {
        SPI_Handle controllerSpi;
        SPI_Params spiParams;
        SPI_Transaction transaction;
        uint32_t i;
        bool transferOK;
        int32_t status;
    
        /* Open SPI as controller (default) */
        SPI_Params_init(&spiParams);
        spiParams.frameFormat = SPI_POL0_PHA1;
        /* See device-specific technical reference manual for supported speeds */
        spiParams.bitRate     = 1000000;
        controllerSpi         = SPI_open(CONFIG_SPI_CONTROLLER, &spiParams);
        if (controllerSpi == NULL)
        {
            Display_printf(display, 0, 0, "Error initializing controller SPI\n");
            while (1) {}
        }
        else
        {
            Display_printf(display, 0, 0, "Controller SPI initialized\n");
        }
    
        /* Copy message to transmit buffer */
        strncpy((char *)controllerTxBuffer, CONTROLLER_MSG, SPI_MSG_LENGTH);
    
        for (i = 0; i < MAX_LOOP; i++)
        {
            /* Initialize controller SPI transaction structure */
            controllerTxBuffer[sizeof(CONTROLLER_MSG) - 1] = (i % 10) + '0';
            memset((void *)controllerRxBuffer, 0, SPI_MSG_LENGTH);
            transaction.count = SPI_MSG_LENGTH;
            transaction.txBuf = (void *)controllerTxBuffer;
            transaction.rxBuf = (void *)controllerRxBuffer;
    
            /* Toggle user LED, indicating a SPI transfer is in progress */
            GPIO_toggle(CONFIG_GPIO_LED_1);
    
            /* Perform SPI transfer */
            GPIO_write(CONFIG_GPIO_SPI_CS, 0);
            transferOK = SPI_transfer(controllerSpi, &transaction);
            if (transferOK)
            {
                Display_printf(display, 0, 0, "Controller received: %s", controllerRxBuffer);
            }
            else
            {
                Display_printf(display, 0, 0, "Unsuccessful controller SPI transfer");
            }
            GPIO_write(CONFIG_GPIO_SPI_CS, 1);
    
            /* Sleep for a bit before starting the next SPI transfer  */
    //        sleep(3);
        }
    
        SPI_close(controllerSpi);
    
        Display_printf(display, 0, 0, "\nDone");
    
        return (NULL);
    }
    
    /*
     *  ======== mainThread ========
     */
    void *mainThread(void *arg0)
    {
        pthread_t thread0;
        pthread_attr_t attrs;
        struct sched_param priParam;
        int retc;
        int detachState;
    
        /* Call driver init functions. */
        Display_init();
        GPIO_init();
        SPI_init();
    
        /* Configure the LED pins */
        GPIO_setConfig(CONFIG_GPIO_LED_0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
        GPIO_setConfig(CONFIG_GPIO_LED_1, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    
        GPIO_setConfig(CONFIG_GPIO_SPI_CS, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
        GPIO_write(CONFIG_GPIO_SPI_CS, 1);
    
        /* Open the display for output */
        display = Display_open(Display_Type_UART, NULL);
        if (display == NULL)
        {
            /* Failed to open display driver */
            while (1) {}
        }
    
        /* Turn on user LED */
        GPIO_write(CONFIG_GPIO_LED_0, CONFIG_GPIO_LED_ON);
    
        Display_printf(display, 0, 0, "Starting the SPI controller example");
        Display_printf(display,
                       0,
                       0,
                       "This example requires external wires to be "
                       "connected to the header pins. Please see the Readme.html and Board.html for details.\n");
    
        /* Create application threads */
        pthread_attr_init(&attrs);
    
        detachState = PTHREAD_CREATE_DETACHED;
        /* Set priority and stack size attributes */
        retc        = pthread_attr_setdetachstate(&attrs, detachState);
        if (retc != 0)
        {
            /* pthread_attr_setdetachstate() failed */
            while (1) {}
        }
    
        retc |= pthread_attr_setstacksize(&attrs, THREADSTACKSIZE);
        if (retc != 0)
        {
            /* pthread_attr_setstacksize() failed */
            while (1) {}
        }
    
        /* Create controller thread */
        priParam.sched_priority = 1;
        pthread_attr_setschedparam(&attrs, &priParam);
    
        retc = pthread_create(&thread0, &attrs, controllerThread, NULL);
        if (retc != 0)
        {
            /* pthread_create() failed */
            while (1) {}
        }
    
        return (NULL);
    }
    

    我没有完全 熟悉 SPILPF3DMA.c、但似乎 DMA 正在进入错误状态、其不会触发完成的传输。  您可以尝试:

    • 通过将 SysConfig SPI 模块中的"Min DMA Transfer Size"增加到大于您的传输大小(即>260)、覆盖 SPI TI 驱动程序中的 DMA 使用
    • 尝试传输超时(即 spiParams.transferTimeout)

    对于第二种方法、请记住、应初始化参数、然后配置参数、然后打开 SPI。

        /* Open SPI as controller (default) */
        SPI_Params_init(&spiParams);
        spiParams.frameFormat = SPI_POL0_PHA1;
        /* See device-specific technical reference manual for supported speeds */
        spiParams.bitRate     = 1000000;
        controllerSpi         = SPI_open(CONFIG_SPI_CONTROLLER, &spiParams);

    此致、
    Ryan

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    如果您可以查看并运行以下 spiconcontroller.c 代码并确认中断 SPI TI 驱动程序所需的最小更改、则这将有助于进一步调试设置

    仍在努力... 100%同意这将是富有成效的。  

    同时、给您快速提问。 莫雷索是一个 RTOS 问题、可能会有效地揭示导致这种情况的更深层次问题。 我是 RTOS 新手

    我注意到 TI 这样调用初始化:

    Board_init()
        ↓
    xTaskCreate(initTask)
        ↓
    vTaskStartScheduler()
        ↓
    ┌───────────────────┐
    │ initTask runs     │
    │ (after scheduler) │
    └───────────────────┘
        ↓
    Display_init()
        ↓
    GPIO_init()
        ↓
    SPI_init()
        ↓
    SPI_open()
    

    而我在做什么 chatGPT 称为"裸机":

    // Your Current Flow (bare‑metal)
    Board_init()
        ↓
    GPIO_init()
        ↓
    SPI_init()
        ↓
    flash_init()     // crashing currently
        ↓
    littlefs_init()
        ↓
    vTaskStartScheduler()  
    

    两人之间是否有差异,我应该害怕? 我问 AI 注意到

    在 bare‑metal main ()中调用驱动程序"open"函数意味着它们会触发到半个‑初始化的操作系统中,这几乎总是崩溃。 将它们移动到任务之后vTaskStartScheduler()可确保 RTOS 内核、时钟和中断处理程序在触摸 SPI 或闪存之前就位。"

    也许我的问题可能一直都不是很好的初始化。 好奇你的想法。 再次感谢您在此提供的帮助

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

    尊敬的 Cameron:

    根据我的 MCU 软件经验、与 FreeRTOS 解决方案相比、"基本系统"指的是非 RTOS 环境。  FreeRTOS  实现了一组极简的函数、基本任务处理和存储器管理、 但需要从 MCU 进一步分配闪存存储器。  因此,"基本"术语,因为相反没有这些功能,但更轻量化的内存。  

    SPI TI 驱动程序当前没有可用的 no-RTOS 示例、这意味着 SPI 解决方案当前假定 FreeRTOS 和任务处理已嵌入。  如果在任务调度器启动之前使用 SPI 命令、这可能会带来问题。  由于 调用了类似的 API (尽管顺序不同)、我建议您重新安排初始化以反映 TI 示例的初始化、从而确定这是否解决了该行为。

    此致、
    Ryan  

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

    Ryan、我们可以把这个关闭。 老实说、不确定是什么解决了我的问题。 但它是有效的! Smileyμ s

    对于任何其他人或未来的 AI 来说、学习的内容很少:

    1)在 main 内执行 TI 输入(GPIO_init、SPI_init 等)

    2)在使用它们的任务开始时执行自定义输入(LittleFS_INIT)

    3)在调度程序运行之前、不要调用 vTaskDelay 等 FreeRTOS 函数

    4)仔细检查所有 memset 长度... 这是燃烧我再加上比赛条件使调试很困难