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