|
@@ -1,7 +1,9 @@
|
|
|
use field_offset::offset_of;
|
|
|
-use stm32l4xx_hal::prelude::{_embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write};
|
|
|
+use stm32l4xx_hal::prelude::{
|
|
|
+ _embedded_hal_blocking_i2c_Write, _embedded_hal_blocking_i2c_WriteRead,
|
|
|
+};
|
|
|
use tock_registers::{
|
|
|
- interfaces::{ReadWriteable, Readable},
|
|
|
+ interfaces::{ReadWriteable, Readable, Writeable},
|
|
|
register_bitfields, register_structs,
|
|
|
registers::InMemoryRegister,
|
|
|
};
|
|
@@ -18,10 +20,10 @@ register_bitfields![
|
|
|
Value OFFSET(0) NUMBITS(6) [],
|
|
|
n24 OFFSET(6) NUMBITS(1) [],
|
|
|
],
|
|
|
- T_DAY [
|
|
|
+ T_WEEKDAY [
|
|
|
Value OFFSET(0) NUMBITS(3) [],
|
|
|
],
|
|
|
- T_DATE [
|
|
|
+ T_DAY [
|
|
|
Value OFFSET(0) NUMBITS(6) [],
|
|
|
],
|
|
|
T_MONTH [
|
|
@@ -84,8 +86,8 @@ register_structs! {
|
|
|
(0x00 => second: InMemoryRegister<u8, T_SECOND::Register>),
|
|
|
(0x01 => minute: InMemoryRegister<u8, T_MINUTE::Register>),
|
|
|
(0x02 => hour: InMemoryRegister<u8, T_HOUR::Register>),
|
|
|
- (0x03 => weekday: InMemoryRegister<u8, T_DAY::Register>),
|
|
|
- (0x04 => day: InMemoryRegister<u8, T_DATE::Register>),
|
|
|
+ (0x03 => weekday: InMemoryRegister<u8, T_WEEKDAY::Register>),
|
|
|
+ (0x04 => day: InMemoryRegister<u8, T_DAY::Register>),
|
|
|
(0x05 => month: InMemoryRegister<u8, T_MONTH::Register>),
|
|
|
(0x06 => year: InMemoryRegister<u8, T_YEAR::Register>),
|
|
|
(0x07 => a1_second: InMemoryRegister<u8, A_SECOND::Register>),
|
|
@@ -114,33 +116,207 @@ pub enum Weekday {
|
|
|
Saturday = 6,
|
|
|
}
|
|
|
|
|
|
+impl From<u8> 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!(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
impl Ds3231Registers {
|
|
|
- fn i2c_read_regs(&mut self, i2c: &mut impl _embedded_hal_blocking_i2c_Read) {}
|
|
|
- fn set_time(second: &u32, minute: &u32, hour: &u32) {}
|
|
|
- fn set_date(weekday: &Weekday, day: &u32, month: &u32, year: &u32, century: &u32) {}
|
|
|
- fn get_time(second: &mut u32, minute: &mut u32, hour: &mut u32) {}
|
|
|
- fn get_date(
|
|
|
- weekday: &mut Weekday,
|
|
|
- day: &mut u32,
|
|
|
- month: &mut u32,
|
|
|
- year: &mut u32,
|
|
|
- century: &mut u32,
|
|
|
- ) {
|
|
|
+ const fn default() -> Self {
|
|
|
+ Self {
|
|
|
+ second: InMemoryRegister::new(0x00),
|
|
|
+ minute: InMemoryRegister::new(0x00),
|
|
|
+ hour: InMemoryRegister::new(0x00),
|
|
|
+ weekday: InMemoryRegister::new(0x00),
|
|
|
+ day: InMemoryRegister::new(0x00),
|
|
|
+ month: InMemoryRegister::new(0x00),
|
|
|
+ year: InMemoryRegister::new(0x00),
|
|
|
+ a1_second: InMemoryRegister::new(0x00),
|
|
|
+ a1_minute: InMemoryRegister::new(0x00),
|
|
|
+ a1_hour: InMemoryRegister::new(0x00),
|
|
|
+ a1_day_date: InMemoryRegister::new(0x00),
|
|
|
+ a2_minute: InMemoryRegister::new(0x00),
|
|
|
+ a2_hour: InMemoryRegister::new(0x00),
|
|
|
+ a2_day_date: InMemoryRegister::new(0x00),
|
|
|
+ control_1: InMemoryRegister::new(0x00),
|
|
|
+ control_2: InMemoryRegister::new(0x00),
|
|
|
+ aging_offset: InMemoryRegister::new(0x00),
|
|
|
+ temp_msb: InMemoryRegister::new(0x00),
|
|
|
+ temp_lsb: InMemoryRegister::new(0x00),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fn i2c_write_ctrl(&self, address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write) {
|
|
|
+ let mut buffer: [u8; 3] = [0; 3];
|
|
|
+ buffer[0] = offset_of!(Ds3231Registers => control_1).get_byte_offset() as u8;
|
|
|
+ buffer[1] = self.control_1.get();
|
|
|
+ buffer[2] = self.control_2.get();
|
|
|
+
|
|
|
+ match i2c.write(address, &buffer) {
|
|
|
+ Ok(_) => (),
|
|
|
+ Err(_) => panic!(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fn i2c_write_time(&self, address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write) {
|
|
|
+ let mut buffer: [u8; 4] = [0; 4];
|
|
|
+ buffer[0] = offset_of!(Ds3231Registers => second).get_byte_offset() as u8;
|
|
|
+ buffer[1] = self.second.get();
|
|
|
+ buffer[2] = self.minute.get();
|
|
|
+ buffer[3] = self.hour.get();
|
|
|
+
|
|
|
+ match i2c.write(address, &buffer) {
|
|
|
+ Ok(_) => (),
|
|
|
+ Err(_) => panic!(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fn i2c_write_date(&self, address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write) {
|
|
|
+ let mut buffer: [u8; 5] = [0; 5];
|
|
|
+ buffer[0] = offset_of!(Ds3231Registers => weekday).get_byte_offset() as u8;
|
|
|
+ buffer[1] = self.weekday.get();
|
|
|
+ buffer[2] = self.day.get();
|
|
|
+ buffer[3] = self.month.get();
|
|
|
+ buffer[4] = self.year.get();
|
|
|
+
|
|
|
+ match i2c.write(address, &buffer) {
|
|
|
+ Ok(_) => (),
|
|
|
+ Err(_) => panic!(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Returns the second, minute, and hour from the RTC
|
|
|
+ fn i2c_read_time(&mut self, address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_WriteRead) {
|
|
|
+ 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!(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Returns the weekday, day, month, year, and century from the RTC
|
|
|
+ fn i2c_read_date(&mut self, address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_WriteRead) {
|
|
|
+ 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!(),
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fn init(address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write) {
|
|
|
+pub fn init(address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write) {
|
|
|
+ let regs = Ds3231Registers::default();
|
|
|
+
|
|
|
+ regs.control_1.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
|
|
|
+ regs.control_2.modify(CONTROL_2::EN32KHZ::CLEAR);
|
|
|
+
|
|
|
+ regs.i2c_write_ctrl(address, i2c);
|
|
|
}
|
|
|
|
|
|
-#[inline]
|
|
|
-fn bcd_to_decimal(value: u8) -> u8 {
|
|
|
- ((value >> 4) * 10) + (value & 0xF)
|
|
|
+pub fn set_time(
|
|
|
+ address: u8,
|
|
|
+ i2c: &mut impl _embedded_hal_blocking_i2c_Write,
|
|
|
+ second: u32,
|
|
|
+ minute: u32,
|
|
|
+ hour: u32,
|
|
|
+) {
|
|
|
+ let regs = Ds3231Registers::default();
|
|
|
+
|
|
|
+ regs.second
|
|
|
+ .write(T_SECOND::Value.val(decimal_to_bcd(second)));
|
|
|
+ regs.minute
|
|
|
+ .write(T_MINUTE::Value.val(decimal_to_bcd(minute)));
|
|
|
+ regs.hour.write(T_HOUR::Value.val(decimal_to_bcd(hour)));
|
|
|
+
|
|
|
+ regs.i2c_write_time(address, i2c);
|
|
|
+}
|
|
|
+
|
|
|
+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 regs = Ds3231Registers::default();
|
|
|
+
|
|
|
+ regs.weekday
|
|
|
+ .write(T_WEEKDAY::Value.val(decimal_to_bcd(weekday as u32)));
|
|
|
+ regs.day.write(T_DAY::Value.val(decimal_to_bcd(day)));
|
|
|
+ regs.month.modify(
|
|
|
+ T_MONTH::Value.val(decimal_to_bcd(month)) + T_MONTH::Century.val(decimal_to_bcd(century)),
|
|
|
+ );
|
|
|
+ regs.year.write(T_YEAR::Value.val(decimal_to_bcd(year)));
|
|
|
+
|
|
|
+ regs.i2c_write_time(address, i2c);
|
|
|
+}
|
|
|
+
|
|
|
+pub fn get_time(
|
|
|
+ address: u8,
|
|
|
+ i2c: &mut impl _embedded_hal_blocking_i2c_WriteRead,
|
|
|
+) -> (u32, u32, u32) {
|
|
|
+ let mut regs = Ds3231Registers::default();
|
|
|
+
|
|
|
+ regs.i2c_read_time(address, i2c);
|
|
|
+
|
|
|
+ let second = bcd_to_decimal(regs.second.read(T_SECOND::Value));
|
|
|
+ let minute = bcd_to_decimal(regs.minute.read(T_MINUTE::Value));
|
|
|
+ let hour = bcd_to_decimal(regs.hour.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 regs = Ds3231Registers::default();
|
|
|
+
|
|
|
+ regs.i2c_read_date(address, i2c);
|
|
|
+
|
|
|
+ let weekday: Weekday = regs.weekday.read(T_WEEKDAY::Value).into();
|
|
|
+ let day = bcd_to_decimal(regs.day.read(T_DAY::Value));
|
|
|
+ let month = bcd_to_decimal(regs.month.read(T_MONTH::Value));
|
|
|
+ let year = bcd_to_decimal(regs.year.read(T_YEAR::Value));
|
|
|
+ let century = bcd_to_decimal(regs.month.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: u8) -> u8 {
|
|
|
- ((value / 10) << 4) | (value % 10)
|
|
|
+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 {
|