diff --git a/.cargo/config.toml b/.cargo/config.toml index ffa8a50..b535b48 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -35,5 +35,5 @@ rustflags = [ runner = "probe-rs run --chip RP2040 --protocol swd --speed 150" [env] -DEFMT_RTT_BUFFER_SIZE = { value = "8192", force = true } +DEFMT_RTT_BUFFER_SIZE = { value = "1024", force = true } DEFMT_LOG = { value = "info", force = true } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 0a6d1a5..973f314 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,34 +4,44 @@ #![no_std] #![no_main] +mod rotary; + +use cortex_m::singleton; use defmt::println; use defmt_rtt as _; +use embedded_hal::digital::InputPin; use hal::Sio; use hal::gpio::{FunctionPio0, Pin}; use hal::pac; use hal::pio::PIOExt; use panic_probe as _; use rp2040_hal as hal; +use rp2040_hal::clocks::ClockSource; +use rp2040_hal::dma::{DMAExt, ReadTarget, SingleChannel, WriteTarget, single_buffer}; +use rp2040_hal::fugit::RateExtU32; +use rp2040_hal::pac::adc::cs::W; +use rp2040_hal::pll::PLLConfig; +use rp2040_hal::pll::common_configs::PLL_USB_48MHZ; + +use crate::rotary::{RotaryEnc, RotaryResponse}; #[unsafe(link_section = ".boot_loader")] #[used] pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; -const WAVE_TABLE: [i8; 256] = [ - 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 62, 65, 68, 70, - 73, 75, 78, 80, 83, 85, 87, 90, 92, 94, 96, 98, 100, 102, 104, 105, 107, 109, 110, 112, 113, - 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 125, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 125, 125, 124, 124, 123, 123, 122, 121, 120, 119, 118, 117, 115, 114, 113, - 111, 110, 108, 106, 105, 103, 101, 99, 97, 95, 93, 91, 89, 86, 84, 82, 79, 77, 74, 72, 69, 66, - 64, 61, 58, 56, 53, 50, 47, 44, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 4, 1, -1, -4, - -7, -10, -13, -16, -20, -23, -26, -29, -32, -35, -38, -41, -44, -47, -50, -52, -55, -58, -61, - -63, -66, -69, -71, -74, -76, -79, -81, -84, -86, -88, -90, -93, -95, -97, -99, -101, -103, - -104, -106, -108, -109, -111, -112, -114, -115, -116, -118, -119, -120, -121, -122, -122, -123, - -124, -124, -125, -125, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -125, -125, - -124, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -113, -112, -110, -109, -107, - -106, -104, -102, -100, -98, -96, -94, -92, -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, - -65, -63, -60, -57, -54, -52, -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, - -9, -6, -3, 0, +// const WAVE_TABLE: [u8; 128] = [ +// 128, 134, 140, 146, 152, 159, 165, 171, 176, 182, 188, 193, 199, 204, 209, 213, 218, 222, 226, +// 230, 234, 237, 240, 243, 246, 248, 250, 252, 253, 254, 255, 255, 255, 255, 255, 254, 253, 252, +// 250, 248, 246, 243, 240, 237, 234, 230, 227, 222, 218, 214, 209, 204, 199, 193, 188, 182, 177, +// 171, 165, 159, 153, 146, 140, 134, 128, 121, 115, 109, 103, 97, 91, 85, 79, 73, 67, 62, 57, 51, +// 46, 42, 37, 33, 29, 25, 21, 18, 15, 12, 9, 7, 5, 3, 2, 1, 0, 0, 0, 0, 0, 1, 2, 3, 5, 7, 9, 12, +// 14, 18, 21, 24, 28, 32, 37, 41, 46, 51, 56, 61, 67, 72, 78, 84, 90, 96, 102, 108, 115, 121, +// ]; + +const WAVE_TABLE: [u8; 64] = [ + 128, 140, 152, 165, 176, 188, 199, 209, 218, 226, 234, 240, 246, 250, 253, 255, 255, 255, 253, + 250, 246, 240, 234, 227, 218, 209, 199, 188, 177, 165, 153, 140, 128, 115, 103, 91, 79, 67, 57, + 46, 37, 29, 21, 15, 9, 5, 2, 0, 0, 0, 2, 5, 9, 14, 21, 28, 37, 46, 56, 67, 78, 90, 102, 115, ]; /// Entry point to our bare-metal application. @@ -41,6 +51,56 @@ const WAVE_TABLE: [i8; 256] = [ #[rp2040_hal::entry] fn main() -> ! { let mut pac = pac::Peripherals::take().unwrap(); + 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); + + 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); + + const XTAL_FREQ_HZ: u32 = 12_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 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, + ) + .unwrap(); + + let pll_sys = hal::pll::setup_pll_blocking( + pac.PLL_SYS, + xosc.operating_frequency(), + PLLConfig { + vco_freq: 1500.MHz(), + refdiv: 1, + post_div1: 6, + post_div2: 1, + }, + &mut clocks, + &mut pac.RESETS, + ) + .unwrap(); + clocks.init_default(&xosc, &pll_sys, &pll_usb).unwrap(); + + let pll_freq = pll_sys.get_freq(); + + println!("Booted at {}MHz", pll_freq.to_MHz()); let sio = Sio::new(pac.SIO); let pins = hal::gpio::Pins::new( @@ -56,14 +116,7 @@ fn main() -> ! { let led_pin_id = led.id().num; // Define a PIO program which reads data from the TX FIFO bit by bit - let program = pio::pio_asm!( - ".wrap_target", - " out pins, 8", - " out pins, 8", - " out pins, 8", - " out pins, 8", - ".wrap" - ); + let program = pio::pio_asm!(".wrap_target", " out pins, 8", ".wrap"); let dyn_pin_array = [ pins.gpio3.into_function::().into_dyn_pin(), @@ -82,7 +135,7 @@ fn main() -> ! { // Initialize and start PIO let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS); let installed = pio.install(&program.program).unwrap(); - let (int, frac) = (1, 0); + let (mut clock_div, mut frac) = (3, 0); let (mut sm, _, mut tx) = rp2040_hal::pio::PIOBuilder::from_installed_program(installed) .set_pins(led_pin_id, 1) .out_pins(dyn_pin_array[3].id().num, 8) @@ -90,7 +143,7 @@ fn main() -> ! { .out_sticky(true) .autopull(true) .buffers(rp2040_hal::pio::Buffers::OnlyTx) - .clock_divisor_fixed_point(int, frac) + .clock_divisor_fixed_point(clock_div, frac) .build(sm0); // The GPIO pin needs to be configured as an output. sm.set_pindirs([(led_pin_id, hal::pio::PinDir::Output)]); @@ -101,44 +154,201 @@ fn main() -> ! { pindirs[i] = (p.id().num, hal::pio::PinDir::Output); }); sm.set_pindirs(pindirs); - tx.write(0b1111_1111_1111_1111_1111_1111_1111_1111); - sm.start(); - // let pgroup: PinGroup = PinGroup::new(); + let mut sm = sm.start(); - // let pgroup = pgroup.add_pin(pins.gpio3.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio4.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio5.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio6.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio7.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio8.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio9.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio10.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio11.into_push_pull_output()); - // let pgroup = pgroup.add_pin(pins.gpio12.into_push_pull_output()); - // let mut pgroup = pgroup.add_pin(pins.gpio13.into_push_pull_output()); + const PROCESSED_TABLE_LEN: usize = WAVE_TABLE.len() * 64; + println!("Processed wave table length: {}", PROCESSED_TABLE_LEN); + let mut processed_wave_table = [0u32; PROCESSED_TABLE_LEN]; - let mut processed_wave_table = [0u32; 64]; - - for i in 0..256 { + for i in 0..PROCESSED_TABLE_LEN { let mut packed = 0u32; for j in 0..4 { - let val = (WAVE_TABLE[i] as i32) + 127; - let val = val as u32; + let val = (WAVE_TABLE[i % WAVE_TABLE.len()] as u32) / 4 + 64; packed |= val.rotate_right(8 * j as u32); } - processed_wave_table[i / 4] = packed; + processed_wave_table[i] = packed; + } + let dma = pac.DMA.split(&mut pac.RESETS); + + let tx_buf = singleton!(: [u32; PROCESSED_TABLE_LEN] = processed_wave_table).unwrap(); + + let (rx_addr, rx_count) = tx_buf.rx_address_count(); + + let rx_addr = singleton!(: u32 = rx_addr).unwrap(); + + println!("rx addr count: {}", tx_buf.rx_address_count()); + println!("rx inc: {}", tx_buf.rx_increment()); + println!("tx addr count: {}", tx.tx_address_count()); + println!("tx fifo addr: {}", tx.fifo_address()); + println!("tx inc: {}", tx.tx_increment()); + + let (tx_addr, _) = tx.tx_address_count(); + + // trying to get around lack of ring buffer support in rp-hal + unsafe { + let pac = pac::Peripherals::steal(); + + println!("ctrl reg: {:b}", pac.DMA.ch(0).ch_al1_ctrl().read().bits()); + + pac.DMA.ch(0).ch_al1_ctrl().write(|w| { + w.chain_to().bits(1); + w.en().set_bit(); + w.data_size().size_word(); + w.incr_read().set_bit(); + w + }); + + println!( + "\nCH0\n\nctrl reg: {:b}", + pac.DMA.ch(0).ch_ctrl_trig().read().bits() + ); + + pac.DMA.ch(0).ch_read_addr().write(|w| { + w.bits(*rx_addr); + w + }); + + pac.DMA.ch(0).ch_trans_count().write(|w| { + w.bits(rx_count); + w + }); + + pac.DMA.ch(0).ch_al2_write_addr_trig().write(|w| { + w.bits(tx_addr); + w + }); + let read_addr = pac.DMA.ch(0).ch_read_addr().read().bits(); + println!("ch0 ctrl read addr ptr: {}", read_addr); + let write_addr = pac.DMA.ch(0).ch_write_addr().read().bits(); + println!("ch0 ctrl write addr ptr: {}", write_addr); + + pac.DMA.ch(1).ch_ctrl_trig().write(|w| { + w.data_size().size_word(); + w.en().set_bit(); + w + }); + + let dma_ch0_read_addr_trig = pac.DMA.ch(0).ch_al3_read_addr_trig().as_ptr(); + + println!("{}", dma_ch0_read_addr_trig); + println!("{}", rx_addr.rx_address_count()); + + pac.DMA.ch(1).ch_read_addr().write(|w| { + w.bits(rx_addr.rx_address_count().0); + w + }); + + pac.DMA.ch(1).ch_trans_count().write(|w| { + w.bits(1); + w + }); + + pac.DMA.ch(1).ch_write_addr().write(|w| { + w.bits(dma_ch0_read_addr_trig as u32); + w + }); + println!( + "\nCH1\n\nctrl reg: {:b}", + pac.DMA.ch(1).ch_ctrl_trig().read().bits() + ); + let read_addr = pac.DMA.ch(1).ch_read_addr().read().bits(); + println!("ctrl read addr ptr: {}", read_addr); + let write_addr = pac.DMA.ch(1).ch_write_addr().read().bits(); + println!("ctrl write addr ptr: {}", write_addr); } - println!("alive"); - println!("{:?}", processed_wave_table); - let mut tick = 0usize; - // PIO runs in background, independently from CPU + // println!("{:?}", processed_wave_table); + + let mut tick = 0u32; + + let mut rotary = RotaryEnc::default(); + + let mut clk_pin = pins.gpio16.into_pull_up_input(); + let mut dt_pin = pins.gpio17.into_pull_up_input(); + let mut mode_switch = pins.gpio18.into_pull_up_input(); + + let mut mode = FreqSelectMode::Coarse; + let mut last_mode_chg = 0; + let mut mode_chg_allowed = true; + + let mut last_rotary = 0; + let mut rotary_input_allowed = true; + const CYCLES_PER_ROTARY: u32 = 1_000; + + const TICKS_PER_DMA_CHECK: u32 = (PROCESSED_TABLE_LEN / WAVE_TABLE.len() * 4 - 25) as u32; + println!("Ticks per dma check: {}", TICKS_PER_DMA_CHECK); + + println!("Start main loop"); loop { - while !tx.is_full() { - let out = processed_wave_table[tick % 64]; - tick = tick.wrapping_add(1); - tx.write(out); + tick = tick.wrapping_add(1); + + // println!("tx ful"); + if !rotary_input_allowed && tick.wrapping_sub(last_rotary) > CYCLES_PER_ROTARY { + rotary_input_allowed = true; + } + + if !mode_chg_allowed && tick.wrapping_sub(last_mode_chg) > CYCLES_PER_ROTARY { + mode_chg_allowed = true; + } + + if mode_chg_allowed { + if mode_switch.is_low().unwrap() { + mode = match mode { + FreqSelectMode::Coarse => FreqSelectMode::Fine, + FreqSelectMode::Fine => FreqSelectMode::Coarse, + }; + + last_mode_chg = tick; + mode_chg_allowed = false; + } + } + + if rotary_input_allowed { + let clk_now = clk_pin.is_low().unwrap(); + let dt_now = dt_pin.is_low().unwrap(); + + match rotary.update(clk_now, dt_now) { + RotaryResponse::NOTHING => { + continue; + } + RotaryResponse::UP => { + match mode { + FreqSelectMode::Coarse => clock_div = clock_div.saturating_add(1), + FreqSelectMode::Fine => frac = frac.saturating_add(1), + } + + // println!("Rotary up"); + } + RotaryResponse::DOWN => { + match mode { + FreqSelectMode::Coarse => clock_div = 3.max(clock_div - 1), + FreqSelectMode::Fine => frac = frac.saturating_sub(1), + } + // println!("Rotary dn"); + } + } + sm.clock_divisor_fixed_point(clock_div, frac); + last_rotary = tick; + rotary_input_allowed = false; + + let this_base_freq = + pll_freq.to_Hz() as usize / (clock_div as usize * WAVE_TABLE.len() * 4); + let next_freq = + pll_freq.to_Hz() as usize / ((clock_div + 1) as usize * WAVE_TABLE.len() * 4); + + // this is pretty inaccurate as clock_div gets smaller + let calc_freq = this_base_freq - ((this_base_freq - next_freq) * (frac as usize)) / 255; + println!( + "mode {}\nclock_div {}, frac {}, freq {}", + mode, clock_div, frac, calc_freq + ); } } } + +#[derive(defmt::Format)] +enum FreqSelectMode { + Coarse, + Fine, +} diff --git a/src/rotary.rs b/src/rotary.rs new file mode 100644 index 0000000..99cd305 --- /dev/null +++ b/src/rotary.rs @@ -0,0 +1,88 @@ +#[derive(Clone, Default)] +pub struct RotaryEnc { + state: RotaryEncState, + wait_for_reset: bool, + primed: bool +} + +impl RotaryEnc { + pub fn update(&mut self, clk: bool, dt: bool) -> RotaryResponse { + + let state_now = RotaryEncState::from_pin_states(clk, dt); + + if state_now == self.state { + return RotaryResponse::NOTHING; + } + + let response = match state_now { + RotaryEncState::BOTH_OFF => { + self.primed = false; + self.wait_for_reset = false; + RotaryResponse::NOTHING + } + RotaryEncState::CLK_ON | RotaryEncState::DT_ON => { + match (self.wait_for_reset, self.state) { + (false, RotaryEncState::BOTH_OFF) => { + self.primed = true; + } + _ => () + } + RotaryResponse::NOTHING + }, + RotaryEncState::BOTH_ON => { + match (self.primed, self.state) { + (true, RotaryEncState::CLK_ON) => { + self.primed = false; + RotaryResponse::UP + } + (true, RotaryEncState::DT_ON) => { + self.primed = false; + RotaryResponse::DOWN + } + _ => { + self.primed = false; + self.wait_for_reset = true; + RotaryResponse::NOTHING + } + } + } + }; + + self.state = state_now; + + response + } +} + +pub enum RotaryResponse { + NOTHING, + UP, + DOWN +} + +#[derive(Clone, Copy, Default, PartialEq, Eq)] +enum RotaryEncState { + #[default] + BOTH_OFF, + CLK_ON, + DT_ON, + BOTH_ON +} + +impl RotaryEncState { + pub fn from_pin_states(clk: bool, dt: bool) -> Self { + if clk && dt { + return Self::BOTH_ON; + } + + if clk { + return Self::CLK_ON; + } + + if dt { + return Self::DT_ON; + } + + Self::BOTH_OFF + } +} \ No newline at end of file