-
Notifications
You must be signed in to change notification settings - Fork 252
Fix toggle() for some old microcontrollers #642
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
Conversation
|
As I had mentioned in #501 (comment), the current code is correct for most AVR MCUs. But it seems ATmega16 does not support this? I think we'll have to special-case the toggle code in some way for MCUs that don't support atomic pin toggling then... Unfortunately, it gets even more complex because the code you suggested is not atomically toggling the pin either. So for ATmega16, the register access would additionally need to be wrapped in a critical section. |
|
Yes, ATmega16 does not support toggling the output with the PIN register. The PIN register is readonly (see here, p.66). At least ATmega8 (datasheet, p.65) and ATmega32A (datasheet, p.70) do not support this feature either. Maybe there are some more MCUs that do not support his. I did not read all datasheets. What is your idea for handling the special cases? A new parameter for the Can you explain the atomic issue in more detail? As far as I know I cannot have multiple pin references at the same time? So it shouldn't be a problem, right? Am I missing something? In addition there are other occurrences of |
Yes, something like this. Whatever way requires the least macro trickery to jump through. One idea that comes to mind: A boolean literal macro variable which is then checked in a static if condition. This gets optimized away and should be doable without too much macro vodoo. #[inline]
unsafe fn out_toggle(&mut self) {
// +-- this macro variable needs to be defined somehwere in the macro
// | pattern as an `:literal` and then filled with `true` or `false`
// | during macro calls.
// v
if $chip_supports_atomic_toggle {
(*<$port>::ptr()).[<pin $name:lower>].write(|w| {
w.[<p $name:lower $pin>]().set_bit()
})
} else {
// TODO: alternative impl goes here
}
}
The problems are present when two tasks try to access two pins in the same bank (=same registers) concurrently. Mostly, this comes up with interactions with interrupt routines. All pin operations must be written in a way where concurrent accesses cannot lead to inconsistent state. There are two ways to do this:
Right now, most of the pin operations leverage the first option.
This is where things get really tricky: The compiler is able to optimize some The reason your You may be able to use the if self.out_get() {
self.out_clear();
} else {
self.out_set();
}but you need to verify that this actually lowers to atomic register accesses and it should be evaluated whether this leads to less instructions than just wrapping your If you have questions, let me know. |
|
Thanks very much for your detailed explanations. That is very helpful. I will try to implement your suggestions and update this pull request soon. |
The if else solution needs one more instruction than the critical section solution. So I used the critical section here. |
I think this is unfortunately not a reliable indicator. To give two examples, here is an excerpt from the ATmega328P datasheet, stating that it does support the feature: And this is from the ATmega168 datasheet: I think the only way to figure this out is to check each datasheet for a similar passage :( I would assume that most chips do support this though, only that one very old family seems not to. |
|
You are right. The following microcontrollers do indeed support this feature: atmega48p, atmega88p, atmega168, atmega328p, attiny88 |
Rahix
left a comment
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.
Very nice, thanks a lot for fixing this!


Some microcontrollers do not support toggling the output via the PIN register. On these microcontrollers the PIN register is readonly and toggling is done by reading and modifying the PORT register.
I tested this on an ATMega16.
Related to #501.