Skip to content

Commit 79af5d8

Browse files
Add RTC drivers.
Prints out time on startup. Needs APIs so OS can query and set time.
1 parent fbbc3ed commit 79af5d8

4 files changed

Lines changed: 191 additions & 14 deletions

File tree

Cargo.lock

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ fugit = "0.3"
4242
pc-keyboard = "0.7.0"
4343
# Useful queues and other structures
4444
heapless = "0.7"
45+
# The Dallas DS1307 RTC driver
46+
ds1307 = "0.5.0"
47+
# The Microchip MCP7940x RTC driver
48+
mcp794xx = "0.3.0"
49+
# I2C Bus Sharing
50+
shared-bus = "0.2"
4551

4652
[[bin]]
4753
name = "neotron-pico-bios"

src/main.rs

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
4545
// Sub-modules
4646
// -----------------------------------------------------------------------------
4747

48+
pub mod rtc;
4849
pub mod vga;
4950

5051
// -----------------------------------------------------------------------------
@@ -64,6 +65,7 @@ use cortex_m_rt::entry;
6465
use critical_section::Mutex;
6566
use defmt::info;
6667
use defmt_rtt as _;
68+
use ds1307::{Datelike, Timelike};
6769
use embedded_hal::{
6870
blocking::spi::{Transfer as _SpiTransfer, Write as _SpiWrite},
6971
digital::v2::{InputPin, OutputPin},
@@ -121,6 +123,19 @@ struct Hardware {
121123
interrupts_pending: u8,
122124
/// The number of IRQs we've had
123125
irq_count: u32,
126+
/// Our I2C Bus
127+
i2c: shared_bus::BusManagerSimple<
128+
hal::i2c::I2C<
129+
pac::I2C1,
130+
(
131+
Pin<bank0::Gpio14, Function<hal::gpio::I2C>>,
132+
Pin<bank0::Gpio15, Function<hal::gpio::I2C>>,
133+
),
134+
hal::i2c::Controller,
135+
>,
136+
>,
137+
/// Our RTC
138+
rtc: rtc::Rtc,
124139
}
125140

126141
/// Flips between true and false so we always send a unique read request
@@ -159,8 +174,6 @@ struct Pins {
159174
blue2: Pin<bank0::Gpio12, Function<pac::PIO0>>,
160175
blue3: Pin<bank0::Gpio13, Function<pac::PIO0>>,
161176
npower_save: Pin<bank0::Gpio23, Output<PushPull>>,
162-
i2c_sda: Pin<bank0::Gpio14, Function<hal::gpio::I2C>>,
163-
i2c_scl: Pin<bank0::Gpio15, Function<hal::gpio::I2C>>,
164177
spi_cipo: Pin<bank0::Gpio16, Function<hal::gpio::Spi>>,
165178
nspi_cs_io: Pin<bank0::Gpio17, Output<PushPull>>,
166179
spi_clk: Pin<bank0::Gpio18, Function<hal::gpio::Spi>>,
@@ -372,11 +385,28 @@ fn main() -> ! {
372385
sio.gpio_bank0,
373386
&mut pp.RESETS,
374387
pp.SPI0,
388+
pp.I2C1,
375389
clocks,
376390
delay,
377391
);
378392
hw.init_io_chip();
379393
hw.set_hdd_led(false);
394+
match hw.rtc.get_time(hw.i2c.acquire_i2c()) {
395+
Ok(time) => {
396+
defmt::info!(
397+
"Time: {:04}-{:02}-{:02} {:02}:{:02}:{:02}",
398+
time.year(),
399+
time.month(),
400+
time.day(),
401+
time.hour(),
402+
time.minute(),
403+
time.second()
404+
);
405+
}
406+
Err(_) => {
407+
defmt::info!("Time: Unknown");
408+
}
409+
}
380410

381411
nirq_io.set_interrupt_enabled(hal::gpio::Interrupt::EdgeLow, true);
382412
nirq_io.set_interrupt_enabled(hal::gpio::Interrupt::EdgeHigh, true);
@@ -483,6 +513,7 @@ impl Hardware {
483513
sio: hal::sio::SioGpioBank0,
484514
resets: &mut pac::RESETS,
485515
spi: pac::SPI0,
516+
i2c: pac::I2C1,
486517
clocks: ClocksManager,
487518
delay: cortex_m::delay::Delay,
488519
) -> (Hardware, IrqPin) {
@@ -492,6 +523,27 @@ impl Hardware {
492523
// pin 25 to track render loop timing. This avoids trying to 'move' the pin
493524
// over to Core 1.
494525
let _pico_led = hal_pins.led.into_push_pull_output();
526+
let raw_i2c = hal::i2c::I2C::i2c1(
527+
i2c,
528+
{
529+
let mut pin = hal_pins.gpio14.into_mode();
530+
pin.set_drive_strength(hal::gpio::OutputDriveStrength::EightMilliAmps);
531+
pin.set_slew_rate(hal::gpio::OutputSlewRate::Fast);
532+
pin
533+
},
534+
{
535+
let mut pin = hal_pins.gpio15.into_mode();
536+
pin.set_drive_strength(hal::gpio::OutputDriveStrength::EightMilliAmps);
537+
pin.set_slew_rate(hal::gpio::OutputSlewRate::Fast);
538+
pin
539+
},
540+
100.kHz(),
541+
resets,
542+
&clocks.system_clock,
543+
);
544+
let i2c = shared_bus::BusManagerSimple::new(raw_i2c);
545+
let proxy = i2c.acquire_i2c();
546+
let rtc = rtc::Rtc::new(proxy);
495547

496548
(
497549
Hardware {
@@ -587,18 +639,6 @@ impl Hardware {
587639
pin.set_slew_rate(hal::gpio::OutputSlewRate::Fast);
588640
pin
589641
},
590-
i2c_sda: {
591-
let mut pin = hal_pins.gpio14.into_mode();
592-
pin.set_drive_strength(hal::gpio::OutputDriveStrength::EightMilliAmps);
593-
pin.set_slew_rate(hal::gpio::OutputSlewRate::Fast);
594-
pin
595-
},
596-
i2c_scl: {
597-
let mut pin = hal_pins.gpio15.into_mode();
598-
pin.set_drive_strength(hal::gpio::OutputDriveStrength::EightMilliAmps);
599-
pin.set_slew_rate(hal::gpio::OutputSlewRate::Fast);
600-
pin
601-
},
602642
spi_cipo: {
603643
let mut pin = hal_pins.gpio16.into_mode();
604644
pin.set_drive_strength(hal::gpio::OutputDriveStrength::EightMilliAmps);
@@ -652,11 +692,20 @@ impl Hardware {
652692
bmc_buffer: [0u8; 64],
653693
interrupts_pending: 0,
654694
irq_count: 0,
695+
i2c,
696+
rtc,
655697
},
656698
hal_pins.gpio20.into_pull_up_input(),
657699
)
658700
}
659701

702+
// i2c_sda: {
703+
//
704+
// },
705+
// i2c_scl: {
706+
//
707+
// },
708+
660709
/// Perform some SPI operation with the I/O chip selected.
661710
///
662711
/// You are required to have called `self.release_cs_lines()` previously,

src/rtc.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! RTC support for the Neotron Pico BIOS
2+
//!
3+
//! We could have one of two RTCs fitted. This module abstracts that away.
4+
5+
pub use ds1307::{DateTimeAccess, NaiveDateTime};
6+
7+
pub enum Rtc {
8+
Ds1307,
9+
Mcp7940n,
10+
None,
11+
}
12+
13+
impl Rtc {
14+
/// Create a new RTC object.
15+
///
16+
/// The `bus` is dropped at the end of the function, so use a `&mut`
17+
/// reference, or a disposable shared-bus proxy.
18+
pub fn new<T, E>(bus: T) -> Rtc
19+
where
20+
T: embedded_hal::blocking::i2c::WriteRead<Error = E>
21+
+ embedded_hal::blocking::i2c::Write<Error = E>,
22+
{
23+
let mut mcp7940n = mcp794xx::Mcp794xx::new_mcp7940n(bus);
24+
25+
match mcp7940n.is_oscillator_running() {
26+
Ok(true) => {
27+
defmt::info!("MCP7940N found and ticking");
28+
return Rtc::Mcp7940n;
29+
}
30+
Ok(false) => {
31+
defmt::info!("MCP7940N found, and has started ticking");
32+
let _ = mcp7940n.enable_external_oscillator();
33+
let _ = mcp7940n.enable();
34+
return Rtc::Mcp7940n;
35+
}
36+
Err(_e) => defmt::info!("MCP7940N not found"),
37+
}
38+
39+
let bus = mcp7940n.destroy();
40+
41+
let mut ds1307 = ds1307::Ds1307::new(bus);
42+
match ds1307.running() {
43+
Ok(true) => {
44+
defmt::info!("DS1307 found and ticking");
45+
return Rtc::Ds1307;
46+
}
47+
Ok(false) => {
48+
defmt::info!("DS1307 found, and has started ticking");
49+
let _ = ds1307.set_running();
50+
return Rtc::Ds1307;
51+
}
52+
Err(_e) => defmt::info!("DS1307 not found"),
53+
}
54+
55+
Rtc::None
56+
}
57+
58+
/// Get the current wall-time.
59+
///
60+
/// We don't care about timezones - this is basic Gregorian naive wall-clock date/time.
61+
///
62+
/// You get `Err()` if the RTC doesn't respond or wasn't found on start-up.
63+
pub fn get_time<T, E>(&self, bus: T) -> Result<NaiveDateTime, ()>
64+
where
65+
T: embedded_hal::blocking::i2c::WriteRead<Error = E>
66+
+ embedded_hal::blocking::i2c::Write<Error = E>,
67+
{
68+
match self {
69+
Self::Ds1307 => {
70+
let mut driver = ds1307::Ds1307::new(bus);
71+
driver.datetime().map_err(|_e| ())
72+
}
73+
Self::Mcp7940n => {
74+
let mut driver = mcp794xx::Mcp794xx::new_mcp7940n(bus);
75+
driver.datetime().map_err(|_e| ())
76+
}
77+
Self::None => Err(()),
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)