Skip to content

Commit

Permalink
- Added support for RP2040's RTC peripheral (#317)
Browse files Browse the repository at this point in the history
- Added example showing how the RTC is used

Co-authored-by: Hayden Riddiford <[email protected]>
  • Loading branch information
haydenridd and Hayden Riddiford authored Dec 19, 2024
1 parent da30fd0 commit 6207849
Show file tree
Hide file tree
Showing 5 changed files with 370 additions and 3 deletions.
1 change: 1 addition & 0 deletions examples/raspberrypi/rp2xxx/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub fn build(b: *std.Build) void {
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_i2c-bus-scan", .file = "src/rp2040_only/i2c_bus_scan.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pwm", .file = "src/rp2040_only/pwm.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_random", .file = "src/rp2040_only/random.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_rtc", .file = "src/rp2040_only/rtc.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_spi-host", .file = "src/rp2040_only/spi_host.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-echo", .file = "src/rp2040_only/uart_echo.zig" },
.{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-log", .file = "src/rp2040_only/uart_log.zig" },
Expand Down
102 changes: 102 additions & 0 deletions examples/raspberrypi/rp2xxx/src/rp2040_only/rtc.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const std = @import("std");
const microzig = @import("microzig");
const rp2xxx = microzig.hal;
const time = rp2xxx.time;

const pin_config = rp2xxx.pins.GlobalConfiguration{
.GPIO25 = .{
.name = "led",
.direction = .out,
},
};

pub const microzig_options = .{
.interrupts = .{
.RTC_IRQ = .{ .C = &rtc_isr },
},
};

/// Enables/disables interrupts to prevent data-races with variables
/// shared between ISRs and normal code
///
/// TODO: microzig/core/src/interrupt.zig might provide a more general implementation
/// for this in the future.
const CriticalSection = struct {
isr_reg_value: usize = 0,

pub fn enter(self: *CriticalSection) void {
var val: usize = undefined;
asm volatile (
\\mrs %[val], primask
\\movs r1, #1
\\msr primask, r1
: [val] "=r" (val),
:
: "r1", "cc"
);
self.isr_reg_value = val;
}

pub fn exit(self: *CriticalSection) void {
const val = self.isr_reg_value;
asm volatile ("msr primask, %[val]"
:
: [val] "r" (val),
);
}
};
var critical_section: CriticalSection = .{};

var _fast_blink: bool = false;
/// Access underlying _sleep_time via volatile * to prevent reads from being optimized away
var fast_blink_vp: *volatile bool = &_fast_blink;

fn rtc_isr() callconv(.C) void {

// Important to disable + re-enable the RTC alarm so that the interrupt doesn't
// continue firing for the entire time the alarm condition is met
// (For example, it would fire the entire second that it matches the desired "second" count)
rp2xxx.rtc.alarm.disable();
rp2xxx.rtc.alarm.enable();

// Every 1 minute, alternate between fast and slow blinking
critical_section.enter();
fast_blink_vp.* = !fast_blink_vp.*;
critical_section.exit();
}

pub fn main() !void {
const pins = pin_config.apply();

// Configure initial datetime for RTC
rp2xxx.rtc.set_datetime(.{
.year = 2024,
.month = 12,
.day = 25,
.day_of_week = .Monday,
.hour = 0,
.minute = 0,
.second = 0,
});
rp2xxx.rtc.apply(rp2xxx.clock_config);

// Configure an alarm that goes off once a minute (whenever second == 0)
rp2xxx.rtc.alarm.configure(.{ .second = 0 });

// Enable peripheral level alarm
rp2xxx.rtc.irq.enable();

// Enable top level NVIC alarm
rp2xxx.irq.enable(.RTC_IRQ);

while (true) {

// Disable interrupts during volatile read of fast_blink to prevent data races
critical_section.enter();
const fast_blink = fast_blink_vp.*;
critical_section.exit();

pins.led.toggle();
time.sleep_ms(if (fast_blink) 500 else 1000);
}
}
4 changes: 4 additions & 0 deletions port/raspberrypi/rp2xxx/src/hal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ pub const pwm = @import("hal/pwm.zig");
pub const rand = @import("hal/random.zig");
pub const resets = @import("hal/resets.zig");
pub const rom = @import("hal/rom.zig");
pub const rtc = switch (compatibility.cpu) {
.RP2040 => @import("hal/rtc.zig"),
.RP2350 => {}, // No explicit "RTC" module on RP2350
};
pub const spi = @import("hal/spi.zig");
pub const i2c = @import("hal/i2c.zig");
pub const time = @import("hal/time.zig");
Expand Down
7 changes: 4 additions & 3 deletions port/raspberrypi/rp2xxx/src/hal/clocks/rp2040.zig
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ pub const config = struct {
/// - TODO: Could vary the PLL frequency based on sys_freq input to open up a broader range of SYS frequencies
/// - CLK_USB sourced from PLL_USB @ 48 MHz
/// - CLK_ADC sourced from PLL_USB @ 48 MHz
/// - CLK_RTC sourced from CLK_SYS
/// - CLK_RTC sourced from PLL_USB @ 48 MHz and divided down to ~65484 Hz
/// - CLK_PERI sourced from CLK_SYS
pub fn system(sys_freq: u32, ref_freq: u32) Global {
var cfg: Global = .{};
Expand Down Expand Up @@ -359,14 +359,15 @@ pub const config = struct {
.integer_divisor = 1,
};

// RTC gets connected to PLL_USB
// RTC gets connected to PLL_USB, and divided down to ~65484 Hz in order
// to be a suitable clock for 1 second reference.
cfg.rtc =
.{
.input = .{
.source = .pll_usb,
.freq = 48_000_000,
},
.integer_divisor = 1,
.integer_divisor = 733,
};

// CLK_PERI is sourced from CLK_SYS, divisor hard coded to 1 on RP2040
Expand Down
Loading

0 comments on commit 6207849

Please sign in to comment.