AnsweredAssumed Answered

CubeMX gen. project, CDC_Transmit_FS not working reliable

Question asked by Br_ggemann.Dieter on Aug 26, 2016
Latest reply on Dec 30, 2017 by A. M.
In a CubeMX (lastest version now) generated project CDC_Transmit_FS resp.
USBD_CDC_TransmitPacket do not work reliable. On every few request there are
no data readable on host from the device (/dev/ttyACM0) despite the function
returns a complete write.

Target is a STM32F103CBT6

The trigger and reading on the host is at the command line
It shall be replaced by a config tool later:

Terminal line is set raw (stty -F /dev/ttyACM0 raw) and
works at 115200 8N1.

Commandline:
printf 'push_settings\0' > /dev/ttyACM0; cat flashpage.0x0801F400.bin > /dev/ttyACM0

and the other direction which is malfunctioning some times:
printf 'pull_settings\0' > /dev/ttyACM0; ./read-settings.pl > flashpage.0x0801F400-down.bin

read-settings.pl is the perl script below. It just don't return if malfunctioning happens.
#!/usr/bin/perl
 
open $device, "< /dev/ttyACM0" or die;
 
my $buffer;
my $bufsize=1024;
my $total=0;
my $read=0;
 
while ( $total < 1024 and  $read = sysread($device , $buffer , $bufsize, $total ) )
{
   $total += $read;
}
close $device;
syswrite ( STDOUT, $buffer);

The debugging implementation on the MCU is as the follows.

usbd_cdc_if.c:

...
#define APP_TX_DATA_SIZE  32
#define APP_RX_DATA_SIZE  64
...
 
static int8_t CDC_Init_FS(void)
{
    /* USER CODE BEGIN 3 */
    // Set Application Buffers
    USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0);
    USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
    return (USBD_OK);
    /* USER CODE END 3 */
}
 
...
 
// This is functioning very well and fills reliable my 1k
// received_data buffer in 16 steps. Rearming in main.
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
    /* USER CODE BEGIN 6 */
    if ( cdc_received_tot + *Len > 1024 )
    {
        cdc_received_tot = 0;
    }
    // store bytes from UserRxBufferFS at next received_data index
    memcpy(&received_data[cdc_received_tot], Buf, *Len);
 
    cdc_received_tot += *Len;
    cdc_received = 1;
 
    USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
 
    return (USBD_OK);
 
    /* USER CODE END 6 */
}
 
...
 
uint16_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
    uint8_t result = USBD_OK;
 
    /* USER CODE BEGIN 7 */
    uint16_t bytes_written = 0;
    uint16_t bytes_to_write;
 
    USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) hUsbDeviceFS.pClassData;
    if (hcdc->TxState != 0)
    {
        return USBD_BUSY;
    }
 
    // Check if USB interface is connected
    if ( hUsbDeviceFS.dev_state != USBD_STATE_CONFIGURED )
    {
        return = USBD_FAIL;
    }
 
    else
    {
        // Busy wait if USB is busy or exit on success or disconnection happens
        while (bytes_written < Len)
        {
 
            HAL_Delay(10);
 
            bytes_to_write = (Len - bytes_written) > APP_TX_DATA_SIZE ? APP_TX_DATA_SIZE : ( Len - bytes_written );
 
            memcpy(UserTxBufferFS, &Buf[bytes_written], bytes_to_write );
 
            USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, bytes_to_write);
 
            //Check if USB disconnected while retrying
            if ( hUsbDeviceFS.dev_state != USBD_STATE_CONFIGURED )
            {
                return USBD_FAIL;
            }
 
            // Try send
            result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
            if (result == USBD_OK)
            {
                bytes_written += bytes_to_write;
            }
            else if (result != USBD_BUSY) // other error
            {
                return result;
            }
        }
    }
 
    /* USER CODE END 7 */
    return bytes_written;
 
}

