use field_offset::offset_of; use stm32l4xx_hal::prelude::{ _embedded_hal_blocking_i2c_Write, _embedded_hal_blocking_i2c_WriteRead, }; use tock_registers::{ interfaces::{ReadWriteable, Readable, Writeable}, register_bitfields, register_structs, registers::InMemoryRegister, }; register_bitfields![ u8, T_SECOND [ Value OFFSET(0) NUMBITS(7) [], ], T_MINUTE [ Value OFFSET(0) NUMBITS(7) [], ], T_HOUR [ Value OFFSET(0) NUMBITS(6) [], n24 OFFSET(6) NUMBITS(1) [], ], T_WEEKDAY [ Value OFFSET(0) NUMBITS(3) [], ], T_DAY [ Value OFFSET(0) NUMBITS(6) [], ], T_MONTH [ Value OFFSET(0) NUMBITS(5) [], Century OFFSET(7) NUMBITS(1) [], ], T_YEAR [ Value OFFSET(0) NUMBITS(8) [], ], A_SECOND [ Value OFFSET(0) NUMBITS(7) [], M1 OFFSET(7) NUMBITS(1) [], ], A_MINUTE [ Value OFFSET(0) NUMBITS(7) [], M2 OFFSET(7) NUMBITS(1) [], ], A_HOUR [ Value OFFSET(0) NUMBITS(6) [], n24 OFFSET(6) NUMBITS(1) [], M3 OFFSET(7) NUMBITS(1) [], ], A_DAY_DATE [ Value OFFSET(0) NUMBITS(6) [], nDT OFFSET(6) NUMBITS(1) [], M4 OFFSET(7) NUMBITS(1) [], ], CONTROL_1 [ A1IE 0, A2IE 1, INTCN 2, RS1 3, RS2 4, CONV 5, BBSQW 6, nEOSC 7, ], CONTROL_2 [ A1F 0, A2F 1, BSY 2, EN32KHZ 3, OSF 7, ], AGING_OFFSET [ Value OFFSET(0) NUMBITS(7) [], Sign OFFSET(7) NUMBITS(1) [], ], TEMP_MSB [ Value OFFSET(0) NUMBITS(7) [], Sign OFFSET(7) NUMBITS(1) [], ], TEMP_LSB [ Value OFFSET(6) NUMBITS(2) [], ], ]; register_structs! { Ds3231Registers { (0x00 => second: InMemoryRegister), (0x01 => minute: InMemoryRegister), (0x02 => hour: InMemoryRegister), (0x03 => weekday: InMemoryRegister), (0x04 => day: InMemoryRegister), (0x05 => month: InMemoryRegister), (0x06 => year: InMemoryRegister), (0x07 => a1_second: InMemoryRegister), (0x08 => a1_minute: InMemoryRegister), (0x09 => a1_hour: InMemoryRegister), (0x0A => a1_day_date: InMemoryRegister), (0x0B => a2_minute: InMemoryRegister), (0x0C => a2_hour: InMemoryRegister), (0x0D => a2_day_date: InMemoryRegister), (0x0E => control_1: InMemoryRegister), (0x0F => control_2: InMemoryRegister), (0x10 => aging_offset: InMemoryRegister), (0x11 => temp_msb: InMemoryRegister), (0x12 => temp_lsb: InMemoryRegister), (0x13 => @END), } } pub enum Weekday { Sunday = 0, Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, } impl From for Weekday { fn from(value: u8) -> Self { match value { 0 => Weekday::Sunday, 1 => Weekday::Monday, 2 => Weekday::Tuesday, 3 => Weekday::Wednesday, 4 => Weekday::Thursday, 5 => Weekday::Friday, 6 => Weekday::Saturday, _ => panic!(), } } } pub fn init(address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write) { let ctrl1: InMemoryRegister = InMemoryRegister::new(0); let ctrl2: InMemoryRegister = InMemoryRegister::new(0); ctrl1.modify( CONTROL_1::nEOSC::CLEAR // Enable internal oscillator on VBAT + CONTROL_1::BBSQW::CLEAR // Disable outputs on VBAT + CONTROL_1::RS1::CLEAR // Set square wave output to 1Hz + CONTROL_1::RS2::CLEAR // Set square wave output to 1Hz + CONTROL_1::INTCN::CLEAR // Enable square wave output + CONTROL_1::A1IE::CLEAR // Disable alarm 1 + CONTROL_1::A2IE::CLEAR, // Disable alarm 2 ); // Disable 32kHz output ctrl2.modify(CONTROL_2::EN32KHZ::CLEAR); let buffer: [u8; 3] = [ offset_of!(Ds3231Registers => control_1).get_byte_offset() as u8, ctrl1.get(), ctrl2.get(), ]; match i2c.write(address, &buffer) { Ok(_) => (), Err(_) => panic!(), } } pub fn set_time( address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write, second: u32, minute: u32, hour: u32, ) { let second_reg: InMemoryRegister = InMemoryRegister::new(0); let minute_reg: InMemoryRegister = InMemoryRegister::new(0); let hour_reg: InMemoryRegister = InMemoryRegister::new(0); second_reg.write(T_SECOND::Value.val(decimal_to_bcd(second))); minute_reg.write(T_MINUTE::Value.val(decimal_to_bcd(minute))); hour_reg.write(T_HOUR::Value.val(decimal_to_bcd(hour))); let buffer: [u8; 4] = [ offset_of!(Ds3231Registers => second).get_byte_offset() as u8, second_reg.get(), minute_reg.get(), hour_reg.get(), ]; match i2c.write(address, &buffer) { Ok(_) => (), Err(_) => panic!(), } } pub fn set_date( address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write, weekday: Weekday, day: u32, month: u32, year: u32, century: u32, ) { let weekday_reg: InMemoryRegister = InMemoryRegister::new(0); let day_reg: InMemoryRegister = InMemoryRegister::new(0); let month_reg: InMemoryRegister = InMemoryRegister::new(0); let year_reg: InMemoryRegister = InMemoryRegister::new(0); weekday_reg.write(T_WEEKDAY::Value.val(decimal_to_bcd(weekday as u32))); day_reg.write(T_DAY::Value.val(decimal_to_bcd(day))); month_reg.modify( T_MONTH::Value.val(decimal_to_bcd(month)) + T_MONTH::Century.val(decimal_to_bcd(century)), ); year_reg.write(T_YEAR::Value.val(decimal_to_bcd(year))); let buffer: [u8; 5] = [ offset_of!(Ds3231Registers => weekday).get_byte_offset() as u8, weekday_reg.get(), day_reg.get(), month_reg.get(), year_reg.get(), ]; match i2c.write(address, &buffer) { Ok(_) => (), Err(_) => panic!(), } } pub fn get_time( address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_WriteRead, ) -> (u32, u32, u32) { let mut buffer: [u8; 3] = [0; 3]; match i2c.write_read( address, &[offset_of!(Ds3231Registers => second).get_byte_offset() as u8], &mut buffer, ) { Ok(_) => (), Err(_) => panic!(), } let second_reg: InMemoryRegister = InMemoryRegister::new(buffer[0]); let minute_reg: InMemoryRegister = InMemoryRegister::new(buffer[1]); let hour_reg: InMemoryRegister = InMemoryRegister::new(buffer[2]); let second = bcd_to_decimal(second_reg.read(T_SECOND::Value)); let minute = bcd_to_decimal(minute_reg.read(T_MINUTE::Value)); let hour = bcd_to_decimal(hour_reg.read(T_HOUR::Value)); (second, minute, hour) } pub fn get_date( address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_WriteRead, ) -> (Weekday, u32, u32, u32, u32) { let mut buffer: [u8; 4] = [0; 4]; match i2c.write_read( address, &[offset_of!(Ds3231Registers => weekday).get_byte_offset() as u8], &mut buffer, ) { Ok(_) => (), Err(_) => panic!(), } let weekday_reg: InMemoryRegister = InMemoryRegister::new(buffer[0]); let day_reg: InMemoryRegister = InMemoryRegister::new(buffer[1]); let month_reg: InMemoryRegister = InMemoryRegister::new(buffer[2]); let year_reg: InMemoryRegister = InMemoryRegister::new(buffer[3]); let weekday: Weekday = weekday_reg.read(T_WEEKDAY::Value).into(); let day = bcd_to_decimal(day_reg.read(T_DAY::Value)); let month = bcd_to_decimal(month_reg.read(T_MONTH::Value)); let year = bcd_to_decimal(year_reg.read(T_YEAR::Value)); let century = bcd_to_decimal(month_reg.read(T_MONTH::Century)); (weekday, day, month, year, century) } #[inline] fn bcd_to_decimal(value: u8) -> u32 { (((value >> 4) * 10) + (value & 0xF)) as u32 } #[inline] fn decimal_to_bcd(value: u32) -> u8 { (((value / 10) << 4) | (value % 10)) as u8 } pub fn in_dst(weekday: Weekday, day: u32, month: u32, hour_24: u32) -> bool { let prev_sunday: i32 = day as i32 - weekday as i32; match month { 0..=2 | 12.. => false, 4..=10 => true, 3 => match prev_sunday { ..8 => false, 15.. => true, d if d == day as i32 => hour_24 >= 2, _ => true, }, 11 => match prev_sunday { ..=0 => true, 8.. => false, d if d == day as i32 => hour_24 < 2, _ => false, }, } } #[cfg(test)] mod test { use super::*; #[test] fn dst_test() { // 2020 - begins Mar 8th @ 2AM (Sunday), ends Nov 1st @ 2AM (Sunday) assert!(in_dst(Weekday::Sunday, 8, 3, 1) == false); assert!(in_dst(Weekday::Sunday, 8, 3, 2) == true); assert!(in_dst(Weekday::Sunday, 8, 3, 3) == true); assert!(in_dst(Weekday::Sunday, 1, 11, 1) == true); assert!(in_dst(Weekday::Sunday, 1, 11, 2) == false); assert!(in_dst(Weekday::Sunday, 1, 11, 3) == false); // 2021 - begins Mar 14th @ 2AM (Sunday), ends Nov 7th @ 2AM (Sunday) assert!(in_dst(Weekday::Sunday, 14, 3, 1) == false); assert!(in_dst(Weekday::Sunday, 14, 3, 2) == true); assert!(in_dst(Weekday::Sunday, 14, 3, 3) == true); assert!(in_dst(Weekday::Sunday, 7, 11, 1) == true); assert!(in_dst(Weekday::Sunday, 7, 11, 2) == false); assert!(in_dst(Weekday::Sunday, 7, 11, 3) == false); // 2022 - begins Mar 13th @ 2AM (Sunday), ends Nov 6th @ 2AM (Sunday) assert!(in_dst(Weekday::Sunday, 13, 3, 1) == false); assert!(in_dst(Weekday::Sunday, 13, 3, 2) == true); assert!(in_dst(Weekday::Sunday, 13, 3, 3) == true); assert!(in_dst(Weekday::Sunday, 6, 11, 1) == true); assert!(in_dst(Weekday::Sunday, 6, 11, 2) == false); assert!(in_dst(Weekday::Sunday, 6, 11, 3) == false); // 2023 - begins Mar 12th @ 2AM (Sunday), ends Nov 5th @ 2AM (Sunday) assert!(in_dst(Weekday::Sunday, 12, 3, 1) == false); assert!(in_dst(Weekday::Sunday, 12, 3, 2) == true); assert!(in_dst(Weekday::Sunday, 12, 3, 3) == true); assert!(in_dst(Weekday::Sunday, 5, 11, 1) == true); assert!(in_dst(Weekday::Sunday, 5, 11, 2) == false); assert!(in_dst(Weekday::Sunday, 5, 11, 3) == false); // Sanity check other dates in 2021 assert!(in_dst(Weekday::Friday, 1, 1, 0) == false); assert!(in_dst(Weekday::Monday, 1, 2, 0) == false); assert!(in_dst(Weekday::Monday, 1, 3, 0) == false); assert!(in_dst(Weekday::Thursday, 1, 4, 0) == true); assert!(in_dst(Weekday::Saturday, 1, 5, 0) == true); assert!(in_dst(Weekday::Tuesday, 1, 6, 0) == true); assert!(in_dst(Weekday::Thursday, 1, 7, 0) == true); assert!(in_dst(Weekday::Sunday, 1, 8, 0) == true); assert!(in_dst(Weekday::Wednesday, 1, 9, 0) == true); assert!(in_dst(Weekday::Friday, 1, 10, 0) == true); assert!(in_dst(Weekday::Monday, 1, 11, 0) == true); assert!(in_dst(Weekday::Wednesday, 1, 12, 0) == false); } }