This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF52 USB instability with USB CDC and USB MSC instantiated

Hello,

I'm working with nRF52840 and SDK v15.2.

I implemented an USB layer with CDC and MSC classes based on nRF examples. All seems to work fine. The USB Serial port is available and I can send/receive data from it. The MSC drive is also properly mounted and I can access it.

But after receiving / sending data over USB serial port for 3, 10 or 15 minutes (it's random), I can't receive data anymore.

Looking at the USB event, I don't receive APP_USBD_CDC_ACM_USER_EVT_RX_DONE event reported from cdc_acm_user_ev_handler. But I noticed I receive APP_USBD_EVT_DRV_RESET event from USBD User defined event processor handler. 

The issue doesn't appear if USB MSC class is not instantiated.

Do you have any idea how to solve this issue ?

Best regards,

Aurélie

Parents
  • Hi,

    Thanks for the suggestion about upgrading the SDK but I would rather keep the 15.2 version and find a workaround.

    I found how to reproduce this USB instability with a simple example, based on usbd_msc example (attached main.c modified below). I added USB CDC support and send a frame every minute to the com port.

    After several minutes, the com port is no more available and neither the mass storage.

    I didn't monitor the USB events for this example.

    /**
     * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, 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.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
     *
     */
    #include <stdint.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include <inttypes.h>
    #include <stdlib.h>
    
    #include "sdk_config.h"
    
    #include "nrf.h"
    #include "nrf_block_dev.h"
    #include "nrf_block_dev_ram.h"
    #include "nrf_block_dev_empty.h"
    #include "nrf_block_dev_qspi.h"
    #include "nrf_block_dev_sdc.h"
    #include "nrf_drv_usbd.h"
    #include "nrf_drv_clock.h"
    #include "nrf_gpio.h"
    #include "nrf_atomic.h"
    #include "nrf_drv_power.h"
    
    #include "ff.h"
    #include "diskio_blkdev.h"
    
    #include "app_usbd.h"
    #include "app_usbd_core.h"
    #include "app_usbd_string_desc.h"
    #include "app_usbd_msc.h"
    #include "app_error.h"
    #include "app_timer.h"
    
    // ### Add app_usbd_cdc_acm.h to the list of include files. 
    #include "app_usbd_cdc_acm.h"
    
    #include "bsp.h"
    
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    /**@file
     * @defgroup usbd_msc_example main.c
     * @{
     * @ingroup usbd_msc_example
     * @brief USBD MSC example
     *
     */
    
    /**
     * @brief Enable power USB detection
     *
     * Configure if example supports USB port connection
     */
    #ifndef USBD_POWER_DETECTION
    #define USBD_POWER_DETECTION true
    #endif
    
    /**
     * @brief Mass storage class user event handler
     */
    static void msc_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_msc_user_event_t     event);
    
    
    #define SDC_SCK_PIN     (NRF_GPIO_PIN_MAP(0, 19)) /* SD_CARD_MCLK */  ///< SDC serial clock (SCK) pin.
    #define SDC_MOSI_PIN    (NRF_GPIO_PIN_MAP(0, 14)) /* SD_CARD_MOSI */  ///< SDC serial data in (DI) pin.
    #define SDC_MISO_PIN    (NRF_GPIO_PIN_MAP(0, 21)) /* SD_CARD_MISO */  ///< SDC serial data out (DO) pin.
    #define SDC_CS_PIN      (NRF_GPIO_PIN_MAP(1, 0)) /* SD_CARD_CSN */   ///< SDC chip select (CS) pin.
    
    /**
     * @brief  SDC block device definition
     */
    NRF_BLOCK_DEV_SDC_DEFINE(
        m_block_dev_sdc,
        NRF_BLOCK_DEV_SDC_CONFIG(
            SDC_SECTOR_SIZE,
            APP_SDCARD_CONFIG(SDC_MOSI_PIN, SDC_MISO_PIN, SDC_SCK_PIN, SDC_CS_PIN)
         ),
         NFR_BLOCK_DEV_INFO_CONFIG("Nordic", "SDC", "1.00")
    );
    
    
    /**
     * @brief Block devices list passed to @ref APP_USBD_MSC_GLOBAL_DEF
     */
    #define BLOCKDEV_LIST() (                                   \
        NRF_BLOCKDEV_BASE_ADDR(m_block_dev_sdc, block_dev)      \
    )
    
    /**
     * @brief Endpoint list passed to @ref APP_USBD_MSC_GLOBAL_DEF
     */
    // ### Change the MSC endpoints in APP_USBD_MSC_ENDPOINT_LIST from 1 to 3 to avoid conflict with the CDC endpoints
    #define ENDPOINT_LIST() APP_USBD_MSC_ENDPOINT_LIST(3, 3)
    
    /**
     * @brief Mass storage class work buffer size
     */
    #define MSC_WORKBUFFER_SIZE (1024)
    
    /**
     * @brief Mass storage class instance
     */
    APP_USBD_MSC_GLOBAL_DEF(m_app_msc,
                            0,
                            msc_user_ev_handler,
                            ENDPOINT_LIST(),
                            BLOCKDEV_LIST(),
                            MSC_WORKBUFFER_SIZE);
    
    /**
     * @brief Timer instance
     */
    APP_TIMER_DEF(ssm_timer_id);
    
    /**
     * @brief  USB connection status
     */
    static bool m_usb_connected = false;
    
    
    static FATFS m_filesystem;
    
    static bool fatfs_init(void)
    {
        FRESULT ff_result;
        DSTATUS disk_state = STA_NOINIT;
    
        memset(&m_filesystem, 0, sizeof(FATFS));
    
        // Initialize FATFS disk I/O interface by providing the block device.
        static diskio_blkdev_t drives[] =
        {
            DISKIO_BLOCKDEV_CONFIG(NRF_BLOCKDEV_BASE_ADDR(m_block_dev_sdc, block_dev), NULL)
        };
    
        diskio_blockdev_register(drives, ARRAY_SIZE(drives));
    
        NRF_LOG_INFO("Initializing disk 0 (SDC)...");
        disk_state = disk_initialize(0);
        if (disk_state)
        {
            NRF_LOG_ERROR("Disk initialization failed.");
            return false;
        }
    
        NRF_LOG_INFO("Mounting volume...");
        ff_result = f_mount(&m_filesystem, "", 1);
        if (ff_result != FR_OK)
        {
            if (ff_result == FR_NO_FILESYSTEM)
            {
                NRF_LOG_ERROR("Mount failed. Filesystem not found. Please format device.");
            }
            else
            {
                NRF_LOG_ERROR("Mount failed: %u", ff_result);
            }
            return false;
        }
    
        return true;
    }
    
    static void fatfs_ls(void)
    {
        DIR dir;
        FRESULT ff_result;
        FILINFO fno;
    
        if (m_usb_connected)
        {
            NRF_LOG_ERROR("Unable to operate on filesystem while USB is connected");
            return;
        }
    
        NRF_LOG_INFO("\r\nListing directory: /");
        ff_result = f_opendir(&dir, "/");
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("Directory listing failed: %u", ff_result);
            return;
        }
    
        uint32_t entries_count = 0;
        do
        {
            ff_result = f_readdir(&dir, &fno);
            if (ff_result != FR_OK)
            {
                NRF_LOG_ERROR("Directory read failed: %u", ff_result);
                return;
            }
    
            if (fno.fname[0])
            {
                if (fno.fattrib & AM_DIR)
                {
                    NRF_LOG_RAW_INFO("   <DIR>   %s\r\n",(uint32_t)fno.fname);
                }
                else
                {
                    NRF_LOG_RAW_INFO("%9lu  %s\r\n", fno.fsize, (uint32_t)fno.fname);
                }
            }
    
            ++entries_count;
            NRF_LOG_FLUSH();
        } while (fno.fname[0]);
    
    
        NRF_LOG_RAW_INFO("Entries count: %u\r\n", entries_count);
    }
    
    static void fatfs_file_create(void)
    {
        FRESULT ff_result;
        FIL file;
        char filename[16];
    
        if (m_usb_connected)
        {
            NRF_LOG_ERROR("Unable to operate on filesystem while USB is connected");
            return;
        }
    
        (void)snprintf(filename, sizeof(filename), "HELLO.txt");
    
        NRF_LOG_RAW_INFO("Creating random file: %s ...", (uint32_t)filename);
        NRF_LOG_FLUSH();
    
        ff_result = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to open or create file: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
    
        ff_result = f_close(&file);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to close file: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
        NRF_LOG_RAW_INFO("done\r\n");
    }
    
    static void fatfs_uninit(void)
    {
        NRF_LOG_INFO("Un-initializing disk 0 (QSPI)...");
        UNUSED_RETURN_VALUE(disk_uninitialize(0));
    }
    
    
    static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                        app_usbd_cdc_acm_user_event_t event);
    
    #define CDC_ACM_COMM_INTERFACE  1
    #define CDC_ACM_COMM_EPIN       NRF_DRV_USBD_EPIN2
    
    #define CDC_ACM_DATA_INTERFACE  2
    #define CDC_ACM_DATA_EPIN       NRF_DRV_USBD_EPIN1
    #define CDC_ACM_DATA_EPOUT      NRF_DRV_USBD_EPOUT1
    
    /**
     * @brief Interfaces list passed to @ref APP_USBD_CDC_ACM_GLOBAL_DEF
     * */
    #define CDC_ACM_INTERFACES_CONFIG()                 \
        APP_USBD_CDC_ACM_CONFIG(CDC_ACM_COMM_INTERFACE, \
                                CDC_ACM_COMM_EPIN,      \
                                CDC_ACM_DATA_INTERFACE, \
                                CDC_ACM_DATA_EPIN,      \
                                CDC_ACM_DATA_EPOUT)
    
    /**
     * @brief CDC_ACM class instance
     * */
    APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm,
                                cdc_acm_user_ev_handler,
                               CDC_ACM_COMM_INTERFACE,
    							CDC_ACM_DATA_INTERFACE,
    							CDC_ACM_COMM_EPIN,
    							CDC_ACM_DATA_EPIN,
    							CDC_ACM_DATA_EPOUT,
    							APP_USBD_CDC_COMM_PROTOCOL_AT_V250
    );
    
    static char m_rx_buffer[NRF_DRV_USBD_EPSIZE * 16];
    static char m_tx_buffer[NRF_DRV_USBD_EPSIZE];
    
    /**
     * @brief User event handler @ref app_usbd_cdc_acm_user_ev_handler_t (headphones)
     * */
    static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                        app_usbd_cdc_acm_user_event_t event)
    {
        app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
    
        switch (event)
        {
            case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
            {
                /*Setup first transfer*/
                ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
                                                       m_rx_buffer,
                                                       sizeof(m_rx_buffer));
                APP_ERROR_CHECK(ret);
                break;
            }
            case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
                break;
            case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
                break;
            case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
            {
                /*Get amount of data transfered*/
                size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
                NRF_LOG_INFO("RX: size: %lu char: %c\r\n", size, m_rx_buffer[0]);
    
    
                /*Setup next transfer*/
                ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
                                                       m_rx_buffer,
                                                       sizeof(m_rx_buffer));
    
                ASSERT(ret == NRF_SUCCESS); /*Should not happen*/
                break;
            }
            default:
                break;
        }
    }
    // ### ----------------------------------------------------
    
    /**
     * @brief Class specific event handler.
     *
     * @param p_inst    Class instance.
     * @param event     Class specific event.
     */
    static void msc_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_msc_user_event_t     event)
    {
        UNUSED_PARAMETER(p_inst);
        UNUSED_PARAMETER(event);
    }
    
    /**
     * @brief USBD library specific event handler.
     *
     * @param event     USBD library event.
     */
    static void usbd_user_ev_handler(app_usbd_event_type_t event)
    {
        switch (event)
        {
            case APP_USBD_EVT_DRV_SUSPEND:
                break;
            case APP_USBD_EVT_DRV_RESUME:
                break;
            case APP_USBD_EVT_STARTED:
                break;
            case APP_USBD_EVT_STOPPED:
                UNUSED_RETURN_VALUE(fatfs_init());
                app_usbd_disable();
                bsp_board_leds_off();
                break;
            case APP_USBD_EVT_POWER_DETECTED:
                NRF_LOG_INFO("USB power detected");
    
                if (!nrf_drv_usbd_is_enabled())
                {
                    fatfs_uninit();
                    app_usbd_enable();
                }
                break;
            case APP_USBD_EVT_POWER_REMOVED:
                NRF_LOG_INFO("USB power removed");
                app_usbd_stop();
                m_usb_connected = false;
                break;
            case APP_USBD_EVT_POWER_READY:
                NRF_LOG_INFO("USB ready");
                app_usbd_start();
                m_usb_connected = true;
                break;
            case APP_USBD_EVT_DRV_RESET:
                NRF_LOG_INFO("USB reset");
                break;
            default:
                break;
        }
    }
    
    static void ssm_app_timer_handler(void *ctx)
    {
        ret_code_t ret;
        static int  frame_counter;
    
        size_t size = sprintf(m_tx_buffer, "Hello USB demo: %u\r\n", frame_counter);
    
        ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, m_tx_buffer, size);
        if (ret == NRF_SUCCESS)
        {
            ++frame_counter;
        }
    }
    
    int main(void)
    {
        ret_code_t ret;
        static const app_usbd_config_t usbd_config = {
            .ev_state_proc = usbd_user_ev_handler
        };
    
        ret = NRF_LOG_INIT(app_usbd_sof_timestamp_get);
        APP_ERROR_CHECK(ret);
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        ret = nrf_drv_clock_init();
        APP_ERROR_CHECK(ret);
    
        nrf_drv_clock_lfclk_request(NULL);
        ret = app_timer_init();
        APP_ERROR_CHECK(ret);
    
        if (fatfs_init())
        {
            fatfs_ls();
            fatfs_file_create();
        }
    
        ret = app_usbd_init(&usbd_config);
        APP_ERROR_CHECK(ret);
        
        // ### Copy and paste the CDC class init phase 
        app_usbd_class_inst_t const * class_cdc_acm = app_usbd_cdc_acm_class_inst_get(&m_app_cdc_acm);
        ret = app_usbd_class_append(class_cdc_acm);
        APP_ERROR_CHECK(ret);    
        
        app_usbd_class_inst_t const * class_inst_msc = app_usbd_msc_class_inst_get(&m_app_msc);
        ret = app_usbd_class_append(class_inst_msc);
        APP_ERROR_CHECK(ret);
    
        NRF_LOG_INFO("USBD MSC example started.");
    
        if (USBD_POWER_DETECTION)
        {
            ret = app_usbd_power_events_enable();
            APP_ERROR_CHECK(ret);
        }
        else
        {
            NRF_LOG_INFO("No USB power detection enabled\r\nStarting USB now");
    
            app_usbd_enable();
            app_usbd_start();
            m_usb_connected = true;
        }
    
        app_timer_create(&ssm_timer_id, APP_TIMER_MODE_REPEATED, ssm_app_timer_handler);
        app_timer_start(ssm_timer_id, APP_TIMER_TICKS(60000), NULL);
        
        while (true)
        {
            while (app_usbd_event_queue_process())
            {
                /* Nothing to do */
            }
    
            UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
            /* Sleep CPU only if there was no interrupt since last loop processing */
            __WFE();
        }
    }
    
    /** @} */
    

    Do you see any issue on the USB CDC and MSC implementation that could explain the issue ?

  • Is it possible to reproduce without an SD card? If not, I will not be able to test/debug your example until I get hold of an SD Card board on Monday.

Reply Children
No Data
Related