Implemented basic protocol parsing

This commit is contained in:
Sebastian 2022-08-20 18:27:10 +02:00
parent 7a4e4a89cb
commit a7ae3731a4
3 changed files with 244 additions and 126 deletions

80
src/display.rs Normal file
View file

@ -0,0 +1,80 @@
use core::fmt::Write;
use embassy_stm32::i2c;
use embassy_stm32::peripherals;
use embassy_stm32::time::Hertz;
use embassy_time::{Duration, Timer};
use embedded_graphics::{
mono_font::MonoTextStyle,
pixelcolor::BinaryColor,
prelude::*,
primitives::{PrimitiveStyleBuilder, Rectangle},
text::Text,
};
use profont::{PROFONT_12_POINT, PROFONT_7_POINT};
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306};
use arrayvec::ArrayString;
#[embassy_executor::task]
pub async fn display_task(i2c1: peripherals::I2C1, sda: peripherals::PB6, scl: peripherals::PB7) {
let i2c = i2c::I2c::new(i2c1, sda, scl, Hertz::hz(100_000), i2c::Config::default());
let interface = I2CDisplayInterface::new(i2c);
let mut display = Ssd1306::new(interface, DisplaySize128x32, DisplayRotation::Rotate0)
.into_buffered_graphics_mode();
display.init().unwrap();
let text_large = MonoTextStyle::new(&PROFONT_12_POINT, BinaryColor::On);
let text_large_inv = MonoTextStyle::new(&PROFONT_12_POINT, BinaryColor::Off);
let text_small = MonoTextStyle::new(&PROFONT_7_POINT, BinaryColor::On);
let style_filled = PrimitiveStyleBuilder::new()
.fill_color(BinaryColor::On)
.build();
loop {
display.clear();
Text::new("AFG rotor ctrl v0.1.0", Point::new(0, 6), text_small)
.draw(&mut display)
.unwrap();
Text::new("AZ: 23", Point::new(1, 17), text_large)
.draw(&mut display)
.unwrap();
Text::new("EL: 42", Point::new(64, 17), text_large)
.draw(&mut display)
.unwrap();
Rectangle::new(Point::new(0, 19), Size::new(128, 23))
.into_styled(style_filled)
.draw(&mut display)
.unwrap();
Text::new("AZ: 23", Point::new(1, 30), text_large_inv)
.draw(&mut display)
.unwrap();
Text::new("EL: 42", Point::new(64, 30), text_large_inv)
.draw(&mut display)
.unwrap();
/*
let now = Instant::now().as_millis();
let mut buf = ArrayString::<20>::new();
write!(&mut buf, "{}", now).expect("Can't write");
Text::new(&buf, Point::new(0, 20), text_large)
.draw(&mut display)
.unwrap();
*/
display.flush().unwrap();
Timer::after(Duration::from_millis(500)).await;
}
}

View file

@ -11,24 +11,17 @@ use panic_probe as _;
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::time::Hertz;
use embassy_stm32::usb::{Driver, Instance};
use embassy_stm32::{i2c, interrupt, Config};
use embassy_time::{Duration, Instant, Timer};
use embassy_usb::driver::EndpointError;
use embassy_usb::Builder;
use embassy_usb_serial::{CdcAcmClass, State};
use futures::future::join;
use embassy_stm32::Config;
use embassy_time::{Duration, Timer};
use embedded_graphics::{
mono_font::MonoTextStyle, pixelcolor::BinaryColor, prelude::*, text::Text,
};
use profont::PROFONT_12_POINT;
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306};
mod display;
use display::display_task;
use arrayvec::ArrayString;
mod usb;
use usb::usb_task;
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
async fn main(spawner: Spawner) {
let mut config = Config::default();
config.rcc.hse = Some(Hertz(8_000_000));
config.rcc.sys_ck = Some(Hertz(48_000_000));
@ -46,116 +39,6 @@ async fn main(_spawner: Spawner) {
Timer::after(Duration::from_millis(10)).await;
}
// Create the driver, from the HAL.
let irq = interrupt::take!(USB_LP_CAN1_RX0);
let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
// Create embassy-usb Config
let config = embassy_usb::Config::new(0xc0de, 0xcafe);
//config.max_packet_size_0 = 64;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut control_buf = [0; 7];
let mut state = State::new();
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut control_buf,
None,
);
// Create classes on the builder.
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
let usb_fut = usb.run();
let i2c = i2c::I2c::new(
p.I2C1,
p.PB6,
p.PB7,
Hertz::hz(100_000),
i2c::Config::default(),
);
let interface = I2CDisplayInterface::new(i2c);
let display_fut = async {
let mut display = Ssd1306::new(interface, DisplaySize128x32, DisplayRotation::Rotate0)
.into_buffered_graphics_mode();
display.init().unwrap();
let text_style = MonoTextStyle::new(&PROFONT_12_POINT, BinaryColor::On);
loop {
display.clear();
Text::new("Hello world", Point::new(0, 7), text_style)
.draw(&mut display)
.unwrap();
let now = Instant::now().as_millis();
let mut buf = ArrayString::<20>::new();
write!(&mut buf, "{}", now).expect("Can't write");
Text::new(&buf, Point::new(0, 20), text_style)
.draw(&mut display)
.unwrap();
display.flush().unwrap();
Timer::after(Duration::from_millis(500)).await;
}
};
// Do stuff with the class!
let echo_fut = async {
loop {
class.wait_connection().await;
defmt::info!("Connected");
let _ = echo(&mut class).await;
defmt::info!("Disconnected");
}
};
// Run everything concurrently.
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
join(join(usb_fut, display_fut), echo_fut).await;
}
struct Disconnected {}
impl From<EndpointError> for Disconnected {
fn from(val: EndpointError) -> Self {
match val {
EndpointError::BufferOverflow => panic!("Buffer overflow"),
EndpointError::Disabled => Disconnected {},
}
}
}
async fn echo<'d, T: Instance + 'd>(
class: &mut CdcAcmClass<'d, Driver<'d, T>>,
) -> Result<(), Disconnected> {
let mut buf = [0; 64];
loop {
let n = class.read_packet(&mut buf).await?;
let data = &buf[..n];
defmt::info!("data: {:x}", data);
class.write_packet(">".as_bytes()).await?;
class.write_packet(data).await?;
class.write_packet("\n".as_bytes()).await?;
}
spawner.spawn(usb_task(p.USB, p.PA12, p.PA11)).unwrap();
spawner.spawn(display_task(p.I2C1, p.PB6, p.PB7)).unwrap();
}

155
src/usb.rs Normal file
View file

@ -0,0 +1,155 @@
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::peripherals;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb::Driver;
use embassy_stm32::{interrupt, Config};
use embassy_time::{Duration, Timer};
use embassy_usb::Builder;
use embassy_usb_serial::{CdcAcmClass, State};
use futures::future::join;
use arrayvec::{ArrayString, ArrayVec};
#[embassy_executor::task]
pub async fn usb_task(usb: peripherals::USB, dp_pin: peripherals::PA12, dm_pin: peripherals::PA11) {
let irq = interrupt::take!(USB_LP_CAN1_RX0);
let driver = Driver::new(usb, irq, dp_pin, dm_pin);
// Create embassy-usb Config
let config = embassy_usb::Config::new(0xc0de, 0xcafe);
//config.max_packet_size_0 = 64;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut control_buf = [0; 7];
let mut state = State::new();
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut control_buf,
None,
);
// Create classes on the builder.
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
// Build the builder.
let mut usb = builder.build();
// Do stuff with the class!
let usb_handler_fut = async {
loop {
class.wait_connection().await;
defmt::info!("USB connected");
let mut packet = [0; 64];
let mut buffer = ArrayVec::<u8, 64>::new();
loop {
let n = match class.read_packet(&mut packet).await {
Ok(n) => n,
Err(err) => {
defmt::error!("Unable to read packet: {}", err);
break;
}
};
if buffer.try_extend_from_slice(&packet[..n]).is_err() {
buffer.clear();
buffer.try_extend_from_slice(&packet[..n]).unwrap();
}
let mut line_end = 0;
for i in 0..buffer.len() {
if buffer[i] == '\r' as u8 {
line_end = i;
break;
}
}
defmt::info!("Line end: {}", line_end);
defmt::info!("buffer: {}", buffer.as_slice());
if line_end > 0 {
let cmd = parse_command(&buffer.as_slice()[..line_end]);
for _ in 0..line_end + 1 {
buffer.remove(0);
}
defmt::info!("truncated buffer");
if cmd != Gs232Cmd::Unkown {
match class.write_packet(b"\r").await {
Ok(_) => {}
Err(err) => {
defmt::error!("Unable to write packet: {}", err);
break;
}
};
}
}
}
defmt::info!("USB disconnected");
}
};
join(usb.run(), usb_handler_fut).await;
}
#[derive(PartialEq)]
enum Gs232Cmd {
Unkown,
GetAl,
GetEz,
GetAlEz,
MoveTo(u16, u16),
Stop,
}
fn parse_command(data: &[u8]) -> Gs232Cmd {
match data[0] as char {
'B' => {
if data.len() == 1 {
Gs232Cmd::GetAl
} else {
Gs232Cmd::Unkown
}
}
'C' => {
if data.len() == 2 && data[1] as char == '2' {
Gs232Cmd::GetAlEz
} else if data.len() == 1 {
Gs232Cmd::GetEz
} else {
Gs232Cmd::Unkown
}
}
'W' => {
if data.len() == 8 {
Gs232Cmd::Unkown
} else {
Gs232Cmd::Unkown
}
}
'S' => {
if data.len() == 1 {
Gs232Cmd::Stop
} else {
Gs232Cmd::Unkown
}
}
_ => Gs232Cmd::Unkown,
}
}