From 742dfddce17e1e1ed2260c442f25995c2a566753 Mon Sep 17 00:00:00 2001 From: Frederik Menke Date: Sun, 13 Aug 2023 15:33:33 +0200 Subject: [PATCH] Factor out parsing to separate file --- rp2040/src/main.rs | 88 ++++------------------------- rp2040/src/red_commands.rs | 111 +++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 78 deletions(-) create mode 100644 rp2040/src/red_commands.rs diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index b2f2930..60305fe 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -1,9 +1,8 @@ -//! Blinks the LED on a Pico board -//! -//! This will blink an LED attached to GP25, which is the pin the Pico uses for the on-board LED. #![no_std] #![no_main] +mod red_commands; + use bsp::{entry, hal::pwm::FreeRunning}; use defmt::*; use defmt_rtt as _; @@ -28,19 +27,6 @@ use bsp::hal::{ /// that range, it might be an EEPROM!!! const I2C_ADDR: u8 = 25; -/// Size of the i2c receive FIFO in bytes -const I2C_RECV_FIFO_SIZE: usize = 16; - -// Which registers can be written over i2c -mod i2c_registers { - // Spindle speed from 0 to 65535 as 2 bytes - pub const SPINDLE_SPEED: u8 = 0; -} - -enum I2CMessage { - SpindleSpeed(u16), -} - #[entry] fn main() -> ! { info!("Program start"); @@ -72,91 +58,37 @@ fn main() -> ! { &mut pac.RESETS, ); - let mut i2c = hal::I2C::new_peripheral_event_iterator( + let i2c = hal::I2C::new_peripheral_event_iterator( pac.I2C1, pins.gpio18.into_mode(), pins.gpio19.into_mode(), &mut pac.RESETS, I2C_ADDR.into(), ); + let mut red_commands = red_commands::RedCommandIterator::new(i2c); let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS); - let mut pwm = pwm_slices.pwm1; info!("Top: {}", pwm.get_top()); - pwm.set_ph_correct(); pwm.enable(); let pwm = pwm.into_mode::(); let mut channel_b = pwm.channel_b; use embedded_hal::PwmPin; let _channel_pin_b_3 = channel_b.output_to(pins.gpio3); - channel_b.set_duty(u16::MAX); channel_b.enable(); - info!("PWM enabled"); loop { - use hal::i2c::peripheral::I2CEvent; - let potentially_read_bytes = match i2c.next() { - None => None, - Some(I2CEvent::Stop) => { - info!("Stop"); - None + let command = red_commands.next(); + command.map(|message| { + if let red_commands::RedCommand::SetSpindleSpeed(duty) = message { + channel_b.set_duty(duty); + info!("Duty: {}", channel_b.get_duty()); } - Some(I2CEvent::Start) => { - info!("Start"); - None - } - Some(I2CEvent::TransferWrite) => { - info!("Start"); - let mut buf = [0; I2C_RECV_FIFO_SIZE]; - let read_count = i2c.read(&mut buf); - if read_count >= buf.len() { - error!( - "Hit i2c receive FIFO limit: read {} bytes of data from I2C, ignoring transfer.", - I2C_RECV_FIFO_SIZE - ); - // empty out i2c recv FIFO - while let 1.. = i2c.read(&mut buf) {} - None - } else { - info!("read {} bytes", read_count); - info!("content: {:?}", buf); - Some(buf) - } - } - Some(_) => { - info!("something"); - None - } - }; - - potentially_read_bytes - .and_then(|bytes| parse_i2c_message(&bytes).ok()) - .map(|message| { - if let I2CMessage::SpindleSpeed(duty) = message { - channel_b.set_duty(duty); - info!("Duty: {}", channel_b.get_duty()); - } - }); + }); delay.delay_ms(10); } } - -fn parse_i2c_message(bytes: &[u8]) -> Result { - if bytes.len() == 0 { - error!("Can't parse empty buffer!"); - return Err(()); - } - if bytes.len() >= 3 && bytes[0] == i2c_registers::SPINDLE_SPEED { - let speed = u16::from_le_bytes(bytes[1..3]); - let speed = (speed_higher << 8) + speed_lower; - Ok(I2CMessage::SpindleSpeed(speed)) - } else { - error!("could not parse i2c message: {}", bytes); - Err(()) - } -} diff --git a/rp2040/src/red_commands.rs b/rp2040/src/red_commands.rs new file mode 100644 index 0000000..5a3eaea --- /dev/null +++ b/rp2040/src/red_commands.rs @@ -0,0 +1,111 @@ +use core::ops::Deref; + +use defmt::*; +use defmt_rtt as _; +use pac::i2c0::RegisterBlock as I2CBlock; +use panic_probe as _; +use rp_pico::hal; +use rp_pico::hal::i2c::peripheral::I2CPeripheralEventIterator; +// Provide an alias for our BSP so we can switch targets quickly. +// Uncomment the BSP you included in Cargo.toml, the rest of the code does not need to change. +use rp_pico as bsp; + +use bsp::hal::pac; + +/// Size of the i2c receive FIFO in bytes +const I2C_RECV_FIFO_SIZE: usize = 16; + +// Which registers can be written over i2c +mod i2c_registers { + // Spindle speed from 0 to 65535 as 2 bytes + pub const SPINDLE_SPEED: u8 = 0; +} + +/// Commands that might be sent by the red Olinuxino board +pub enum RedCommand { + SetSpindleSpeed(u16), +} + +/// Given an i2c connection, this iterator will read new commands from +/// the i2c, parse them and return a proper `RedCommand`. If no command +/// was received from the bus, this will return `None`, but the iterator +/// will not stop working. +pub struct RedCommandIterator { + i2c_iter: I2CPeripheralEventIterator, +} + +impl RedCommandIterator { + pub fn new( + i2c_iter: I2CPeripheralEventIterator, + ) -> RedCommandIterator { + RedCommandIterator { i2c_iter } + } +} + +impl Iterator for RedCommandIterator +where + Block: Deref, +{ + type Item = RedCommand; + + fn next(&mut self) -> Option { + use hal::i2c::peripheral::I2CEvent; + let potentially_read_bytes = match self.i2c_iter.next() { + None => None, + Some(I2CEvent::Stop) => { + info!("Stop"); + None + } + Some(I2CEvent::Start) => { + info!("Start"); + None + } + Some(I2CEvent::TransferWrite) => { + info!("Start"); + let mut buf = [0; I2C_RECV_FIFO_SIZE]; + let read_count = self.i2c_iter.read(&mut buf); + if read_count >= buf.len() { + error!( + "Hit i2c receive FIFO limit: read {} bytes of data from I2C, ignoring transfer.", + I2C_RECV_FIFO_SIZE + ); + // empty out i2c recv FIFO + while let 1.. = self.i2c_iter.read(&mut buf) {} + None + } else { + info!("read {} bytes", read_count); + info!("content: {:?}", buf); + Some(buf) + } + } + Some(_) => { + info!("something"); + None + } + }; + + potentially_read_bytes.and_then(|bytes| parse_i2c_message(&bytes).ok()) + } +} + +fn parse_i2c_message(bytes: &[u8]) -> Result { + if bytes.len() == 0 { + error!("Can't parse empty buffer!"); + return Err(()); + } + + let (command, args) = bytes.split_at(core::mem::size_of::()); + let command = command[0]; + + match command { + i2c_registers::SPINDLE_SPEED if args.len() >= core::mem::size_of::() => { + let (int_bytes, _rest) = args.split_at(core::mem::size_of::()); + let speed = u16::from_le_bytes(int_bytes.try_into().unwrap()); + Ok(RedCommand::SetSpindleSpeed(speed)) + } + _ => { + error!("Unknown i2c register address: {}", command); + Err(()) + } + } +}