Very effective infinite signal gen up to ~300kHz, analog HW limited
This commit is contained in:
@@ -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 }
|
||||
316
src/main.rs
316
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::<FunctionPio0>().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,
|
||||
}
|
||||
|
||||
88
src/rotary.rs
Normal file
88
src/rotary.rs
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user