3

I'm working on USB CDC on ATSAMD21. The code which i'm using is ATMEL START example for USB CD Echo on D21. I'm working on atmel studio.

Requirement:

In my application the host send data to the device and i need to read that data and send back different data to the host from different function.

Here in echo example the data reception and transfer is using the call back. I'm not aware how to use a write call inside another function. Here i'm attaching the code below

Read & Echo the data:

Here in this function it is for read the data and echo it back to the host.

  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)usb_device_cb_bulk_out);

    cdcdf_acm_register_callback(CDCDF_ACM_CB_WRITE, (FUNC_PTR)usb_device_cb_bulk_in);
    /* Start Rx */
    cdcdf_acm_read((uint8_t *)usbd_cdc_buffer, sizeof(usbd_cdc_buffer));
}

/* No error. */
return false;

}

/////////////////////////////////////

Read the data from host:

   static bool usb_device_cb_bulk_out(const uint8_t ep, const enum  
   usb_xfer_code rc, const uint32_t count)
  {

   cdcdf_acm_write((uint8_t *)usbd_cdc_buffer, count);

 return false;  

}

Write back the data:

  static bool usb_device_cb_bulk_in(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count)
{
/* Echo data. */
cdcdf_acm_read((uint8_t *)usbd_cdc_buffer, sizeof(usbd_cdc_buffer));

/* No error. */
return false;

}

I need to use this read call inside another function outside. I used the read call directly in another function, it was not able to send the data. How can i make this call use in another function.Any help will be appreciated.

Here the callback will direct to the read and write functions.

2 Answers2

3

This is a fun one!

From the main loop, using a stack allocated char buffer:

    const char hello[] = "Hi There!\r\n";
    cdcdf_acm_write((void*)hello, 11);

works for me, but curiously using a pointer to a string constant:

    const char* hello = "Hi There!\r\n";
    cdcdf_acm_write((void*)hello, 11);

does not output anything! Similarly, a static const buffer

const char hello[] = "Hi There!\r\n";

int main(void)
{
    // init ...

    while (1) 
    {
        delay_ms(200);
        cdcdf_acm_write((void*)hello, 11);
    }

}

doesn't work either, but a non-const static buffer:

char hello[] = "Hi There!\r\n";

int main(void)
{
    // init ...

    while (1) 
    {
        delay_ms(200);
        cdcdf_acm_write((void*)hello, 11);
    }

}

does!

My hypothesis is that cdcdf_acm_write() has a problem somewhere in the USB stack that cannot read data straight from flash, which seems like a very unexpected pitfall, especially as outputting a string constant is probably the first thing one does while implementing the communication functionality!

dognotdog
  • 681
  • 3
  • 14
  • The mind boggles. One thing I wanted to add: This works as well, e.g. non const, non-static char buffer: `char buffer[64]; snprintf(buffer, 64, "Result is %i\r\n", c); cdcdf_acm_write((void*)buffer, strnlen(buffer, 64));` – Jens Jan 16 '21 at 10:39
0

I've hit this post so many times in my searches, and I've finally solved the issue - sharing now!

The atmel generated code uses pre-allocated caches to store data if it is located outside of RAM.

In hpl_usb.c

#define _IN_RAM(a, s) ((0x20000000 <= (uint32_t)(a)) && (((uint32_t)(a) + (s)) < (0x20000000 + 0x00032000)))

/** Check if the address should be placed in RAM. */
#define _usb_is_addr4dma(addr, size) _IN_RAM((addr), (size))

int32_t _usb_d_dev_ep_trans(const struct usb_d_transfer *trans) {
...
    if (!_usb_is_addr4dma(trans->buf, trans->size) || (!_usb_is_aligned(trans->buf))
        || (!dir && (trans->size < ept->size))) {
        if (!ept->cache) {
            return -USB_ERR_FUNC;
        }
        /* Use cache all the time. */
        use_cache = true;
    }
...
}

Thus, if no cache is allocated and you're trying to transfer a string literal/const (stored in flash) then the transfer will fail.

Caches are automatically allocated based on some defines/configs. In hpl_usb_config.h

#ifndef CONF_USB_EP1_I_CACHE
#define CONF_USB_EP1_I_CACHE 64
#endif

"I_CACHE" is for data being sent from device to host, and EndPoint1 is the endpoint being used for my data transfer.

Alternatively, you can use AtmelStart to configure this. link to atmelstart picture