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?
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.
View all questions in Keil forum