Factor out parsing to separate file
This commit is contained in:
parent
f5d4d25d37
commit
742dfddce1
|
@ -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::<FreeRunning>();
|
||||
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<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
111
rp2040/src/red_commands.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue