Skip to content

Instantly share code, notes, and snippets.

@riskable
Last active November 20, 2022 23:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save riskable/608c5e0e73a8a48da507d99a32ec281b to your computer and use it in GitHub Desktop.
Save riskable/608c5e0e73a8a48da507d99a32ec281b to your computer and use it in GitHub Desktop.
Multi-core Rust example on rp2040 with RTIC (on one core)
// 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