#![no_std] #![no_main] use panic_halt as _; use core::fmt::Write; use gd32vf103xx_hal::{ pac::USART0, serial::{self, Config, Parity, Rx, StopBits, Tx}, }; use longan_nano::{ hal::{pac, prelude::*}, led::{rgb, Led, BLUE, GREEN, RED}, }; use riscv_rt::entry; use ushell::{autocomplete::*, history::*, *}; const MAX_COMMAND_LEN: usize = 16; const HISTORY_SIZE: usize = 4; const COMMANDS: usize = 5; const SHELL_PROMPT: &str = "#> "; const CR: &str = "\r\n"; const HELP: &str = "\r\n\ \x1b[31mL\x1b[32mE\x1b[34mD\x1b[33m Shell\x1b[0m\r\n\r\n\ USAGE:\r\n\ \tcommand [arg]\r\n\r\n\ COMMANDS:\r\n\ \ton Switch led channel on [r,g,b,a]\r\n\ \toff Switch led channel off [r,g,b,a]\r\n\ \tstatus Get leds status\r\n\ \tclear Clear screen\r\n\ \thelp Print this message\r\n "; struct Context { red: RED, green: GREEN, blue: BLUE, shell: UShell< ushell::Serial, Rx>, StaticAutocomplete<{ COMMANDS }>, LRUHistory<{ MAX_COMMAND_LEN }, { HISTORY_SIZE }>, { MAX_COMMAND_LEN }, >, } #[entry] fn main() -> ! { let dp = pac::Peripherals::take().unwrap(); // Configure clocks let mut rcu = dp .RCU .configure() .ext_hf_clock(8.mhz()) .sysclk(108.mhz()) .freeze(); let mut afio = dp.AFIO.constrain(&mut rcu); let gpioa = dp.GPIOA.split(&mut rcu); let gpioc = dp.GPIOC.split(&mut rcu); let tx = gpioa.pa9.into_alternate_push_pull(); let rx = gpioa.pa10.into_floating_input(); let config = Config { baudrate: 115_200.bps(), parity: Parity::ParityNone, stopbits: StopBits::STOP1, }; let uart = serial::Serial::new(dp.USART0, (tx, rx), config, &mut afio, &mut rcu); let (tx, rx) = uart.split(); let autocomplete = StaticAutocomplete(["clear", "help", "status", "off ", "on "]); let history = LRUHistory::default(); let shell = UShell::new(ushell::Serial::from_parts(tx, rx), autocomplete, history); let (mut red, mut green, mut blue) = rgb(gpioc.pc13, gpioa.pa1, gpioa.pa2); red.off(); green.off(); blue.off(); let mut ctx = Context { shell, red, green, blue, }; loop { poll_serial(&mut ctx); } } fn poll_serial(ctx: &mut Context) { match ctx.shell.poll() { Ok(Some(Input::Command((cmd, args)))) => { match cmd { "help" => { ctx.shell.write_str(HELP).ok(); } "clear" => { ctx.shell.clear().ok(); } "status" => { let red = if ctx.red.is_on() { "On" } else { "Off" }; let green = if ctx.green.is_on() { "On" } else { "Off" }; let blue = if ctx.blue.is_on() { "On" } else { "Off" }; write!( ctx.shell, "{0:}Red: {1:}{0:}Green: {2:}{0:}Blue: {3:}{0:}", CR, red, green, blue, ) .ok(); } "on" => { match args { "r" | "red" => ctx.red.on(), "g" | "green" => ctx.green.on(), "b" | "blue" => ctx.blue.on(), "a" | "all" => { ctx.red.on(); ctx.green.on(); ctx.blue.on(); } _ => { write!(ctx.shell, "{0:}unsupported color channel", CR).ok(); } } ctx.shell.write_str(CR).ok(); } "off" => { match args { "r" | "red" => ctx.red.off(), "g" | "green" => ctx.green.off(), "b" | "blue" => ctx.blue.off(), "a" | "all" => { ctx.red.off(); ctx.green.off(); ctx.blue.off(); } _ => { write!(ctx.shell, "{0:}unsupported color channel", CR).ok(); } } ctx.shell.write_str(CR).ok(); } "" => { ctx.shell.write_str(CR).ok(); } _ => { write!(ctx.shell, "{0:}unsupported command{0:}", CR).ok(); } } ctx.shell.write_str(SHELL_PROMPT).ok(); } _ => {} } }