1use std::convert::TryFrom;
2
3use regex::Regex;
4
5use crate::*;
6
7#[derive(Debug)]
8pub struct GpioPin {
9 pub port: String,
10 pub register: String,
11 pub signal: SignalType,
12 pub label: Option<String>,
13 pub pin_state: Option<PinStateType>,
14 pub speed: Option<SpeedType>,
15 pub pu_pd: Option<PullType>,
16 pub mode_default_output_pp: Option<ModeOutputType>,
17}
18
19pub fn get_gpios(config: &ConfigParams<'_>) -> anyhow::Result<(Vec<char>, Vec<GpioPin>)> {
20 let re = Regex::new(r"^P[A-K]\d{1,2}").unwrap();
22
23 let mut gpio_params = Vec::new();
25 for (name, params) in config {
26 if let Some(name_match) = re.find(name) {
27 gpio_params.push((name_match.as_str(), params));
28 }
29 }
30
31 gpio_params.sort_by(|(a, _), (b, _)| human_sort::compare(a, b));
33
34 let mut gpios = Vec::new();
36 let mut ports = Vec::new();
37 for (name, parameters) in gpio_params {
38 let gpio: GpioPin = GpioPin::new(name, parameters).context(format!("Pin: {}", name))?;
39
40 if let SignalType::Peripheral(ref signal) = gpio.signal {
42 if signal == "RCC_OSC_OUT" || signal == "RCC_OSC_IN" {
43 continue;
44 }
45 };
46
47 ports.push(gpio.port.chars().last().unwrap());
48 gpios.push(gpio);
49 }
50
51 ports.dedup();
52
53 Ok((ports, gpios))
54}
55
56impl GpioPin {
57 pub fn new(name: &str, parameters: &HashMap<&str, &str>) -> anyhow::Result<Self> {
58 let (port, register) = parse_name(name)?;
59
60 let signal = parse_mandatory_param(parameters, "Signal")?;
61 let label = parameters.get("GPIO_Label").map(|&s| String::from(s));
62
63 let pin_state = parse_optional_param(parameters, "PinState")?;
64 let pu_pd = parse_optional_param(parameters, "GPIO_PuPd")?;
65
66 let speed = parse_optional_param(parameters, "GPIO_Speed")?;
67 let mode_default_output_pp = parse_optional_param(parameters, "GPIO_ModeDefaultOutputPP")?;
68
69 Ok(GpioPin {
70 port,
71 register,
72 signal,
73 label,
74 pin_state,
75 speed,
76 pu_pd,
77 mode_default_output_pp,
78 })
79 }
80
81 pub fn get_name(&self) -> String {
82 let register = &self.register;
83
84 match &self.label {
85 Some(label) => label.clone(),
86 None => match self.signal {
87 SignalType::Peripheral(ref name) => name.to_lowercase(),
88 SignalType::AdcInput => f!("adc_{register}"),
89 _ => f!("gpio_{register}"),
90 },
91 }
92 }
93}
94
95fn parse_name(name: &str) -> anyhow::Result<(String, String)> {
96 let port_char_upper = name
97 .chars()
98 .nth(1)
99 .ok_or_else(|| anyhow!("could not parse port"))?;
100 let pin: u8 = name[2..]
101 .parse()
102 .map_err(|_| anyhow!("could not parse pin number"))?;
103
104 let port_char_lower = port_char_upper.to_ascii_lowercase();
105 let port = f!("GPIO{port_char_upper}");
106 let register = f!("p{port_char_lower}{pin}");
107 Ok((port, register))
108}
109
110#[derive(Debug, PartialEq)]
111pub enum SignalType {
112 GpioInput,
113 GpioOutput,
114 AdcInput,
115 Peripheral(String),
116}
117
118impl TryFrom<&str> for SignalType {
119 type Error = anyhow::Error;
120
121 fn try_from(text: &str) -> anyhow::Result<Self, Self::Error> {
122 match text {
123 "GPIO_Input" => Ok(SignalType::GpioInput),
124 "GPIO_Output" => Ok(SignalType::GpioOutput),
125 "GPIO_Analog" => Ok(SignalType::AdcInput),
126 _ => Ok(SignalType::Peripheral(String::from(text))),
127 }
128 }
129}
130
131parameter!(PinStateType, [GPIO_PIN_SET, GPIO_PIN_RESET]);
132parameter!(
133 PullType,
134 [GPIO_PULLUP, GPIO_PULLDOWN, GPIO_NOPULL],
135 default = GPIO_NOPULL
136);
137parameter!(
138 ModeOutputType,
139 [GPIO_MODE_OUTPUT_OD, GPIO_MODE_OUTPUT_PP],
140 default = GPIO_MODE_OUTPUT_PP
141);
142parameter!(
143 SpeedType,
144 [
145 GPIO_SPEED_FREQ_LOW,
146 GPIO_SPEED_FREQ_MEDIUM,
147 GPIO_SPEED_FREQ_HIGH,
148 GPIO_SPEED_FREQ_VERY_HIGH
149 ],
150 default = GPIO_SPEED_FREQ_LOW
151);