-
Notifications
You must be signed in to change notification settings - Fork 2.1k
cpu/efm32/uart: uart_init(): begin with TX pin at idle level #12380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cpu/efm32/uart: uart_init(): begin with TX pin at idle level #12380
Conversation
else TX will be asserted low for a few usec until the peripheral is initialized and deasserts it
I wonder: what happens when you don't call In that case the change looks good and the oscilloscope trace shows it fixes a bug. |
I do think efm32 is different here but I share your curiosity and I've already forgotten what I found when I looked in to that so I'll look in to it again. |
I have checked the EMDRV library as a reference, and it does something similar (this method is invoked from both The only thing it that we now have something that calls a RIOT-OS API with a magic (unsupported) option. |
@@ -68,7 +68,7 @@ int uart_init(uart_t dev, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) | |||
|
|||
/* initialize the pins */ | |||
gpio_init(uart_config[dev].rx_pin, GPIO_IN_PU); | |||
gpio_init(uart_config[dev].tx_pin, GPIO_OUT); | |||
gpio_init(uart_config[dev].tx_pin, GPIO_OUT | 1); /* 1 for high */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be an option to invoke gpio_set(uart_config[dev].tx_pin)
? That will also set the pin, and we're not using an undocumented option.
In the I2c driver, I do something similar (but for a different reason).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we call gpio_set()
first, it gets overridden by gpio_init()
. And if we call it after, we still get an unwanted TX assertion even though the duration is much shorter.
I agree 100%. Do you think we can allow it for now and use it to exemplify the problem? Basically efm32 sets/clears the output during |
Since you already Possibly |
That still causes an assertion on the order of 1.5 usec which is surprisingly large. It's still enough to look like a start bit and cause a garbage character 0xff at 115200 baud. But even if it were much smaller I still wouldn't like it as a solution from a hardware-pedantic point of view. |
Another option is to replace the RIOT-OS API to initialize GPIO and use the EMLIB API directly. |
Since you are using them anyway to initialize the UART, that would be fine too. But if that's more than one function call, better do something like |
I agree. But I'm not sure if extending the options is the best idea. Since @haukepetersen wrote most of |
@benpicco @benemorius I have patched the Gecko SDK API to expose an additional function that lets us decide if we want to change the pin state when setting the mode. That change is here. It's a compatible change, because it also provides the 'old' I also tested this very briefly in a separate branch. @benemorius I think this should fix the issue. Can you give it a try? If so, then I will make it proper. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is internal to efm32, using platform specific extensions to the GPIO API should be permissible.
@benemorius were you able to check if the alternative branch works for you? If yes, than I will update Gecko SDK and provide that additional function. |
@basilfx I checked just now on tradfri after removing my patch and it works for me. At first I got |
Thanks for testing, I'll ensure that this new function will be in the next Gecko SDK update! |
Contribution description
This corrects an unintentional momentary assertion of the UART TX pin during
uart_init()
which fixes some edge cases that are sensitive to that.Currently when
uart_init()
callsgpio_init()
the TX pin is set to output low. It remains low until some (configuration-dependent) time later when the UART hardware initializes it to the idle (high) level. In a lot of cases this hasn't caused a problem but of course it's wrong behavior.Testing procedure
Reproducing the bug may be deceptively nontrivial without simply checking for it on a scope.
The specific case that triggered an issue for me was using LPUART at 9600 baud connected to a peer that has a pullup on its RX with an application that calls
uart_write()
very soon afteruart_init()
(hello-world
does this).The LPUART takes a while to initialize, so TX is asserted for about 200 usec which is significant and would normally be interpreted as a single garbage character. However since
uart_write()
is called so soon after this, the legitimate characters are also misinterpreted until the bus goes idle long enough for the receiver to resynchronize on the start bit of a clean character. Forhello-world
this means that ALL characters are received as garbage since there is no such idle period until the end of the output.The green trace (2) is with this PR and looks normal. The orange trace (R1) is without this PR and is a perfect match to the green trace except for the extra pulse just before the legitimate characters begin. Despite the otherwise perfect match, all characters appeared in my terminal as garbage because of the extra pulse.
This took quite some effort to find since it's very easy to accidentally mask the buggy behavior and that's why I've ended up with such a detailed explanation for a small and obvious change.