Can we use read_callbacks when using Standard Serial I/O (stdio) driver (ASF 3)?

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

I've been using printf, scanf, putchar, and getchar() without any issues. Since I needed to transfer files from Teraterm to my SAMD20G18 microcontroller, I decided to use ASF's xModem example from https://github.com/avrxml/asf/blob/master/sam/components/audio/codec/wm8731/example/xmodem.c  Have been using usart_serial_getchar and usart_serial_putchar which are blocking methods.

And there was no function for usart_serial_is_rx_ready(usart). So I implemented one to check if rx interrupt flag was set or not

 

bool usart_is_rx_ready(struct usart_module *const usart)
{
	return (usart->hw->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_RXC);
}

Doesn't seem to work perfectly. So can I instead use usart_callbacks with the Standard Serial driver from the ASF library or should I just fine tune the above function?

p.s.: I'm using ASF3.

 

Regards,
Jenson

This topic has a solution.

Newbie to the world of Atmel SAM D microcontrollers.

Last Edited: Mon. Feb 12, 2018 - 07:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ASF3 or ASF4?  Are you using the sync or async usart drivers?

In ASF4 for whatever reason it requires you to use the sync drivers with START.  But it's easy enough to manually change it over to async drivers after the code is generated.  The comments below are with respect to ASF4.

 

The async drivers use a ringbuffer on the RX side, no buffering on the TX side.  So for transmits such as fwrite(), be sure the characters fully leave the USART before calling another fwrite() otherwise you will corrupt the pending transmit buffer.  Alternately implement a TX ringbuffer just above the driver but below your application, filling the driver TX buffer in the callback function.  The RX side is straight forward as there already is a ringbuffer in the driver.  For "is it empty/full/done sending/" type added functions, but sure to check the driver's ringbuffer as well as the low level registers.  And of course the buggy Atmel drivers don't disable interrupts appropriately or consistently with the ring buffers, especially on full conditions.  So be generous with CRITICAL_SECTION_ENTER()/LEAVE(), or even better implement peripheral specific interrupt disable/enable before checking the driver ring buffers at the application layer.

 

Stdio functions can be buffered in the c library via setbuf() and its companion functions.  In this case be very careful NOT to look into the driver's ringbuffers or transmit holding registers directly as the library might be buffering data.  fflush() is your friend here.  so is checking the return value of fgetc() < 0 for an empty receive buffer.

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

Hi Scott, 

 

I should have mentioned that I'm using ASF3 and not ASF4. I don't need to change anything on the transmit side. It's the receive side that needs to be non-blocking, if I want to check if the user has pressed a key or if Teraterm is sending a file over. 

 

Regards,
Jenson

Newbie to the world of Atmel SAM D microcontrollers.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I did a quick test based on the D21 XPlainedPro getting started project, seems to work well enough with your implementation of  usart_is_rx_ready (called usart_serial_is_rx_ready here as in xmodem.c). What is the problem in your case?

int main(void)
{
    struct port_config pin;

    system_init();

    /*Configure UART console.*/
    configure_console();

    /*Initialize the delay driver*/
    delay_init();


    /* Output example information */
    puts(STRING_HEADER);

    /*Enable system interrupt*/
    system_interrupt_enable_global();

    /*Configures PORT for LED0*/
    port_get_config_defaults(&pin);
    pin.direction = PORT_PIN_DIR_OUTPUT;
    port_pin_set_config(LED0_PIN, &pin);

    port_pin_set_output_level(LED0_PIN, LED0_INACTIVE);
    static int tick = 0;
    while (1) {
        delay_us(100);
        if (tick++ % 3000 == 0) {
            port_pin_toggle_output_level(LED0_PIN);		
        }
        if (usart_serial_is_rx_ready(&cdc_uart_module)) {
            uint8_t c;
            c = getchar();
            if (c == 'r') {
                printf("Starting XModem receive\r\n");
                static int8_t fileBuf[8*1024];
                unsigned size = xmodem_receive_file(&cdc_uart_module, fileBuf);
                printf("\r\nFile size %u received\r\n", size);
            }
            else {
                printf("Usage:\r\n");
                printf("r to receive a file\r\n");
            }
        }
    }
}

/Lars

 

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

Hi Lars,

 

Sorry for the late reply. Cheers for providing an implementation which is working on your board. I will give it a try on my custom board. Also, realized callbacks work so it helped me switch states based on a key press. 

 

With regards,
Jenson

Newbie to the world of Atmel SAM D microcontrollers.

Last Edited: Mon. Feb 12, 2018 - 07:06 AM