@@ 0,0 1,142 @@
+use core::marker::PhantomData;
+
+use alloc::boxed::Box;
+use stm32f1xx_hal::rtc::Rtc;
+
+use crate::{
+ brightness_manager::BrightnessManager, button::ButtonState,
+ clock_display_viewer::ClockDisplayViewer, clock_state::ClockState, display_view::DisplayViews,
+};
+
+pub struct ClockApp {
+ rtc: Rtc,
+ display: ClockDisplayViewer,
+ state: ClockState,
+ buttons: [Box<dyn ClockButton + Send>; 4],
+ brightness: BrightnessManager,
+}
+
+struct AppState<'a> {
+ rtc: &'a mut Rtc,
+ display: &'a mut ClockDisplayViewer,
+ state: &'a mut ClockState,
+ brightness: &'a mut BrightnessManager,
+}
+
+trait ClockButton {
+ fn handle(&self, state: ButtonState, state: AppState);
+}
+
+struct ButtonSwitchView;
+struct ButtonChangeTime;
+
+struct Up;
+struct Down;
+struct ButtonBrightness<Direction> {
+ direction: PhantomData<Direction>,
+}
+
+pub enum ClockInterrupt {
+ Rtc,
+ DisplayTimer,
+}
+
+impl ClockApp {
+ pub fn new(rtc: Rtc, display: ClockDisplayViewer, state: ClockState) -> Self {
+ Self {
+ rtc,
+ display,
+ state,
+ buttons: [
+ Box::new(ButtonSwitchView),
+ Box::new(ButtonChangeTime),
+ Box::new(ButtonBrightness::<Down> {
+ direction: PhantomData::<Down>,
+ }),
+ Box::new(ButtonBrightness::<Up> {
+ direction: PhantomData::<Up>,
+ }),
+ ],
+ brightness: BrightnessManager::new(),
+ }
+ }
+
+ pub fn interrupt(&mut self, interrupt: ClockInterrupt) {
+ match interrupt {
+ ClockInterrupt::Rtc => {
+ self.state.second_elapsed();
+ self.rtc.clear_second_flag();
+ }
+ ClockInterrupt::DisplayTimer => {
+ let _ = self.display.update(&self.state);
+ self.brightness.update(&self.state);
+ self.brightness.apply_brightness(&mut self.display);
+ }
+ }
+ }
+
+ pub fn handle_button(&mut self, index: usize, state: ButtonState) {
+ self.buttons[index].handle(
+ state,
+ AppState {
+ rtc: &mut self.rtc,
+ display: &mut self.display,
+ state: &mut self.state,
+ brightness: &mut self.brightness,
+ },
+ );
+ }
+
+ pub fn display(&mut self) -> &mut ClockDisplayViewer {
+ &mut self.display
+ }
+}
+
+impl ClockButton for ButtonSwitchView {
+ fn handle(&self, state: ButtonState, app: AppState) {
+ match state {
+ ButtonState::JustPressed => {
+ let display = app.display;
+ let current_view =
+ display.current_view().unwrap_or(DisplayViews::ClockView) as usize;
+ display.set_current_view(((current_view + 1) % DisplayViews::count()).into());
+ }
+ _ => (),
+ }
+ }
+}
+
+impl ClockButton for ButtonBrightness<Down> {
+ fn handle(&self, state: ButtonState, app: AppState) {
+ match state {
+ ButtonState::JustPressed => {
+ app.brightness
+ .set_brightness(app.brightness.brightness() as i8 - 10);
+ }
+ _ => (),
+ }
+ }
+}
+
+impl ClockButton for ButtonBrightness<Up> {
+ fn handle(&self, state: ButtonState, app: AppState) {
+ match state {
+ ButtonState::JustPressed => {
+ app.brightness
+ .set_brightness(app.brightness.brightness() as i8 + 10);
+ }
+ _ => (),
+ }
+ }
+}
+
+impl ClockButton for ButtonChangeTime {
+ fn handle(&self, state: ButtonState, app: AppState) {
+ match state {
+ ButtonState::JustPressed => {
+ app.rtc.set_time(app.rtc.current_time() + 2);
+ }
+ _ => (),
+ }
+ }
+}
@@ 0,0 1,300 @@
+#![no_std]
+#![no_main]
+#![feature(alloc_error_handler)]
+
+extern crate alloc;
+
+pub mod brightness_manager;
+pub mod button;
+pub mod calendar;
+pub mod clock_app;
+pub mod clock_display;
+pub mod clock_display_viewer;
+pub mod clock_state;
+pub mod count_down;
+pub mod display;
+pub mod display_view;
+pub mod linear_interpolation;
+pub mod number_digits;
+pub mod seven_segments;
+
+use alloc::boxed::Box;
+use button::{ActiveHigh, Button, ButtonState};
+use calendar::Calendar;
+use clock_app::{ClockApp, ClockInterrupt};
+use clock_display::{ClockDisplay, DisplayPart};
+use clock_display_viewer::ClockDisplayViewer;
+use clock_state::ClockState;
+use core::{alloc::Layout, cell::RefCell, convert::Infallible, panic::PanicInfo};
+use cortex_m::asm::wfi;
+use cortex_m_rt::entry;
+use count_down::{CountDown, CountDowner};
+use critical_section::Mutex;
+use display::Display;
+use display_view::DisplayViews;
+use embedded_alloc::Heap;
+use embedded_hal::digital::v2::OutputPin;
+use fugit::MicrosDurationU32;
+use stm32f1xx_hal::{
+ pac,
+ pac::interrupt,
+ prelude::*,
+ rtc::{
+ RestoredOrNewRtc::{New, Restored},
+ Rtc,
+ },
+ time::MonoTimer,
+ timer::{Event, Tim1NoRemap, Tim2NoRemap, Tim3NoRemap, TimerExt},
+};
+
+use defmt_rtt as _;
+
+#[global_allocator]
+static HEAP: Heap = Heap::empty();
+
+static APP: Mutex<RefCell<Option<ClockApp>>> = Mutex::new(RefCell::new(Option::None));
+
+#[interrupt]
+fn RTC() {
+ critical_section::with(|cs| {
+ let mut app = APP.borrow_ref_mut(cs);
+ let app = app.as_mut().unwrap();
+
+ app.interrupt(ClockInterrupt::Rtc);
+ });
+}
+
+#[interrupt]
+fn TIM4() {
+ critical_section::with(|cs| {
+ let mut app = APP.borrow_ref_mut(cs);
+ let app = app.as_mut().unwrap();
+
+ app.interrupt(ClockInterrupt::DisplayTimer);
+ });
+}
+
+#[entry]
+fn main() -> ! {
+ {
+ use core::mem::MaybeUninit;
+ const HEAP_SIZE: usize = 512;
+ static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
+ unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
+ }
+
+ // Get access to the core peripherals from the cortex-m crate
+ let cp = cortex_m::Peripherals::take().unwrap();
+ // Get access to the device specific peripherals from the peripheral access crate
+ let dp = pac::Peripherals::take().unwrap();
+
+ // Take ownership over the raw flash and rcc devices and convert them into the corresponding
+ // HAL structs
+ let mut pwr = dp.PWR;
+ let mut flash = dp.FLASH.constrain();
+ let rcc = dp.RCC.constrain();
+ let mut backup_domain = rcc.bkp.constrain(dp.BKP, &mut pwr);
+
+ // Freeze the configuration of all the clocks in the system and store the frozen frequencies in
+ // `clocks`
+ let clocks = rcc
+ .cfgr
+ .use_hse(8.MHz())
+ .sysclk(24.MHz())
+ .pclk1(24.MHz())
+ .pclk2(24.MHz())
+ .freeze(&mut flash.acr);
+
+ let mut gpiob = dp.GPIOB.split();
+ let mut gpioa = dp.GPIOA.split();
+ let mut gpioc = dp.GPIOC.split();
+ let mut afio = dp.AFIO.constrain();
+
+ let (_, pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4);
+
+ let mut led1 = gpiob.pb12.into_open_drain_output(&mut gpiob.crh);
+ let mut led2 = gpiob.pb11.into_open_drain_output(&mut gpiob.crh);
+ let mut led3 = gpiob.pb1.into_open_drain_output(&mut gpiob.crl);
+ let mut led4 = gpiob.pb0.into_open_drain_output(&mut gpiob.crl);
+
+ let leds: [&mut dyn OutputPin<Error = Infallible>; 4] =
+ [&mut led1, &mut led2, &mut led3, &mut led4];
+
+ let btn1 = Button::<ActiveHigh>::new(Box::new(gpiob.pb15.into_pull_down_input(&mut gpiob.crh)));
+ let btn2 = Button::<ActiveHigh>::new(Box::new(gpiob.pb14.into_pull_down_input(&mut gpiob.crh)));
+ let btn3 = Button::<ActiveHigh>::new(Box::new(gpiob.pb13.into_pull_down_input(&mut gpiob.crh)));
+ let btn4 = Button::<ActiveHigh>::new(Box::new(gpioc.pc13.into_pull_down_input(&mut gpioc.crh)));
+
+ let mut btns: [Button<ActiveHigh>; 4] = [btn1, btn2, btn3, btn4];
+
+ let a = gpiob.pb10.into_open_drain_output(&mut gpiob.crh);
+ let b = gpiob.pb2.into_open_drain_output(&mut gpiob.crl);
+ let c = gpiob.pb8.into_open_drain_output(&mut gpiob.crh);
+ let d = gpiob.pb6.into_open_drain_output(&mut gpiob.crl);
+ let e = gpiob.pb9.into_open_drain_output(&mut gpiob.crh);
+ let f = pb3.into_open_drain_output(&mut gpiob.crl);
+ let g = pb4.into_open_drain_output(&mut gpiob.crl);
+ let dpp = gpiob.pb7.into_open_drain_output(&mut gpiob.crl);
+
+ let dig1 = gpioa.pa6.into_alternate_open_drain(&mut gpioa.crl);
+ let dig2 = gpioa.pa3.into_alternate_open_drain(&mut gpioa.crl);
+ let dig3 = gpioa.pa7.into_alternate_open_drain(&mut gpioa.crl);
+ let dig4 = gpioa.pa8.into_alternate_open_drain(&mut gpioa.crh);
+ let dig5 = gpioa.pa9.into_alternate_open_drain(&mut gpioa.crh);
+ let dig6 = gpioa.pa2.into_alternate_open_drain(&mut gpioa.crl);
+ let dig7 = gpioa.pa10.into_alternate_open_drain(&mut gpioa.crh);
+ let dig8 = gpioa.pa1.into_alternate_open_drain(&mut gpioa.crl);
+
+ let pwm_freq = 2.kHz();
+ let pins1 = (dig4, dig5, dig7);
+ let pwm1 = dp
+ .TIM1
+ .pwm_hz::<Tim1NoRemap, _, _>(pins1, &mut afio.mapr, pwm_freq, &clocks);
+
+ let pins2 = (dig8, dig6, dig2);
+ let pwm2 = dp
+ .TIM2
+ .pwm_hz::<Tim2NoRemap, _, _>(pins2, &mut afio.mapr, pwm_freq, &clocks);
+
+ let pins3 = (dig1, dig3);
+ let pwm3 = dp
+ .TIM3
+ .pwm_hz::<Tim3NoRemap, _, _>(pins3, &mut afio.mapr, pwm_freq, &clocks);
+
+ let mut tim4 = dp.TIM4.counter_us(&clocks);
+ tim4.listen(Event::Update);
+ tim4.start(10.micros()).unwrap();
+
+ let countdown: Box<dyn CountDown<Time = MicrosDurationU32> + Send> =
+ Box::new(CountDowner::new(tim4));
+
+ let (dig4, dig5, dig7) = pwm1.split();
+ let (dig8, dig6, dig2) = pwm2.split();
+ let (dig1, dig3) = pwm3.split();
+
+ let display = Display::<8>::new(
+ [
+ Box::new(a),
+ Box::new(b),
+ Box::new(c),
+ Box::new(d),
+ Box::new(e),
+ Box::new(f),
+ Box::new(g),
+ Box::new(dpp),
+ ],
+ [
+ Box::new(dig1),
+ Box::new(dig2),
+ Box::new(dig3),
+ Box::new(dig4),
+ Box::new(dig5),
+ Box::new(dig6),
+ Box::new(dig7),
+ Box::new(dig8),
+ ],
+ countdown,
+ );
+
+ let display = ClockDisplay::new(display);
+ let mut display = ClockDisplayViewer::new(display);
+ display.set_current_view(DisplayViews::ClockSecondsView);
+
+ let mut rtc = match Rtc::restore_or_new(dp.RTC, &mut backup_domain) {
+ Restored(rtc) => rtc,
+ New(rtc) => rtc,
+ };
+
+ rtc.listen_seconds();
+
+ // Initialize the state inside of a critical section,
+ // to make sure that if a second will elapse during state initialization,
+ // an interrupt will be called afterwards.
+ critical_section::with(|cs| {
+ let current_time = rtc.current_time();
+ unsafe {
+ // RTC interrupt cannot be called prior APP being Some,
+ // otherwise a panic would result.
+ cortex_m::peripheral::NVIC::unmask(interrupt::RTC);
+ }
+
+ let state = ClockState::new(
+ Calendar::from_seconds(Calendar::new(0, 0, 0, 1, 7, 2023), current_time),
+ MonoTimer::new(cp.DWT, cp.DCB, clocks),
+ );
+
+ let app = ClockApp::new(rtc, display, state);
+ APP.borrow(cs).replace(Some(app));
+ });
+
+ unsafe {
+ cortex_m::peripheral::NVIC::unmask(interrupt::TIM4);
+ }
+
+ let mut delay = cp.SYST.delay(&clocks);
+
+ loop {
+ for (i, btn) in btns.iter_mut().enumerate() {
+ btn.update();
+
+ if btn.is_pressed() {
+ leds[i].set_low().unwrap();
+ } else {
+ leds[i].set_high().unwrap();
+ }
+
+ let state = btn.state();
+ if state != ButtonState::Off {
+ critical_section::with(|cs| {
+ let mut app = APP.borrow_ref_mut(cs);
+ let app = app.as_mut().unwrap();
+
+ app.handle_button(i, state);
+ });
+ }
+ }
+
+ delay.delay_ms(50u16);
+ }
+}
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ defmt::error!("{}", defmt::Display2Format(info));
+ defmt::flush();
+
+ cortex_m::peripheral::NVIC::mask(interrupt::RTC);
+ critical_section::with(|cs| {
+ let mut app = APP.borrow_ref_mut(cs);
+ let app = app.as_mut().unwrap();
+ let display = app.display();
+ display.clear_current_view();
+ let _ = display
+ .clock_display()
+ .show_text(DisplayPart::MainDisplay, "Erro");
+ display.clock_display().hide(DisplayPart::SideDisplay1);
+ display.clock_display().hide(DisplayPart::SideDisplay2);
+ });
+
+ loop {
+ wfi();
+ }
+}
+
+#[alloc_error_handler]
+fn oom(_: Layout) -> ! {
+ cortex_m::peripheral::NVIC::mask(interrupt::RTC);
+ critical_section::with(|cs| {
+ let mut app = APP.borrow_ref_mut(cs);
+ let app = app.as_mut().unwrap();
+ let display = app.display();
+ display.clear_current_view();
+ let _ = display
+ .clock_display()
+ .show_text(DisplayPart::MainDisplay, "oom");
+ });
+
+ loop {
+ wfi();
+ }
+}