Factor out parsing to separate file

This commit is contained in:
Frederik Menke 2023-08-13 15:33:33 +02:00
parent f5d4d25d37
commit 742dfddce1
2 changed files with 121 additions and 78 deletions

View file

@ -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_std]
#![no_main] #![no_main]
mod red_commands;
use bsp::{entry, hal::pwm::FreeRunning}; use bsp::{entry, hal::pwm::FreeRunning};
use defmt::*; use defmt::*;
use defmt_rtt as _; use defmt_rtt as _;
@ -28,19 +27,6 @@ use bsp::hal::{
/// that range, it might be an EEPROM!!! /// that range, it might be an EEPROM!!!
const I2C_ADDR: u8 = 25; 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] #[entry]
fn main() -> ! { fn main() -> ! {
info!("Program start"); info!("Program start");
@ -72,91 +58,37 @@ fn main() -> ! {
&mut pac.RESETS, &mut pac.RESETS,
); );
let mut i2c = hal::I2C::new_peripheral_event_iterator( let i2c = hal::I2C::new_peripheral_event_iterator(
pac.I2C1, pac.I2C1,
pins.gpio18.into_mode(), pins.gpio18.into_mode(),
pins.gpio19.into_mode(), pins.gpio19.into_mode(),
&mut pac.RESETS, &mut pac.RESETS,
I2C_ADDR.into(), I2C_ADDR.into(),
); );
let mut red_commands = red_commands::RedCommandIterator::new(i2c);
let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS); let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
let mut pwm = pwm_slices.pwm1; let mut pwm = pwm_slices.pwm1;
info!("Top: {}", pwm.get_top()); info!("Top: {}", pwm.get_top());
pwm.set_ph_correct(); pwm.set_ph_correct();
pwm.enable(); pwm.enable();
let pwm = pwm.into_mode::<FreeRunning>(); let pwm = pwm.into_mode::<FreeRunning>();
let mut channel_b = pwm.channel_b; let mut channel_b = pwm.channel_b;
use embedded_hal::PwmPin; use embedded_hal::PwmPin;
let _channel_pin_b_3 = channel_b.output_to(pins.gpio3); let _channel_pin_b_3 = channel_b.output_to(pins.gpio3);
channel_b.set_duty(u16::MAX); channel_b.set_duty(u16::MAX);
channel_b.enable(); channel_b.enable();
info!("PWM enabled"); info!("PWM enabled");
loop { loop {
use hal::i2c::peripheral::I2CEvent; let command = red_commands.next();
let potentially_read_bytes = match i2c.next() { command.map(|message| {
None => None, if let red_commands::RedCommand::SetSpindleSpeed(duty) = message {
Some(I2CEvent::Stop) => { channel_b.set_duty(duty);
info!("Stop"); info!("Duty: {}", channel_b.get_duty());
None
} }
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); delay.delay_ms(10);
} }
} }
fn parse_i2c_message(bytes: &[u8]) -> Result<I2CMessage, ()> {
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(())
}
}

111
rp2040/src/red_commands.rs Normal file
View file

@ -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<Block, Pins> {
i2c_iter: I2CPeripheralEventIterator<Block, Pins>,
}
impl<Block, Pins> RedCommandIterator<Block, Pins> {
pub fn new(
i2c_iter: I2CPeripheralEventIterator<Block, Pins>,
) -> RedCommandIterator<Block, Pins> {
RedCommandIterator { i2c_iter }
}
}
impl<Block, Pins> Iterator for RedCommandIterator<Block, Pins>
where
Block: Deref<Target = I2CBlock>,
{
type Item = RedCommand;
fn next(&mut self) -> Option<Self::Item> {
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<RedCommand, ()> {
if bytes.len() == 0 {
error!("Can't parse empty buffer!");
return Err(());
}
let (command, args) = bytes.split_at(core::mem::size_of::<u8>());
let command = command[0];
match command {
i2c_registers::SPINDLE_SPEED if args.len() >= core::mem::size_of::<u16>() => {
let (int_bytes, _rest) = args.split_at(core::mem::size_of::<u16>());
let speed = u16::from_le_bytes(int_bytes.try_into().unwrap());
Ok(RedCommand::SetSpindleSpeed(speed))
}
_ => {
error!("Unknown i2c register address: {}", command);
Err(())
}
}
}