summaryrefslogblamecommitdiff
path: root/src/main.rs
blob: 5af812e1e8e3e03fa1258016949f5ad7f657ba37 (plain) (tree)
1
2
3
4
5
6
7
8
9
          
                
                     


                                      
 
                                                 
                                

          

           
          
        
 
         






                                       
 






                                                 
                          
                                    


              
                      
             


            

 
                    
                                        
                                         
                                  
                                  
                                   
                     
                       
                           
                  

                               
                
                                








                         


               

 


















































































                                        













                                   











































                                                     





















                                                       


                                                          







                            





                                                

                                             

                                                



                                                           








                                        
                     




                                                    
                      
              


                               











                                                                             
                    

                  


                             
            





                                       



                                



                                      
                                                                 








                                                          
                         



                                     
                                                     

                                                    



         





                                                

                                             

                                                



                                                           








                                        
                     




                                                    
                      
              


                               











                                                                             
                    

                  


                             
            





                                      



                                



                                      
                                                                 












                                                          
                                                     



                                                         

 



                                  

                                         

                             

                                     

































                                                         
                                                     




                                 








































                                                        
 














                                               

                                   
                                               







                               
                          



                                  
           


                             
 

























                                                                              














                                                                 
                            


                                                              










                                                           
 
 































































                                                            

                  










                                                      




                                                       
     
 












                                                                        
 
                                     


                                                     



                                                                       



















                                                             
     
 
 
                            

                           
                            
                                     






                                                                 
                                           

                                           
                                                
                
                                                       

                

                                         
                                          

         
          









                                                             
          






                                         



























                                                     
                                    






                                         

 
                      
                        


                              

 
                    
                      
                                         
                                       

                                            




                              

                                           




                                                     
                                                    

                 



















                                                                           

                     
     

 



                                             

                                          

 

                                                   
 
                                      
                                 
                                 
                               
                               
                               
 

                              
 










                                                                                           
 




                                                                                                     


                                                                                               
 



                            
                           







                                                 
                                                                                              
      
 
           



                                                     
      
 
                                                                       


                                 

                                      
                                                    
                                                   


                                                     
                                                    

                                                      


                                                      

                                                  
                             

                                        
                                        

                                             
 
                                     
                             


                                                

                               





                                                                
                                                                
                                    
                                    
                                   
                                   
                                   





                                             
 
 




                                                                                    
                         
 

                                                         
                                                               

                                                     



           
 
#![no_std]
#![feature(asm)]
#![feature(const_fn)]

#[macro_use] extern crate stm32f103xx;
extern crate cortex_m;

use stm32f103xx::{Interrupt, Peripherals, gpioa};
use core::cell::{Cell, RefCell};

mod mutex;
mod i2c;
mod ds3231;
mod at24c;
mod tim;

#[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;
    }
}

struct ShiftRegister<'a> {
    gpioa: &'a gpioa::RegisterBlock,
    width: u8,
}

#[derive(Clone, Copy)]
struct Time {
    sec: u8,
    min: u8,
    hr: u8,
}

struct GlobalState {
    perip: Option<Peripherals<'static>>,
    disp: Option<ShiftRegister<'static>>,
    btn1: Option<Button<'static>>,
    btn2: Option<Button<'static>>,
    i2c: Option<i2c::I2C<'static>>,
    i2c_inited: bool,
    sync_cnt: Cell<u8>,
    buff: RefCell<[u8; 6]>,
    disp_on: bool,
    blinky: RefCell<[bool; 6]>,
    blink_state: Cell<bool>,
    pidx: usize,
    panels: [&'static Panel; 5],
}

struct Button<'a> {
    state: Cell<bool>,
    long: Cell<bool>,
    timer: tim::Timer<'a>
}

enum ButtonResult {
    FalseAlarm,
    ShortPress,
    LongPress
}

trait Panel {
    fn btn1_short(&self) -> bool {false}
    fn btn1_long(&self) -> bool {false}
    fn btn2_short(&self) -> bool {false}
    fn btn2_long(&self) -> bool {false}
    fn update_output(&self);
}

#[derive(PartialEq, Clone, Copy)]
enum TimePanelState {
    Inactive,
    View,
    EditHr,
    EditMin,
    EditSec,
}

struct TimePanel<'a> {
    gs: &'a GlobalState,
    state: Cell<TimePanelState>, 
    time: RefCell<Time>,
    tmp: RefCell<Time>
}

