use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GpioNum(pub u8);
impl fmt::Display for GpioNum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GPIO{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PeripheralSignal {
UartTx(u8),
UartRx(u8),
UartCts(u8),
UartRts(u8),
SpiSck(u8),
SpiMosi(u8),
SpiMiso(u8),
SpiCs(u8, u8),
I2cScl(u8),
I2cSda(u8),
Pwm(u8, char),
Adc(u8),
AdcChannel(u8, u8),
Dac(u8),
UsbDp,
UsbDm,
SwdClk,
SwdDio,
Other(String),
}
impl PeripheralSignal {
pub fn name(&self) -> String {
match self {
Self::UartTx(i) => format!("UART{i}_TX"),
Self::UartRx(i) => format!("UART{i}_RX"),
Self::UartCts(i) => format!("UART{i}_CTS"),
Self::UartRts(i) => format!("UART{i}_RTS"),
Self::SpiSck(i) => format!("SPI{i}_SCK"),
Self::SpiMosi(i) => format!("SPI{i}_MOSI"),
Self::SpiMiso(i) => format!("SPI{i}_MISO"),
Self::SpiCs(i, cs) => format!("SPI{i}_CS{cs}"),
Self::I2cScl(i) => format!("I2C{i}_SCL"),
Self::I2cSda(i) => format!("I2C{i}_SDA"),
Self::Pwm(s, c) => format!("PWM{s}_{c}"),
Self::Adc(ch) => format!("ADC{ch}"),
Self::AdcChannel(inst, ch) => format!("ADC{inst}_IN{ch}"),
Self::Dac(ch) => format!("DAC{ch}"),
Self::UsbDp => "USB_DP".to_string(),
Self::UsbDm => "USB_DM".to_string(),
Self::SwdClk => "SWD_CLK".to_string(),
Self::SwdDio => "SWD_DIO".to_string(),
Self::Other(s) => s.clone(),
}
}
pub fn parse(name: &str) -> Option<Self> {
let clean = name.trim().to_ascii_uppercase();
if let Some(rest) = clean
.strip_prefix("UART")
.or_else(|| clean.strip_prefix("USART"))
.or_else(|| clean.strip_prefix("U"))
{
if let Some(inst_char) = rest.chars().next() {
if let Some(digit) = inst_char.to_digit(10) {
let inst = digit as u8;
let tail = &rest[1..].trim_start_matches('_');
if tail.contains("TX") {
return Some(Self::UartTx(inst));
}
if tail.contains("RX") {
return Some(Self::UartRx(inst));
}
if tail.contains("CTS") {
return Some(Self::UartCts(inst));
}
if tail.contains("RTS") {
return Some(Self::UartRts(inst));
}
}
}
}
if let Some(rest) = clean.strip_prefix("SPI") {
if let Some(inst_char) = rest.chars().next() {
if let Some(digit) = inst_char.to_digit(10) {
let inst = digit as u8;
let tail = &rest[1..].trim_start_matches('_');
if tail.contains("SCK") || tail.contains("CLK") {
return Some(Self::SpiSck(inst));
}
if tail.contains("MOSI") || tail.contains("TX") {
return Some(Self::SpiMosi(inst));
}
if tail.contains("MISO") || tail.contains("RX") {
return Some(Self::SpiMiso(inst));
}
if tail.contains("CS") || tail.contains("SS") {
return Some(Self::SpiCs(inst, 0));
}
}
}
}
if let Some(rest) = clean.strip_prefix("I2C") {
if let Some(inst_char) = rest.chars().next() {
if let Some(digit) = inst_char.to_digit(10) {
let inst = digit as u8;
let tail = &rest[1..].trim_start_matches('_');
if tail.contains("SCL") || tail.contains("CLK") {
return Some(Self::I2cScl(inst));
}
if tail.contains("SDA") || tail.contains("DAT") {
return Some(Self::I2cSda(inst));
}
}
}
}
if let Some(rest) = clean.strip_prefix("ADC") {
if let Some((inst, ch)) = rest.split_once("_IN") {
if let (Ok(inst), Ok(ch)) = (inst.parse::<u8>(), ch.parse::<u8>()) {
return Some(Self::AdcChannel(inst, ch));
}
}
let tail = rest.trim_start_matches('_').trim_start_matches("IN");
if let Ok(ch) = tail.parse::<u8>() {
return Some(Self::Adc(ch));
}
}
if clean.contains("USB_DP") || clean == "USB_D+" || clean == "DP" {
return Some(Self::UsbDp);
}
if clean.contains("USB_DM") || clean == "USB_D-" || clean == "DM" {
return Some(Self::UsbDm);
}
None
}
}
pub fn parse_port_pin(name: &str) -> Option<(char, u8)> {
split_port_pin(name)
.filter(|(_, pin)| *pin < 16)
.map(|(port, pin)| (port, pin as u8))
}
fn split_port_pin(name: &str) -> Option<(char, u32)> {
let clean = name.trim().to_ascii_uppercase();
let rest = clean.strip_prefix('P')?;
let port = rest.chars().next().filter(|c| ('A'..='K').contains(c))?;
let digits = &rest[1..];
if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
digits.parse::<u32>().ok().map(|pin| (port, pin))
}
pub fn parse_gpio_index(name: &str) -> Option<u32> {
let clean = name.trim().to_ascii_uppercase();
["GPIO", "GP", "IO"].iter().find_map(|prefix| {
let digits = clean.strip_prefix(prefix)?;
if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
digits.parse::<u32>().ok()
})
}
pub fn is_gpio_designator(name: &str) -> bool {
parse_gpio_index(name).is_some() || split_port_pin(name).is_some()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StrappingRequirement {
MustBeHigh {
purpose: &'static str,
violation_consequence: &'static str,
},
MustBeLow {
purpose: &'static str,
violation_consequence: &'static str,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct PinCapabilities {
pub is_gpio: bool,
pub is_analog: bool,
pub is_input_only: bool,
pub is_reserved_flash: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MuxFunction {
pub signal: PeripheralSignal,
pub af_number: u8,
pub af_name: &'static str,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PinSpec {
pub gpio: Option<GpioNum>,
pub name: &'static str,
pub capabilities: PinCapabilities,
pub strapping: Option<StrappingRequirement>,
pub functions: &'static [MuxFunction],
}
impl PinSpec {
pub fn supports_signal(&self, signal: &PeripheralSignal) -> bool {
self.functions.iter().any(|f| &f.signal == signal)
}
pub fn get_af(&self, signal: &PeripheralSignal) -> Option<&'static MuxFunction> {
self.functions.iter().find(|f| &f.signal == signal)
}
}
#[derive(Debug, Clone)]
pub struct SiliconTarget {
pub name: &'static str,
pub family: &'static str,
pub pins: &'static [PinSpec],
}
impl SiliconTarget {
pub fn find_pin(&self, name_or_gpio: &str) -> Option<&'static PinSpec> {
let clean = name_or_gpio.trim().to_ascii_uppercase();
for pin in self.pins {
if pin.name.eq_ignore_ascii_case(&clean) {
return Some(pin);
}
if let Some(gpio) = pin.gpio {
if format!("GPIO{}", gpio.0).eq_ignore_ascii_case(&clean)
|| format!("GP{}", gpio.0).eq_ignore_ascii_case(&clean)
|| format!("IO{}", gpio.0).eq_ignore_ascii_case(&clean)
|| format!("{}", gpio.0) == clean
{
return Some(pin);
}
}
}
None
}
pub fn find_valid_pins_for_signal(&self, signal: &PeripheralSignal) -> Vec<&'static PinSpec> {
self.pins
.iter()
.filter(|p| p.supports_signal(signal))
.collect()
}
}