Redirect STDIO to USB CDC serial port?

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd like to redirect STDIO to USB CDC (virtual serial port). SAME54 + ASF4 project set up with Atmel Start.

 

It seems that Atmel Start supports redirecting STDIO to a USART, but not to USB.

My current workaround is to use sprintf() to print formatted text to a buffer, and cdcdf_acm_write() to write the buffer to USB CDC:

 

	char buffer[50];

	sprintf(buffer, "One plus one is %i\n", 2);
	cdcdf_acm_write((uint8_t *)buffer, strlen(buffer));

 

It's functional, but not pretty. I'd much prefer to use printf() directly. 

 

Anyone have a solution? 

 

[cross-posted to the AVR Freaks ASF4 forum in reply to someone who had this problem 2+ years ago, but not clear if it was ever solved] 

Last Edited: Fri. Mar 15, 2019 - 11:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I played with this in the cdc start example by adding the stdio redirect (the "TARGET IO" from that will be unused). All code changed is in usb_start.c. Seems ok but not much tested. It works by providing a read and a write function so a stdio_io_init() call can be made.
I could not get it reliable (was also really slow) without buffering in the output hence the write function is a lot more complicated than read.

#include "atmel_start.h"
#include "usb_start.h"

#if CONF_USBD_HS_SP
static uint8_t single_desc_bytes[] = {
    /* Device descriptors and Configuration descriptors list. */
    CDCD_ACM_HS_DESCES_LS_FS};
static uint8_t single_desc_bytes_hs[] = {
    /* Device descriptors and Configuration descriptors list. */
    CDCD_ACM_HS_DESCES_HS};
#define CDCD_ECHO_BUF_SIZ CONF_USB_CDCD_ACM_DATA_BULKIN_MAXPKSZ_HS
#else
static uint8_t single_desc_bytes[] = {
    /* Device descriptors and Configuration descriptors list. */
    CDCD_ACM_DESCES_LS_FS};
#define CDCD_ECHO_BUF_SIZ CONF_USB_CDCD_ACM_DATA_BULKIN_MAXPKSZ
#endif

static struct usbd_descriptors single_desc[]
    = {{single_desc_bytes, single_desc_bytes + sizeof(single_desc_bytes)}
#if CONF_USBD_HS_SP
       ,
       {single_desc_bytes_hs, single_desc_bytes_hs + sizeof(single_desc_bytes_hs)}
#endif
};

/** Ctrl endpoint buffer */
static uint8_t ctrl_buffer[64];


volatile bool cdcTransferRead = false;
volatile uint32_t cdcTransferReadLen;
volatile bool cdcTransferWrite = false;

static bool cdcWriteDone(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count)
{
    cdcTransferWrite = false;
    return false;
}

static bool cdcReadDone(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count)
{
    cdcTransferReadLen = count;
    cdcTransferRead = false;
    return false;
}

static int32_t cdcRead(struct io_descriptor *const io_descr, uint8_t *const buf, const uint16_t length)
{
    cdcTransferRead = true;
    if (cdcdf_acm_read(buf, length) != USB_OK) {
        cdcTransferRead = false;
        return 0;
    }
    while(cdcTransferRead);
    return (int32_t)cdcTransferReadLen;
}

static uint8_t outBuf[80];
static uint32_t outLen = 0;
static int32_t cdcWrite(struct io_descriptor *const io_descr, const uint8_t *const buf, const uint16_t length)
{
    size_t i = 0;
    uint16_t left = length;
    const uint8_t* p = buf;
    while(left > 0) {
        bool transfer = false;
        for(i = 0; !transfer && outLen < sizeof(outBuf) && i < left; i++) {
            outBuf[outLen++] = p[i];
            if (p[i] == '\n') {
                transfer = true;
            }
        }
        if (outLen < sizeof(outBuf) && i == left && !transfer) {
            break;
        }
        cdcTransferWrite = true;
        cdcdf_acm_write(outBuf, outLen);
        while(cdcTransferWrite);
        left -= i;
        p += i;
        outLen = 0;
    }
    return length;
}


/**
 * \brief Callback invoked when Line State Change
 */
static bool usb_device_cb_state_c(usb_cdc_control_signal_t state)
{
    if (state.rs232.DTR) {
        /* Callbacks must be registered after endpoint allocation */
        cdcdf_acm_register_callback(CDCDF_ACM_CB_READ, (FUNC_PTR)cdcReadDone);
        cdcdf_acm_register_callback(CDCDF_ACM_CB_WRITE, (FUNC_PTR)cdcWriteDone);
        static struct io_descriptor  cdcIo;
        cdcIo.write = cdcWrite;
        cdcIo.read = cdcRead;
        stdio_io_init(&cdcIo);
    }
    else {
        stdio_io_init(NULL);
    }

    /* No error. */
    return false;
}

/**
 * \brief CDC ACM Init
 */
void cdc_device_acm_init(void)
{
    /* usb stack init */
    usbdc_init(ctrl_buffer);

    /* usbdc_register_funcion inside */
    cdcdf_acm_init();

    usbdc_start(single_desc);
    usbdc_attach();
}

void cdcd_acm_example(void)
{
    while (!cdcdf_acm_is_enabled()) {
        // wait cdc acm to be installed
    };

    cdcdf_acm_register_callback(CDCDF_ACM_CB_STATE_C, (FUNC_PTR)usb_device_cb_state_c);

}

void usb_init(void)
{

    cdc_device_acm_init();
}

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks very much for this sample! I'm just getting started with the CDC and it clarifies this side of the plumbing.

 

After reading the write code, I took a stab at simplifying.

static int32_t cdcWrite(struct io_descriptor *const io_descr, const uint8_t *const buf, const uint16_t length)
{
    const uint8_t* end = buf + length;
    for (uint8_t* p = buf; p < end; ++p) {
        outBuf[outLen++] = *p;

        if (*p == '\n' || outLen==sizeof(outBuf)) {
            cdcTransferWrite = true;
            cdcdf_acm_write(outBuf, outLen);
            while(cdcTransferWrite);
            outLen = 0;
        }
    }
    return length;
}

 

- Frank (The Things Network New York)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yet another version of the CDC callbacks. Thank you Lajon for providing initial version!
 

volatile bool cdcTransferRead = false, cdcTransferWrite = false;
volatile uint32_t cdcTransferReadLen, cdcTransferWriteLen;

static bool cdcWriteDone(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count)
{
    cdcTransferWriteLen = count;
    cdcTransferWrite = false;
    return false;
}

static bool cdcReadDone(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count)
{
    cdcTransferReadLen = count;
    cdcTransferRead = false;
    return false;
}

static int32_t cdcRead(struct io_descriptor *const io_descr, uint8_t *const buf, const uint16_t length)
{    
    cdcTransferRead = true;
    if (cdcdf_acm_read(buf, length) != USB_OK)
    {
        cdcTransferRead = false;
        return 0;
    }
    while(cdcTransferRead);
    return (int32_t) cdcTransferReadLen;
}

static int32_t cdcWrite(struct io_descriptor *const io_descr, const uint8_t *const buf, const uint16_t length)
{      
    cdcTransferWrite = true;
    if (cdcdf_acm_write(buf, length) != USB_OK)
    {
        cdcTransferWrite = false;
        return 0;
    }
    while(cdcTransferWrite);
    return (int32_t) cdcTransferWriteLen;
}

 

Last Edited: Mon. Oct 19, 2020 - 11:37 PM