#[derive(Clone, Copy)]
struct Date {
    yr: u8,
    mon: u8,
    day: u8
}

#[derive(PartialEq, Clone, Copy)]
enum DatePanelState {
    Inactive,
    View,
    EditYr,
    EditMon,
    EditDay
}

struct DatePanel<'a> {
    gs: &'a GlobalState,
    state: Cell<DatePanelState>,
    date: RefCell<Date>,
    tmp: RefCell<Date>
}

#[derive(PartialEq, Clone, Copy)]
enum TempPanelState {
    Inactive,
    View
}

struct TempPanel<'a> {
    state: Cell<TempPanelState>,
    temp: Cell<ds3231::Temp>,
    gs: &'a GlobalState,
}

#[derive(PartialEq, Clone, Copy)]
enum CountdownPanelState {
    Inactive,
    View,
    EditWhole,
    Edit3,
    Edit2,
    Edit1,
    Edit0,
    Edit2m,
    Edit1m,
    OnGoing,
    OnGoingPaused,
    TimeUp
}

struct CountdownPanel<'a> {
    state: Cell<CountdownPanelState>,
    presets: RefCell<[[u8; 6]; 2]>,
    counter: Cell<u32>,
    didx: Cell<u8>,
    gs: &'a GlobalState,
}

#[derive(PartialEq, Clone, Copy)]
enum CountupPanelState {
    Inactive,
    View,
    OnGoing,
    OnGoingPaused
}

struct CountupPanel<'a> {
    state: Cell<CountupPanelState>,
    counter: Cell<u32>,
    gs: &'a GlobalState,
}

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> Button<'a> {
    fn new(timer: tim::Timer<'a>, thres: u32) -> Self {
        /* in milliseconds */
        timer.init(thres * (8_000_000 / 1000));
        Button {state: Cell::new(false),
                long: Cell::new(false),
                timer}
    }

    fn press(&self) {
        if !self.state.get() {
            self.state.set(true);
            self.long.set(false);
            self.timer.reset();
            self.timer.go();
        }
    }

    fn release(&self) -> ButtonResult {
        if self.state.get() {
            self.timer.stop();
            self.state.set(false);
            if self.long.get() { ButtonResult::LongPress }
            else { ButtonResult::ShortPress }
        } else { ButtonResult::FalseAlarm }
    }

    fn timeout(&self) {
        self.timer.stop();
        self.long.set(true);
    }
}

