mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-30 13:50:38 +00:00
Cleanup esp-metadata (#3686)
* Clean up esp-metadata a bit * Update esp-metadata/src/cfg/gpio.rs Co-authored-by: Juraj Sadel <jurajsadel@gmail.com> * Document public API, arrange structs better --------- Co-authored-by: Juraj Sadel <jurajsadel@gmail.com>
This commit is contained in:
parent
9d452a108f
commit
101de4eab8
@ -30,7 +30,7 @@
|
|||||||
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
||||||
//! `gpio` peripheral to access the appropriate registers.
|
//! `gpio` peripheral to access the appropriate registers.
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_generated_gpio_extras.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_generated_iomux_signals.rs"));
|
||||||
|
|
||||||
macro_rules! rtcio_analog {
|
macro_rules! rtcio_analog {
|
||||||
(
|
(
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
||||||
//! `gpio` peripheral to access the appropriate registers.
|
//! `gpio` peripheral to access the appropriate registers.
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_generated_gpio_extras.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_generated_iomux_signals.rs"));
|
||||||
|
|
||||||
macro_rules! rtc_pins {
|
macro_rules! rtc_pins {
|
||||||
( $( $pin_num:expr )+ ) => {
|
( $( $pin_num:expr )+ ) => {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
||||||
//! `gpio` peripheral to access the appropriate registers.
|
//! `gpio` peripheral to access the appropriate registers.
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_generated_gpio_extras.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_generated_iomux_signals.rs"));
|
||||||
|
|
||||||
macro_rules! rtc_pins {
|
macro_rules! rtc_pins {
|
||||||
( $( $pin_num:expr )+ ) => {
|
( $( $pin_num:expr )+ ) => {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
||||||
//! `gpio` peripheral to access the appropriate registers.
|
//! `gpio` peripheral to access the appropriate registers.
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_generated_gpio_extras.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_generated_iomux_signals.rs"));
|
||||||
|
|
||||||
crate::gpio::lp_io::lp_gpio! {
|
crate::gpio::lp_io::lp_gpio! {
|
||||||
0
|
0
|
||||||
|
@ -27,4 +27,4 @@
|
|||||||
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
||||||
//! `gpio` peripheral to access the appropriate registers.
|
//! `gpio` peripheral to access the appropriate registers.
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_generated_gpio_extras.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_generated_iomux_signals.rs"));
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
||||||
//! `gpio` peripheral to access the appropriate registers.
|
//! `gpio` peripheral to access the appropriate registers.
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_generated_gpio_extras.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_generated_iomux_signals.rs"));
|
||||||
|
|
||||||
macro_rules! rtcio_analog {
|
macro_rules! rtcio_analog {
|
||||||
($pin_num:expr, $pin_reg:expr, $hold:ident) => {
|
($pin_num:expr, $pin_reg:expr, $hold:ident) => {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
//! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the
|
||||||
//! `gpio` peripheral to access the appropriate registers.
|
//! `gpio` peripheral to access the appropriate registers.
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_generated_gpio_extras.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_generated_iomux_signals.rs"));
|
||||||
|
|
||||||
macro_rules! rtcio_analog {
|
macro_rules! rtcio_analog {
|
||||||
($pin_num:expr, $pin_reg:expr, $hold:ident) => {
|
($pin_num:expr, $pin_reg:expr, $hold:ident) => {
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
pub(crate) mod gpio;
|
||||||
|
pub(crate) mod i2c_master;
|
||||||
|
|
||||||
|
pub(crate) use gpio::*;
|
||||||
|
pub(crate) use i2c_master::*;
|
||||||
|
|
||||||
/// Represents a value in the driver configuration.
|
/// Represents a value in the driver configuration.
|
||||||
pub(crate) enum Value {
|
pub(crate) enum Value {
|
||||||
Unset,
|
Unset,
|
||||||
@ -17,6 +23,10 @@ impl From<Option<u32>> for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The support status of a given peripheral driver.
|
||||||
|
///
|
||||||
|
/// This is defined in device metadata, and is used to generate the peripheral
|
||||||
|
/// support table.
|
||||||
#[derive(Debug, Default, Clone, Copy, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Default, Clone, Copy, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub(crate) enum SupportStatus {
|
pub(crate) enum SupportStatus {
|
||||||
@ -49,82 +59,6 @@ impl SupportStatus {
|
|||||||
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct EmptyInstanceConfig {}
|
pub(crate) struct EmptyInstanceConfig {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub(crate) struct I2cMasterInstanceConfig {
|
|
||||||
pub sys_instance: String,
|
|
||||||
pub scl: String,
|
|
||||||
pub sda: String,
|
|
||||||
pub interrupt: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub(crate) enum PinCapability {
|
|
||||||
Input,
|
|
||||||
Output,
|
|
||||||
Analog,
|
|
||||||
Rtc,
|
|
||||||
Touch,
|
|
||||||
UsbDm,
|
|
||||||
UsbDp,
|
|
||||||
// Pin has USB pullup according to the IO MUX Function list
|
|
||||||
UsbDevice,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub(crate) struct AfMap {
|
|
||||||
#[serde(rename = "0")]
|
|
||||||
af0: Option<String>,
|
|
||||||
#[serde(rename = "1")]
|
|
||||||
af1: Option<String>,
|
|
||||||
#[serde(rename = "2")]
|
|
||||||
af2: Option<String>,
|
|
||||||
#[serde(rename = "3")]
|
|
||||||
af3: Option<String>,
|
|
||||||
#[serde(rename = "4")]
|
|
||||||
af4: Option<String>,
|
|
||||||
#[serde(rename = "5")]
|
|
||||||
af5: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AfMap {
|
|
||||||
pub fn get(&self, af: usize) -> Option<&str> {
|
|
||||||
match af {
|
|
||||||
0 => self.af0.as_deref(),
|
|
||||||
1 => self.af1.as_deref(),
|
|
||||||
2 => self.af2.as_deref(),
|
|
||||||
3 => self.af3.as_deref(),
|
|
||||||
4 => self.af4.as_deref(),
|
|
||||||
5 => self.af5.as_deref(),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub(crate) struct PinConfig {
|
|
||||||
/// The GPIO pin number.
|
|
||||||
pub pin: usize,
|
|
||||||
pub kind: Vec<PinCapability>,
|
|
||||||
// Pin => Input/OutputSignal
|
|
||||||
#[serde(default)]
|
|
||||||
pub alternate_functions: AfMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub(crate) struct IoMuxSignal {
|
|
||||||
pub name: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub id: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub(crate) struct GpioPinsAndSignals {
|
|
||||||
pub pins: Vec<PinConfig>,
|
|
||||||
pub input_signals: Vec<IoMuxSignal>,
|
|
||||||
pub output_signals: Vec<IoMuxSignal>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A peripheral instance for which a driver is implemented.
|
/// A peripheral instance for which a driver is implemented.
|
||||||
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub(crate) struct PeriInstance<I = EmptyInstanceConfig> {
|
pub(crate) struct PeriInstance<I = EmptyInstanceConfig> {
|
||||||
@ -134,9 +68,17 @@ pub(crate) struct PeriInstance<I = EmptyInstanceConfig> {
|
|||||||
pub instance_config: I,
|
pub instance_config: I,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single cell in the peripheral support table.
|
||||||
pub(crate) struct SupportItem {
|
pub(crate) struct SupportItem {
|
||||||
|
/// The human-readable name of the driver in the table (leftmost cell.)
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
|
/// The ID of the driver ([device.<config_group>]) in the TOML, that this
|
||||||
|
/// item corresponds to.
|
||||||
pub config_group: &'static str,
|
pub config_group: &'static str,
|
||||||
|
/// When the driver's configuration is not present in the device's TOML,
|
||||||
|
/// these symbols decide whether to generate a Not Available or a Not
|
||||||
|
/// Supported cell. If the device has one of these symbols, the support
|
||||||
|
/// status will be Not Supported (i.e. yet to be implemented).
|
||||||
pub symbols: &'static [&'static str],
|
pub symbols: &'static [&'static str],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
380
esp-metadata/src/cfg/gpio.rs
Normal file
380
esp-metadata/src/cfg/gpio.rs
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
//! This module contains configuration used in [device.gpio], as well as
|
||||||
|
//! functions that generate code for esp-hal.
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::format_ident;
|
||||||
|
|
||||||
|
use crate::number;
|
||||||
|
|
||||||
|
/// Additional properties (besides those defined in cfg.rs) for [device.gpio].
|
||||||
|
/// These don't get turned into symbols, but are used to generate code.
|
||||||
|
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub(crate) struct GpioPinsAndSignals {
|
||||||
|
/// The list of GPIO pins and their properties.
|
||||||
|
pub pins: Vec<PinConfig>,
|
||||||
|
|
||||||
|
/// The list of peripheral input signals.
|
||||||
|
pub input_signals: Vec<IoMuxSignal>,
|
||||||
|
|
||||||
|
/// The list of peripheral output signals.
|
||||||
|
pub output_signals: Vec<IoMuxSignal>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Properties of a single GPIO pin.
|
||||||
|
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub(crate) struct PinConfig {
|
||||||
|
/// The GPIO pin number.
|
||||||
|
pub pin: usize,
|
||||||
|
|
||||||
|
/// Different capabilities implemented by this pin.
|
||||||
|
pub kind: Vec<PinCapability>,
|
||||||
|
|
||||||
|
/// Available alternate functions for this pin.
|
||||||
|
#[serde(default)]
|
||||||
|
pub alternate_functions: AfMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pin capabilities. Some of these will cause a trait to be implemented for the
|
||||||
|
/// given pin singleton. `UsbDevice` currently only changes what happens on GPIO
|
||||||
|
/// driver initialization.
|
||||||
|
#[derive(Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub(crate) enum PinCapability {
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
Analog,
|
||||||
|
Rtc,
|
||||||
|
Touch,
|
||||||
|
UsbDm,
|
||||||
|
UsbDp,
|
||||||
|
// Pin has USB pullup according to the IO MUX Function list
|
||||||
|
UsbDevice,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Available alternate functions for a given GPIO pin.
|
||||||
|
///
|
||||||
|
/// Alternate functions allow bypassing the GPIO matrix by selecting a different
|
||||||
|
/// path in the multiplexers controlled by MCU_SEL.
|
||||||
|
///
|
||||||
|
/// Values of this struct correspond to rows in the IO MUX Pad List table.
|
||||||
|
///
|
||||||
|
/// Used in [device.gpio.pins[X].alternate_functions]. The GPIO function is not
|
||||||
|
/// written here as that is common to all pins. The values are signal names
|
||||||
|
/// listed in [device.gpio.input_signals] or [device.gpio.output_signals].
|
||||||
|
/// `None` means the pin does not provide the given alternate function.
|
||||||
|
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub(crate) struct AfMap {
|
||||||
|
#[serde(rename = "0")]
|
||||||
|
af0: Option<String>,
|
||||||
|
#[serde(rename = "1")]
|
||||||
|
af1: Option<String>,
|
||||||
|
#[serde(rename = "2")]
|
||||||
|
af2: Option<String>,
|
||||||
|
#[serde(rename = "3")]
|
||||||
|
af3: Option<String>,
|
||||||
|
#[serde(rename = "4")]
|
||||||
|
af4: Option<String>,
|
||||||
|
#[serde(rename = "5")]
|
||||||
|
af5: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AfMap {
|
||||||
|
/// Returns the signal associated with the nth alternate function.
|
||||||
|
///
|
||||||
|
/// Note that not all alternate functions are defined. The number of the
|
||||||
|
/// GPIO function is available separately. Not all alternate function have
|
||||||
|
/// IO signals.
|
||||||
|
pub fn get(&self, af: usize) -> Option<&str> {
|
||||||
|
match af {
|
||||||
|
0 => self.af0.as_deref(),
|
||||||
|
1 => self.af1.as_deref(),
|
||||||
|
2 => self.af2.as_deref(),
|
||||||
|
3 => self.af3.as_deref(),
|
||||||
|
4 => self.af4.as_deref(),
|
||||||
|
5 => self.af5.as_deref(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An input or output peripheral signal. The names usually match the signal
|
||||||
|
/// name in the Peripheral Signal List table, without the `in` or `out` suffix.
|
||||||
|
/// If the `id` is `None`, the signal cannot be routed through the GPIO matrix.
|
||||||
|
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub(crate) struct IoMuxSignal {
|
||||||
|
/// The name of the signal.
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// The numeric ID of the signal, if the signal can be routed through the
|
||||||
|
/// GPIO matrix.
|
||||||
|
#[serde(default)]
|
||||||
|
pub id: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_gpios(gpio: &super::GpioProperties) -> TokenStream {
|
||||||
|
let pin_numbers = gpio
|
||||||
|
.pins_and_signals
|
||||||
|
.pins
|
||||||
|
.iter()
|
||||||
|
.map(|pin| number(pin.pin))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let pin_peris = gpio
|
||||||
|
.pins_and_signals
|
||||||
|
.pins
|
||||||
|
.iter()
|
||||||
|
.map(|pin| format_ident!("GPIO{}", pin.pin))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let pin_attrs = gpio
|
||||||
|
.pins_and_signals
|
||||||
|
.pins
|
||||||
|
.iter()
|
||||||
|
.map(|pin| {
|
||||||
|
let mut attrs = vec![];
|
||||||
|
pin.kind.iter().for_each(|kind| match kind {
|
||||||
|
PinCapability::Input => attrs.push(quote::quote! { Input }),
|
||||||
|
PinCapability::Output => attrs.push(quote::quote! { Output }),
|
||||||
|
PinCapability::Analog => attrs.push(quote::quote! { Analog }),
|
||||||
|
PinCapability::Rtc => {
|
||||||
|
attrs.push(quote::quote! { RtcIo });
|
||||||
|
if pin.kind.contains(&PinCapability::Output) {
|
||||||
|
attrs.push(quote::quote! { RtcIoOutput });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PinCapability::Touch => attrs.push(quote::quote! { Touch }),
|
||||||
|
PinCapability::UsbDm => attrs.push(quote::quote! { UsbDm }),
|
||||||
|
PinCapability::UsbDp => attrs.push(quote::quote! { UsbDp }),
|
||||||
|
PinCapability::UsbDevice => attrs.push(quote::quote! { UsbDevice }),
|
||||||
|
});
|
||||||
|
|
||||||
|
attrs
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let pin_afs = gpio
|
||||||
|
.pins_and_signals
|
||||||
|
.pins
|
||||||
|
.iter()
|
||||||
|
.map(|pin| {
|
||||||
|
let mut input_afs = vec![];
|
||||||
|
let mut output_afs = vec![];
|
||||||
|
|
||||||
|
for af in 0..6 {
|
||||||
|
let Some(signal) = pin.alternate_functions.get(af) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let af_variant = quote::format_ident!("_{af}");
|
||||||
|
let mut found = false;
|
||||||
|
|
||||||
|
// Is the signal present among the input signals?
|
||||||
|
if let Some(signal) = gpio
|
||||||
|
.pins_and_signals
|
||||||
|
.input_signals
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.name == signal)
|
||||||
|
{
|
||||||
|
let signal_tokens = TokenStream::from_str(&signal.name).unwrap();
|
||||||
|
input_afs.push(quote::quote! { #af_variant => #signal_tokens });
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the signal present among the output signals?
|
||||||
|
if let Some(signal) = gpio
|
||||||
|
.pins_and_signals
|
||||||
|
.output_signals
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.name == signal)
|
||||||
|
{
|
||||||
|
let signal_tokens = TokenStream::from_str(&signal.name).unwrap();
|
||||||
|
output_afs.push(quote::quote! { #af_variant => #signal_tokens });
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
found,
|
||||||
|
"Signal '{signal}' not found in input signals for GPIO pin {}",
|
||||||
|
pin.pin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
( #(#input_afs)* ) ( #(#output_afs)* )
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let io_mux_accessor = if gpio.remap_iomux_pin_registers {
|
||||||
|
let iomux_pin_regs = gpio.pins_and_signals.pins.iter().map(|pin| {
|
||||||
|
let pin = number(pin.pin);
|
||||||
|
let reg = format_ident!("GPIO{pin}");
|
||||||
|
let accessor = format_ident!("gpio{pin}");
|
||||||
|
|
||||||
|
quote::quote! { #pin => transmute::<&'static io_mux::#reg, &'static io_mux::GPIO0>(iomux.#accessor()), }
|
||||||
|
});
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
pub(crate) fn io_mux_reg(gpio_num: u8) -> &'static crate::pac::io_mux::GPIO0 {
|
||||||
|
use core::mem::transmute;
|
||||||
|
|
||||||
|
use crate::{pac::io_mux, peripherals::IO_MUX};
|
||||||
|
|
||||||
|
let iomux = IO_MUX::regs();
|
||||||
|
unsafe {
|
||||||
|
match gpio_num {
|
||||||
|
#(#iomux_pin_regs)*
|
||||||
|
other => panic!("GPIO {} does not exist", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote::quote! {
|
||||||
|
pub(crate) fn io_mux_reg(gpio_num: u8) -> &'static crate::pac::io_mux::GPIO {
|
||||||
|
crate::peripherals::IO_MUX::regs().gpio(gpio_num as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut io_type_macro_calls = vec![];
|
||||||
|
for (pin, attrs) in pin_peris.iter().zip(pin_attrs.iter()) {
|
||||||
|
io_type_macro_calls.push(quote::quote! {
|
||||||
|
#( crate::io_type!(#attrs, #pin); )*
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a macro that can select between a `then` and an `else` branch based
|
||||||
|
// on whether a pin implement a certain attribute.
|
||||||
|
//
|
||||||
|
// In essence this expands to (in case of pin = GPIO5, attr = Analog):
|
||||||
|
// `if typeof(GPIO5) == Analog { then_tokens } else { else_tokens }`
|
||||||
|
let if_pin_is_type = {
|
||||||
|
let mut branches = vec![];
|
||||||
|
|
||||||
|
for (pin, attr) in pin_peris.iter().zip(pin_attrs.iter()) {
|
||||||
|
branches.push(quote::quote! {
|
||||||
|
#( (#pin, #attr, $then_tt:tt else $else_tt:tt ) => { $then_tt }; )*
|
||||||
|
});
|
||||||
|
|
||||||
|
branches.push(quote::quote! {
|
||||||
|
(#pin, $t:tt, $then_tt:tt else $else_tt:tt ) => { $else_tt };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
macro_rules! if_pin_is_type {
|
||||||
|
#(#branches)*
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use if_pin_is_type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delegates AnyPin functions to GPIOn functions when the pin implements a
|
||||||
|
// certain attribute.
|
||||||
|
//
|
||||||
|
// In essence this expands to (in case of attr = Analog):
|
||||||
|
// `if typeof(anypin's current value) == Analog { call $code } else { panic }`
|
||||||
|
let impl_for_pin_type = {
|
||||||
|
let mut impl_branches = vec![];
|
||||||
|
for (gpionum, peri) in pin_numbers.iter().zip(pin_peris.iter()) {
|
||||||
|
impl_branches.push(quote::quote! {
|
||||||
|
#gpionum => $crate::peripherals::if_pin_is_type!(#peri, $on_type, {{
|
||||||
|
#[allow(unused_unsafe, unused_mut)]
|
||||||
|
let mut $inner_ident = unsafe { $crate::peripherals::#peri::steal() };
|
||||||
|
#[allow(unused_braces)]
|
||||||
|
$code
|
||||||
|
}} else {
|
||||||
|
$otherwise
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
macro_rules! impl_for_pin_type {
|
||||||
|
($any_pin:ident, $inner_ident:ident, $on_type:tt, $code:tt else $otherwise:tt) => {
|
||||||
|
match $any_pin.number() {
|
||||||
|
#(#impl_branches)*
|
||||||
|
_ => $otherwise,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($any_pin:ident, $inner_ident:ident, $on_type:tt, $code:tt) => {
|
||||||
|
impl_for_pin_type!($any_pin, $inner_ident, $on_type, $code else { panic!("Unsupported") })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub(crate) use impl_for_pin_type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
crate::gpio! {
|
||||||
|
#( (#pin_numbers, #pin_peris #pin_afs) )*
|
||||||
|
}
|
||||||
|
|
||||||
|
#( #io_type_macro_calls )*
|
||||||
|
|
||||||
|
#if_pin_is_type
|
||||||
|
#impl_for_pin_type
|
||||||
|
|
||||||
|
#io_mux_accessor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_iomux_signals(gpio: &super::GpioProperties) -> TokenStream {
|
||||||
|
let input_signals = render_signals("InputSignal", &gpio.pins_and_signals.input_signals);
|
||||||
|
let output_signals = render_signals("OutputSignal", &gpio.pins_and_signals.output_signals);
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
#input_signals
|
||||||
|
#output_signals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_signals(enum_name: &str, signals: &[IoMuxSignal]) -> TokenStream {
|
||||||
|
if signals.is_empty() {
|
||||||
|
// If there are no signals, we don't need to generate an enum.
|
||||||
|
return quote::quote! {};
|
||||||
|
}
|
||||||
|
let mut variants = vec![];
|
||||||
|
|
||||||
|
for signal in signals {
|
||||||
|
// First, process only signals that have an ID.
|
||||||
|
let Some(id) = signal.id else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = format_ident!("{}", signal.name);
|
||||||
|
let value = number(id);
|
||||||
|
variants.push(quote::quote! {
|
||||||
|
#name = #value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for signal in signals {
|
||||||
|
// Now process signals that do not have an ID.
|
||||||
|
if signal.id.is_some() {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = format_ident!("{}", signal.name);
|
||||||
|
variants.push(quote::quote! {
|
||||||
|
#name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let enum_name = format_ident!("{enum_name}");
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub enum #enum_name {
|
||||||
|
#(#variants)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
esp-metadata/src/cfg/i2c_master.rs
Normal file
48
esp-metadata/src/cfg/i2c_master.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::format_ident;
|
||||||
|
|
||||||
|
use crate::{cfg::I2cMasterProperties, generate_for_each_macro};
|
||||||
|
|
||||||
|
/// Instance configuration, used in [device.i2c_master.instances]
|
||||||
|
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub(crate) struct I2cMasterInstanceConfig {
|
||||||
|
/// The name of the instance in the `esp_hal::system::Peripheral` enum
|
||||||
|
pub sys_instance: String,
|
||||||
|
|
||||||
|
/// IOMUX signal name of the instane's SCL signal.
|
||||||
|
pub scl: String,
|
||||||
|
|
||||||
|
/// IOMUX signal name of the instane's SDA signal.
|
||||||
|
pub sda: String,
|
||||||
|
|
||||||
|
/// The name of the instance's interrupt handler.
|
||||||
|
pub interrupt: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates `for_each_i2c_master!` which can be used to implement the I2C
|
||||||
|
/// master Instance trait for the relevant peripherals. The macro generates code
|
||||||
|
/// for each [device.i2c_master.instances[X]] instance.
|
||||||
|
pub(crate) fn generate_i2c_master_peripehrals(i2c: &I2cMasterProperties) -> TokenStream {
|
||||||
|
let i2c_master_instance_cfgs = i2c
|
||||||
|
.instances
|
||||||
|
.iter()
|
||||||
|
.map(|instance| {
|
||||||
|
let instance_config = &instance.instance_config;
|
||||||
|
|
||||||
|
let instance = format_ident!("{}", instance.name.to_uppercase());
|
||||||
|
|
||||||
|
let sys = format_ident!("{}", instance_config.sys_instance);
|
||||||
|
let sda = format_ident!("{}", instance_config.sda);
|
||||||
|
let scl = format_ident!("{}", instance_config.scl);
|
||||||
|
let int = format_ident!("{}", instance_config.interrupt);
|
||||||
|
|
||||||
|
// The order and meaning of these tokens must match their use in the
|
||||||
|
// `for_each_i2c_master!` call.
|
||||||
|
quote::quote! {
|
||||||
|
#instance, #sys, #scl, #sda, #int
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
generate_for_each_macro("i2c_master", &i2c_master_instance_cfgs)
|
||||||
|
}
|
@ -10,7 +10,7 @@ use proc_macro2::TokenStream;
|
|||||||
use quote::format_ident;
|
use quote::format_ident;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::cfg::{IoMuxSignal, SupportItem, SupportStatus, Value};
|
use crate::cfg::{SupportItem, SupportStatus, Value};
|
||||||
|
|
||||||
macro_rules! include_toml {
|
macro_rules! include_toml {
|
||||||
(Config, $file:expr) => {{
|
(Config, $file:expr) => {{
|
||||||
@ -374,13 +374,11 @@ impl Config {
|
|||||||
|
|
||||||
self.generate_properties(out_dir, "_generated.rs");
|
self.generate_properties(out_dir, "_generated.rs");
|
||||||
self.generate_gpios(out_dir, "_generated_gpio.rs");
|
self.generate_gpios(out_dir, "_generated_gpio.rs");
|
||||||
self.generate_gpio_extras(out_dir, "_generated_gpio_extras.rs");
|
self.generate_iomux_signals(out_dir, "_generated_iomux_signals.rs");
|
||||||
self.generate_peripherals(out_dir, "_generated_peris.rs");
|
self.generate_peripherals(out_dir, "_generated_peris.rs");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_properties(&self, out_dir: &Path, file_name: &str) {
|
fn generate_properties(&self, out_dir: &Path, file_name: &str) {
|
||||||
let out_file = out_dir.join(file_name).to_string_lossy().to_string();
|
|
||||||
|
|
||||||
let mut g = TokenStream::new();
|
let mut g = TokenStream::new();
|
||||||
|
|
||||||
let chip_name = self.name();
|
let chip_name = self.name();
|
||||||
@ -452,7 +450,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
save(&out_file, g);
|
save(out_dir.join(file_name), g);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_gpios(&self, out_dir: &Path, file_name: &str) {
|
fn generate_gpios(&self, out_dir: &Path, file_name: &str) {
|
||||||
@ -461,320 +459,31 @@ impl Config {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let out_file = out_dir.join(file_name).to_string_lossy().to_string();
|
let tokens = cfg::generate_gpios(gpio);
|
||||||
|
|
||||||
let pin_numbers = gpio
|
save(out_dir.join(file_name), tokens);
|
||||||
.pins_and_signals
|
|
||||||
.pins
|
|
||||||
.iter()
|
|
||||||
.map(|pin| number(pin.pin))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let pin_peris = gpio
|
|
||||||
.pins_and_signals
|
|
||||||
.pins
|
|
||||||
.iter()
|
|
||||||
.map(|pin| format_ident!("GPIO{}", pin.pin))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let pin_attrs = gpio
|
|
||||||
.pins_and_signals
|
|
||||||
.pins
|
|
||||||
.iter()
|
|
||||||
.map(|pin| {
|
|
||||||
let mut attrs = vec![];
|
|
||||||
pin.kind.iter().for_each(|kind| match kind {
|
|
||||||
cfg::PinCapability::Input => attrs.push(quote::quote! { Input }),
|
|
||||||
cfg::PinCapability::Output => attrs.push(quote::quote! { Output }),
|
|
||||||
cfg::PinCapability::Analog => attrs.push(quote::quote! { Analog }),
|
|
||||||
cfg::PinCapability::Rtc => {
|
|
||||||
attrs.push(quote::quote! { RtcIo });
|
|
||||||
if pin.kind.contains(&cfg::PinCapability::Output) {
|
|
||||||
attrs.push(quote::quote! { RtcIoOutput });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg::PinCapability::Touch => attrs.push(quote::quote! { Touch }),
|
|
||||||
cfg::PinCapability::UsbDm => attrs.push(quote::quote! { UsbDm }),
|
|
||||||
cfg::PinCapability::UsbDp => attrs.push(quote::quote! { UsbDp }),
|
|
||||||
cfg::PinCapability::UsbDevice => attrs.push(quote::quote! { UsbDevice }),
|
|
||||||
});
|
|
||||||
|
|
||||||
attrs
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let pin_afs = gpio
|
|
||||||
.pins_and_signals
|
|
||||||
.pins
|
|
||||||
.iter()
|
|
||||||
.map(|pin| {
|
|
||||||
let mut input_afs = vec![];
|
|
||||||
let mut output_afs = vec![];
|
|
||||||
|
|
||||||
for af in 0..6 {
|
|
||||||
let Some(signal) = pin.alternate_functions.get(af) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let af_variant = quote::format_ident!("_{af}");
|
|
||||||
let mut found = false;
|
|
||||||
|
|
||||||
// Is the signal present among the input signals?
|
|
||||||
if let Some(signal) = gpio
|
|
||||||
.pins_and_signals
|
|
||||||
.input_signals
|
|
||||||
.iter()
|
|
||||||
.find(|s| s.name == signal)
|
|
||||||
{
|
|
||||||
let signal_tokens = TokenStream::from_str(&signal.name).unwrap();
|
|
||||||
input_afs.push(quote::quote! { #af_variant => #signal_tokens });
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the signal present among the output signals?
|
|
||||||
if let Some(signal) = gpio
|
|
||||||
.pins_and_signals
|
|
||||||
.output_signals
|
|
||||||
.iter()
|
|
||||||
.find(|s| s.name == signal)
|
|
||||||
{
|
|
||||||
let signal_tokens = TokenStream::from_str(&signal.name).unwrap();
|
|
||||||
output_afs.push(quote::quote! { #af_variant => #signal_tokens });
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
found,
|
|
||||||
"Signal '{signal}' not found in input signals for GPIO pin {}",
|
|
||||||
pin.pin
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
quote::quote! {
|
|
||||||
( #(#input_afs)* ) ( #(#output_afs)* )
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let io_mux_accessor = if gpio.remap_iomux_pin_registers {
|
|
||||||
let iomux_pin_regs = gpio.pins_and_signals.pins.iter().map(|pin| {
|
|
||||||
let pin = number(pin.pin);
|
|
||||||
let reg = format_ident!("GPIO{pin}");
|
|
||||||
let accessor = format_ident!("gpio{pin}");
|
|
||||||
|
|
||||||
quote::quote! { #pin => transmute::<&'static io_mux::#reg, &'static io_mux::GPIO0>(iomux.#accessor()), }
|
|
||||||
});
|
|
||||||
|
|
||||||
quote::quote! {
|
|
||||||
pub(crate) fn io_mux_reg(gpio_num: u8) -> &'static crate::pac::io_mux::GPIO0 {
|
|
||||||
use core::mem::transmute;
|
|
||||||
|
|
||||||
use crate::{pac::io_mux, peripherals::IO_MUX};
|
|
||||||
|
|
||||||
let iomux = IO_MUX::regs();
|
|
||||||
unsafe {
|
|
||||||
match gpio_num {
|
|
||||||
#(#iomux_pin_regs)*
|
|
||||||
other => panic!("GPIO {} does not exist", other),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote::quote! {
|
|
||||||
pub(crate) fn io_mux_reg(gpio_num: u8) -> &'static crate::pac::io_mux::GPIO {
|
|
||||||
crate::peripherals::IO_MUX::regs().gpio(gpio_num as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut io_type_macro_calls = vec![];
|
|
||||||
for (pin, attrs) in pin_peris.iter().zip(pin_attrs.iter()) {
|
|
||||||
io_type_macro_calls.push(quote::quote! {
|
|
||||||
#( crate::io_type!(#attrs, #pin); )*
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates a macro that can select between a `then` and an `else` branch based
|
|
||||||
// on whether a pin implement a certain attribute.
|
|
||||||
//
|
|
||||||
// In essence this expands to (in case of pin = GPIO5, attr = Analog):
|
|
||||||
// `if typeof(GPIO5) == Analog { then_tokens } else { else_tokens }`
|
|
||||||
let if_pin_is_type = {
|
|
||||||
let mut branches = vec![];
|
|
||||||
|
|
||||||
for (pin, attr) in pin_peris.iter().zip(pin_attrs.iter()) {
|
|
||||||
branches.push(quote::quote! {
|
|
||||||
#( (#pin, #attr, $then_tt:tt else $else_tt:tt ) => { $then_tt }; )*
|
|
||||||
});
|
|
||||||
|
|
||||||
branches.push(quote::quote! {
|
|
||||||
(#pin, $t:tt, $then_tt:tt else $else_tt:tt ) => { $else_tt };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
quote::quote! {
|
|
||||||
macro_rules! if_pin_is_type {
|
|
||||||
#(#branches)*
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) use if_pin_is_type;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Delegates AnyPin functions to GPIOn functions when the pin implements a
|
|
||||||
// certain attribute.
|
|
||||||
//
|
|
||||||
// In essence this expands to (in case of attr = Analog):
|
|
||||||
// `if typeof(anypin's current value) == Analog { call $code } else { panic }`
|
|
||||||
let impl_for_pin_type = {
|
|
||||||
let mut impl_branches = vec![];
|
|
||||||
for (gpionum, peri) in pin_numbers.iter().zip(pin_peris.iter()) {
|
|
||||||
impl_branches.push(quote::quote! {
|
|
||||||
#gpionum => $crate::peripherals::if_pin_is_type!(#peri, $on_type, {{
|
|
||||||
#[allow(unused_unsafe, unused_mut)]
|
|
||||||
let mut $inner_ident = unsafe { $crate::peripherals::#peri::steal() };
|
|
||||||
#[allow(unused_braces)]
|
|
||||||
$code
|
|
||||||
}} else {
|
|
||||||
$otherwise
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
quote::quote! {
|
|
||||||
macro_rules! impl_for_pin_type {
|
|
||||||
($any_pin:ident, $inner_ident:ident, $on_type:tt, $code:tt else $otherwise:tt) => {
|
|
||||||
match $any_pin.number() {
|
|
||||||
#(#impl_branches)*
|
|
||||||
_ => $otherwise,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($any_pin:ident, $inner_ident:ident, $on_type:tt, $code:tt) => {
|
|
||||||
impl_for_pin_type!($any_pin, $inner_ident, $on_type, $code else { panic!("Unsupported") })
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub(crate) use impl_for_pin_type;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let g = quote::quote! {
|
|
||||||
crate::gpio! {
|
|
||||||
#( (#pin_numbers, #pin_peris #pin_afs) )*
|
|
||||||
}
|
|
||||||
|
|
||||||
#( #io_type_macro_calls )*
|
|
||||||
|
|
||||||
#if_pin_is_type
|
|
||||||
#impl_for_pin_type
|
|
||||||
|
|
||||||
#io_mux_accessor
|
|
||||||
};
|
|
||||||
|
|
||||||
save(&out_file, g);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO temporary name, we likely don't want a new file for these
|
fn generate_iomux_signals(&self, out_dir: &Path, file_name: &str) {
|
||||||
fn generate_gpio_extras(&self, out_dir: &Path, file_name: &str) {
|
|
||||||
let Some(gpio) = self.device.peri_config.gpio.as_ref() else {
|
let Some(gpio) = self.device.peri_config.gpio.as_ref() else {
|
||||||
// No GPIOs defined, nothing to do.
|
// No GPIOs defined, nothing to do.
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let out_file = out_dir.join(file_name).to_string_lossy().to_string();
|
let tokens = cfg::generate_iomux_signals(gpio);
|
||||||
|
|
||||||
let input_signals = render_signals("InputSignal", &gpio.pins_and_signals.input_signals);
|
save(out_dir.join(file_name), tokens);
|
||||||
let output_signals = render_signals("OutputSignal", &gpio.pins_and_signals.output_signals);
|
|
||||||
|
|
||||||
let g = quote::quote! {
|
|
||||||
#input_signals
|
|
||||||
#output_signals
|
|
||||||
};
|
|
||||||
|
|
||||||
save(&out_file, g);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_peripherals(&self, out_dir: &Path, file_name: &str) {
|
fn generate_peripherals(&self, out_dir: &Path, file_name: &str) {
|
||||||
let out_file = out_dir.join(file_name).to_string_lossy().to_string();
|
let mut tokens = TokenStream::new();
|
||||||
|
|
||||||
let i2c_master_instance_cfgs = self
|
// TODO: repeat for all drivers that have Instance traits
|
||||||
.device
|
if let Some(i2c) = self.device.peri_config.i2c_master.as_ref() {
|
||||||
.peri_config
|
tokens.extend(cfg::generate_i2c_master_peripehrals(i2c));
|
||||||
.i2c_master
|
|
||||||
.iter()
|
|
||||||
.flat_map(|peri| {
|
|
||||||
peri.instances.iter().map(|instance| {
|
|
||||||
let instance_config = &instance.instance_config;
|
|
||||||
|
|
||||||
let instance = format_ident!("{}", instance.name.to_uppercase());
|
|
||||||
|
|
||||||
let sys = format_ident!("{}", instance_config.sys_instance);
|
|
||||||
let sda = format_ident!("{}", instance_config.sda);
|
|
||||||
let scl = format_ident!("{}", instance_config.scl);
|
|
||||||
let int = format_ident!("{}", instance_config.interrupt);
|
|
||||||
|
|
||||||
// The order and meaning of these tokens must match their use in the
|
|
||||||
// `for_each_i2c_master!` call.
|
|
||||||
quote::quote! {
|
|
||||||
#instance, #sys, #scl, #sda, #int
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let for_each_i2c_master = generate_for_each_macro("i2c_master", &i2c_master_instance_cfgs);
|
|
||||||
|
|
||||||
let g = quote::quote! {
|
|
||||||
#for_each_i2c_master
|
|
||||||
};
|
};
|
||||||
|
|
||||||
save(&out_file, g);
|
save(out_dir.join(file_name), tokens);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_signals(enum_name: &str, signals: &[IoMuxSignal]) -> TokenStream {
|
|
||||||
if signals.is_empty() {
|
|
||||||
// If there are no signals, we don't need to generate an enum.
|
|
||||||
return quote::quote! {};
|
|
||||||
}
|
|
||||||
let mut variants = vec![];
|
|
||||||
|
|
||||||
for signal in signals {
|
|
||||||
// First, process only signals that have an ID.
|
|
||||||
let Some(id) = signal.id else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = format_ident!("{}", signal.name);
|
|
||||||
let value = number(id);
|
|
||||||
variants.push(quote::quote! {
|
|
||||||
#name = #value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for signal in signals {
|
|
||||||
// Now process signals that do not have an ID.
|
|
||||||
if signal.id.is_some() {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = format_ident!("{}", signal.name);
|
|
||||||
variants.push(quote::quote! {
|
|
||||||
#name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let enum_name = format_ident!("{enum_name}");
|
|
||||||
|
|
||||||
quote::quote! {
|
|
||||||
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub enum #enum_name {
|
|
||||||
#(#variants)*
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user