Last active
November 20, 2022 23:20
-
-
Save riskable/608c5e0e73a8a48da507d99a32ec281b to your computer and use it in GitHub Desktop.
Multi-core Rust example on rp2040 with RTIC (on one core)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is just a subsection of the code (the parts relevant to multi-core stuff) | |
// It won't "just work" | |
use rp2040_hal as hal; | |
use hal::multicore::{Multicore, Stack}; | |
use hal::pac; | |
use hal::sio::Sio; | |
use heapless::spsc::Queue; | |
// These are markers are used to indicate the beginning and end | |
// of ADC channel readings being passed through the SioFifo: | |
const START_ADC_CHANNEL_MSG: u32 = 0xFEEDBEEF; | |
const END_ADC_CHANNEL_MSG: u32 = 0xDEADBEEF; | |
// Note: Since ADC readings will always be less than 5000 anything | |
// >5000 can work 👍 | |
static mut CORE1_STACK: Stack<4096> = Stack::new(); | |
fn core1_task() -> ! { | |
let mut pac = unsafe { pac::Peripherals::steal() }; | |
let core = unsafe { pac::CorePeripherals::steal() }; | |
let mut sio = Sio::new(pac.SIO); | |
let pins = hal::gpio::Pins::new( | |
pac.IO_BANK0, | |
pac.PADS_BANK0, | |
sio.gpio_bank0, | |
&mut pac.RESETS, | |
); | |
// Enable ADC | |
let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS); | |
// Enable the temperature sense channel | |
// let mut temperature_sensor = adc.enable_temp_sensor(); | |
// let temp_sens_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap(); | |
let adc_pin_0 = pins.gpio27.into_floating_input(); | |
let adc_pin_1 = pins.gpio26.into_floating_input(); | |
let mut analog_pins = (adc_pin_0, adc_pin_1); | |
// Setup PB12-PB15 for accessing S0-S3 on the 74HC4067 multiplexers | |
let s0 = pins.gpio4.into_push_pull_output(); | |
let s1 = pins.gpio5.into_push_pull_output(); | |
let s2 = pins.gpio6.into_push_pull_output(); | |
let s3 = pins.gpio7.into_push_pull_output(); | |
let en = DummyPin; // Just run it to GND to keep always-enabled | |
let select_pins = (s0, s1, s2, s3, en); | |
let mut multiplexer = Multiplexer::new(select_pins); | |
// A place to store read values before sending them to the other core: | |
let mut adc_values: Queue<u32, { (config::KEYBOARD_NUM_MULTIPLEXERS * 16) + 1 }> = Queue::new(); | |
// The first thing core0 sends us is the system bus frequency. | |
// The systick is based on this frequency, so we need that to | |
// be accurate when sleeping via cortex_m::delay::Delay | |
let sys_freq = sio.fifo.read_blocking(); | |
let mut delay = cortex_m::delay::Delay::new(core.SYST, sys_freq); | |
defmt::println!("ADC reading task started on CORE1"); // TEMP | |
loop { | |
for chan in 0..16 { | |
multiplexer.set_channel(chan); | |
for mux in 0..config::KEYBOARD_NUM_MULTIPLEXERS { | |
let millivolts: u16 = match mux { | |
0 => adc.read(&mut analog_pins.0).unwrap(), | |
1 => adc.read(&mut analog_pins.1).unwrap(), | |
_ => 0, // SERIOUS BUSINESS NUMPAD only has 2 multiplexers | |
// (the 3rd "mux" is imaginary and used by the IR sensor) | |
}; | |
let rounded_mv = Div::div(millivolts, crate::keeb::MV_DIVISOR); | |
// Now record the state of each channel: | |
let _ = adc_values.enqueue(rounded_mv as u32).unwrap(); | |
} | |
} | |
delay.delay_us(crate::keeb::SCAN_TIME_US); | |
// defmt::println!("CORE1_TASK_COMPLETE {:?}", word); // TEMP | |
sio.fifo.write_blocking(START_ADC_CHANNEL_MSG); | |
for _ in 0..adc_values.len() { | |
let val = adc_values.dequeue().unwrap(); | |
sio.fifo.write_blocking(val); | |
} | |
// This probably isn't necessary since we know how many values we'll always be getting: | |
sio.fifo.write_blocking(END_ADC_CHANNEL_MSG); | |
} | |
} | |
// Long ass RTIC setup/init stuff would go here (NOTE: I'm using monotonics) | |
// This is the important multi-core setup stuff from init(): | |
let mut sio = Sio::new(c.device.SIO); | |
let mut mc = crate::Multicore::new(&mut c.device.PSM, &mut c.device.PPB, &mut sio); | |
let cores = mc.cores(); | |
let core1 = &mut cores[1]; | |
let _test = core1.spawn(crate::core1_task, unsafe { &mut crate::CORE1_STACK.mem }); | |
// Let core1 know how fast the system clock is running | |
let sys_freq = clocks.system_clock.freq().integer(); | |
let mut siofifo = sio.fifo; | |
siofifo.write_blocking(sys_freq); | |
// This function below gets kicked off inside init() like so: | |
// read_analog_channels_fifo::spawn_at(monotonics::now() + fugit::ExtU32::micros(crate::keeb::SCAN_TIME_US)).unwrap(); | |
// This checks siofifo for incoming analog channel data and records it into channel_states | |
#[task(priority = 1, capacity = 1, shared = [siofifo, channel_states])] | |
fn read_analog_channels_fifo(mut c: read_analog_channels_fifo::Context) { | |
let now = monotonics::now(); | |
c.shared.siofifo.lock(|fifo| { | |
let input = fifo.read(); // This is non-blocking | |
if let Some(word) = input { | |
if word == crate::START_ADC_CHANNEL_MSG { | |
// We're being sent channel data; write it all to channel_states | |
c.shared.channel_states.lock(|chs| { | |
for chan in 0..16 { | |
for mux in 0..config::KEYBOARD_NUM_MULTIPLEXERS { | |
let rounded_mv = fifo.read_blocking(); | |
chs[mux][chan as usize].record_value(rounded_mv as u16); | |
} | |
} | |
}); | |
} else if word == crate::END_ADC_CHANNEL_MSG { | |
// defmt::println!("Read ADC values {:?}", word); // TEMP | |
} | |
} | |
}); | |
read_analog_channels_fifo::spawn_at(now + fugit::ExtU32::micros(crate::keeb::SCAN_TIME_US)) | |
.unwrap(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment