diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock deleted file mode 100644 index bf85884..0000000 --- a/firmware/Cargo.lock +++ /dev/null @@ -1,230 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "jerk_control" -version = "0.1.0" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.123" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" - -[[package]] -name = "motion-tester" -version = "0.1.0" -dependencies = [ - "jerk_control", - "structopt", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syn" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "unicode-segmentation" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index 07f73d0..1a4463b 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = [ "jerk_control", "motion-tester" ] +members = [ "jerk_control", "motion-tester", "film-scanner-fw" ] diff --git a/firmware/film-scanner-fw/Cargo.toml b/firmware/film-scanner-fw/Cargo.toml new file mode 100644 index 0000000..818f524 --- /dev/null +++ b/firmware/film-scanner-fw/Cargo.toml @@ -0,0 +1,89 @@ + +[package] +name = "film-scanner-fw" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + + +cortex-m = "0.7.3" +cortex-m-rt = "0.7.0" +cortex-m-rtic = "1.0.0" +embedded-hal = { version = "0.2.5", features=["unproven"] } +embedded-time = "0.12.0" + +# jnc +usb-device = "0.2.8" +usbd-serial = "0.1.1" +usbd-hid = "0.5.1" +futures = { version = "0.3", default-features = false, optional = true } + +defmt = "0.3.0" +defmt-rtt = "0.3.1" +panic-probe = { version = "0.3.0", features = ["print-defmt"] } + +# We're using a Pico by default on this template +rp-pico = "0.2.0" + +# but you can use any BSP. Uncomment this to use the pro_micro_rp2040 BSP instead +# sparkfun-pro-micro-rp2040 = "0.1.0" + +# If you're not going to use a Board Support Package you'll need these: +rp2040-hal = { version="0.4.0", features=["rt"] } + +rp2040-boot2 = {version="0.2.0", optional = true } + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true +incremental = false +opt-level = 3 +overflow-checks = true + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false +incremental = false +lto = 'fat' +opt-level = 3 +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 \ No newline at end of file diff --git a/firmware/film-scanner-fw/build.rs b/firmware/film-scanner-fw/build.rs new file mode 100644 index 0000000..c275a36 --- /dev/null +++ b/firmware/film-scanner-fw/build.rs @@ -0,0 +1,31 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); +} \ No newline at end of file diff --git a/firmware/film-scanner-fw/memory.x b/firmware/film-scanner-fw/memory.x new file mode 100644 index 0000000..070eac7 --- /dev/null +++ b/firmware/film-scanner-fw/memory.x @@ -0,0 +1,15 @@ +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; \ No newline at end of file diff --git a/firmware/film-scanner-fw/src/fmt.rs b/firmware/film-scanner-fw/src/fmt.rs new file mode 100644 index 0000000..e42eb3f --- /dev/null +++ b/firmware/film-scanner-fw/src/fmt.rs @@ -0,0 +1,42 @@ +// NOTE: This file came from: +// https://github.com/Nashenas88/dactyl-manuform-kb2040-rs/blob/main/src/fmt.rs + +//! Formatting module for helping with logging over a serial connection. This +//! is useful on the kb2040 since there are no debugging pins exposed on the +//! board. + +use core::fmt; + +pub(crate) struct Wrapper<'a> { + buf: &'a mut [u8], + offset: usize, +} + +impl<'a> Wrapper<'a> { + pub(crate) fn new(buf: &'a mut [u8]) -> Self { + Wrapper { buf, offset: 0 } + } +} + +impl<'a> fmt::Write for Wrapper<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + let bytes = s.as_bytes(); + + // Skip over already-copied data. + let remainder = &mut self.buf[self.offset..]; + // Check if there is space remaining (return error instead of panicking). + if remainder.len() < bytes.len() { + return Err(core::fmt::Error); + } + + // Make the two slices the same length. + let remainder = &mut remainder[..bytes.len()]; + // Copy. + remainder.copy_from_slice(bytes); + + // Update offset to avoid overwriting. + self.offset += bytes.len(); + + Ok(()) + } +} diff --git a/firmware/film-scanner-fw/src/main.rs b/firmware/film-scanner-fw/src/main.rs new file mode 100644 index 0000000..8b77ccd --- /dev/null +++ b/firmware/film-scanner-fw/src/main.rs @@ -0,0 +1,523 @@ +#![no_std] +#![no_main] + +use defmt_rtt as _; +use panic_probe as _; + +mod fmt; + +#[rtic::app(device = rp_pico::hal::pac, peripherals = true)] +mod app { + + use crate::fmt::Wrapper; + use core::fmt::Write; + use embedded_hal::digital::v2::OutputPin; + use embedded_time::duration::Extensions; + + use rp_pico::hal; + use rp_pico::pac; + use rp_pico::XOSC_CRYSTAL_FREQ; + + // USB Device support + use usb_device::{class_prelude::*, prelude::*}; + // USB Communications Class Device support + use usbd_serial::SerialPort; + + // Blinck time 5 seconds + const SCAN_TIME_US: u32 = 5000000; // 200000; // 5000000; // 1000000; // 200000; + + // IMPORTANT: The USB-Serial with RTIC github project example that I'm following. + // I tried to use the Pico board examples of USB-Serial (without interrupts + // and with interrupts with success, but when using with RTIC I could not make + // it work when merged with the RTIC example.) So I asked some questions + // in the in Matrix chat and received links to examples of there github + // project where it was working, then a used and adapted some parts there + // in this project template. + // This were the kind folks that helped me in the Matrix chat, the 3 projects + // that they suggest me to study are good examples of programs made with RTIC + // and USB and should be studied. + // + // Paul Daniel Faria + // https://github.com/Nashenas88/dactyl-manuform-kb2040-rs/blob/main/src/main.rs#L80 + // + // see also: + // korken89 + // https://github.com/korken89/pico-probe/tree/master/src + // + // see also: + // Mathias + // https://github.com/mgottschlag/rp2040-usb-sound-card/blob/b8078b57361c1b08755e5ab5f9992c56457ec18b/src/main.rs#L188 + // + // + // Global Static variable, has to be written inside unsafe blocks. + // A reference can be obtained with as_ref() method. + + pub struct Counter { + counter: u32, + enable: bool, + } + + impl Counter { + fn new() -> Self { + Counter { + counter: 0_u32, + enable: true, + } + } + + fn get(&self) -> u32 { + self.counter + } + + fn reset(&mut self) { + self.counter = 0_u32; + } + + fn increment(&mut self) { + self.counter += 1_u32; + } + + fn enable(&mut self, state: bool) { + self.enable = state; + } + } + + #[shared] + struct Shared { + timer: hal::Timer, + alarm: hal::timer::Alarm0, + led: hal::gpio::Pin, + led_blink_enable: bool, + + serial: SerialPort<'static, hal::usb::UsbBus>, + usb_dev: usb_device::device::UsbDevice<'static, hal::usb::UsbBus>, + + counter: Counter, + } + + #[local] + struct Local {} + + #[init(local = [usb_bus: Option> = None])] + fn init(c: init::Context) -> (Shared, Local, init::Monotonics) { + //******* + // Initialization of the system clock. + + let mut resets = c.device.RESETS; + let mut watchdog = hal::watchdog::Watchdog::new(c.device.WATCHDOG); + + // Configure the clocks - The default is to generate a 125 MHz system clock + let clocks = hal::clocks::init_clocks_and_plls( + XOSC_CRYSTAL_FREQ, + c.device.XOSC, + c.device.CLOCKS, + c.device.PLL_SYS, + c.device.PLL_USB, + &mut resets, + &mut watchdog, + ) + .ok() + .unwrap(); + + //******* + // Initialization of the USB and Serial and USB Device ID. + + // USB + // + // Set up the USB driver + // The bus that is used to manage the device and class below. + let usb_bus: &'static _ = + c.local + .usb_bus + .insert(UsbBusAllocator::new(hal::usb::UsbBus::new( + c.device.USBCTRL_REGS, + c.device.USBCTRL_DPRAM, + clocks.usb_clock, + true, + &mut resets, + ))); + + // Set up the USB Communications Class Device driver. + let serial = SerialPort::new(usb_bus); + + // Create a USB device with a fake VID and PID + let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(2) // from: https://www.usb.org/defined-class-codes + .build(); + + //******* + // Initialization of the LED GPIO and the timer. + + let sio = hal::Sio::new(c.device.SIO); + let pins = rp_pico::Pins::new( + c.device.IO_BANK0, + c.device.PADS_BANK0, + sio.gpio_bank0, + &mut resets, + ); + let mut led = pins.led.into_push_pull_output(); + led.set_low().unwrap(); + + let mut timer = hal::Timer::new(c.device.TIMER, &mut resets); + let mut alarm = timer.alarm_0().unwrap(); + let _ = alarm.schedule(SCAN_TIME_US.microseconds()); + alarm.enable_interrupt(&mut timer); + + // Enable led_blink. + let led_blink_enable = true; + + // Reset the counter + let counter = Counter::new(); + + //******** + // Return the Shared variables struct, the Local variables struct and the XPTO Monitonics + // (Note: Read again the RTIC book in the section of Monotonics timers) + ( + Shared { + timer, + alarm, + led, + led_blink_enable, + + serial, + usb_dev, + + counter, + }, + Local {}, + init::Monotonics(), + ) + } + + /// Task that blinks the rp-pico onboard LED and that send a message "LED ON!" and "LED OFF!" do USB-Serial. + #[task( + binds = TIMER_IRQ_0, + priority = 1, + shared = [timer, alarm, led, led_blink_enable, serial, counter], + local = [tog: bool = true], + )] + fn timer_irq(mut cx: timer_irq::Context) { + let mut buf = [0u8; 64]; + + let led = cx.shared.led; + let led_blink_enable = cx.shared.led_blink_enable; + let counter = cx.shared.counter; + + let tog = cx.local.tog; + + // Blinks the LED ON / OFF. + (led, led_blink_enable, counter).lock(|led_a, led_blink_enable_a, counter_a| { + let led_state_str: &str; + if *led_blink_enable_a { + if *tog { + led_a.set_high().unwrap(); + led_state_str = "ON "; + } else { + led_a.set_low().unwrap(); + led_state_str = "OFF"; + } + let _ = writeln!( + Wrapper::new(&mut buf), + "LED {}! counter = {}", + led_state_str, + counter_a.get() + ); + } + if counter_a.enable { + counter_a.increment(); + } + + if *led_blink_enable_a { + *tog = !*tog; + } + }); + + // Clears the timer interrupt and Set's the new delta_time in the future. + let mut timer = cx.shared.timer; + let mut alarm = cx.shared.alarm; + (alarm).lock(|a| { + (timer).lock(|timer_a| { + a.clear_interrupt(timer_a); + let _ = a.schedule(SCAN_TIME_US.microseconds()); + }); + }); + + // Write the message "blabla! 2" do USB-Serial. + cx.shared.serial.lock(|s| { + write_serial(s, unsafe { core::str::from_utf8_unchecked(&buf) }, false); + }); + + /* + // Write the message "blabla! 2" do USB-Serial. + c.shared.serial.lock(|s| { + let mut buf = [0u8; 64]; + let _ = writeln!(Wrapper::new(&mut buf), "blabla! {}", 2); /*"{:?}"*/ + write_serial(s, unsafe { core::str::from_utf8_unchecked(&buf) }, false); + }); + */ + } + + /// Usb interrupt handler. Runs every time the host requests new data. + #[task(binds = USBCTRL_IRQ, priority = 3, shared = [led, led_blink_enable, serial, usb_dev, counter])] + fn usb_rx(cx: usb_rx::Context) { + let led = cx.shared.led; + let led_blink_enable = cx.shared.led_blink_enable; + + let usb_dev = cx.shared.usb_dev; + let serial = cx.shared.serial; + let counter = cx.shared.counter; + + (led, led_blink_enable, usb_dev, serial, counter).lock( + |led_a, led_blink_enable_a, usb_dev_a, serial_a, counter_a| { + // Check for new data + if usb_dev_a.poll(&mut [serial_a]) { + let mut buf = [0u8; 64]; + match serial_a.read(&mut buf) { + Err(_e) => { + // Do nothing + // let _ = serial_a.write(b"Error."); + // let _ = serial_a.flush(); + } + Ok(0) => { + // Do nothing + let _ = serial_a.write(b"Didn't received data."); + let _ = serial_a.flush(); + } + Ok(_count) => { + match_usb_serial_buf( + &buf, + led_a, + led_blink_enable_a, + serial_a, + counter_a, + ); + + /* + // Code to echo the characters in Upper Case. + + // Convert to upper case + buf.iter_mut().take(count).for_each(|b| { + b.make_ascii_uppercase(); + }); + if count > 0 { + let _ = serial_a.write(b"Received data! "); + let _ = serial_a.flush(); + } + + // Send back to the host + let mut wr_ptr = &buf[..count]; + while !wr_ptr.is_empty() { + match serial_a.write(wr_ptr) { + Ok(len) => { + wr_ptr = &wr_ptr[len..]; + // if wr_ptr.len() == 0 { + // let _ = serial_a.write(b""); + let _ = serial_a.flush(); + // } + } + // On error, just drop unwritten data. + // One possible error is Err(WouldBlock), meaning the USB + // write buffer is full. + Err(_) => break, + }; + } + + */ + } + } + } + }, + ); + } + + // Task with least priority that only runs when nothing else is running. + #[idle(local = [x: u32 = 0])] + fn idle(_cx: idle::Context) -> ! { + // Locals in idle have lifetime 'static + // let _x: &'static mut u32 = cx.local.x; + + //hprintln!("idle").unwrap(); + + loop { + cortex_m::asm::nop(); + } + } + + /* New Tasks */ + + /// This function come from the github with USB-Serial example (see link above). + /// + /// Helper function to ensure all data is written across the serial interface. + fn write_serial(serial: &mut SerialPort<'static, hal::usb::UsbBus>, buf: &str, block: bool) { + let write_ptr = buf.as_bytes(); + + // Because the buffer is of constant size and initialized to zero (0) we here + // add a test to determine the size that's really occupied by the str that we + // wan't to send. From index zero to first byte that is as the zero byte value. + let mut index = 0; + while index < write_ptr.len() && write_ptr[index] != 0 { + index += 1; + } + let mut write_ptr = &write_ptr[0..index]; + + while !write_ptr.is_empty() { + match serial.write(write_ptr) { + Ok(len) => write_ptr = &write_ptr[len..], + // Meaning the USB write buffer is full + Err(UsbError::WouldBlock) => { + if !block { + break; + } + } + // On error, just drop unwritten data. + Err(_) => break, + } + } + // let _ = serial.write("\n".as_bytes()); + let _ = serial.flush(); + } + + fn match_usb_serial_buf( + buf: &[u8; 64], + led: &mut hal::gpio::Pin, + led_blink_enable: &mut bool, + serial: &mut SerialPort<'static, hal::usb::UsbBus>, + counter: &mut Counter, + ) { + let _buf_len = buf.len(); + match buf[0] { + // Print Menu + b'M' | b'm' => { + write_serial(serial, "M - Print Menu\n", false); + print_menu(serial); + } + // 0 - Reset counter + b'0' => { + write_serial(serial, "M - Print Menu\n", false); + counter.reset(); + } + // 1 - Increment counter + b'1' => { + write_serial(serial, "1 - Increment counter\n", false); + counter.increment(); + } + // 2 - Start continues counter + b'2' => { + write_serial(serial, "2 - Start continues counter\n", false); + counter.enable(true); + } + // 3 - Stop continues counter + b'3' => { + write_serial(serial, "3 - Stop continues counter\n", false); + counter.enable(false); + } + // 4 - Get switch and LED state + b'4' => { + write_serial(serial, "4 - Get switch and LED state\n", false); + + // GPIO 25 onboard LED, we are going to read the bit 8 of the gpio_status register. + // OUTFROMPERI - output signal from selected peripheral, before register + // override is applied. + // See pag 272 of the Pico Datasets: + // https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#_gpio_functions + + let led_status_reg = + unsafe { (*pac::IO_BANK0::ptr()).gpio[25].gpio_status.read().bits() }; + + // Reserved bit. + // let sio_pin_value = unsafe { (*pac::SIO::ptr()).gpio_out.read().bits() }; + + let (led_bool, led_status) = if ((led_status_reg & 1 << 8) >> 8) == 1_u32 { + (true, "ON") + } else { + (false, "OFF") + }; + + let mut buf = [0u8; 64]; + let _ = writeln!( + Wrapper::new(&mut buf), + "LED Status {:b}, {} LED {}", + led_status_reg, + led_bool, + led_status + ); + write_serial( + serial, + unsafe { core::str::from_utf8_unchecked(&buf) }, + false, + ); + + // unsafe { (*pac::TIMER::ptr()).timerawh.read().bits() }; + } + // 5 - Set LED on + b'5' => { + write_serial(serial, "5 - Set LED on\n", false); + *led_blink_enable = false; + let _ = led.set_high(); + } + // 6 - Set LED off + b'6' => { + write_serial(serial, "6 - Set LED off\n", false); + *led_blink_enable = false; + let _ = led.set_low(); + } + // 7 - Set LED blink enable + b'7' => { + write_serial(serial, "7 - Set LED blink enable\n", false); + *led_blink_enable = true; + } + b'8' => { + write_serial(serial, "8 - Display data rate\n", false); + + let data_rate = serial.line_coding().data_rate(); + let mut buf = [0u8; 64]; + let _ = writeln!(Wrapper::new(&mut buf), "Data rate: {} bit/s", data_rate); + write_serial( + serial, + unsafe { core::str::from_utf8_unchecked(&buf) }, + false, + ); + } + _ => { + write_serial( + serial, + unsafe { core::str::from_utf8_unchecked(buf) }, + false, + ); + write_serial(serial, "Invalid option!\n", false); + } + } + } + + fn print_menu(serial: &mut SerialPort<'static, hal::usb::UsbBus>) { + let mut _buf = [0u8; 273]; + + // Create the Menu. + let menu_str = "***************** +* Menu: +* +* M / m - Print menu +* 0 - Reset counter +* 1 - Increment counter +* 2 - Start continues counter +* 3 - Stop continues counter +* 4 - Get switch and LED state +* 5 - Set LED on +* 6 - Set LED off +* 7 - Set LED blink enable +* 8 - Display data rate +***************** +Enter option: "; + + write_serial(serial, menu_str, true); + + // Send out the data to USB-Serial. + // let _ = serial.write(menu_str); + + // let _ = writeln!(Wrapper::new(&mut buf), &menu_str); + // write_serial(serial, unsafe { core::str::from_utf8_unchecked(menu_str) }); + } +} diff --git a/firmware/shell.nix b/firmware/shell.nix index d24f6e5..3f3ae40 100644 --- a/firmware/shell.nix +++ b/firmware/shell.nix @@ -1,10 +1,12 @@ { pkgs ? import {} }: pkgs.mkShell { - buildInputs = [ - pkgs.stdenv - + buildInputs = with pkgs; [ + tio + stdenv + elf2uf2-rs + probe-run # keep this line if you use bash - pkgs.bashInteractive + bashInteractive ]; }