|
|
|
|
@@ -2,33 +2,31 @@
|
|
|
|
|
#![no_main]
|
|
|
|
|
|
|
|
|
|
use cortex_m::prelude::_embedded_hal_PwmPin;
|
|
|
|
|
use defmt::info;
|
|
|
|
|
use defmt::{println, write};
|
|
|
|
|
|
|
|
|
|
use defmt::info;
|
|
|
|
|
use defmt::println;
|
|
|
|
|
use defmt_rtt as _;
|
|
|
|
|
use panic_probe as _;
|
|
|
|
|
|
|
|
|
|
use rp2040_hal::pll::PLLConfig;
|
|
|
|
|
use rp2040_hal::pll::common_configs::PLL_USB_48MHZ;
|
|
|
|
|
// Alias for our HAL crate
|
|
|
|
|
use rp2040_hal as hal;
|
|
|
|
|
|
|
|
|
|
// A shorter alias for the Peripheral Access Crate, which provides low-level
|
|
|
|
|
// register access
|
|
|
|
|
use hal::fugit::RateExtU32;
|
|
|
|
|
use hal::pac;
|
|
|
|
|
|
|
|
|
|
// Some traits we need
|
|
|
|
|
use embedded_hal::digital::InputPin;
|
|
|
|
|
use embedded_hal::digital::OutputPin;
|
|
|
|
|
use embedded_hal::digital::{InputPin, StatefulOutputPin};
|
|
|
|
|
use sh1106_pico_rs::graphics::GraphicsBuf;
|
|
|
|
|
use sh1106_pico_rs::sh1106::SH1106Dev;
|
|
|
|
|
|
|
|
|
|
/// The linker will place this boot block at the start of our program image. We
|
|
|
|
|
/// need this to help the ROM bootloader get our code up and running.
|
|
|
|
|
/// Note: This boot block is not necessary when using a rp-hal based BSP
|
|
|
|
|
/// as the BSPs already perform this step.
|
|
|
|
|
/// NB if OVERCLOCKING
|
|
|
|
|
/// you MUST use a BOOT_LOADER which can tolerate the OC
|
|
|
|
|
/// the GENERIC_03H one cannot (unsure why) but W25Q080 has been solid for me
|
|
|
|
|
#[unsafe(link_section = ".boot_loader")]
|
|
|
|
|
#[used]
|
|
|
|
|
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
|
|
|
|
|
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
|
|
|
|
|
|
|
|
|
|
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
|
|
|
|
/// if your board has a different frequency
|
|
|
|
|
@@ -41,21 +39,51 @@ fn main() -> ! {
|
|
|
|
|
let core = pac::CorePeripherals::take().unwrap();
|
|
|
|
|
|
|
|
|
|
// Set up the watchdog driver - needed by the clock setup code
|
|
|
|
|
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
|
|
|
|
|
// let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
|
|
|
|
|
|
|
|
|
|
hal::vreg::set_voltage(
|
|
|
|
|
&mut pac.VREG_AND_CHIP_RESET,
|
|
|
|
|
pac::vreg_and_chip_reset::vreg::VSEL_A::VOLTAGE1_30,
|
|
|
|
|
);
|
|
|
|
|
// settle
|
|
|
|
|
// cortex_m::asm::delay(3_000_000);
|
|
|
|
|
|
|
|
|
|
let xosc = hal::xosc::setup_xosc_blocking_custom_delay(pac.XOSC, XTAL_FREQ_HZ.Hz(), 128)
|
|
|
|
|
.map_err(|_x| false)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
// watchdog.enable_tick_generation((XTAL_FREQ_HZ / 1_000_000) as u8);
|
|
|
|
|
|
|
|
|
|
// Configure the clocks
|
|
|
|
|
let clocks = hal::clocks::init_clocks_and_plls(
|
|
|
|
|
XTAL_FREQ_HZ,
|
|
|
|
|
pac.XOSC,
|
|
|
|
|
pac.CLOCKS,
|
|
|
|
|
pac.PLL_SYS,
|
|
|
|
|
let mut clocks = hal::clocks::ClocksManager::new(pac.CLOCKS);
|
|
|
|
|
|
|
|
|
|
let pll_usb = rp2040_hal::pll::setup_pll_blocking(
|
|
|
|
|
pac.PLL_USB,
|
|
|
|
|
xosc.operating_frequency(),
|
|
|
|
|
PLL_USB_48MHZ,
|
|
|
|
|
&mut clocks,
|
|
|
|
|
&mut pac.RESETS,
|
|
|
|
|
&mut watchdog,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let mut delay = cortex_m::delay::Delay::new(core.SYST, 133_000_000u32);
|
|
|
|
|
let pll_sys = hal::pll::setup_pll_blocking(
|
|
|
|
|
pac.PLL_SYS,
|
|
|
|
|
xosc.operating_frequency(),
|
|
|
|
|
PLLConfig {
|
|
|
|
|
vco_freq: 1500.MHz(),
|
|
|
|
|
refdiv: 1,
|
|
|
|
|
post_div1: 5,
|
|
|
|
|
post_div2: 1,
|
|
|
|
|
},
|
|
|
|
|
&mut clocks,
|
|
|
|
|
&mut pac.RESETS,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
clocks.init_default(&xosc, &pll_sys, &pll_usb).unwrap();
|
|
|
|
|
|
|
|
|
|
// let mut delay = cortex_m::delay::Delay::new(core.SYST, 200_000_000);
|
|
|
|
|
|
|
|
|
|
// The single-cycle I/O block controls our GPIO pins
|
|
|
|
|
let sio = hal::Sio::new(pac.SIO);
|
|
|
|
|
|
|
|
|
|
@@ -67,14 +95,6 @@ fn main() -> ! {
|
|
|
|
|
&mut pac.RESETS,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS);
|
|
|
|
|
let mut adc_pin = hal::adc::AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
|
|
|
|
|
let mut adc_fifo = adc
|
|
|
|
|
.build_fifo()
|
|
|
|
|
.set_channel(&mut adc_pin)
|
|
|
|
|
.clock_divider(47999, 0)
|
|
|
|
|
.start_paused();
|
|
|
|
|
|
|
|
|
|
let mut led_pin = pins.gpio25.into_push_pull_output();
|
|
|
|
|
led_pin.set_high();
|
|
|
|
|
|
|
|
|
|
@@ -83,11 +103,15 @@ fn main() -> ! {
|
|
|
|
|
|
|
|
|
|
// Configure PWM0
|
|
|
|
|
let pwm0 = &mut pwm_slices.pwm0;
|
|
|
|
|
pwm0.set_ph_correct();
|
|
|
|
|
pwm0.clr_ph_correct();
|
|
|
|
|
pwm0.set_div_int(1);
|
|
|
|
|
pwm0.set_div_frac(0);
|
|
|
|
|
pwm0.disable();
|
|
|
|
|
pwm0.clear_interrupt();
|
|
|
|
|
pwm0.enable();
|
|
|
|
|
|
|
|
|
|
pwm0.channel_a.output_to(pins.gpio16);
|
|
|
|
|
let mut pwm_pin = pins.gpio16;
|
|
|
|
|
pwm_pin.set_drive_strength(rp2040_hal::gpio::OutputDriveStrength::TwelveMilliAmps);
|
|
|
|
|
pwm0.channel_a.output_to(pwm_pin);
|
|
|
|
|
|
|
|
|
|
let mut on_off_input = pins.gpio15.into_pull_up_input();
|
|
|
|
|
|
|
|
|
|
@@ -99,13 +123,12 @@ fn main() -> ! {
|
|
|
|
|
|
|
|
|
|
let mut broadcast_on = false;
|
|
|
|
|
|
|
|
|
|
let mut current_tune = 108;
|
|
|
|
|
let mut current_tune = 10;
|
|
|
|
|
|
|
|
|
|
const MAX_TUNE: u16 = 350u16;
|
|
|
|
|
const MIN_TUNE: u16 = 4u16;
|
|
|
|
|
|
|
|
|
|
pwm0.set_top(current_tune);
|
|
|
|
|
pwm0.channel_a.set_duty(current_tune / 2);
|
|
|
|
|
set_tune(current_tune, pwm0);
|
|
|
|
|
|
|
|
|
|
// Create the I²C drive
|
|
|
|
|
let mut i2c = hal::I2C::i2c0(
|
|
|
|
|
@@ -117,30 +140,41 @@ fn main() -> ! {
|
|
|
|
|
&clocks.system_clock,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let mut delay = cortex_m::delay::Delay::new(core.SYST, 200_000);
|
|
|
|
|
|
|
|
|
|
let mut sh1106_dev = SH1106Dev::new(&mut delay, &mut i2c);
|
|
|
|
|
sh1106_dev.set_vertical_flip(&mut i2c, true);
|
|
|
|
|
let sh1106_dev = sh1106_dev;
|
|
|
|
|
let mut gfx_buf = GraphicsBuf::new();
|
|
|
|
|
|
|
|
|
|
let mut last_gfx_update = 0u32;
|
|
|
|
|
|
|
|
|
|
// Infinite loop, fading LED up and down
|
|
|
|
|
loop {
|
|
|
|
|
tick = tick.saturating_add(1);
|
|
|
|
|
tick = tick.wrapping_add(1);
|
|
|
|
|
|
|
|
|
|
if tick.wrapping_sub(last_gfx_update) > 1_000_000 {
|
|
|
|
|
if tick.wrapping_sub(last_gfx_update) > 500_000 {
|
|
|
|
|
gfx_buf.clear();
|
|
|
|
|
gfx_buf.draw_string(20, 20, "Hello Radio!");
|
|
|
|
|
gfx_buf.draw_string(20, 14, "Hello Radio!");
|
|
|
|
|
|
|
|
|
|
let tuned_freq = 125_000u32 / (current_tune * 2) as u32;
|
|
|
|
|
let mut tune_str_buf = [0u8; 4];
|
|
|
|
|
u16_into_str(tuned_freq as u16, &mut tune_str_buf);
|
|
|
|
|
let tune_str = str::from_utf8(&tune_str_buf).unwrap();
|
|
|
|
|
info!("{}, {}", tune_str, tune_str.len());
|
|
|
|
|
gfx_buf.draw_string(20, 34, tune_str);
|
|
|
|
|
let tuned_freq = 300_000 / (current_tune as u32);
|
|
|
|
|
let mut tune_str_buf = [0u8; 5];
|
|
|
|
|
let len = u32_into_str(tuned_freq, &mut tune_str_buf);
|
|
|
|
|
let tune_str = str::from_utf8(&tune_str_buf[tune_str_buf.len() - len..]).unwrap();
|
|
|
|
|
// info!("val {}, len {}", tuned_freq, len);
|
|
|
|
|
// info!("{}, {}", tune_str, tune_str.len());
|
|
|
|
|
gfx_buf.draw_string(20, 28, tune_str);
|
|
|
|
|
gfx_buf.sprites[0].y = match gfx_buf.sprites[0].y {
|
|
|
|
|
32 => 33,
|
|
|
|
|
_ => 32,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let len = u32_into_str(current_tune as u32, &mut tune_str_buf);
|
|
|
|
|
let tune_str = str::from_utf8(&tune_str_buf[tune_str_buf.len() - len..]).unwrap();
|
|
|
|
|
// info!("len {}", len);
|
|
|
|
|
|
|
|
|
|
gfx_buf.draw_string(64, 28, tune_str);
|
|
|
|
|
|
|
|
|
|
match broadcast_on {
|
|
|
|
|
true => gfx_buf.draw_string(20, 48, "ON THE AIR"),
|
|
|
|
|
true => gfx_buf.draw_string(20, 42, "ON THE AIR"),
|
|
|
|
|
false => gfx_buf.draw_string(20, 48, "Offline"),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -154,6 +188,7 @@ fn main() -> ! {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if on_off_input.is_low().unwrap() {
|
|
|
|
|
info!("Broadcast toggle");
|
|
|
|
|
last_input = tick;
|
|
|
|
|
broadcast_on = !broadcast_on;
|
|
|
|
|
|
|
|
|
|
@@ -165,26 +200,47 @@ fn main() -> ! {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if tune_up_input.is_low().unwrap() {
|
|
|
|
|
info!("Tune DOWN");
|
|
|
|
|
last_input = tick;
|
|
|
|
|
current_tune = MAX_TUNE.min(current_tune + 2);
|
|
|
|
|
pwm0.set_top(current_tune);
|
|
|
|
|
pwm0.channel_a.set_duty(current_tune / 2);
|
|
|
|
|
set_tune(current_tune, pwm0);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if tune_dn_input.is_low().unwrap() {
|
|
|
|
|
info!("Tune UP");
|
|
|
|
|
last_input = tick;
|
|
|
|
|
current_tune = MIN_TUNE.max(current_tune - 2);
|
|
|
|
|
pwm0.set_top(current_tune);
|
|
|
|
|
pwm0.channel_a.set_duty(current_tune / 2);
|
|
|
|
|
set_tune(current_tune, pwm0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn u16_into_str(mut value: u16, str_buf: &mut [u8; 4]) {
|
|
|
|
|
for i in 0..4 {
|
|
|
|
|
str_buf[3 - i] = b'0' + (value % 10) as u8;
|
|
|
|
|
fn set_tune(
|
|
|
|
|
tune: u16,
|
|
|
|
|
pwm: &mut rp2040_hal::pwm::Slice<rp2040_hal::pwm::Pwm0, rp2040_hal::pwm::FreeRunning>,
|
|
|
|
|
) {
|
|
|
|
|
pwm.set_top(tune - 1);
|
|
|
|
|
pwm.channel_a.set_duty(tune / 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns the length of the final str removing leading zeroes
|
|
|
|
|
fn u32_into_str(mut value: u32, str_buf: &mut [u8]) -> usize {
|
|
|
|
|
let mut len = str_buf.len();
|
|
|
|
|
|
|
|
|
|
// info!("val {}", value);
|
|
|
|
|
for i in 0..len {
|
|
|
|
|
// info!("val {}, i {}, digit {}", value, i, value % 10);
|
|
|
|
|
str_buf[len - i - 1] = b'0' + (value % 10) as u8;
|
|
|
|
|
value /= 10;
|
|
|
|
|
|
|
|
|
|
if value <= 0 {
|
|
|
|
|
// info!("Ran out of digits at {}", i);
|
|
|
|
|
len = i + 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
info!("utf8 buf {}", str_buf);
|
|
|
|
|
}
|
|
|
|
|
// info!("utf8 buf {}, len {}", str_buf, len);
|
|
|
|
|
|
|
|
|
|
len
|
|
|
|
|
}
|
|
|
|
|
|