main.c:
    while (1)
    {
...
        // 200 Hz, flag from timer callback which don't have to be paced in this part/mode of the program
        if (PeriodElapsed == 1)
        {
 
...
                if ( cdc_received == 1)
                {
                    if (rcv_settings == 1)
                    {
                        if (cdc_received_tot < 1024)
                        {
                            cdc_received = 0;
                            USBD_CDC_ReceivePacket(&hUsbDeviceFS);
                        }
                        else
                        {
 
                            sprintf(buf, "%d", cdc_received_tot);
                            BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
                            BSP_LCD_SetBackColor(LCD_COLOR_BLACK);
                            BSP_LCD_FillRect(0, 1 * 12, BSP_LCD_GetXSize(), 12);
                            BSP_LCD_SetTextColor(LCD_COLOR_YELLOW);
                            BSP_LCD_DisplayStringAtLine(1, (uint8_t *) buf);
 
                            cdc_received = 0;
                            cdc_received_tot = 0;
                            USBD_CDC_ReceivePacket(&hUsbDeviceFS);
                            rcv_settings = 0;
 
                            if (erase_flash_page() != HAL_OK)
                            {
                                Error_Handler();
                            }
                            else
                            {
                                if (write_flash_vars((uint32_t*) received_data, 256, 0) != HAL_OK)
                                {
                                    Error_Handler();
                                }
 
                            }
                        }
                    }
                    else if (strcmp((const char *) received_data, (const char *) "bootloader") == 0)
                    {
                            //setting flag in flash
                            //so reborn as bootloader we can know that we should not start this live immediately again
                            // write string "DFU" (4 bytes incl. trailing \0) to last 32 bit wide space of flash page
                            write_flash_vars((uint32_t*) "DFU", 1, 1020);
 
                            // is this really needed? I think it is not
                            HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
                            HAL_NVIC_DisableIRQ(USART1_IRQn);
                            HAL_NVIC_DisableIRQ(TIM2_IRQn);
 
                            // set stackpointer pointing to bootloader startup and reset system
                            __set_MSP(*(__IO uint32_t*) BOOTLOADER_ADDRESS);
                            HAL_NVIC_SystemReset();
                    }
                    else if (strcmp((const char *) received_data, (const char *) "push_settings") == 0)
                    {
                        cdc_received_tot = 0;
                        cdc_received = 0;
                        rcv_settings = 1;
                        USBD_CDC_ReceivePacket(&hUsbDeviceFS);
                    }
                    else if (strcmp((const char *) received_data, (const char *) "pull_settings") == 0)
                    {
                        cdc_received_tot = 0;
                        cdc_received = 0;
                        snd_settings = 1;
                        USBD_CDC_ReceivePacket(&hUsbDeviceFS);
                        read_flash_vars((uint32_t *) flash_buf, 256, 0);
 
                        BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
                        BSP_LCD_SetBackColor(LCD_COLOR_BLACK);
                        BSP_LCD_FillRect(0, 0 * 12, BSP_LCD_GetXSize(), 12);
                        BSP_LCD_SetTextColor(LCD_COLOR_YELLOW);
                        BSP_LCD_DisplayStringAtLine(0, (uint8_t *) "XXXX");
 
                    }
                }
 
                if (snd_settings == 1 )
                {
                    usb_res = CDC_Transmit_FS((uint8_t*) flash_buf, 1024);
 
                    sprintf(buf, "%d", usb_res);
                    BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
                    BSP_LCD_SetBackColor(LCD_COLOR_BLACK);
                    BSP_LCD_FillRect(0, 0 * 12, BSP_LCD_GetXSize(), 12);
                    BSP_LCD_SetTextColor(LCD_COLOR_YELLOW);
                    BSP_LCD_DisplayStringAtLine(0, (uint8_t *) buf);
 
                    if ( usb_res != USBD_BUSY)
                    {
                        snd_settings = 0;
                    }
                }
            }
...
 
        }
    }
...

On the display one can read always the completed 1024 bytes even if there is nothing to read
from /dev/ttyACM0. Of course variables like cdc_received_tot and so on are declared volatile.

How to debug this thing?
Bug in CDC Lib?

Kind regards

Outcomes