@@ -65,7 +65,7 @@ use cortex_m_rt::entry;
6565use critical_section:: Mutex ;
6666use defmt:: info;
6767use defmt_rtt as _;
68- use ds1307:: { Datelike , Timelike } ;
68+ use ds1307:: { Datelike , NaiveDateTime , Timelike } ;
6969use embedded_hal:: {
7070 blocking:: spi:: { Transfer as _SpiTransfer, Write as _SpiWrite} ,
7171 digital:: v2:: { InputPin , OutputPin } ,
@@ -97,6 +97,9 @@ use neotron_common_bios as common;
9797// Types
9898// -----------------------------------------------------------------------------
9999
100+ /// What we count our system ticks in
101+ type Duration = fugit:: Duration < u64 , 1 , 1_000_000 > ;
102+
100103/// The type of our IRQ input pin from the MCP23S17.
101104type IrqPin = Pin < bank0:: Gpio20 , Input < PullUp > > ;
102105
@@ -130,8 +133,10 @@ struct Hardware {
130133 irq_count : u32 ,
131134 /// Our I2C Bus
132135 i2c : shared_bus:: BusManagerSimple < hal:: i2c:: I2C < pac:: I2C1 , I2cPins , hal:: i2c:: Controller > > ,
133- /// Our RTC
136+ /// Our External RTC (on the I2C bus)
134137 rtc : rtc:: Rtc ,
138+ /// The time we started up at, in microseconds since the Neotron epoch
139+ bootup_at : Duration ,
135140 /// A Timer
136141 timer : hal:: timer:: Timer ,
137142}
@@ -269,6 +274,9 @@ static API_CALLS: common::Api = common::Api {
269274 power_idle,
270275} ;
271276
277+ /// Seconds between the Neotron Epoch (2000-011-011T00:00:00) and the UNIX Epoch (1970-01-01T00:00:00).
278+ const SECONDS_BETWEEN_UNIX_AND_NEOTRON_EPOCH : i64 = 946684800 ;
279+
272280extern "C" {
273281 static mut _flash_os_start: u32 ;
274282 static mut _flash_os_len: u32 ;
@@ -381,31 +389,15 @@ fn main() -> ! {
381389 pp. IO_BANK0 ,
382390 pp. PADS_BANK0 ,
383391 sio. gpio_bank0 ,
384- & mut pp. RESETS ,
385392 pp. SPI0 ,
386393 pp. I2C1 ,
394+ pp. TIMER ,
387395 clocks,
388396 delay,
389- pp. TIMER ,
397+ & mut pp. RESETS ,
390398 ) ;
391399 hw. init_io_chip ( ) ;
392400 hw. set_hdd_led ( false ) ;
393- match hw. rtc . get_time ( hw. i2c . acquire_i2c ( ) ) {
394- Ok ( time) => {
395- defmt:: info!(
396- "Time: {:04}-{:02}-{:02} {:02}:{:02}:{:02}" ,
397- time. year( ) ,
398- time. month( ) ,
399- time. day( ) ,
400- time. hour( ) ,
401- time. minute( ) ,
402- time. second( )
403- ) ;
404- }
405- Err ( _) => {
406- defmt:: info!( "Time: Unknown" ) ;
407- }
408- }
409401
410402 nirq_io. set_interrupt_enabled ( hal:: gpio:: Interrupt :: EdgeLow , true ) ;
411403 nirq_io. set_interrupt_enabled ( hal:: gpio:: Interrupt :: EdgeHigh , true ) ;
@@ -511,12 +503,12 @@ impl Hardware {
511503 bank : pac:: IO_BANK0 ,
512504 pads : pac:: PADS_BANK0 ,
513505 sio : hal:: sio:: SioGpioBank0 ,
514- resets : & mut pac:: RESETS ,
515506 spi : pac:: SPI0 ,
516507 i2c : pac:: I2C1 ,
508+ timer : pac:: TIMER ,
517509 clocks : ClocksManager ,
518510 delay : cortex_m:: delay:: Delay ,
519- timer : pac:: TIMER ,
511+ resets : & mut pac:: RESETS ,
520512 ) -> ( Hardware , IrqPin ) {
521513 let hal_pins = rp_pico:: Pins :: new ( bank, pads, sio, resets) ;
522514 // We construct the pin here and then throw it away. Then Core 1 does
@@ -544,8 +536,30 @@ impl Hardware {
544536 ) ;
545537 let i2c = shared_bus:: BusManagerSimple :: new ( raw_i2c) ;
546538 let proxy = i2c. acquire_i2c ( ) ;
547- let rtc = rtc:: Rtc :: new ( proxy) ;
539+ let mut external_rtc = rtc:: Rtc :: new ( proxy) ;
548540 let timer = hal:: timer:: Timer :: new ( timer, resets) ;
541+ // Do a conversion from external RTC time (chrono::NaiveDateTime) to a format we can track
542+ let ticks_at_boot_us = match external_rtc. get_time ( i2c. acquire_i2c ( ) ) {
543+ Ok ( time) => {
544+ defmt:: info!(
545+ "Time: {:04}-{:02}-{:02} {:02}:{:02}:{:02}" ,
546+ time. year( ) ,
547+ time. month( ) ,
548+ time. day( ) ,
549+ time. hour( ) ,
550+ time. minute( ) ,
551+ time. second( )
552+ ) ;
553+ let ticks_at_boot_us =
554+ time. timestamp_micros ( ) - ( SECONDS_BETWEEN_UNIX_AND_NEOTRON_EPOCH * 1_000_000 ) ;
555+ defmt:: info!( "Ticks at boot: {}" , ticks_at_boot_us) ;
556+ ticks_at_boot_us
557+ }
558+ Err ( _) => {
559+ defmt:: info!( "Time: Unknown" ) ;
560+ 0
561+ }
562+ } ;
549563
550564 (
551565 Hardware {
@@ -695,7 +709,8 @@ impl Hardware {
695709 interrupts_pending : 0 ,
696710 irq_count : 0 ,
697711 i2c,
698- rtc,
712+ rtc : external_rtc,
713+ bootup_at : Duration :: from_ticks ( ticks_at_boot_us as u64 ) ,
699714 timer,
700715 } ,
701716 hal_pins. gpio20 . into_pull_up_input ( ) ,
@@ -890,7 +905,7 @@ impl Hardware {
890905 if INTERRUPT_PENDING . load ( Ordering :: Relaxed ) {
891906 // The IO chip reports 0 for pending, 1 for not pending.
892907 self . interrupts_pending = self . io_read_interrupts ( ) ^ 0xFF ;
893- defmt:: info !( "MCP23S17 IRQ pins: 0b{:08b}" , self . interrupts_pending) ;
908+ defmt:: debug !( "MCP23S17 IRQ pins: 0b{:08b}" , self . interrupts_pending) ;
894909 // Change the debug LEDs so we can see the interrupts
895910 self . irq_count = self . irq_count . wrapping_add ( 1 ) ;
896911 self . set_debug_leds ( ( self . irq_count & 0x0F ) as u8 ) ;
@@ -1285,8 +1300,22 @@ pub extern "C" fn serial_read(
12851300/// If the BIOS does not have a battery-backed clock, or if that battery has
12861301/// failed to keep time, the system starts up assuming it is the epoch.
12871302pub extern "C" fn time_clock_get ( ) -> common:: Time {
1288- // TODO: Read from the MCP7940N
1289- common:: Time { secs : 0 , nsecs : 0 }
1303+ // We track real-time using the RP2040 1 MHz timer. We noted the wall-time at boot-up.
1304+
1305+ let ticks_since_epoch = critical_section:: with ( |cs| {
1306+ let mut lock = HARDWARE . borrow_ref_mut ( cs) ;
1307+ let hw = lock. as_mut ( ) . unwrap ( ) ;
1308+ // We can unwrap this because we set the day-of-week correctly on startup
1309+ let ticks_since_boot = hw. timer . get_counter ( ) . duration_since_epoch ( ) ;
1310+ ticks_since_boot + hw. bootup_at
1311+ } ) ;
1312+
1313+ let nanos_since_epoch = ticks_since_epoch. to_nanos ( ) ;
1314+
1315+ let secs = ( nanos_since_epoch / 1_000_000_000 ) as u32 ;
1316+ let nsecs = ( nanos_since_epoch % 1_000_000_000 ) as u32 ;
1317+ defmt:: info!( "Time is {}.{:09}s" , secs, nsecs) ;
1318+ common:: Time { secs, nsecs }
12901319}
12911320
12921321/// Set the current wall time.
@@ -1298,8 +1327,49 @@ pub extern "C" fn time_clock_get() -> common::Time {
12981327/// time (e.g. the user has updated the current time, or if you get a GPS
12991328/// fix). The BIOS should push the time out to the battery-backed Real
13001329/// Time Clock, if it has one.
1301- pub extern "C" fn time_clock_set ( _time : common:: Time ) {
1302- // TODO: Update the MCP7940N RTC
1330+ pub extern "C" fn time_clock_set ( time : common:: Time ) {
1331+ critical_section:: with ( |cs| {
1332+ let mut lock = HARDWARE . borrow_ref_mut ( cs) ;
1333+ let hw = lock. as_mut ( ) . unwrap ( ) ;
1334+
1335+ // 1. How long have we been running, in 1 MHz ticks?
1336+ let ticks_since_boot = hw. timer . get_counter ( ) . duration_since_epoch ( ) ;
1337+
1338+ // 2. What is the given time as 1 MHz ticks since the epoch?
1339+ let ticks_since_epoch = u64:: from ( time. secs ) * 1_000_000 + u64:: from ( time. nsecs / 1000 ) ;
1340+ let ticks_since_epoch = Duration :: from_ticks ( ticks_since_epoch) ;
1341+
1342+ // 3. Work backwards, and find the time it must have been when we booted up.
1343+ let ticks_at_boot = ticks_since_epoch
1344+ . checked_sub ( ticks_since_boot)
1345+ . unwrap_or_else ( || Duration :: from_ticks ( 0 ) ) ;
1346+
1347+ // 4. Store that value
1348+ defmt:: info!( "Ticks at boot: {}" , ticks_at_boot. ticks( ) ) ;
1349+ hw. bootup_at = ticks_at_boot;
1350+
1351+ // 5. Convert to calendar time
1352+ if let Some ( new_time) = NaiveDateTime :: from_timestamp_opt (
1353+ i64:: from ( time. secs ) + SECONDS_BETWEEN_UNIX_AND_NEOTRON_EPOCH ,
1354+ time. nsecs ,
1355+ ) {
1356+ // 6. Update the hardware RTC as well
1357+ match hw. rtc . set_time ( hw. i2c . acquire_i2c ( ) , new_time) {
1358+ Ok ( _) => {
1359+ defmt:: info!( "Time set in RTC OK" ) ;
1360+ }
1361+ Err ( rtc:: Error :: BusError ( _) ) => {
1362+ defmt:: warn!( "Failed to talk to RTC to set time" ) ;
1363+ }
1364+ Err ( rtc:: Error :: DriverBug ) => {
1365+ defmt:: warn!( "RTC driver failed" ) ;
1366+ }
1367+ Err ( rtc:: Error :: NoRtcFound ) => {
1368+ defmt:: info!( "Not setting time - no RTC found" ) ;
1369+ }
1370+ }
1371+ }
1372+ } ) ;
13031373}
13041374
13051375/// Get the configuration data block.
0 commit comments