SAMD51 ASF4 USBSerial

ASF4 is a powerful tool for configuring Atmel devices. It is far from perfect (you can configure clocks so your device won’t work) and very poorly documented. To make matters worse (see below) important structures are declared in c files instead of h files meaning important functions are not callable.

USBSerial examples are in ASF3 but ASF3 doesn’t support modern Atmel devices like SAMD51. ASF4 has a “USB Echo” example for SAMD21 but it is essentially undocumented, particularly opaque,(like most ASF4 code) and utterly useless because, eriously, why would you want an ISR which does nothing but echo back USB input?

There are stacks like TinyUSB which work with SAMD51 but then you have to fit in ASF4 and the ASF4 .astart files are obsolete so if you try and use them ASF4 produces garbage.I have managed to get TinyUSB to work with ASF4 but I decided to try getting USBSerial to work with only ASF4 and not using any other stack. This was not easy since ASF4 output is opaque and poorly documented but I think I got it going.

There are 10 core routines:

bool    CheckUSBOutAvailable( void );       //Check if output can be written

void    USBFlush( void );                   //Flush the output buffer (send to terminal)

int     CheckUSBSerialRXAvailable( void );  //Any data in (like UART RX Ready)

bool    CheckUSBAttached( void )            //True if USB attached and initialized

int     USBSerialRead( );                   //Wait for input from USB Serial and return with it

void    USBSerialWrite( int c );            //Write to USB Serial and flush the buffer

bool    USBSerialAttached( void );          //True if USB Serial is attached and ready

void    USBSerialWaitDTR( void );           //If USB_FLOW_CONTROL true wait for DTR

int     _read (int fd, const void buf, size_t count);  stdio used by scanf, etc

int     _write( int fd, const void buf, size_t count ); stdio used by printf, getchr

Note that for getchar to work as expected, stdin is unbuffered via

	setbuf(stdin, NULL);        //No buffering on input (makes getchar work)

This should not be an issue since USB is buffered and I buffer that with a ring buffer.

Limitations

  • Only 1 endpoint
  • Hardware flow control is not yet fully implemented because for strange reasons ASF4 important structures are defined in c files instead of h files (for example cdcdf_acm_func_data within cdcdf_acm.c and important structures in hal_usb_device.c meaning you can’t use functions inside either without modifying them. Vitally important functions are only accessible from within these c files. For example, to change RTS I need access to the rs232 structure which I can only get with something like this uint8_t FindBuf( void ) { int8_t ep_index = _usb_d_find_ep( ENDPOINT ); struct usb_d_ep ept = &usb_d_inst.ep[ep_index]; uint8_t req = ept->xfer.req; //<======= req is uint8_t to rs232 return( req ); }

I suspect that in order to have > 1 endpoint or working hardware flow control I will have to modify some of the ASF4 files, which sort of misses the point.

Since I can’t figure out how to access the endpoint without modifying cdcdf_acm.c I capure the endpoint and use that to get the status of the USB port through _usb_d_dev_ep_get_status(). The write (print) port endpoint is usually 0x81 single port and the The write (print) port endpoint is usually 0x01 I capture it instead of using a #define because I am hopeful I can extend this to several channels.

I have found that most ASF4 based examples will work if you create the ASF4 files for the desires hardware (i.e. SAMD51, SAMD21) and just copy the example over. I therefore suspect USB_Serial will work on SAMD21 just by doing that (though you might need to adjust timeouts, etc).

I haven’t tried it but the stdio for ASF4 ARM and AVR are different. I believe an AVR implementation would be something like

FILE  USB_SERIAL_STREAM; //Global stdio FILE structure at the top of USB_Serial.c

and this added to the end of USBSerial_Init() and remove _read() and _write() from the source.

USB_SERIAL_STREAM.put = USBSerialWrite;     //Write a character

USB_SERIAL_STREAM.get = USBSerialRead;      //Read a character

USB_SERIAL_STREAM.flags = _FDEV_SETUP_RW;

stdout = stdin = &USB_SERIAL_STREAM;

Note: Dec 22 Push I recreated an ASF4 packaged and checked for differences. Apparently I had included a number of directories from previous work which were not needed and deleted. I also renamed usb_cdc_echo.c to USB_Serial_example. Besides the files

USB_Serial.c
USB_Serial.h
USB_Serial_example.c

The only changes from an ASF4 package are in the

atmel_devices_cdc.inf file 

where I shortened the strings from

"Communication Device Class ASF example"

to

"CDC ASF"

link to project https://gitlab.com/bjpiccioni/samd51-asf4-usbserial

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: