Hi everybody,
Could you please help me: i need to use an uart interrupt routine in a RTX code. I've already found some examples for the uart interrupt routine, but i cannot make it work using tasks. Has anybody an example to show me how to use it. Thanks
Hello,
Using tasks should not change things too much. Often events are used to signal to a task that an interrupt has occurred.
For example:
TASK -
__task void task_uart(void) { for (;;) { os_evt_wait_or(CHAR_READY_EVENT, 0xFFFF); // handle char... NVIC_EnableIRQ(UART_IQRn); } }
IRQ -
void UART_IRQHandler (void) { uint32_t intsrc, tmp; /* Determine the interrupt source */ intsrc = UART_GetIntId(UART_PORT); tmp = intsrc & UART_IIR_INTID_MASK; // Receive Data Available if (tmp & UART_IIR_INTID_RDA) { last_received_char = UART_ReceiveByte(UART_PORT); NVIC_DisableIRQ(UART_IQRn); isr_evt_set (CHAR_READY_EVENT, t_uart); // Send Event Flag to task } NVIC_ClearPendingIRQ(TERNIAL_IQRn); // Clear Interrupt }
Hope this helps.
Thanks, i'll try it. Just a couple of question: this is just an example, i've to adapt it to my code. I cannot find anywhere the function UART_ReceiveByte, right? Other question. I've already seen the NVIC in other examples, but i cannot understand how it works and what is it function. Thanks
Yes, this is generic code you will have to adapt it.
What micro are you using?
NVIC stands for Nested Vector Interrupt Controller. This is specific to Cortex-M devices.
If you are using a Cortex-M device search for the CMSIS drivers for your device.
I'm using an STMf103ze. I tried to adapt a code that i found on the example provided by Keil. I want to dialogue with an inertial platform, acquiring data each time that it change the position. So i want to work using tasks and an uart. I'm trying to learn how to do it using hyperterminal for sending a character to the dev kit and manage it, but i'm encountering come difficulties.
Have you looked at the examples under:
C:\Keil\ARM\Boards\ST
Also try:
www.st.com/.../app
Yes, but i have some problems with them. I think i've to work a little bit more with it. Thanks for the link. i will cull it. Thanks.
You normally don't turn off any interrupts because you have received data. If the task can't handle every single character in real time, then the design must be able to live with the receiver hw FIFO and optional application ring buffer. Since the hw FIFO has fixed size, the ISR must handle interrupts to offload to sw ring buffer. Besides, there are more than one reason for an UART to generate an interrupt.
Hi Per,
I don't understand why 'turning off' the receive interrupt is a bad idea? It's not like the hardware will stop receiving the data? I've often done this and never encountered a problem? Am I missing something?
static void process_uart_irq(int32_t a_uart_source) { int32_t l_bytes ; uint32_t l_now = T3TC ; // guard againt an overflow of interrupts if ( (l_now - s_uart_isr_previous_entry_time[a_uart_source]) < T3_x_MICROSECONDS(20) ) { if (++s_uart_isr_flood[a_uart_source] > UART_ISR_FLOOD_RESET_THRSHOLD) { // reset peripheral reset_uart(a_uart_source) ; s_uart_isr_flood[a_uart_source] = 0 ; } } else { s_uart_isr_flood[a_uart_source] = 0 ; } s_uart_isr_previous_entry_time[a_uart_source] = l_now ; switch ( (*sp_effective_IIR[a_uart_source]>>1) & 0x7) // interogate bits 3:1 to identify interrupt reason { case 0x6: // Character Time-out Indicator (CTI) { if (circular_buffer_enqueue(&g_data_interface.uart_rx_circular_buffer[a_uart_source], *sp_effective_RBR[a_uart_source] ) ) { // buffer full // reset buffer circular_buffer_init(&g_data_interface.uart_rx_circular_buffer[a_uart_source], g_data_interface.uart_rx_circular_buffer[a_uart_source].size) ; } } break ; case 0x2: // Receive Data Available (RDA) { // collect frame characters l_bytes = BLUETOOTH_UART_RX_FIFO_TRIGGER_LEVEL ; do { if (circular_buffer_enqueue(&g_data_interface.uart_rx_circular_buffer[a_uart_source], *sp_effective_RBR[a_uart_source] ) ) { // buffer full // reset buffer circular_buffer_init(&g_data_interface.uart_rx_circular_buffer[a_uart_source], g_data_interface.uart_rx_circular_buffer[a_uart_source].size) ; break ; } } while ( (--l_bytes) > 0) ; } break ;
There is no reason to disable interrupts - notice the usage of a circular buffer (which is emptied somewhere else). The application should be allowed to be interrupted while processing previous received data. You probably never encountered problems but reduced your program's responsiveness.
Notice how the ISR posts data in the buffer and "forgets" about it, greatly reducing ISR overhead.
Disabling the interrupt also increased the chances that you will actually miss a character, if you processing time is too long resulting in hardware buffer overflow. Hardware flow control can help with that, though.
I agree that you can't 'process' the data with the interrupt disabled as this could obviously take too long but I see no reason why you can't have the circular buffer in the task space.
Basically this is how I set it up and this has always worked for communication with terminals and also with machines using defined protocols.
__task void task_uart(void) { for (;;) { os_evt_wait_or(CHAR_READY_EVENT, 0xFFFF); // ADD BYTE TO MESSAGE OR BUFFER OR WHATEVER STRUCTURE NVIC_EnableIRQ(UART_IQRn); //IF ALL DATA IS RECEIVED (Meaning will depend on application) // swap to second receive buffer post message received event... } } void UART_IRQHandler (void) { uint32_t intsrc, tmp; /* Determine the interrupt source */ intsrc = UART_GetIntId(UART_PORT); tmp = intsrc & UART_IIR_INTID_MASK; // Receive Data Available if (tmp & UART_IIR_INTID_RDA) { last_received_char = UART_ReceiveByte(UART_PORT); NVIC_DisableIRQ(UART_IQRn); isr_evt_set (CHAR_READY_EVENT, t_uart); // Send Event Flag to task } NVIC_ClearPendingIRQ(TERNIAL_IQRn); // Clear Interrupt }
Since "isr_evt_set" has a queue behind it (as far as I can remember), there is no risk of missing signals even if your application does not respond fast enough for short bursts of data. Therefore, as long as the queue is big enough (RTX allows setting its size up to a limit (at least, officially), FreeRTOS can offer an arbitrary size and type) there is really no reason to disable the interrupt source. Why did you do it in the first place? Maybe there was some good reason for it that we are not aware of?
When your only buffer is the receive buffer (single-character, double-buffered or full FIFO) in the UART, then it doesn't matter if you turn off the interrupt. If the task isn't consuming the data fast enough and turns on the interrupt again (and the ISR has time to run) before the hw in the UART suffers a overrun, you will lose data.
When you have buffer space in RAM, then keeping the interrupt enabled means that the UART can generate a new interrupt and more data can be buffered in your RAM even if the task hasn't had the time to consume the previous data. But this only works if the interrupt is kept enabled - with interrupts disabled, you are removing the ability to buffer a burst of data into RAM.
Some protocols can't stand any jitter in processing, so it doesn't matter. But many protocols are packet-based and don't care if 1, 10 or 100 characters are buffered, as long as the device has a reasonable response time from the end of the packet until the receiver reacts with an acknowledge or other response data.
When you only allow one character/interrupt and turns off interrupts between receive and processing, then your receive task must run with very high priority. When the receive task is allowed to process data in bursts, you can reduce the receive priority as long as all higher-prio tasks are guaranteed to leave leave some CPU time with a reasonable max interval.
Next thing is that you often have both receive and transmit. And then you normally have interrupts to inform that the UART can accept more data - normally using FIFO or at least double-buffering. With interrupts disabled, you will not keep the outgoing channel full. And not all protocols have a 1:1 relation between incomming and outgoing data.
If you make use of hardware handshake signals, you may want to get interrupts for modem control line changes too, allowing you to block transmission of more characters if the other side isn't ready. Having interrupts disabled, will slow down the detection of these modem control line signals until your receive task have had time to run.
Another thing to consider here is that if you activate a receive FIFO in the UART, then a single receive interrupt need not mean that you have one character available. You may have one (in case of a FIFO timeout), but you might just as well have filled the FIFO past the receive watermark. So with receive FIFO enabled, the ISR would have to continue to pick up characters until the UART status claims it doesn't have any more characters.
One thing here is that it is possible to use a mailbox feature and send received characters as the mailbox "pointer" to the read task. The size of the mailbox obviously controls the number of characters that can be queued this way.
It is also possible to just signal the read task that there are one or more characters available in the incomming circular buffer. The read task then clears this signal when it starts processing data. If the ISR is run during the processing, and inserts more data, then the read task might get accidentaly activated once without any more data avaiable. But that isn't really a problem.
The concept of just flagging existence of data means that there isn't any issue of any queue depth for the RTOS. It's just a question if the read task should freeze or not when it has consumed all data in the ring buffer and then performs a new wait. Has the ISR run during the read process, then the task will not block (but might not find more data). Has the ISR not run during the read process, then the read task will get stuck waiting for a new wakeup signal.