#![no_std] #![feature(asm)] #![feature(const_cell_new)] #![feature(const_refcell_new)] #[macro_use] extern crate stm32f103xx; extern crate cortex_m; use stm32f103xx::{Interrupt, Peripherals, gpioa}; use core::cell::{Cell, UnsafeCell}; use core::mem::uninitialized; mod mutex; mod i2c; mod ds3231; mod at24c; mod tim; #[inline(always)] fn get_gs() -> &'static mut GlobalState<'static> { unsafe {GS.as_mut().unwrap()} } #[inline] fn digits2bcds(digs: &[u8]) -> u32 { let mut res: u32 = 0; for d in digs.iter().rev() { res = (res << 4) | (*d as u32); } res } fn inc_rotate(n: &mut u8, reset: u8, limit: u8) { *n += 1; if *n == limit { *n = reset; } } #[inline] fn countdown(n: u32) -> u32 { if n > 0 { n - 1 } else { n } } fn render1(buff: &mut [u8; 6], mut n: u32) { for i in 0..buff.len() { buff[i] = (n % 10) as u8; n /= 10; } } fn render3(buff: &mut [u8; 6], n1: u8, n2: u8, n3: u8) { buff[1] = n3 / 10; buff[0] = n3 - buff[1] * 10; buff[3] = n2 / 10; buff[2] = n2 - buff[3] * 10; buff[5] = n1 / 10; buff[4] = n1 - buff[5] * 10; } struct ShiftRegister<'a> { gpioa: &'a gpioa::RegisterBlock, width: u8, } #[derive(Clone, Copy)] struct Time { sec: u8, min: u8, hr: u8, } #[derive(Clone, Copy)] enum DispState { On, Off, TempOn } struct GlobalState<'a> { perip: Peripherals<'a>, disp: ShiftRegister<'a>, btn1: Button<'a>, btn2: Button<'a>, events: EventTimer<'a>, i2c: i2c::I2C<'a>, sync_cnt: Cell, buff: [u8; 6], disp_state: Cell, tempon_cycle: Cell, tempon_peroid: Cell, tempon_cnt: Cell, blinky: [bool; 6], blink_state: Cell, blinky_enabled: Cell>, pidx: usize, time_panel: TimePanel, date_panel: DatePanel, temp_panel: TempPanel, cd_panel: CountdownPanel, cu_panel: CountupPanel, set_panel: SettingPanel, panels: [&'a mut Panel<'a>; 6], } #[derive(Clone, Copy)] enum ButtonState { Idle, PressedLock, PressedUnlock, ReleaseLock, Continous } #[derive(Clone, Copy)] enum ButtonMode { Release, Immediate, Continous } struct Button<'a> { state: Cell, long: Cell, events: &'a EventTimer<'a>, ev_id: Cell, mode: Cell } enum ButtonResult { FalseAlarm, ShortPress, LongPress } trait Panel<'a> { fn btn1_press(&'a mut self) -> bool { get_gs().btn1.press(ButtonMode::Release); true } fn btn1_release_extra(&mut self) {} fn btn1_release(&'a mut self) -> bool { self.btn1_release_extra(); let gs = get_gs(); let btn1 = &gs.btn1; match btn1.release() { ButtonResult::FalseAlarm => true, ButtonResult::ShortPress => self.btn1_short(), ButtonResult::LongPress => { gs.disp_state.set(DispState::Off); gs.tempon_cnt.set(gs.tempon_cycle.get()); true } } } fn btn1_short(&mut self) -> bool {false} fn btn1_long(&mut self) -> bool {false} fn btn2_short(&mut self) -> bool {false} fn btn2_long(&mut self) -> bool {false} fn update_output(&self); } trait Timeoutable<'a> { fn timeout(&'a self); } #[derive(Clone, Copy)] enum TimePanelState { Inactive, View, EditHr, EditMin, EditSec, } struct TimePanel { state: Cell, time: Time, tmp: Time, blink_enabled: Cell } #[derive(Clone, Copy)] struct Date { yr: u8, mon: u8, day: u8 } #[derive(Clone, Copy)] enum DatePanelState { Inactive, View, EditYr, EditMon, EditDay } struct DatePanel { state: Cell, date: Date, tmp: Date, blink_enabled: Cell } #[derive(Clone, Copy)] enum TempPanelState { Inactive, View } struct TempPanel { state: Cell, temp: Cell } #[derive(Clone, Copy)] enum CountdownPanelState { Inactive, View, EditWhole, Edit3, Edit2, Edit1, OnGoing, OnGoingPaused, TimeUp } struct CountdownPanel { state: Cell, presets: [(u8, u8, u8); 4], counter: Cell, didx: Cell, blink_enabled: Cell } #[derive(Clone, Copy)] enum CountupPanelState { Inactive, View, OnGoing, OnGoingPaused } struct CountupPanel { state: Cell, counter: Cell } #[derive(Clone, Copy)] enum SettingPanelState { Inactive, View, SettingChoice, Edit3, Edit2, Edit1, Edit0 } #[derive(Clone, Copy)] enum SettingIdx { TempOnCycle, TempOnPeroid } struct SettingPanel { state: Cell, tmp: [u8; 6], idx: Cell } #[derive(Clone, Copy)] struct AlarmEvent<'a> { cb: &'a Timeoutable<'a>, cnt: u32, free: bool } struct EventTimer<'a> { events: UnsafeCell<[AlarmEvent<'a>; 8]> } impl<'a> ShiftRegister<'a> { fn new(gpioa: &'a gpioa::RegisterBlock, width: u8) -> Self { ShiftRegister{gpioa, width} } fn output_bits(&self, bits: u32) { let bsrr = &self.gpioa.bsrr; for i in (0..self.width).rev() { bsrr.write(|w| w.br1().reset()); /* feed the ser */ match (bits >> i) & 1 { 0 => bsrr.write(|w| w.br0().reset()), 1 => bsrr.write(|w| w.bs0().set()), _ => panic!() } /* shift (trigger the sclk) */ bsrr.write(|w| w.bs1().set()); } /* latch on (trigger the clk) */ bsrr.write(|w| w.br2().reset()); bsrr.write(|w| w.bs2().set()); } } impl Time { fn tick(&mut self) { self.sec += 1; if self.sec == 60 { self.min += 1; self.sec = 0; } if self.min == 60 { self.hr += 1; self.min = 0; } if self.hr == 24 { self.hr = 0; } } } impl<'a> Timeoutable<'a> for Button<'a> { fn timeout(&'a self) { use ButtonState::*; self.state.set(match self.state.get() { PressedLock => { match self.mode.get() { ButtonMode::Immediate => (), _ => self.ev_id.set(self.events.add(self, BUTTON_LONGPRESS_THRES)) } PressedUnlock }, PressedUnlock => { match self.mode.get() { ButtonMode::Continous => { self.ev_id.set(self.events.add(self, BUTTON_CONT_CYCLE)); Continous }, ButtonMode::Release => { self.long.set(true); PressedUnlock }, ButtonMode::Immediate => PressedUnlock } }, ReleaseLock => Idle, Continous => { let gs = get_gs(); gs.panels[gs.pidx].btn1_short(); self.ev_id.set(self.events.add(self, BUTTON_CONT_CYCLE)); Continous }, s => s }); } } impl<'a> Button<'a> { fn new(events: &'a EventTimer<'a>) -> Self { Button {state: Cell::new(ButtonState::Idle), long: Cell::new(false), ev_id: Cell::new(0), mode: Cell::new(ButtonMode::Release), events} } fn press(&'a self, mode: ButtonMode) { if let ButtonState::Idle = self.state.get() { self.mode.set(mode); self.state.set(ButtonState::PressedLock); self.long.set(false); self.ev_id.set(self.events.add(self, BUTTON_PRESSLOCK_PEROID)); } } fn release(&'a self) -> ButtonResult { match self.state.get() { ButtonState::PressedUnlock => { let mut r = ButtonResult::ShortPress; if let ButtonMode::Immediate = self.mode.get() { r = ButtonResult::FalseAlarm; } else { if self.long.get() { /* ev_id already dropped */ r = ButtonResult::LongPress; } else { self.events.drop(self.ev_id.get()); } } self.state.set(ButtonState::ReleaseLock); self.ev_id.set(self.events.add(self, BUTTON_PRESSLOCK_PEROID)); r }, ButtonState::Continous => { self.events.drop(self.ev_id.get()); self.state.set(ButtonState::ReleaseLock); self.ev_id.set(self.events.add(self, BUTTON_PRESSLOCK_PEROID)); ButtonResult::FalseAlarm }, _ => ButtonResult::FalseAlarm } } } impl<'a> Panel<'a> for TimePanel { fn btn1_press(&'a mut self) -> bool { use TimePanelState::*; get_gs().btn1.press(match self.state.get() { EditHr | EditMin | EditSec => { self.blink_enabled.set(false); self.update_output(); ButtonMode::Continous }, _ => ButtonMode::Release }); true } fn btn1_release_extra(&mut self) { use TimePanelState::*; match self.state.get() { EditHr | EditMin | EditSec => { self.blink_enabled.set(true); self.update_output(); }, _ => () } } fn btn1_short(&mut self) -> bool { use TimePanelState::*; { let tmp = &mut self.tmp; match self.state.get() { View => { self.state.set(Inactive); return false; }, /* yield to the next panel */ EditHr => inc_rotate(&mut tmp.hr, 0, 24), EditMin => inc_rotate(&mut tmp.min, 0, 60), EditSec => inc_rotate(&mut tmp.sec, 0, 60), Inactive => self.state.set(View) }; } self.update_output(); true } fn btn2_short(&mut self) -> bool { use TimePanelState::*; let s = match self.state.get() { View => { let tmp = &mut self.tmp; let time = &mut self.time; tmp.hr = time.hr; tmp.min = time.min; tmp.sec = time.sec; EditHr }, EditHr => EditMin, EditMin => EditSec, EditSec => { let tmp = &self.tmp; ds3231::DS3231(&get_gs().i2c) .write_time(tmp.hr, tmp.min, tmp.sec); self.time = *tmp; View }, s => s }; self.state.set(s); self.update_output(); true } fn update_output(&self) { use TimePanelState::*; let s = self.state.get(); get_gs().update_blinky( if self.blink_enabled.get() { match s { EditHr => 0b110000, EditMin => 0b001100, EditSec => 0b000011, _ => 0x0, } } else { 0x0 }); let gs = get_gs(); let time = &self.time; let tmp = &self.tmp; match s { View => gs.render3(time.hr, time.min, time.sec), _ => gs.render3(tmp.hr, tmp.min, tmp.sec) } gs.display(); } } impl TimePanel { fn update_clock(&mut self, clk: Option