Created
August 27, 2023 12:21
-
-
Save justacec/91c4a2b9f529b861f5431b2bead06c8f to your computer and use it in GitHub Desktop.
Rust Embedded Mores Code Example
This file contains hidden or 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
[package] | |
edition = "2021" | |
name = "rp2040-pong" | |
version = "0.1.0" | |
[dependencies] | |
cortex-m = "0.7" | |
cortex-m-rt = "0.7" | |
#rtic = { git = "https://github.com/rtic-rs/rtic", features = ["thumbv6-backend"]} | |
#rtic-monotonics = { git = "https://github.com/rtic-rs/rtic", features = ["rp2040"] } | |
rtic = { version = "2", features = ["thumbv6-backend"] } | |
rtic-monotonics = { version = "1", features = ["rp2040"] } | |
embedded-hal = { version = "0.2", features = ["unproven"] } | |
heapless = "0.7" | |
fugit = "0.3" | |
usb-device = "0.2" | |
usbd-serial = "0.1" | |
defmt = "0.3" | |
defmt-rtt = "0.3" | |
panic-probe = { version = "0.3", features = ["print-defmt"] } | |
# We're using a Pico by default on this template | |
rp-pico = { version = "0.7", features = ["rt"] } | |
pio-proc = "0.2" | |
pio = "0.2" | |
# but you can use any BSP. Uncomment this to use the pro_micro_rp2040 BSP instead | |
# sparkfun-pro-micro-rp2040 = "0.3" | |
# If you're not going to use a Board Support Package you'll need these: | |
# rp2040-hal = { version="0.6", features=["rt"] } | |
# rp2040-boot2 = "0.2" | |
# cargo build/run | |
[profile.dev] | |
codegen-units = 1 | |
debug = 2 | |
debug-assertions = true | |
incremental = false | |
opt-level = 0 | |
overflow-checks = true | |
# cargo build/run --release | |
[profile.release] | |
codegen-units = 1 | |
debug = 2 | |
debug-assertions = false | |
incremental = false | |
lto = 'fat' | |
opt-level = 0 | |
overflow-checks = false | |
# do not optimize proc-macro crates = faster builds from scratch | |
[profile.dev.build-override] | |
codegen-units = 8 | |
debug = false | |
debug-assertions = false | |
opt-level = 0 | |
overflow-checks = false | |
[profile.release.build-override] | |
codegen-units = 8 | |
debug = false | |
debug-assertions = false | |
opt-level = 0 | |
overflow-checks = false | |
# cargo test | |
[profile.test] | |
codegen-units = 1 | |
debug = 2 | |
debug-assertions = true | |
incremental = false | |
opt-level = 3 | |
overflow-checks = true | |
# cargo test --release | |
[profile.bench] | |
codegen-units = 1 | |
debug = 2 | |
debug-assertions = false | |
incremental = false | |
lto = 'fat' | |
opt-level = 3 |
This file contains hidden or 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
#![no_std] | |
#![no_main] | |
#![feature(type_alias_impl_trait)] | |
mod morseencoder; | |
#[rtic::app( | |
device = rp_pico::hal::pac, | |
dispatchers = [TIMER_IRQ_1, TIMER_IRQ_2] | |
)] | |
mod app { | |
use rp_pico::hal::{ | |
clocks, gpio, | |
gpio::{pin::bank0::Gpio25, FunctionPio0, Pin}, | |
pac, | |
pac::PIO0, | |
sio::Sio, | |
usb, | |
watchdog::Watchdog, | |
}; | |
use rp_pico::XOSC_CRYSTAL_FREQ; | |
// use core::mem::MaybeUninit; | |
// use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin}; | |
// use fugit::RateExtU32; | |
use rtic_monotonics::rp2040::*; | |
use usb_device::{class_prelude::*, prelude::*}; | |
use usbd_serial::SerialPort; | |
use defmt::*; | |
use defmt_rtt as _; | |
use panic_probe as _; | |
use crate::morseencoder::MorseEncoder; | |
// rtic_monotonics::make_rp2040_monotonic_handler!(); | |
#[shared] | |
struct Shared { | |
usb_dev: UsbDevice<'static, usb::UsbBus>, | |
serial: SerialPort<'static, usb::UsbBus>, | |
me: MorseEncoder<PIO0>, | |
} | |
#[local] | |
struct Local { | |
led: gpio::Pin<Gpio25, FunctionPio0>, | |
// USB_BUS: &'static UsbBusAllocator<usb::UsbBus>, | |
// USB_DEVICE: &'static UsbDevice<'static, usb::UsbBus>, | |
// USB_SERIAL: Option<SerialPort<'static, usb::UsbBus>>, | |
} | |
#[init] | |
fn init(mut ctx: init::Context) -> (Shared, Local) { | |
let rp2040_timer_token = rtic_monotonics::create_rp2040_monotonic_token!(); | |
// Configure the clocks, watchdog - The default is to generate a 125 MHz system clock | |
Timer::start(ctx.device.TIMER, &mut ctx.device.RESETS, rp2040_timer_token); // default rp2040 clock-rate is 125MHz | |
let mut watchdog = Watchdog::new(ctx.device.WATCHDOG); | |
let clocks = clocks::init_clocks_and_plls( | |
XOSC_CRYSTAL_FREQ, | |
ctx.device.XOSC, | |
ctx.device.CLOCKS, | |
ctx.device.PLL_SYS, | |
ctx.device.PLL_USB, | |
&mut ctx.device.RESETS, | |
&mut watchdog, | |
) | |
.ok() | |
.unwrap(); | |
// Set up the USB driver | |
static mut USB_BUS: Option<usb_device::bus::UsbBusAllocator<usb::UsbBus>> = None; | |
unsafe { | |
USB_BUS.replace(UsbBusAllocator::new(usb::UsbBus::new( | |
ctx.device.USBCTRL_REGS, | |
ctx.device.USBCTRL_DPRAM, | |
clocks.usb_clock, | |
true, | |
&mut ctx.device.RESETS, | |
))); | |
} | |
let serial = SerialPort::new(unsafe { USB_BUS.as_ref().unwrap() }); | |
let usb_dev = UsbDeviceBuilder::new( | |
unsafe { USB_BUS.as_ref().unwrap() }, | |
UsbVidPid(0x16c0, 0x27dd), | |
) | |
.manufacturer("JustTech") | |
.product("Justace Pong") | |
.serial_number("0001") | |
.device_class(usbd_serial::USB_CLASS_CDC) | |
.build(); | |
// unsafe { | |
// pac::NVIC::unmask(pac::Interrupt::USBCTRL_IRQ); | |
// }; | |
// Init LED pin | |
let sio = Sio::new(ctx.device.SIO); | |
let gpioa = rp_pico::Pins::new( | |
ctx.device.IO_BANK0, | |
ctx.device.PADS_BANK0, | |
sio.gpio_bank0, | |
&mut ctx.device.RESETS, | |
); | |
let led: Pin<_, FunctionPio0> = gpioa.led.into_mode(); | |
// led.set_low().unwrap(); | |
// Create the encoder | |
let me = MorseEncoder::init(ctx.device.PIO0, 25, &mut ctx.device.RESETS); | |
// Spawn heartbeat task | |
heartbeat::spawn().ok(); | |
// Return resources and timer | |
( | |
Shared { | |
me, | |
serial, | |
usb_dev: usb_dev, | |
}, | |
Local { led }, | |
) | |
} | |
#[task(binds = USBCTRL_IRQ, priority = 1, shared = [me, usb_dev, serial])] | |
fn usb_stuff(ctx: usb_stuff::Context) { | |
use core::sync::atomic::{AtomicBool, Ordering}; | |
info!("Got USB"); | |
// Clear the USB IRQ | |
pac::NVIC::unpend(pac::Interrupt::USBCTRL_IRQ); | |
/// Note whether we've already printed the "hello" message. | |
static SAID_HELLO: AtomicBool = AtomicBool::new(false); | |
// Grab the global objects. This is OK as we only access them under interrupt. | |
let usb_dev = ctx.shared.usb_dev; | |
let serial = ctx.shared.serial; | |
(usb_dev, serial).lock(|usb_dev, serial| { | |
// Say hello exactly once on start-up | |
if !SAID_HELLO.load(Ordering::Relaxed) { | |
SAID_HELLO.store(true, Ordering::Relaxed); | |
let _ = serial.write(b"Hello, World!\r\n"); | |
} | |
info!("Polling"); | |
// Poll the USB driver with all of our supported USB Classes | |
if usb_dev.poll(&mut [serial]) { | |
let mut buf = [0u8; 64]; | |
match serial.read(&mut buf) { | |
Err(_e) => { | |
// Do nothing | |
info!("USB: Error"); | |
} | |
Ok(0) => { | |
// Do nothing | |
info!("USB Zero"); | |
} | |
Ok(count) => { | |
info!("USB Got Something"); | |
// Convert to upper case | |
buf.iter_mut().take(count).for_each(|b| { | |
b.make_ascii_uppercase(); | |
}); | |
// Send back to the host | |
let mut wr_ptr = &buf[..count]; | |
while !wr_ptr.is_empty() { | |
let _ = serial.write(wr_ptr).map(|len| { | |
wr_ptr = &wr_ptr[len..]; | |
}); | |
} | |
} | |
} | |
} | |
}); | |
info!("Finished USB"); | |
} | |
#[task(priority = 2, local = [led], shared = [me])] | |
async fn heartbeat(mut ctx: heartbeat::Context) { | |
// Flicker the built-in LED | |
// _ = ctx.local.led.toggle(); | |
info!("Sending Character"); | |
ctx.shared.me.lock(|a| a.send_str("Hello World").unwrap()); | |
// Re-spawn this task after 1 second | |
Timer::delay(5000.millis()).await; | |
} | |
} |
This file contains hidden or 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
use bsp::hal::{pac::RESETS, pio, pio::PIOExt, pio::ShiftDirection, pio::PIO, pio::SM0}; | |
use defmt::*; | |
use defmt_rtt as _; | |
use heapless::Vec; | |
use pio_proc; | |
use rp_pico as bsp; | |
const LOOKUP: [u8; 128] = [ | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0b0000000, // Space | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0b10111111, // 0 | |
0b10101111, // 1 | |
0b10100111, // 2 | |
0b10100011, // 3 | |
0b10100001, // 4 | |
0b10100000, // 5 | |
0b10110000, // 6 | |
0b10111000, // 7 | |
0b10111100, // 8 | |
0b10111110, // 9 | |
0, 0, 0, 0, 0, 0, 0, 0b01001000, // A | |
0b10010000, // B | |
0b10010100, // C | |
0b01110000, // D | |
0b00100000, // E | |
0b10000100, // F | |
0b01111000, // G | |
0b10000000, // H | |
0b01000000, // I | |
0b10001110, // J | |
0b01110100, // K | |
0b10001000, // L | |
0b01011000, // M | |
0b01010000, // N | |
0b01111100, // O | |
0b10001100, // P | |
0b10011010, // Q | |
0b01101000, // R | |
0b01100000, // S | |
0b00110000, // T | |
0b01100100, // U | |
0b10000010, // V | |
0b01101100, // W | |
0b10010010, // X | |
0b10010110, // Y | |
0b10011000, // Z | |
0, 0, 0, 0, 0, 0, 0b01001000, // a | |
0b10010000, // b | |
0b10010100, // c | |
0b01110000, // d | |
0b00100000, // e | |
0b10000100, // f | |
0b01111000, // g | |
0b10000000, // h | |
0b01000000, // i | |
0b10001110, // j | |
0b01110100, // k | |
0b10001000, // l | |
0b01011000, // m | |
0b01010000, // n | |
0b01111100, // o | |
0b10001100, // p | |
0b10011010, // q | |
0b01101000, // r | |
0b01100000, // s | |
0b00110000, // t | |
0b01100100, // u | |
0b10000010, // v | |
0b01101100, // w | |
0b10010010, // x | |
0b10010110, // y | |
0b10011000, // z | |
0, 0, 0, 0, 0, | |
]; | |
pub struct MorseEncoder<P> | |
where | |
P: PIOExt, | |
{ | |
pio: PIO<P>, | |
tx: pio::Tx<(P, SM0)>, | |
} | |
impl<P> MorseEncoder<P> | |
where | |
P: PIOExt, | |
{ | |
pub fn init(pio: P, pin_number: i32, reset: &mut RESETS) -> Self { | |
let led_pin_id = pin_number; | |
// Initialize and start PIO | |
let (mut pio, sm0, sm1, _, _) = pio.split(reset); | |
let program_sm0 = pio_proc::pio_file!("src/pio0.pio", select_program("morse")); | |
let installed_sm0 = pio.install(&program_sm0.program).unwrap(); | |
info!( | |
"sm0 offset: {} wrap_target: {}", | |
installed_sm0.offset(), | |
installed_sm0.wrap_target() | |
); | |
let (mut sm_0, _rx, tx) = pio::PIOBuilder::from_program(installed_sm0) | |
.set_pins(led_pin_id as u8, 1) | |
.clock_divisor_fixed_point(50000, 0) | |
.out_shift_direction(ShiftDirection::Left) | |
.build(sm0); | |
// The GPIO pin needs to be configured as an output. | |
sm_0.set_pindirs([(led_pin_id as u8, pio::PinDir::Output)]); | |
let program_sm1 = pio_proc::pio_file!("src/pio0.pio", select_program("delay")); | |
let installed_sm1 = pio.install(&program_sm1.program).unwrap(); | |
info!( | |
"sm1 offset: {} wrap_target: {}", | |
installed_sm1.offset(), | |
installed_sm1.wrap_target() | |
); | |
let (mut sm_1, _, _) = pio::PIOBuilder::from_program(installed_sm1) | |
.clock_divisor_fixed_point(50000, 0) | |
.build(sm1); | |
let sm = sm_0.with(sm_1); | |
sm.sync().start(); | |
MorseEncoder { pio: pio, tx: tx } | |
} | |
pub fn send_char(&mut self, input: char) -> Result<(), ()> { | |
if self.tx.is_full() { | |
return Err(()); | |
} | |
let val = Self::lookup(input as u8); | |
while self.tx.is_full() {} | |
match self.tx.write(val as u32) { | |
true => Ok(()), | |
false => Err(()), | |
} | |
} | |
pub fn send_str(&mut self, input: &str) -> Result<(), ()> { | |
let vals = Self::lookup_str(input); | |
for val in vals { | |
while self.tx.is_full() {} | |
match self.tx.write(val as u32) { | |
true => {} | |
false => return Err(()), | |
} | |
} | |
Ok(()) | |
} | |
pub fn lookup_str(s: &str) -> Vec<u8, 256> { | |
s.as_bytes().iter().map(|c| LOOKUP[*c as usize]).collect() | |
} | |
pub fn lookup(x: u8) -> u8 { | |
LOOKUP[x as usize] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment