Hi there,
I want to write a serial driver over UART for a module. It should have interrupted RX but synchronous TX. Zephyr runs on top of a STM32L496RE.
The problem is I cannot conclude on the best way to do it with Zephyr.
The two approaches that I've seen are the modem_receiver and the console_tty.
I've tried both ways but something is not working correct with either way. The main problem that I have is that after some transmissions, the whole board seems to hang. This is usually solved by inserting a small delay on the getchar implementation. But it's an unsafe solution.
As far as the modem_receiver, the pipes are used for the buffering inside an ISR, but the docs forbid it in the first note. Also I guess a solution with pipes will not work if I have one thread. Right?
The thing is that using only the HAL provided for the MCU (without an OS), everything works fine. Also with freeRTOS.
I believe that I am missing something vital on the docs or the example codes.
Thank you for your time.
I had noticed that mem_receiver using a static buffer on ISR handler銆侷 think it is not safe if another UART's interrupt priority higher that the current processed.
Also according with the docs, the call of uart_fifo_fill outside of an ISR results in undefined behaviour.
If you are using more than one UART check your setup. The driver seems to be unable to handle multiple UARTs sharing the same RX/TX interrupt (which is btw common for STM32). Only use one UART per interrupt.
Maybe @erwango can clarify.
I see. I'm indeed using two different UARTs but I've written different drivers for each UART module.
Were you refering to my driver or the Zephyr's?
Is there an example, an issue or any resource that I can read and "plan an attack" to the problem?
Were you refering to my driver or the Zephyr's?
I was referring to the Zephyr stm32 UART driver.
Is there an example, an issue or any resource that I can read and "plan an attack" to the problem?
Sorry, I麓m not familar with the STM32L496RE. I just checked the reference manual chapter 13.3. The STM32L496RE does not have shared interrupts for USARTs -> Forget about my comment it does not apply to the STM32L496RE .
The data flow of my project is that I utilize one UART to communicate and take pictures from a camera module and then, these data will be transferred through another UART to a wireless network. Meanwhile, a third UART is used for logging/debugging to my PC.
There are cases that Zephyr gives a runtime error for Spinning Threads or it just hangs.
It would be really nice if there was a sample for multiple uarts, or some board specific (e.g. stm32) like #3564 issue recommends.
What I did up to now to be politically correct is to use the Message Queue. I've seen in its source code that it is interrupt-safe so I will not need to obtain irq locks. I also like how clean it looks like.
For one or two iterations of getting data from the buffer, everything is fine. Then everything hangs and Zephyr reports:
USAGE FAULT
Illegal use of the EPSR
Hardware exception
Current thread ID = 0x20000644
Faulting instruction address = 0x20000748
Fatal fault in ISR! Spinning...
My source code for handling one of the UARTs sums up to the one below. I am not using semaphores for now, because I work with one thread.
struct ser_ctx {
struct device *dev;
/* rx data */
uint8_t *rxbuf;
struct k_msgq queue;
} ser;
static uint8_t __aligned(1) uart_rxbuf[1024 * sizeof(uint8_t)];
static void flush(void)
{
uint8_t b;
/* Drain the fifo */
while (uart_fifo_read(ser.dev, &b, 1) > 0) {
;
}
/* Initialize an empty message queue */
k_msgq_init(&ser.queue, ser.rxbuf, sizeof(uint8_t), RX_BUFSIZ);
}
static void rx_isr(void *user_data)
{
struct ser_ctx *ser = user_data;
if (uart_irq_update(ser->dev) &&
uart_irq_rx_ready(ser.dev) {
uint8_t c;
uart_fifo_read(ser->dev, &c, 1);
while (k_msgq_put(&ser->queue, &c, K_NO_WAIT) != 0) {
k_msgq_purge(&ser->queue);
}
}
}
int create(void)
{
ser.dev = device_get_binding(CONFIG_UART_STM32_USART_2_NAME);
if (!ser.dev) {
return -FNODEV;
}
ser.rxbuf = uart_rxbuf;
/* Setup serial */
uart_irq_rx_disable(ser.dev);
uart_irq_tx_disable(ser.dev);
flush();
uart_irq_callback_user_data_set(ser.dev, rx_isr, &ser);
uart_irq_rx_enable(ser.dev);
return 0;
}
int putc(uint8_t c)
{
return uart_poll_out(ser.dev, c);
}
int getc(uint8_t *c)
{
return k_msgq_get(&ser.queue, c, K_FOREVER);
}
uint32_t count(void)
{
return k_msgq_num_used_get(&ser.queue);
}
@gon1332, what about try to upstream your example as part of samples application?
Seems like an interesting application example, and having it pushed would help getting more attention and best chances to solve the issue.
I don't know about your code, but you should try to make it the most generic possible so it can be run on most of platforms.
I think this is the best way to address #3564
@gon1332
The main problem [with subsys/console] that I have is that after some transmissions, the whole board seems to hang. This is usually solved by inserting a small delay on the getchar implementation. But it's an unsafe solution.
Do you have exact code and exact instructions how to reproduce that? If so, could you try them on some other board (preferrably, on non-STM32, to get good variation)? Then both all the above with your results?
My plan regarding subsys/console is to have interface similar to POSIX, i.e. what you could do e.g. on Linux, you would be able to do in a conceptually similar way using "tty" on Zephyr.
And the more I think of it, the more I come to conclusion that UART driver interface Zephyr offers is too low-level - it's very hard, maybe impossible, to write an ISR which would work with any UART device out there. And that's exactly what everyone is doing again and again with all those uart_pipe's and modem_receiver's (and adhoc samples too, yeah). I think that the solution would be to push ISR handling down the driver and expose higher-level interface for it.
@erwango
Off course, but I work on a custom board. Should I use the closest possible, eg a Nucleo one with a STM32L496G even if I haven't got one?
@pfalcon
Do you have exact code and exact instructions how to reproduce that? If so, could you try them on some other board (preferrably, on non-STM32, to get good variation)? Then both all the above with your results?
Unfortunately I do not own other boards. I own 2 boards with STM32L496RE but both of them behave the same. Obviously, I ported this MCU in Zephyr (something I will give to the community some time soon) and all of its peripherals as recommended.
My plan regarding subsys/console is to have interface similar to POSIX, i.e. what you could do e.g. on Linux, you would be able to do in a conceptually similar way using "tty" on Zephyr.
I use it indirectly with printk and CONFIG_UART_CONSOLE and CONFIG_CONSOLE enabled on my board defconfig.
Maybe it's something stm32 specific that I should take care off when I am writing my driver for the module.
And the more I think of it, the more I come to conclusion that UART driver interface Zephyr offers is too low-level..
Yes, I think so, I've seen to many different implementations of the UART drivers. I've implemented myself an UART API for my board and it worked up to now relatively good (with work-arounds, time delays etc), but it is not OK. I use k_pipe for ISR-thread communication or uart_fifo_fill ouside of ISRs, which are promoted by current driver implementations, contrary to the docs
..and here I am..
Maybe it is something STM32 specific that needs more special handling in a UART's driver implementation.
uart_fifo_fill ouside of ISRs, which are promoted by current driver implementations, contrary to the docs
Well, those docs were written by me, based on experience on trying to implement console/subsys on (wide enough) different hardware. But while I have more than one type of board, I don't have all of them still, so something might not work somewhere (and I don't test them all all the time either, so there can be regressions too :-( ).
Btw, I posted https://github.com/zephyrproject-rtos/zephyr/pull/10761 which contain further clarifications to the UART docs, it hopefully can give more hints where all those requirements come from - from wildly varying way of acknowledging interrupts on different hw. So, uart_fifo_fill must be used in ISR, or there's chance of infinite loop due to non-acked IRQ. And then, it doesn't make sense to use it outside ISR, that's why its behavior is stated as "undefined" in this case. On specific hardware, it may have its specific use, but not in general case (at the very least, it may conflict with legitimate use inside ISR).
@gon1332 : Summing up, there doesn't seem to be "the best way" to use UART if fully portable, generic way in Zephyr, unfortunately. The current driver API was designed with leaking hardware specificity, and apparently with the idea of writing hw-specific apps indeed. So yep, that's what people may be reduced to - deal with peculiarities of specific hw and handle them in adhoc way.
That's not good, and there's ideas to resolve that, as was described. But if you need to do something now, what you do sounds like an expected way to do it for now...
One thing I'm also curious about is the fact that the STM uart driver doesn't account for errors.
I was able to induce a ORE error (even with a call to uart_irq_err_disable) by blasting data at the uart before the rx_irq was enabled. This caused the ORE bit to get set, causing infinite IRQ's to be fired for that uart until I cleared the ORE bit.
How are we supposed to account for these types of errors besides being a bit hackish outside of the driver?
Related to my last question, I just saw this PR: https://github.com/zephyrproject-rtos/zephyr/pull/11039
@gon1332 Were you perhaps running into this?
One thing I'm also curious about is the fact that the STM uart driver doesn't account for errors.
I bet, not only it. Each such case should be submitted as an individual ticket, assigned to a SoC maintainer, and being worked on.
@gon1332 , did you get sufficient answers to your question?
If yes, can you close the point?
@erwango The point is clarified.