SAMD21g18: How to configure interrupt on UART byte sent? - Page 1
Forum section: electronics / general

author: reneman
author_rank: Contributor
timestamp: July 29, 2021, 08:53:35 am
content: Hi, I've been reading the samd21 datasheet but I cannot seem to find if this is possible.I would like to have a sercom configured as UART to send a byte (over the TX Line) and trigger an interrupt once the transmission has been completed.Do you have any sample code to do this, or could you point me to the part of the datasheet where this is explained (if it's supported)?Thank you

author: mikerj
author_rank: Super Contributor
timestamp: July 29, 2021, 09:53:27 am
content: This is certainly possible, though I don't have any example code. The interrupt flag you need is called TXC (transmit complete) and it's action is (briefly) described in section 26.6.2.5 of the family datasheet.There is one interrupt vector for each USART, you have configure the INTENSET register for the events you want to raise an interrupt, TXC in your case. Additionally you will need to configure the NVIC interrupt priority (NVIC_SetPriority) and enable the interrupt (NVIC_EnableIRQ) for the USART you wish to use.In the interrupt handler, you need to check which event has caused the interrupt by reading the INTFLAG register and testing the bits. You then should clear the interrupt by writing a 1 to the bit.Note that if you want to send multiple bytes, then waiting for TXC after each one will not provide the best throughput. The data register is double buffered, so you can load the next byte to send whilst the previous one is being transmitted, and you should use the Data Register Empty (INTFLAG.DRE) interrupt in this case to load the next byte in. You'd likely only want to use TXC on the last byte in this case, so you can tell when it has been shifted out.

author: ataradov
author_rank: Super Contributor
timestamp: July 29, 2021, 04:15:17 pm
content: Here is an example of a complete interrupt-driven UART driverhttps://github.com/ataradov/vcp/blob/master/samd11/uart.c

author: reneman
author_rank: Contributor
timestamp: August 02, 2021, 07:52:39 am
content: Quote from: ataradov on July 29, 2021, 04:15:17 pmCould you provide an example of how to use the driver and configure the interrupt routine that is called when a byte is sent over uart?Thank you

author: ataradov
author_rank: Super Contributor
timestamp: August 02, 2021, 03:57:33 pm
content: This is the example. I don't understand what else do you need. UART_SERCOM_IRQ_HANDLER() is the interrupt handler, and "if (flags & SERCOM_USART_INTFLAG_RXC)" is the part that is executed when the byte is received.Note that "#define UART_SERCOM_IRQ_HANDLER irq_handler_sercom0" must match the actual name of the interrupt handler in the vector table. This depends on your startup code.

author: reneman
author_rank: Contributor
timestamp: August 03, 2021, 08:22:25 am
content: Quote from: ataradov on August 02, 2021, 03:57:33 pmAh, now I see. I was a bit confused, because I asked for TXC like @mikerj mentioned but your driver doesn't use it.Will uart_write_byte() trigger an interruption as well, so in the handler can I do if(flags & SERCOM_USART_INTFLAG_TXC) { ... } ?

author: reneman
author_rank: Contributor
timestamp: August 03, 2021, 10:42:33 am
content: Quote from: reneman on August 03, 2021, 08:22:25 amSo, i've tested this. This is my ISR:Code:void irq_handler_sercom5(void){int flags = SERCOM5->USART.INTFLAG.reg;if (flags & SERCOM_USART_INTFLAG_TXC) //BYTE Sent{HAL_GPIO_LED_toggle();}}Which should toggle a led each time a byte is sent over uartThen on main():Edit: AddingCode:SERCOM5->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC;as mikerj suggested, the interrupt is now triggered (once). I think i need yet to acknowledge the interrupt so if works perfectly.Code:NVIC_EnableIRQ(SERCOM5_IRQn);while(1){myclass.uart_putc('a');myclass.delay(1000);}UART Works fine (byte is sent) but the led is never toggled. What am I missing?

author: ataradov
author_rank: Super Contributor
timestamp: August 03, 2021, 04:15:21 pm
content: Are you sure that in your startup code the interrupt handler is called irq_handler_sercom5? I bet it is not.So first of all, figure out of interrupt handler is called at all, then see what flags are set.

author: reneman
author_rank: Contributor
timestamp: August 04, 2021, 07:47:50 am
content: Quote from: ataradov on August 03, 2021, 04:15:21 pmIt was. Only thing missing was SERCOM5->USART.INTFLAG.bit.TXC = 1; at the end of the interrupt routine.Thanks guys

author: ataradov
author_rank: Super Contributor
timestamp: August 04, 2021, 08:00:06 am
content: Quote from: reneman on August 04, 2021, 07:47:50 amThis is incorrect. This will clear all the bits, not just TXC. The correct way of clearing a single bit is this:SERCOM5->USART.INTFLAG.reg = SERCOM_USART_INTFLAG_TXC;Also, in most cases you actually want to use DRE, as my code does, not TXC.

author: reneman
author_rank: Contributor
timestamp: August 05, 2021, 10:31:46 am
content: Quote from: ataradov on August 04, 2021, 08:00:06 amWhat's the difference between DRE and TXC?

author: ataradov
author_rank: Super Contributor
timestamp: August 05, 2021, 04:38:12 pm
content: Quote from: reneman on August 05, 2021, 10:31:46 amThere are two buffers. One is the data buffer and one is the shift register. DRE is set when the value from the data register moved to the shift register. The value is not yet shifted out, but SERCOM is already ready to accept the new data.TXC is set when the byte is actually sent.So if you use DRE, then you have a whole byte duration to react to the interrupt in order to ensure solid uninterrupted stream of bytes. If you use TXC, then you will have gaps between the bytes while firmware reacts to the interrupt and sends a next byte.The only time TXC matters is when device goes to sleep right after sending the byte, for example. In this case you can still use DRE, but also poll for TXC before going to sleep to let it send out the last byte.