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_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
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