#![no_std]
#![no_main]
use panic_halt as _;
use rp2040_hal as hal;
use hal::pac;
use embedded_hal::digital::OutputPin;
use fugit::{HertzU32, RateExtU32};
use hal::clocks::{Clock, ClockSource, ClocksManager, StoppableClock};
use hal::pac::rosc::ctrl::FREQ_RANGE_A;
use hal::pac::{CLOCKS, ROSC};
use hal::rosc::RingOscillator;
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
#[rp2040_hal::entry]
fn main() -> ! {
let desired_rosc_freq: HertzU32 = 150_000_000.Hz();
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
let sio = hal::Sio::new(pac.SIO);
let pins = hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
let mut led_pin = pins.gpio25.into_push_pull_output();
led_pin.set_low().unwrap();
let xosc = hal::xosc::setup_xosc_blocking(pac.XOSC, XTAL_FREQ_HZ.Hz()).unwrap();
let measured_rosc_frequency =
find_target_rosc_frequency(&pac.ROSC, &pac.CLOCKS, desired_rosc_freq);
let rosc = RingOscillator::new(pac.ROSC);
let rosc = rosc.initialize_with_freq(measured_rosc_frequency);
let mut clocks = ClocksManager::new(pac.CLOCKS);
clocks
.system_clock
.configure_clock(&rosc, rosc.get_freq())
.unwrap();
clocks
.peripheral_clock
.configure_clock(&clocks.system_clock, clocks.system_clock.freq())
.unwrap();
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
let _xosc_disabled = xosc.disable();
clocks.usb_clock.disable();
clocks.gpio_output0_clock.disable();
clocks.gpio_output1_clock.disable();
clocks.gpio_output2_clock.disable();
clocks.gpio_output3_clock.disable();
clocks.adc_clock.disable();
clocks.rtc_clock.disable();
let got_to_within_1_mhz_of_target = desired_rosc_freq
.to_kHz()
.abs_diff(measured_rosc_frequency.to_kHz())
< 1000;
if got_to_within_1_mhz_of_target {
led_pin.set_high().unwrap();
loop {
cortex_m::asm::wfi();
}
} else {
loop {
led_pin.set_high().unwrap();
delay.delay_ms(500);
led_pin.set_low().unwrap();
delay.delay_ms(500);
}
}
}
fn rosc_frequency_count_hz(clocks: &CLOCKS) -> HertzU32 {
while clocks.fc0_status().read().running().bit_is_set() {
cortex_m::asm::nop();
}
clocks
.fc0_ref_khz()
.write(|w| unsafe { w.fc0_ref_khz().bits(XTAL_FREQ_HZ / 1000) });
clocks
.fc0_interval()
.write(|w| unsafe { w.fc0_interval().bits(10) });
clocks
.fc0_min_khz()
.write(|w| unsafe { w.fc0_min_khz().bits(0) });
clocks
.fc0_max_khz()
.write(|w| unsafe { w.fc0_max_khz().bits(0xffffffff) });
clocks
.fc0_src()
.write(|w| unsafe { w.fc0_src().bits(0x03) });
while clocks.fc0_status().read().done().bit_is_clear() {
cortex_m::asm::nop();
}
let speed_hz = clocks.fc0_result().read().khz().bits() * 1000;
speed_hz.Hz()
}
fn find_target_rosc_frequency(
rosc: &ROSC,
clocks: &CLOCKS,
target_frequency: HertzU32,
) -> HertzU32 {
reset_rosc_operating_frequency(rosc);
let mut div = 1;
let mut measured_rosc_frequency;
loop {
measured_rosc_frequency = rosc_frequency_count_hz(clocks);
if measured_rosc_frequency > target_frequency {
div += 1;
set_rosc_div(rosc, div);
} else {
break;
}
}
loop {
measured_rosc_frequency = rosc_frequency_count_hz(clocks);
if measured_rosc_frequency > target_frequency {
break;
}
let can_increase = increase_drive_strength(rosc);
if !can_increase {
let can_increase_range = increase_freq_range(rosc);
if !can_increase_range {
break;
}
}
}
measured_rosc_frequency
}
fn set_rosc_div(rosc: &ROSC, div: u32) {
assert!(div <= 32);
let div = if div == 32 { 0 } else { div };
rosc.div().write(|w| unsafe { w.bits(0xaa0 + div) });
}
fn reset_rosc_operating_frequency(rosc: &ROSC) {
set_rosc_div(rosc, 1);
rosc.ctrl().write(|w| w.freq_range().low());
write_freq_stages(rosc, &[0, 0, 0, 0, 0, 0, 0, 0]);
}
fn read_freq_stage(rosc: &ROSC, stage: u8) -> u8 {
match stage {
0 => rosc.freqa().read().ds0().bits(),
1 => rosc.freqa().read().ds1().bits(),
2 => rosc.freqa().read().ds2().bits(),
3 => rosc.freqa().read().ds3().bits(),
4 => rosc.freqb().read().ds4().bits(),
5 => rosc.freqb().read().ds5().bits(),
6 => rosc.freqb().read().ds6().bits(),
7 => rosc.freqb().read().ds7().bits(),
_ => panic!("invalid frequency drive strength stage"),
}
}
fn increase_drive_strength(rosc: &ROSC) -> bool {
const MAX_STAGE_DRIVE: u8 = 3;
let mut stages: [u8; 8] = [0; 8];
for (stage_index, stage) in stages.iter_mut().enumerate() {
*stage = read_freq_stage(rosc, stage_index as u8)
}
let num_stages_at_drive_level = match rosc.ctrl().read().freq_range().variant() {
Some(FREQ_RANGE_A::LOW) => 8,
Some(FREQ_RANGE_A::MEDIUM) => 6,
Some(FREQ_RANGE_A::HIGH) => 4,
Some(FREQ_RANGE_A::TOOHIGH) => panic!("Don't use TOOHIGH freq_range"),
None => {
return false;
}
};
let mut next_i = 0;
for (index, x) in stages[0..num_stages_at_drive_level].windows(2).enumerate() {
if x[1] < x[0] {
next_i = index + 1;
break;
}
}
if stages[next_i] < MAX_STAGE_DRIVE {
stages[next_i] += 1;
let min = *stages[0..num_stages_at_drive_level]
.iter()
.min()
.unwrap_or(&0);
for stage in &mut stages[num_stages_at_drive_level..] {
*stage = min;
}
write_freq_stages(rosc, &stages);
true
} else {
false
}
}
fn write_freq_stages(rosc: &ROSC, stages: &[u8; 8]) {
let passwd: u32 = 0x9696 << 16;
let mut freq_a = passwd;
let mut freq_b = passwd;
for (stage_index, stage) in stages.iter().enumerate().take(4) {
freq_a |= ((*stage & 0x07) as u32) << (stage_index * 4);
}
for (stage_index, stage) in stages.iter().enumerate().skip(4) {
freq_b |= ((*stage & 0x07) as u32) << ((stage_index - 4) * 4);
}
rosc.freqa().write(|w| unsafe { w.bits(freq_a) });
rosc.freqb().write(|w| unsafe { w.bits(freq_b) });
}
fn increase_freq_range(rosc: &ROSC) -> bool {
match rosc.ctrl().read().freq_range().variant() {
None => {
rosc.ctrl().write(|w| w.freq_range().low());
write_freq_stages(rosc, &[0, 0, 0, 0, 0, 0, 0, 0]);
true
}
Some(FREQ_RANGE_A::LOW) => {
rosc.ctrl().write(|w| w.freq_range().medium());
write_freq_stages(rosc, &[0, 0, 0, 0, 0, 0, 0, 0]);
true
}
Some(FREQ_RANGE_A::MEDIUM) => {
rosc.ctrl().write(|w| w.freq_range().high());
write_freq_stages(rosc, &[0, 0, 0, 0, 0, 0, 0, 0]);
true
}
Some(FREQ_RANGE_A::HIGH) | Some(FREQ_RANGE_A::TOOHIGH) => {
false
}
}
}