-
Notifications
You must be signed in to change notification settings - Fork 69
GPIO features #129
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
Merged
Merged
GPIO features #129
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
c4e5a72
Add codegen for GPIO mappings
teskje 8376c90
Add new features for GPIO peripheral versions
teskje f484f6a
Use GPIO pin mappings based on GPIO version
teskje 24fdf8a
Fix most GPIO compilation errors
teskje 99cdd2f
Adjust to the new unsafe DMA register access methods
teskje 9c3b0c3
Revert previous find-replace accidents
teskje 4686000
Revert accidential Cargo.toml autoformat
teskje 127d876
Add codegen README
teskje 2cfe3f1
Update changelog
teskje 938bb36
Build codegen with x86_64-unknown-linux-gnu by default
teskje 4e9236d
codegen: Emit 'auto-generated' comment
teskje add481d
codegen: Describe how 'gpio-' features are generated
teskje d3dfa2e
Update PAC to version 0.12
teskje 4d8ea24
Fix serial module broken during merging
teskje c9a0c7e
rtc: Remove `unsafe`s not needed after PAC update
teskje 9b89122
codegen: Replace clap with structopt
teskje File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[build] | ||
target = "x86_64-unknown-linux-gnu" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/target/ | ||
**/*.rs.bk |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "codegen" | ||
version = "0.1.0" | ||
authors = ["Jan Teske <[email protected]>"] | ||
edition = "2018" | ||
|
||
[dependencies] | ||
anyhow = "1" | ||
once_cell = "1" | ||
regex = "1" | ||
serde-xml-rs = "0.4" | ||
|
||
[dependencies.structopt] | ||
version = "0.3" | ||
default-features = false | ||
|
||
[dependencies.serde] | ||
version = "1" | ||
features = ["derive"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Codegen | ||
|
||
This crate provides code-generation for the stm32f3xx-hal. It reads information | ||
from an [STM32CubeMX](https://www.st.com/en/development-tools/stm32cubemx.html) | ||
database and uses that to output code that can directly be included into the | ||
source code of the stm32f3xx-hal crate. | ||
|
||
For more information on how the STM32CubeMX database is structured, check out | ||
the README in the [cube-parse](https://github.com/dbrgn/cube-parse) repository. | ||
|
||
Because by default cargo tries to use the `x86_64-unknown-linux-gnu` target, | ||
when building `codegen`, due to what's specified in the `.cargo/config`, you | ||
need to manually specify your host's target if it differs from that, e.g.: | ||
|
||
``` | ||
$ cargo run --target x86_64-apple-darwin -- help | ||
``` | ||
|
||
`codgen` can generate the following code: | ||
|
||
- [GPIO mappings](#gpio-mappings) | ||
|
||
## GPIO mappings | ||
|
||
Running `codegen`'s `gpio` subcommand generates the `gpio!` macro | ||
invocations at the end of `src/gpio.rs`. Re-generating those macro-invocations | ||
is simply a matter of deleting the old ones and then executing: | ||
|
||
``` | ||
$ cargo run -- gpio $cubemx_db_path >> ../src/gpio.rs | ||
``` | ||
|
||
`$cubemx_db_path` must be the path to the `db/` directory under an | ||
STM32CubeMX installation. With a default Linux install, this would be | ||
`/opt/stm32cubemx/db`. | ||
|
||
The generated `gpio!` invocations are gated by features whose names are derived | ||
from the respective GPIO internal peripheral (IP) version: | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- gpio-f302 | ||
- gpio-f303 | ||
- gpio-f303e | ||
- gpio-f333 | ||
- gpio-f373 | ||
|
||
`codegen` collects those IP versions from the relevant GPIO IP description | ||
files (located at `$cubemx_db_path/mcu/IP/GPIO-*.xml`). The root `<IP>` element | ||
has a `Version` attribute with a value in the form of "STM32Fxxx_gpio_v1_0". | ||
The feature name is constructed by dropping the parts constant between all | ||
version strings and prepending "gpio-". | ||
|
||
Note that the GPIO IP version names don't necessarily match the MCUs they are | ||
used in. For example, the GPIOs in `STM32F302xB` MCUs have the IP version | ||
"STM32F303_gpio_v1_0". The MCU features of the `stm32f3xx-hal` also select the | ||
correct `gpio-*` features, so users generally don't have to care about these | ||
details. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
use crate::cubemx::ip::gpio; | ||
use anyhow::{Context, Result}; | ||
use once_cell::sync::Lazy; | ||
use regex::Regex; | ||
use std::collections::HashMap; | ||
|
||
struct Port<'a> { | ||
id: char, | ||
pins: Vec<&'a gpio::Pin>, | ||
} | ||
|
||
pub fn gen_mappings(gpio_ips: &[gpio::Ip]) -> Result<()> { | ||
for ip in gpio_ips.iter() { | ||
println!(); | ||
gen_gpio_ip(ip)?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn gen_gpio_ip(ip: &gpio::Ip) -> Result<()> { | ||
let feature = ip_version_to_feature(&ip.version)?; | ||
let ports = merge_pins_by_port(&ip.pins)?; | ||
|
||
println!(r#"#[cfg(feature = "{}")]"#, feature); | ||
gen_gpio_macro_call(&ports, &feature)?; | ||
Ok(()) | ||
} | ||
|
||
fn ip_version_to_feature(ip_version: &str) -> Result<String> { | ||
static VERSION: Lazy<Regex> = | ||
Lazy::new(|| Regex::new(r"^STM32(?P<version>\w+)_gpio_v1_0$").unwrap()); | ||
|
||
let captures = VERSION | ||
.captures(&ip_version) | ||
.with_context(|| format!("invalid GPIO IP version: {}", ip_version))?; | ||
|
||
let version = captures.name("version").unwrap().as_str(); | ||
let feature = format!("gpio-{}", version.to_lowercase()); | ||
Ok(feature) | ||
} | ||
|
||
fn merge_pins_by_port(pins: &[gpio::Pin]) -> Result<Vec<Port>> { | ||
let mut pins_by_port = HashMap::new(); | ||
for pin in pins.iter() { | ||
pins_by_port | ||
.entry(pin.port()?) | ||
.and_modify(|e: &mut Vec<_>| e.push(pin)) | ||
.or_insert_with(|| vec![pin]); | ||
} | ||
|
||
let mut ports = Vec::new(); | ||
for (id, mut pins) in pins_by_port { | ||
pins.sort_by_key(|p| p.number().unwrap_or_default()); | ||
pins.dedup_by_key(|p| p.number().unwrap_or_default()); | ||
ports.push(Port { id, pins }); | ||
} | ||
ports.sort_by_key(|p| p.id); | ||
|
||
Ok(ports) | ||
} | ||
|
||
fn gen_gpio_macro_call(ports: &[Port], feature: &str) -> Result<()> { | ||
println!("gpio!(["); | ||
for port in ports { | ||
gen_port(port, feature)?; | ||
} | ||
println!("]);"); | ||
Ok(()) | ||
} | ||
|
||
fn gen_port(port: &Port, feature: &str) -> Result<()> { | ||
let pac_module = get_port_pac_module(port, feature); | ||
|
||
println!(" {{"); | ||
println!( | ||
" port: ({}/{}, pac: {}),", | ||
port.id, | ||
port.id.to_lowercase(), | ||
pac_module, | ||
); | ||
println!(" pins: ["); | ||
|
||
for pin in &port.pins { | ||
gen_pin(pin)?; | ||
} | ||
|
||
println!(" ],"); | ||
println!(" }},"); | ||
Ok(()) | ||
} | ||
|
||
fn get_port_pac_module(port: &Port, feature: &str) -> &'static str { | ||
// The registers in ports A and B have different reset values due to the | ||
// presence of debug pins, so they get dedicated PAC modules. | ||
match port.id { | ||
'A' => "gpioa", | ||
'B' => "gpiob", | ||
'D' if feature == "gpio-f373" => "gpiod", | ||
_ => "gpioc", | ||
} | ||
} | ||
|
||
fn gen_pin(pin: &gpio::Pin) -> Result<()> { | ||
let nr = pin.number()?; | ||
let reset_mode = get_pin_reset_mode(pin)?; | ||
let afr = if nr < 8 { 'L' } else { 'H' }; | ||
let af_numbers = get_pin_af_numbers(pin)?; | ||
|
||
println!( | ||
" {} => {{ reset: {}, afr: {}/{}, af: {:?} }},", | ||
nr, | ||
reset_mode, | ||
afr, | ||
afr.to_lowercase(), | ||
af_numbers, | ||
); | ||
|
||
Ok(()) | ||
} | ||
|
||
fn get_pin_reset_mode(pin: &gpio::Pin) -> Result<&'static str> { | ||
// Debug pins default to their debug function (AF0), everything else | ||
// defaults to floating input. | ||
let mode = match (pin.port()?, pin.number()?) { | ||
('A', 13) | ('A', 14) | ('A', 15) | ('B', 3) | ('B', 4) => "AF0", | ||
_ => "Input<Floating>", | ||
}; | ||
Ok(mode) | ||
} | ||
|
||
fn get_pin_af_numbers(pin: &gpio::Pin) -> Result<Vec<u8>> { | ||
let mut numbers = Vec::new(); | ||
for signal in &pin.pin_signals { | ||
numbers.push(signal.af()?); | ||
} | ||
|
||
numbers.sort(); | ||
numbers.dedup(); | ||
|
||
Ok(numbers) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
pub mod gpio; | ||
|
||
use crate::cubemx::package::Package; | ||
|
||
pub fn gen_autogen_comment(package: &Package) { | ||
println!("// auto-generated using codegen"); | ||
println!( | ||
"// STM32CubeMX DB release: {}", | ||
package.pack_description.release | ||
); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
use anyhow::{Context, Result}; | ||
use serde::Deserialize; | ||
use std::{ | ||
fs::File, | ||
path::{Path, PathBuf}, | ||
}; | ||
|
||
pub struct Db { | ||
root: PathBuf, | ||
} | ||
|
||
impl Db { | ||
pub fn new<P: Into<PathBuf>>(root: P) -> Self { | ||
Self { root: root.into() } | ||
} | ||
|
||
pub fn load<'de, P: AsRef<Path>, T: Deserialize<'de>>(&self, name: P) -> Result<T> { | ||
let name = name.as_ref(); | ||
let mut path = self.root.join(name); | ||
path.set_extension("xml"); | ||
|
||
let file = File::open(&path).with_context(|| format!("cannot open DB file: {:?}", path))?; | ||
serde_xml_rs::de::from_reader(file) | ||
.with_context(|| format!("cannot parse DB file: {:?}", path)) | ||
} | ||
|
||
pub fn load_mcu<'de, P: AsRef<Path>, T: Deserialize<'de>>(&self, name: P) -> Result<T> { | ||
let mut mcu_path = PathBuf::new(); | ||
mcu_path.push("mcu"); | ||
mcu_path.push(name); | ||
self.load(&mcu_path) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.