impl<'a> Panel for TimePanel<'a> {
    fn btn1_short(&self) -> bool {
        use TimePanelState::*;
        {
            let mut tmp = self.tmp.borrow_mut();
            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(&self) -> bool {
        use TimePanelState::*;
        let s = match self.state.get() {
            View => {
                let mut tmp = self.tmp.borrow_mut();
                let time = self.time.borrow();
                tmp.hr = time.hr;
                tmp.min = time.min;
                tmp.sec = time.sec;
                EditHr
            },
            EditHr => EditMin,
            EditMin => EditSec,
            EditSec => {
                let tmp = self.tmp.borrow();
                ds3231::DS3231(self.gs.i2c.as_ref().unwrap())
                                .write_time(&ds3231::Date{second: tmp.sec,
                                                         minute: tmp.min,
                                                         hour: tmp.hr,
                                                         day: 0,
                                                         date: 0,
                                                         month: 0,
                                                         year: 0,
                                                         am: false,
                                                         am_enabled: false});
                *self.time.borrow_mut() = *tmp;
                View
            },
            s => s
        };
        self.state.set(s);
        self.update_output();
        true
    }

    fn update_output(&self) {
        use TimePanelState::*;
        let s = self.state.get();
        self.gs.update_blinky(match s {
            EditHr => 0b110000,
            EditMin => 0b001100,
            EditSec => 0b000011,
            _ => 0x0,
        });
        let time = self.time.borrow();
        let tmp = self.tmp.borrow();
        match s {
            View => self.gs.render3(time.hr, time.min, time.sec),
            _ => self.gs.render3(tmp.hr, tmp.min, tmp.sec)
        };
        self.gs.display();
    }
}

impl<'a> TimePanel<'a> {
    fn update_clock(&self, clk: Option<Time>) {
        let mut time = self.time.borrow_mut();
        let gs = self.gs;
        match clk {
            Some(clk) => *time = clk,
            None => time.tick()
        }
        if self.state.get() == TimePanelState::View {
            gs.render3(time.hr, time.min, time.sec);
            gs.display();
        }
    }
}

impl<'a> Panel for DatePanel<'a> {
    fn btn1_short(&self) -> bool {
        use DatePanelState::*;
        {
            let mut tmp = self.tmp.borrow_mut();
            match self.state.get() {
                View => {
                    self.state.set(Inactive);
                    return false;
                }, /* yield to the next panel */
                EditYr => inc_rotate(&mut tmp.yr, 0, 100),
                EditMon => inc_rotate(&mut tmp.mon, 1, 13),
                EditDay => inc_rotate(&mut tmp.day, 1, 32),
                Inactive => self.state.set(View)
            };
        }
        self.update_output();
        true
    }

    fn btn2_short(&self) -> bool {
        use DatePanelState::*;
        let s = match self.state.get() {
            View => {
                let date = self.date.borrow();
                let mut tmp = self.tmp.borrow_mut();
                tmp.yr = date.yr;
                tmp.mon = date.mon;
                tmp.day = date.day;
                EditYr
            },
            EditYr => EditMon,
            EditMon => EditDay,
            EditDay => {
                let tmp = self.tmp.borrow();
                ds3231::DS3231(self.gs.i2c.as_ref().unwrap())
                                .write_date(&ds3231::Date{second: 0,
                                                         minute: 0,
                                                         hour: 0,
                                                         day: 0,
                                                         date: tmp.day,
                                                         month: tmp.mon,
                                                         year: tmp.yr,
                                                         am: false,
                                                         am_enabled: false});
                *self.date.borrow_mut() = *tmp;
                View
            },
            s => s
        };
        self.state.set(s);
        self.update_output();
        true
    }

    fn update_output(&self) {
        use DatePanelState::*;
        let s = self.state.get();
        self.gs.update_blinky(match s{
            EditYr => 0b110000,
            EditMon => 0b001100,
            EditDay => 0b000011,
            _ => 0x0,
        });
        let date = self.date.borrow();
        let tmp = self.tmp.borrow();
        match s {
            View => self.gs.render3(date.yr, date.mon, date.day),
            _ => self.gs.render3(tmp.yr, tmp.mon, tmp.day)
        };
        self.gs.display();
    }
}

impl<'a> DatePanel<'a> {
    fn update_clock(&self, d: Option<Date>) {
        let mut date = self.date.borrow_mut();
        match d {
            Some(d) => *date = d,
            None => ()
        }
        if self.state.get() == DatePanelState::View {
            self.gs.render3(date.yr, date.mon, date.day);
            self.gs.display();
        }
    }
}

impl<'a> Panel for TempPanel<'a> {
    fn btn1_short(&self) -> bool {
        use TempPanelState::*;
        match self.state.get() {
            View => {
                self.state.set(Inactive);
                return false;
            },
            Inactive => {
                self.state.set(View);
            }
        }
        self.update_output();
        true
    }

    fn update_output(&self) {
        let mut buff: [u8; 6] = [0xf; 6];
        let temp = self.temp.get();
        let q = temp.quarter * 25;
        let zeros: [u8; 4] = [0; 4];
        let mut c = if temp.cels > 0 { temp.cels } else {
            buff[2..6].clone_from_slice(&zeros);
            -temp.cels
        } as u8;
        let mut i = 2;
        while c > 0 {
            buff[i] = c % 10;
            c /= 10;
            i += 1;
        }
        buff[1] = q / 10;
        buff[0] = q - 10 * buff[1];
        *self.gs.buff.borrow_mut() = buff;
        self.gs.display();
    }
}

impl<'a> TempPanel<'a> {
    fn update_clock(&self, temp: Option<ds3231::Temp>) {
        match temp {
            Some(temp) => self.temp.set(temp),
            None => ()
        }
        if self.state.get() == TempPanelState::View {
            self.update_output();
        }
    }
}

impl<'a> Panel for CountdownPanel<'a> {
    fn btn1_short(&self) -> bool {
        use CountdownPanelState::*;
        let tim = self.gs.perip.as_ref().unwrap().TIM3;
        let timer = tim::Timer(tim);
        {
            let didx = self.didx.get();
            let mut presets = self.presets.borrow_mut();
            let len = presets.len() as u8;
            let p = &mut presets[didx as usize];
            match self.state.get() {
                View => {
                    self.state.set(Inactive);
                    return false;
                },
                EditWhole => {
                    let mut nidx = didx;
                    inc_rotate(&mut nidx, 0, len);
                    self.didx.set(nidx);
                },
                Edit3 => inc_rotate(&mut p[5], 0, 10),
                Edit2 => inc_rotate(&mut p[4], 0, 10),
                Edit1 => inc_rotate(&mut p[3], 0, 10),
                Edit0 => inc_rotate(&mut p[2], 0, 10),
                Edit2m => inc_rotate(&mut p[1], 0, 10),
                Edit1m => inc_rotate(&mut p[0], 0, 10),
                OnGoing => {
                    timer.stop();
                    self.state.set(OnGoingPaused);
                },
                OnGoingPaused => {
                    timer.go();
                    self.state.set(OnGoing);
                },
                TimeUp => self.state.set(View),
                Inactive => self.state.set(View)
            }
        }
        self.update_output();
        true
    }

    fn btn2_long(&self) -> bool {
        use CountdownPanelState::*;
        self.state.set(match self.state.get() {
            OnGoing => OnGoing,
            OnGoingPaused => OnGoingPaused,
            TimeUp => TimeUp,
            _ => {
                self.go();
                OnGoing
            },
        });
        self.update_output();
        true
    }

    fn btn2_short(&self) -> bool {
        use CountdownPanelState::*;
        self.state.set(match self.state.get() {
            View => EditWhole,
            EditWhole => Edit3,
            Edit3 => Edit2,
            Edit2 => Edit1,
            Edit1 => Edit0,
            Edit0 => Edit2m,
            Edit2m => Edit1m,
            Edit1m => {
                self.go();
                OnGoing
            },
            OnGoingPaused => View,
            s => s
        });
        self.update_output();
        true
    }

    fn update_output(&self) {
        use CountdownPanelState::*;
        let s = self.state.get();
        self.gs.update_blinky(match s {
            EditWhole => 0b111111,
            Edit3 => 0b100000,
            Edit2 => 0b010000,
            Edit1 => 0b001000,
            Edit0 => 0b000100,
            Edit2m => 0b000010,
            Edit1m => 0b000001,
            TimeUp => 0b111111,
            _ => 0x0
        });
        match s {
            OnGoing | OnGoingPaused => self.gs.render1(self.counter.get()),
            _ => {
                let preset = &self.presets.borrow()[self.didx.get() as usize];
                self.gs.render(preset);
            }
        }
        self.gs.display();
    }
}

impl<'a> CountdownPanel<'a> {
    fn go(&self) {
        use CountdownPanelState::*;
        let tim = self.gs.perip.as_ref().unwrap().TIM3;
        let timer = tim::Timer(tim);
        let p = &self.presets.borrow()[self.didx.get() as usize];
        let mut x: u32 = 0;
        for v in p.iter().rev() {
            x *= 10;
            x += *v as u32;
        }
        self.counter.set(x);
        timer.reset();
        timer.go();
    }

    fn update_clock(&self) {
        if self.state.get() == CountdownPanelState::Inactive {
            return
        }
        let x = self.counter.get();
        if x == 0 {
            let tim = self.gs.perip.as_ref().unwrap().TIM3;
            let timer = tim::Timer(tim);
            timer.stop();
            self.state.set(CountdownPanelState::TimeUp);
        } else {
            self.counter.set(x - 1);
        }
        self.update_output();
    }
}

impl<'a> Panel for CountupPanel<'a> {
    fn btn1_short(&self) -> bool {
        use CountupPanelState::*;
        let tim = self.gs.perip.as_ref().unwrap().TIM3;
        let timer = tim::Timer(tim);
        match self.state.get() {
            View => {
                self.state.set(Inactive);
                return false;
            },
            OnGoing => {
                timer.stop();
                self.state.set(OnGoingPaused);
            },
            OnGoingPaused => {
                timer.go();
                self.state.set(OnGoing);
            },
            Inactive => self.state.set(View)
        }
        self.update_output();
        true
    }

    fn btn2_short(&self) -> bool {
        use CountupPanelState::*;
        let tim = self.gs.perip.as_ref().unwrap().TIM3;
        let timer = tim::Timer(tim);
        self.state.set(match self.state.get() {
            View => {
                self.counter.set(0);
                timer.reset();
                timer.go();
                OnGoing
            },
            OnGoingPaused => View,
            s => s
        });
        self.update_output();
        true
    }

    fn update_output(&self) {
        use CountupPanelState::*;
        self.gs.update_blinky(match self.state.get() {
            View => 0b111111,
            _ => 0x0
        });
        self.gs.render1(self.counter.get());
        self.gs.display();
    }
}

impl<'a> CountupPanel<'a> {
    fn update_clock(&self) {
        if self.state.get() == CountupPanelState::Inactive {
            return
        }
        let x = self.counter.get() + 1;
        self.counter.set(if x > 999999 {0} else {x});
        self.update_output();
    }
}

impl GlobalState {

    fn render(&self, nbuff: &[u8; 6]) {
        self.buff.borrow_mut().copy_from_slice(nbuff);
    }
    fn render1(&self, mut n: u32) {
        let mut buff = self.buff.borrow_mut();
        for i in 0..buff.len() {
            buff[i] = (n % 10) as u8;
            n /= 10;
        }
    }

    fn render3(&self, n1: u8, n2: u8, n3: u8) {
        let mut buff = self.buff.borrow_mut();
        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;
    }

    fn display(&self) {
        let mut buff = *self.buff.borrow();
        let b = self.blink_state.get();
        let bs = self.blinky.borrow();
        if self.disp_on {
            for (i, v) in buff.iter_mut().enumerate() {
                if b && bs[i] { *v = 0xf; }
            }
        } else {
            for i in buff.iter_mut() { *i = 0xf; }
        }
        self.disp.as_ref().unwrap().output_bits(digits2bcds(&buff[..]));
    }

    fn update_blinky(&self, nb: u8) {
        let tim4 = self.perip.as_ref().unwrap().TIM4;
        let timer = tim::Timer(tim4);
        let en = timer.is_enabled();
        let flag = nb == 0; /* if nothing is blinking */
        for (i, v) in self.blinky.borrow_mut().iter_mut().enumerate() {
            *v = ((nb >> i) & 1) == 1;
        }
        if en && flag {
            self.blink_state.set(false);
            timer.stop();
        } else if !en && !flag {
            self.blink_state.set(false);
            timer.reset();
            timer.go();
        }
    }
    
    fn digits_countup(&self) {
        self.display();
        let mut buff = *self.buff.borrow();
        let mut i = 0;
        let mut carry = 1;
        while carry > 0 && i < buff.len() {
            buff[i] += carry;
            carry = if buff[i] > 9 {buff[i] = 0; 1} else {0};
            i += 1;
        }
    }

 
    fn update_clock(&self) {
        let mut clk = None;
        let mut d = None;
        let mut temp = None;
        if self.sync_cnt.get() == 0 {
            let rtc = ds3231::DS3231(self.i2c.as_ref().unwrap());
            let ds3231::Date{second: sec,
                            minute: min,
                            hour: hr,
                            date: day,
                            month: mon,
                            year: yr, ..} = rtc.read_fulldate();
            self.sync_cnt.set(SYNC_PERIOD);
            clk = Some(Time{sec, min, hr});
            d = Some(Date{yr, mon, day});
            temp = Some(rtc.read_temperature());
        } else {
            self.sync_cnt.set(self.sync_cnt.get() - 1);
        }
        unsafe {
            TIME_PANEL.update_clock(clk);
            DATE_PANEL.update_clock(d);
            TEMP_PANEL.update_clock(temp);
        }
    }
        /*
        let rtc = ds3231::DS3231(self.i2c.as_ref().unwrap());
        rtc.write_fulldate(&ds3231::Date{second: 30,
                                minute: 23,
                                hour: 18,
                                day: 2,
                                date: 10,
                                month: 10,
                                year: 17,
                                am: false,
                                am_enabled: false});
        */
        /*
        let rom = ROM.as_ref().unwrap();
        let mut buf: [u8; 64] = [23; 64];
        rom.write(23, 64, &buf);
        let mut buf2: [u8; 80] = [0; 80];
        rom.read(20, 80, &mut buf2);
        */
        /*
        let x = mutex::Mutex::new(42);
        {
            let y = x.lock();
            let z = *y + 1;
            let w = z;
        }
        */
    #[inline(always)]
    fn tim4_callback() {
        let gs = get_gs();
        {
            let p = gs.perip.as_ref().unwrap();
            p.TIM4.sr.modify(|_, w| w.uif().clear());
        }
        gs.blink_state.set(!gs.blink_state.get());
        gs.display();
    }

    #[inline(always)]
    fn tim3_callback() {
        let gs = get_gs();
        {
            let p = gs.perip.as_ref().unwrap();
            p.TIM3.sr.modify(|_, w| w.uif().clear());
        }
        unsafe {
            CD_PANEL.update_clock();
            CU_PANEL.update_clock();
        }
    }
}

#[inline(always)]
fn get_gs() -> &'static mut GlobalState {
    unsafe {&mut GS}
}

fn systick_handler() {
    // digits_countup();
    let gs = get_gs();
    if !gs.i2c_inited {return}
    gs.update_clock();
}

fn exti3_handler() {
    let gs = get_gs();
    let btn1 = gs.btn1.as_ref().unwrap();
    let p = gs.perip.as_ref().unwrap();
    p.EXTI.pr.write(|w| w.pr3().set_bit());
    let x = p.GPIOA.idr.read().idr3().bit();
    if !x {
        btn1.press();
    } else {
        let gs = get_gs();
        match btn1.release() {
            ButtonResult::FalseAlarm => (),
            ButtonResult::ShortPress => {
                if !gs.panels[gs.pidx].btn1_short() {
                    gs.pidx += 1;
                    if gs.pidx == gs.panels.len() {
                        gs.pidx = 0;
                    }
                    gs.panels[gs.pidx].btn1_short();
                }
            },
            ButtonResult::LongPress => { }
        }
        gs.display();
    }
}

fn exti4_handler() {
    let gs = get_gs();
    let btn2 = gs.btn2.as_ref().unwrap();
    let p = gs.perip.as_ref().unwrap();
    p.EXTI.pr.write(|w| w.pr4().set_bit());
    let x = p.GPIOA.idr.read().idr4().bit();
    if !x {
        btn2.press();
    } else {
        let gs = get_gs();
        match btn2.release() {
            ButtonResult::FalseAlarm => (),
            ButtonResult::ShortPress => {gs.panels[gs.pidx].btn2_short();},
            ButtonResult::LongPress => {gs.panels[gs.pidx].btn2_long();}
        }
        gs.display();
    }
}

fn tim2_handler() {
    let gs = get_gs();
    let p = gs.perip.as_ref().unwrap();
    p.TIM2.sr.modify(|_, w| w.uif().clear());
    //gs.btn1.as_ref().unwrap().timeout();
    gs.btn2.as_ref().unwrap().timeout();
}

fn tim4_handler() { GlobalState::tim4_callback(); }
fn tim3_handler() { GlobalState::tim3_callback(); }

exception!(SYS_TICK, systick_handler);
interrupt!(EXTI4, exti4_handler);
interrupt!(EXTI3, exti3_handler);
interrupt!(TIM2, tim2_handler);
interrupt!(TIM4, tim4_handler);
interrupt!(TIM3, tim3_handler);

const SYNC_PERIOD: u8 = 10;
const BLINK_PERIOD: u32 = 500;

static mut TIME_PANEL: TimePanel = TimePanel{state: Cell::new(TimePanelState::View),
                                        tmp: RefCell::new(Time{sec: 0, min: 0, hr: 0}),
                                        time: RefCell::new(Time{sec: 0, min: 0, hr: 0}),
                                        gs: unsafe{&GS}};
static mut DATE_PANEL: DatePanel = DatePanel{state: Cell::new(DatePanelState::Inactive),
                                        tmp: RefCell::new(Date{yr: 0, mon: 1, day: 1}),
                                        date: RefCell::new(Date{yr: 0, mon: 1, day: 1}),
                                        gs: unsafe{&GS}};
static mut TEMP_PANEL: TempPanel = TempPanel{state: Cell::new(TempPanelState::Inactive),
                                        temp: Cell::new(ds3231::Temp{cels: 0, quarter: 0}),
                                        gs: unsafe{&GS}};

static mut CD_PANEL: CountdownPanel = CountdownPanel{state: Cell::new(CountdownPanelState::Inactive),
                                        presets: RefCell::new([[0; 6]; 2]),
                                        counter: Cell::new(0),
                                        didx: Cell::new(0),
                                        gs: unsafe{&GS}};
static mut CU_PANEL: CountupPanel = CountupPanel{state: Cell::new(CountupPanelState::Inactive),
                                        counter: Cell::new(0),
                                        gs: unsafe{&GS}};

static mut GS: GlobalState =
    GlobalState{perip: None,
                disp: None,
                btn1: None,
                btn2: None,
                i2c: None,
                i2c_inited: false,
                sync_cnt: Cell::new(0),
                buff: RefCell::new([0; 6]),
                disp_on: true,
                blinky: RefCell::new([false; 6]),
                blink_state: Cell::new(false),
                pidx: 0,
                panels: unsafe {[&TIME_PANEL, &DATE_PANEL, &TEMP_PANEL, &CD_PANEL, &CU_PANEL]}
    };

fn init() {
    let gs = get_gs();
    let p = {
        gs.perip = Some(unsafe {Peripherals::all()});
        gs.perip.as_ref().unwrap()
    };

    p.SYST.set_clock_source(cortex_m::peripheral::SystClkSource::Core);
    p.SYST.set_reload(8_000_000);
    p.SYST.enable_interrupt();
    p.SYST.enable_counter();

    /* enable GPIOA, GPIOB and AFIO */
    p.RCC.apb2enr.modify(|_, w| w.iopaen().enabled()
                                .iopben().enabled()
                                .afioen().enabled()
                                .tim1en().enabled());

    p.RCC.apb1enr.modify(|_, w| w.tim2en().enabled()
                                 .tim4en().enabled()
                                 .tim3en().enabled());

    /* GPIO */
    /* enable PA0-2 for manipulating shift register */
    p.GPIOA.odr.modify(|_, w| w.odr3().set_bit()
                               .odr4().set_bit());
    p.GPIOA.crl.modify(|_, w|
        w.mode0().output().cnf0().push()
         .mode1().output().cnf1().push()
         .mode2().output().cnf2().push()
         .mode3().input().cnf3().bits(0b10)
         .mode4().input().cnf4().bits(0b10));

    /* enable PB6 and PB7 for I2C1 */
    p.GPIOB.crl.modify(|_, w|
        w.mode6().output50().cnf6().alt_open()
         .mode7().output50().cnf7().alt_open());

    /* I2C */
    /* enable and reset I2C1 */
    p.RCC.apb1enr.modify(|_, w| w.i2c1en().enabled());
    p.RCC.apb1rstr.modify(|_, w| w.i2c1rst().set_bit());
    p.RCC.apb1rstr.modify(|_, w| w.i2c1rst().clear_bit());

    /* NVIC & EXTI */
    p.AFIO.exticr1.write(|w| unsafe { w.exti3().bits(0b0000) });
    p.AFIO.exticr2.write(|w| unsafe { w.exti4().bits(0b0000) });
    p.NVIC.enable(Interrupt::EXTI3);
    p.NVIC.enable(Interrupt::EXTI4);
    p.NVIC.enable(Interrupt::TIM2);
    p.NVIC.enable(Interrupt::TIM4);
    p.NVIC.enable(Interrupt::TIM3);
    p.EXTI.imr.write(|w| w.mr3().set_bit()
                          .mr4().set_bit());
    p.EXTI.rtsr.write(|w| w.tr3().set_bit()
                           .tr4().set_bit());
    p.EXTI.ftsr.write(|w| w.tr3().set_bit()
                           .tr4().set_bit());


    gs.i2c = Some(i2c::I2C(p.I2C1));
    gs.disp = Some(ShiftRegister::new(p.GPIOA, 24));
    gs.i2c.as_ref().unwrap().init(p.RCC, 0x01, 400_000, i2c::DutyType::DUTY1, true);
    //i2c.init(0x01, 100_000, i2c::DutyType::DUTY1, false);
    gs.disp.as_ref().unwrap().output_bits(0);
    gs.i2c_inited = true;

    gs.btn1 = Some(Button::new(tim::Timer(p.TIM5), 300));
    gs.btn2 = Some(Button::new(tim::Timer(p.TIM2), 300));
    tim::Timer(p.TIM4).init(BLINK_PERIOD * (8_000_000 / 1000));
    tim::Timer(p.TIM3).init(10 * (8_000_000 / 1000));
    gs.update_clock();
}

fn main() {
    init();
}