-
Notifications
You must be signed in to change notification settings - Fork 234
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
Basic GPIO support #24
Conversation
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.
Hi,
thanks for picking this up. Your implementation looks good to me. The only major thing I would change is to move the ownership of the SIO and pads peripherals also into the driver. What do you think about that?
I might have overlooked something with the macro as I am not yet really used to their syntax. 😄
@@ -13,4 +13,4 @@ license = "MIT OR Apache-2.0" | |||
cortex-m = "0.7.1" | |||
embedded-hal = "0.2.4" | |||
nb = "1.0.0" | |||
rp2040-pac = "0.1.1" | |||
rp2040-pac = { git = "https://github.com/rp-rs/rp2040-pac", branch="main" } |
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.
I think this might cause problems when we add this to crates.io. Do you need something specific from the main branch? Maybe we should ask them for a new release in that case.
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.
Yes we'll want a release of that crate again before this one.
Between the crates.io version of the PAC and the main branch, GPIOs seem to have been rearranged quite a bit.
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.
Indeed, a new version would be needed for the PAC; commit rp-rs/rp2040-pac@56198b7 was a breaking change to how GPIOs are handled but it never made it into a release. That commit also breaks the pico-blink-rs
example based on the PAC, but I see a PR has been submitted to fix that.
pub trait GpioExt { | ||
type Parts; | ||
|
||
// TODO: Do we need a marker to check that clocks are up? |
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.
Yes I think a marker or token would be good. But I would keep it this way until the clock modules are implemented.
Sorry this is a large commit :( This adds support for input pins, including pulling them high or low. It also adds two examples: the start of a classic blinky LED example, and an example for reading input.
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.
Great to see so much progress. The implementation looks quite clean and complete to me. I only added some more minor points I noticed.
The only major point would be to use const generics instead of a macro. But I can't find a good example of another HAL already doing that. So if you want to keep the macro feel free to do so we can always come back to this in another iteration.
// const FUNCTION_PIO0: u8 = 6; | ||
// const FUNCTION_PIO1: u8 = 7; | ||
// const FUNCTION_CLOCK: u8 = 8; | ||
// const FUNCTION_USB: u8 = 9; | ||
|
||
macro_rules! gpio { |
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.
After a good nights sleep I think we can solve this without a macro by using const generics.
Instead of types for Gpio0
, Gpio1
etc. there would be just one type Gpio<const N: u8>
. This could make the docs easier and less redundant. And might also make it easier for some editors because the macro does not need to be expanded.
But we can also change it in a future iteration. I actually did not yet try const generics in the wild so I do not know what problems might show up.
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.
Yes I think for this first round it might be good to stick with what the other HALs are doing, though eliminating the macro would be ideal. The macro makes it way harder to understand what is going on.
rp2040-hal/src/gpio.rs
Outdated
self, | ||
) -> $PXi<Output> { | ||
unsafe { | ||
setup_pad_io(&*pac::$PADSX::ptr(), $i); |
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.
I would move the dereferences into their own helper methods. That would move all the unsafe
to one area. In turn all the functions using them would become easier to read.
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.
So I'm relatively new to unsafe
. To me the reason I didn't do that was because I basically didn't want the de-reference to escape the unsafe
block, since it would be unsafe if we were to use it outside of this context.
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.
Oh I see, yes we need to make sure that the reference can't escape. We can use lifetimes to achieve that.
fn pads<'a>(&'a self) -> &'a pac::pads_bank0::RegisterBlock {
unsafe {&*pac::$PADSX::ptr()}
}
Which ensures that the reference to the register block can at most live as long as the Gpio$i
.
To make it simpler we can use lifetime elision which happens whenever you take one reference as parameter and return one reference from the function:
fn pads(&self) -> &pac::pads_bank0::RegisterBlock {
unsafe {&*pac::$PADSX::ptr()}
}
Now that I think about it, it might be better to use &mut
since we modify the register.
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.
Strangely the registers themselves don't seem to require mut
to write to them. I wonder if that means we should follow the same pattern.
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.
Oh yes, that is weird. I would tend to take &mut
but I'm not sure now. The OutputPin
trait for example takes &mut self
.
Co-authored-by: tdittr <tdittr@users.noreply.github.com>
We're technically supposed to wait for these resets to finish before poking at registers. This seems to fix the instability I was seeing on the input example especially (TBH I have no idea how it ever worked)
This moves almost all of the unsafe stuff together. The last remaining bit is going to need a PAC fix.
Co-authored-by: tdittr <tdittr@users.noreply.github.com>
@@ -1,3 +1,11 @@ | |||
[build] | |||
# Instruction set of Cortex-M0+ | |||
target = "thumbv6m-none-eabi" | |||
|
|||
[target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
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.
Why we need those flags?
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.
They seem to be the magic incantations needed to get the embedded Rust stuff to build. I copied them from the sample project.
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.
link-arg=--nmagic
is needed if flash or ram is not aligned to 0x10000
link-arg=-Tlink.x"
is LLD linker (I think needed if you need to link C dependencies)
but i'm not sure why we need the last 2
A quick check on my machine showed that just `cargo check --workspace` didn't verify the examples. Quite silly.
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.
I think we should merge this as it is now and keep on iterating on the function over time :)
Agreed. I'll fix up the simple merge conflict and bring it in. |
CI failure here is because "cargo test" fails due to missing embedded-hal traits on the CI host. |
I don't have a lot of experience with embedded Rust, so I'm definitely looking for feedback. This design is mostly inspired by other HALs like the STM32 ones.
The example code I've included seems to be enough to get the on-board Pico LED to turn on or off. I have no idea if it's legit to bring GPIO out of reset before doing clock stuff, but the clock stuff is WIP in that other PR and I don't feel anywhere near smart enough to help with that 😅
Things that are still TODO for this PR, that I plan to tackle if people are happy with what I've got so far:
Things I'm considering out of scope for this PR:
Related to #4 and #19