stm32_rust_template/apps/
blink.rs

1use crate::apps::App;
2use crate::arch::cortex_m4::systick::get_ticks;
3use crate::driver::gpio::stm32f407::{GpioDriver, pins};
4use crate::driver::gpio::{Direction, Gpio};
5
6const BOUNCE: [u8; 6] = [0b0001, 0b0010, 0b0100, 0b1000, 0b0100, 0b0010];
7const IN_OUT: [u8; 6] = [0b1001, 0b0110, 0b1111, 0b0110, 0b1001, 0b0000];
8const TWINKLE: [u8; 8] = [
9    0b0001, 0b1000, 0b0010, 0b0100, 0b1000, 0b0001, 0b0100, 0b0010,
10];
11const CHASE: [u8; 4] = [0b0001, 0b0010, 0b0100, 0b1000];
12const FAST_MS: u32 = 120;
13const MED_MS: u32 = 220;
14const SLOW_MS: u32 = 320;
15const SMOKE_DELAY_MS: u32 = 700;
16const SMOKE_OFF_DELAY_MS: u32 = 200;
17
18#[derive(Clone, Copy)]
19enum State {
20    Smoke(u8),
21    SmokeDelay(u8),
22    SmokeOffDelay,
23    Bounce(u8, u8),
24    BounceDelay(u8, u8),
25    InOut(u8, u8),
26    InOutDelay(u8, u8),
27    AllOn,
28    AllOnDelay,
29    AllOff,
30    AllOffDelay,
31    Twinkle(u8, u8),
32    TwinkleDelay(u8, u8),
33    Chase(u8),
34    ChaseDelay(u8),
35}
36
37pub struct BlinkApp {
38    gpio_driver: Option<GpioDriver<'static>>,
39    initialized: bool,
40    state: State,
41    last_tick: u32,
42}
43
44impl BlinkApp {
45    pub fn new() -> Self {
46        Self {
47            gpio_driver: None,
48            initialized: false,
49            state: State::Smoke(0),
50            last_tick: 0,
51        }
52    }
53
54    pub fn init(&mut self) -> Result<(), i32> {
55        if self.initialized {
56            return Ok(());
57        }
58        let mut gpio = GpioDriver::new_gpiod();
59
60        // Configure PD12..PD15 as outputs and default OFF
61        for &p in &[pins::PD12, pins::PD13, pins::PD14, pins::PD15] {
62            gpio.set_direction(p, Direction::Output)?;
63            gpio.set_output(p, false);
64        }
65
66        self.gpio_driver = Some(gpio);
67        self.initialized = true;
68        Ok(())
69    }
70
71    #[inline(always)]
72    fn delay(mut cycles: u32) {
73        while cycles > 0 {
74            cortex_m::asm::nop();
75            cycles -= 1;
76        }
77    }
78
79    /// Write a 4-bit pattern to PD12..PD15 in one shot using BSRR.
80    /// bit0→PD12, bit1→PD13, bit2→PD14, bit3→PD15
81    fn write_mask_4(gpio: &mut GpioDriver, mask4: u8) {
82        // Change this depending on your board's LED polarity:
83        const ACTIVE_LOW: bool = false; // set true if LED turns ON when pin = 0
84
85        let pins = [pins::PD12, pins::PD13, pins::PD14, pins::PD15];
86        // Build set/clear masks (we still call per-pin BSRR writes; driver is fast)
87        for (i, &p) in pins.iter().enumerate() {
88            let want_on = ((mask4 >> i) & 1) != 0;
89            let level = if ACTIVE_LOW { !want_on } else { want_on };
90            gpio.set_output(p, level);
91        }
92    }
93
94    pub fn tick(&mut self) {
95        if !self.initialized {
96            return;
97        }
98        if let Some(ref mut gpio) = self.gpio_driver {
99            match self.state {
100                State::Smoke(step) => {
101                    if step < 4 {
102                        for j in 0..4 {
103                            Self::write_mask_4(gpio, if j == step { 1 << j } else { 0 });
104                        }
105                        self.last_tick = get_ticks();
106                        self.state = State::SmokeDelay(step);
107                    } else {
108                        Self::write_mask_4(gpio, 0);
109                        self.last_tick = get_ticks();
110                        self.state = State::SmokeOffDelay;
111                    }
112                }
113                State::SmokeDelay(step) => {
114                    if get_ticks().wrapping_sub(self.last_tick) >= SMOKE_DELAY_MS {
115                        self.state = State::Smoke(step + 1);
116                    }
117                }
118                State::SmokeOffDelay => {
119                    if get_ticks().wrapping_sub(self.last_tick) >= SMOKE_OFF_DELAY_MS {
120                        self.state = State::Bounce(0, 0);
121                    }
122                }
123                State::Bounce(count, step) => {
124                    let m = BOUNCE[step as usize];
125                    Self::write_mask_4(gpio, m);
126                    self.last_tick = get_ticks();
127                    self.state = State::BounceDelay(count, step);
128                }
129                State::BounceDelay(count, step) => {
130                    if get_ticks().wrapping_sub(self.last_tick) >= MED_MS {
131                        let next_step = step + 1;
132                        if next_step >= BOUNCE.len() as u8 {
133                            let next_count = count + 1;
134                            if next_count >= 4 {
135                                self.state = State::InOut(0, 0);
136                            } else {
137                                self.state = State::Bounce(next_count, 0);
138                            }
139                        } else {
140                            self.state = State::Bounce(count, next_step);
141                        }
142                    }
143                }
144                State::InOut(count, step) => {
145                    let m = IN_OUT[step as usize];
146                    Self::write_mask_4(gpio, m);
147                    self.last_tick = get_ticks();
148                    self.state = State::InOutDelay(count, step);
149                }
150                State::InOutDelay(count, step) => {
151                    if get_ticks().wrapping_sub(self.last_tick) >= FAST_MS {
152                        let next_step = step + 1;
153                        if next_step >= IN_OUT.len() as u8 {
154                            let next_count = count + 1;
155                            if next_count >= 6 {
156                                self.state = State::AllOn;
157                            } else {
158                                self.state = State::InOut(next_count, 0);
159                            }
160                        } else {
161                            self.state = State::InOut(count, next_step);
162                        }
163                    }
164                }
165                State::AllOn => {
166                    Self::write_mask_4(gpio, 0b1111);
167                    self.last_tick = get_ticks();
168                    self.state = State::AllOnDelay;
169                }
170                State::AllOnDelay => {
171                    if get_ticks().wrapping_sub(self.last_tick) >= SLOW_MS {
172                        self.state = State::AllOff;
173                    }
174                }
175                State::AllOff => {
176                    Self::write_mask_4(gpio, 0b0000);
177                    self.last_tick = get_ticks();
178                    self.state = State::AllOffDelay;
179                }
180                State::AllOffDelay => {
181                    if get_ticks().wrapping_sub(self.last_tick) >= MED_MS {
182                        self.state = State::Twinkle(0, 0);
183                    }
184                }
185                State::Twinkle(count, step) => {
186                    let m = TWINKLE[step as usize];
187                    Self::write_mask_4(gpio, m);
188                    self.last_tick = get_ticks();
189                    self.state = State::TwinkleDelay(count, step);
190                }
191                State::TwinkleDelay(count, step) => {
192                    if get_ticks().wrapping_sub(self.last_tick) >= FAST_MS {
193                        let next_step = step + 1;
194                        if next_step >= TWINKLE.len() as u8 {
195                            let next_count = count + 1;
196                            if next_count >= 6 {
197                                self.state = State::Chase(0);
198                            } else {
199                                self.state = State::Twinkle(next_count, 0);
200                            }
201                        } else {
202                            self.state = State::Twinkle(count, next_step);
203                        }
204                    }
205                }
206                State::Chase(step) => {
207                    let m = CHASE[step as usize];
208                    Self::write_mask_4(gpio, m);
209                    self.last_tick = get_ticks();
210                    self.state = State::ChaseDelay(step);
211                }
212                State::ChaseDelay(step) => {
213                    if get_ticks().wrapping_sub(self.last_tick) >= FAST_MS {
214                        let next_step = step + 1;
215                        if next_step >= CHASE.len() as u8 {
216                            self.state = State::Bounce(0, 0);
217                        } else {
218                            self.state = State::Chase(next_step);
219                        }
220                    }
221                }
222            }
223        }
224    }
225}
226
227impl App for BlinkApp {
228    fn init(&mut self) -> Result<(), i32> {
229        self.init()
230    }
231    fn loop_step(&mut self) {
232        self.tick();
233    }
234}
235
236pub fn create_simple_blink_app() -> BlinkApp {
237    BlinkApp::new()
238}