Compare commits

..

4 Commits

Author SHA1 Message Date
Astro ee4d24de6a delint 2019-09-14 21:40:49 +02:00
Astro b969f5c057 command_parser: complete to all PidParameter tokens 2019-09-14 21:35:06 +02:00
Astro 700ab47f0e improve output formatting 2019-09-14 21:23:45 +02:00
Astro b6af43feda control the pid 2019-09-14 21:11:26 +02:00
6 changed files with 179 additions and 13 deletions

View File

@ -2,7 +2,7 @@ use embedded_hal::digital::v2::OutputPin;
use embedded_hal::blocking::spi::Transfer; use embedded_hal::blocking::spi::Transfer;
use super::checksum::{ChecksumMode, Checksum}; use super::checksum::{ChecksumMode, Checksum};
use super::AdcError; use super::AdcError;
use super::{regs, regs::RegisterData, Input, RefSource}; use super::{regs, regs::RegisterData, Input};
/// AD7172-2 implementation /// AD7172-2 implementation
/// ///

View File

@ -63,16 +63,19 @@ macro_rules! def_reg {
macro_rules! reg_bit { macro_rules! reg_bit {
($getter: ident, $byte: expr, $bit: expr, $doc: expr) => { ($getter: ident, $byte: expr, $bit: expr, $doc: expr) => {
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> bool { pub fn $getter(&self) -> bool {
self.0[$byte].get_bit($bit) self.0[$byte].get_bit($bit)
} }
}; };
($getter: ident, $setter: ident, $byte: expr, $bit: expr, $doc: expr) => { ($getter: ident, $setter: ident, $byte: expr, $bit: expr, $doc: expr) => {
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> bool { pub fn $getter(&self) -> bool {
self.0[$byte].get_bit($bit) self.0[$byte].get_bit($bit)
} }
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $setter(&mut self, value: bool) { pub fn $setter(&mut self, value: bool) {
self.0[$byte].set_bit($bit, value); self.0[$byte].set_bit($bit, value);
@ -82,32 +85,38 @@ macro_rules! reg_bit {
macro_rules! reg_bits { macro_rules! reg_bits {
($getter: ident, $byte: expr, $bits: expr, $doc: expr) => { ($getter: ident, $byte: expr, $bits: expr, $doc: expr) => {
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> u8 { pub fn $getter(&self) -> u8 {
self.0[$byte].get_bits($bits) self.0[$byte].get_bits($bits)
} }
}; };
($getter: ident, $setter: ident, $byte: expr, $bits: expr, $doc: expr) => { ($getter: ident, $setter: ident, $byte: expr, $bits: expr, $doc: expr) => {
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> u8 { pub fn $getter(&self) -> u8 {
self.0[$byte].get_bits($bits) self.0[$byte].get_bits($bits)
} }
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $setter(&mut self, value: u8) { pub fn $setter(&mut self, value: u8) {
self.0[$byte].set_bits($bits, value); self.0[$byte].set_bits($bits, value);
} }
}; };
($getter: ident, $byte: expr, $bits: expr, $ty: ty, $doc: expr) => { ($getter: ident, $byte: expr, $bits: expr, $ty: ty, $doc: expr) => {
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> $ty { pub fn $getter(&self) -> $ty {
self.0[$byte].get_bits($bits) as $ty self.0[$byte].get_bits($bits) as $ty
} }
}; };
($getter: ident, $setter: ident, $byte: expr, $bits: expr, $ty: ty, $doc: expr) => { ($getter: ident, $setter: ident, $byte: expr, $bits: expr, $ty: ty, $doc: expr) => {
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $getter(&self) -> $ty { pub fn $getter(&self) -> $ty {
self.0[$byte].get_bits($bits).into() self.0[$byte].get_bits($bits).into()
} }
#[allow(unused)]
#[doc = $doc] #[doc = $doc]
pub fn $setter(&mut self, value: $ty) { pub fn $setter(&mut self, value: $ty) {
self.0[$byte].set_bits($bits, value as u8); self.0[$byte].set_bits($bits, value as u8);
@ -156,11 +165,13 @@ impl channel::Data {
reg_bits!(setup, set_setup, 0, 4..=5, "Setup number"); reg_bits!(setup, set_setup, 0, 4..=5, "Setup number");
/// Which input is connected to positive input of this channel /// Which input is connected to positive input of this channel
#[allow(unused)]
pub fn a_in_pos(&self) -> Input { pub fn a_in_pos(&self) -> Input {
((self.0[0].get_bits(0..=1) << 3) | ((self.0[0].get_bits(0..=1) << 3) |
self.0[1].get_bits(5..=7)).into() self.0[1].get_bits(5..=7)).into()
} }
/// Set which input is connected to positive input of this channel /// Set which input is connected to positive input of this channel
#[allow(unused)]
pub fn set_a_in_pos(&mut self, value: Input) { pub fn set_a_in_pos(&mut self, value: Input) {
let value = value as u8; let value = value as u8;
self.0[0].set_bits(0..=1, value >> 3); self.0[0].set_bits(0..=1, value >> 3);
@ -207,11 +218,13 @@ impl filt_con::Data {
def_reg!(Offset, u8, offset, 0x30, 3); def_reg!(Offset, u8, offset, 0x30, 3);
impl offset::Data { impl offset::Data {
#[allow(unused)]
pub fn offset(&self) -> u32 { pub fn offset(&self) -> u32 {
(u32::from(self.0[0]) << 16) | (u32::from(self.0[0]) << 16) |
(u32::from(self.0[1]) << 8) | (u32::from(self.0[1]) << 8) |
u32::from(self.0[2]) u32::from(self.0[2])
} }
#[allow(unused)]
pub fn set_offset(&mut self, value: u32) { pub fn set_offset(&mut self, value: u32) {
self.0[0] = (value >> 16) as u8; self.0[0] = (value >> 16) as u8;
self.0[1] = (value >> 8) as u8; self.0[1] = (value >> 8) as u8;
@ -221,11 +234,13 @@ impl offset::Data {
def_reg!(Gain, u8, gain, 0x38, 3); def_reg!(Gain, u8, gain, 0x38, 3);
impl gain::Data { impl gain::Data {
#[allow(unused)]
pub fn gain(&self) -> u32 { pub fn gain(&self) -> u32 {
(u32::from(self.0[0]) << 16) | (u32::from(self.0[0]) << 16) |
(u32::from(self.0[1]) << 8) | (u32::from(self.0[1]) << 8) |
u32::from(self.0[2]) u32::from(self.0[2])
} }
#[allow(unused)]
pub fn set_gain(&mut self, value: u32) { pub fn set_gain(&mut self, value: u32) {
self.0[0] = (value >> 16) as u8; self.0[0] = (value >> 16) as u8;
self.0[1] = (value >> 8) as u8; self.0[1] = (value >> 8) as u8;

View File

@ -1,10 +1,11 @@
use core::fmt;
use nom::{ use nom::{
IResult, IResult,
branch::alt, branch::alt,
bytes::complete::{tag, take_while1}, bytes::complete::{tag, take_while1},
character::{is_digit, complete::char}, character::{is_digit, complete::char},
combinator::{map, value}, combinator::{map, value},
sequence::{preceded, tuple, Tuple}, sequence::preceded,
multi::fold_many1, multi::fold_many1,
error::ErrorKind, error::ErrorKind,
}; };
@ -12,7 +13,7 @@ use btoi::{btoi, ParseIntegerError};
use super::session::ReportMode; use super::session::ReportMode;
#[derive(Debug)] #[derive(Clone, Debug)]
pub enum Error { pub enum Error {
Parser(ErrorKind), Parser(ErrorKind),
Incomplete, Incomplete,
@ -39,9 +40,43 @@ impl From<ParseIntegerError> for Error {
} }
} }
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Error::Incomplete =>
"incomplete input".fmt(fmt),
Error::UnexpectedInput(c) => {
"unexpected input: ".fmt(fmt)?;
c.fmt(fmt)
}
Error::Parser(e) => {
"parser: ".fmt(fmt)?;
(e as &dyn core::fmt::Debug).fmt(fmt)
}
Error::ParseInteger(e) => {
"parsing number: ".fmt(fmt)?;
e.fmt(fmt)
}
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ShowCommand { pub enum ShowCommand {
ReportMode, ReportMode,
Pid,
}
#[derive(Debug, Clone)]
pub enum PidParameter {
Target,
KP,
KI,
KD,
OutputMin,
OutputMax,
IntegralMin,
IntegralMax,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -53,6 +88,10 @@ pub enum Command {
width: u32, width: u32,
total: u32, total: u32,
}, },
Pid {
parameter: PidParameter,
value: f32,
},
} }
fn whitespace(input: &[u8]) -> IResult<&[u8], ()> { fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
@ -113,17 +152,54 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
Ok((input, Ok(Command::Pwm { width, total }))) Ok((input, Ok(Command::Pwm { width, total })))
} }
fn command(input: &[u8]) -> IResult<&[u8], Command> { fn pid_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
alt((value(Command::Quit, tag("quit")), let (input, parameter) =
report alt((value(PidParameter::Target, tag("target")),
value(PidParameter::KP, tag("kp")),
value(PidParameter::KI, tag("ki")),
value(PidParameter::KD, tag("kd")),
value(PidParameter::OutputMin, tag("output_min")),
value(PidParameter::OutputMax, tag("output_max")),
value(PidParameter::IntegralMin, tag("integral_min")),
value(PidParameter::IntegralMax, tag("integral_max"))
))(input)?;
let (input, _) = whitespace(input)?;
// TODO: parse float
let (input, value) = unsigned(input)?;
let result = value
.map(|value| Command::Pid { parameter, value: value as f32 })
.map_err(|e| e.into());
Ok((input, result))
}
fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("pid")(input)?;
let (input, _) = whitespace(input)?;
alt((
preceded(
whitespace,
pid_parameter
),
|input| Ok((input, Ok(Command::Show(ShowCommand::Pid))))
))(input)
}
fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
alt((value(Ok(Command::Quit), tag("quit")),
|input| report(input).map(|(input, command)| {
(input, Ok(command))
}),
pwm,
pid,
))(input) ))(input)
} }
impl Command { impl Command {
pub fn parse(input: &[u8]) -> Result<Self, Error> { pub fn parse(input: &[u8]) -> Result<Self, Error> {
match command(input) { match command(input) {
Ok((b"", command)) => Ok((b"", result)) =>
Ok(command), result,
Ok((input_remain, _)) => Ok((input_remain, _)) =>
Err(Error::UnexpectedInput(input_remain[0])), Err(Error::UnexpectedInput(input_remain[0])),
Err(e) => Err(e) =>

View File

@ -1,8 +1,8 @@
#![feature(const_fn)] #![feature(const_fn, proc_macro_hygiene)]
#![no_std] #![no_std]
#![no_main] #![no_main]
use cortex_m_rt::{entry, heap_start}; use cortex_m_rt::entry;
use core::fmt::{self, Write}; use core::fmt::{self, Write};
use smoltcp::time::Instant; use smoltcp::time::Instant;
use smoltcp::wire::{IpCidr, IpAddress, EthernetAddress}; use smoltcp::wire::{IpCidr, IpAddress, EthernetAddress};
@ -41,6 +41,7 @@ use command_parser::{Command, ShowCommand};
mod session; mod session;
use self::session::{Session, SessionOutput}; use self::session::{Session, SessionOutput};
mod ad7172; mod ad7172;
mod pid;
pub struct UART0; pub struct UART0;
@ -77,6 +78,15 @@ macro_rules! create_socket {
/// In nanoseconds /// In nanoseconds
const REPORT_INTERVAL: u64 = 100_000; const REPORT_INTERVAL: u64 = 100_000;
const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters {
kp: 1.0,
ki: 1.0,
kd: 1.0,
output_min: 0.0,
output_max: 0xffff as f32,
integral_min: 0.0,
integral_max: 0xffff as f32,
};
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
@ -143,6 +153,8 @@ fn main() -> ! {
// SENS1_{P,N} // SENS1_{P,N}
adc.setup_channel(1, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap(); adc.setup_channel(1, ad7172::Input::Ain2, ad7172::Input::Ain3).unwrap();
let mut pid = pid::Controller::new(DEFAULT_PID_PARAMETERS.clone());
let mut hardware_addr = EthernetAddress(board::get_mac_address()); let mut hardware_addr = EthernetAddress(board::get_mac_address());
if hardware_addr.is_multicast() { if hardware_addr.is_multicast() {
println!("programmed MAC address is invalid, using default"); println!("programmed MAC address is invalid, using default");
@ -200,7 +212,7 @@ fn main() -> ! {
// ADC input // ADC input
adc.data_ready() adc.data_ready()
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
writeln!(stdout, "ADC error: {:?}", e); writeln!(stdout, "ADC error: {:?}", e).unwrap();
None None
}).map(|channel| { }).map(|channel| {
let data = adc.read_data().unwrap(); let data = adc.read_data().unwrap();
@ -241,21 +253,61 @@ fn main() -> ! {
if socket.may_recv() && socket.may_send() { if socket.may_recv() && socket.may_send() {
let output = socket.recv(|buf| session.feed(buf)); let output = socket.recv(|buf| session.feed(buf));
// TODO: use "{}" to display pretty errors
match output { match output {
Ok(SessionOutput::Nothing) => {} Ok(SessionOutput::Nothing) => {}
Ok(SessionOutput::Command(command)) => match command { Ok(SessionOutput::Command(command)) => match command {
Command::Quit => Command::Quit =>
socket.close(), socket.close(),
Command::Report(mode) => { Command::Report(mode) => {
let _ = writeln!(socket, "Report mode: {:?}", mode); let _ = writeln!(socket, "Report mode: {}", mode);
} }
Command::Show(ShowCommand::ReportMode) => { Command::Show(ShowCommand::ReportMode) => {
let _ = writeln!(socket, "Report mode: {:?}", session.report_mode()); let _ = writeln!(socket, "Report mode: {}", session.report_mode());
}
Command::Show(ShowCommand::Pid) => {
let _ = writeln!(socket, "PID settings");
let _ = writeln!(socket, "target: {:.4}", pid.get_target());
let p = pid.get_parameters();
macro_rules! out {
($p: tt) => {
let _ = writeln!(socket, "{}: {:.4}", stringify!($p), p.$p);
};
}
out!(kp);
out!(ki);
out!(kd);
out!(output_min);
out!(output_max);
out!(integral_min);
out!(integral_max);
} }
Command::Pwm { width, total } => { Command::Pwm { width, total } => {
board::set_timer_pwm(width, total); board::set_timer_pwm(width, total);
let _ = writeln!(socket, "PWM duty cycle: {}/{}", width, total); let _ = writeln!(socket, "PWM duty cycle: {}/{}", width, total);
} }
Command::Pid { parameter, value } => {
use command_parser::PidParameter::*;
match parameter {
Target =>
pid.set_target(value),
KP =>
pid.update_parameters(|parameters| parameters.kp = value),
KI =>
pid.update_parameters(|parameters| parameters.ki = value),
KD =>
pid.update_parameters(|parameters| parameters.kd = value),
OutputMin =>
pid.update_parameters(|parameters| parameters.output_min = value),
OutputMax =>
pid.update_parameters(|parameters| parameters.output_max = value),
IntegralMin =>
pid.update_parameters(|parameters| parameters.integral_min = value),
IntegralMax =>
pid.update_parameters(|parameters| parameters.integral_max = value),
}
let _ = writeln!(socket, "PID parameter updated");
}
} }
Ok(SessionOutput::Error(e)) => { Ok(SessionOutput::Error(e)) => {
let _ = writeln!(socket, "Command error: {:?}", e); let _ = writeln!(socket, "Command error: {:?}", e);

View File

@ -56,10 +56,22 @@ impl Controller {
output output
} }
pub fn get_target(&mut self) -> f32 {
self.target
}
pub fn set_target(&mut self, target: f32) { pub fn set_target(&mut self, target: f32) {
self.target = target; self.target = target;
} }
pub fn get_parameters(&self) -> &Parameters {
&self.parameters
}
pub fn update_parameters<F: FnOnce(&mut Parameters)>(&mut self, f: F) {
f(&mut self.parameters);
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.integral = 0.0; self.integral = 0.0;

View File

@ -1,4 +1,5 @@
use core::ops::Deref; use core::ops::Deref;
use core::fmt;
use super::command_parser::{Command, Error as ParserError}; use super::command_parser::{Command, Error as ParserError};
const MAX_LINE_LEN: usize = 64; const MAX_LINE_LEN: usize = 64;
@ -60,6 +61,16 @@ pub enum ReportMode {
Continuous, Continuous,
} }
impl fmt::Display for ReportMode {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
ReportMode::Off => "off",
ReportMode::Once => "once",
ReportMode::Continuous => "continuous",
}.fmt(fmt)
}
}
pub enum SessionOutput { pub enum SessionOutput {
Nothing, Nothing,
Command(Command), Command(Command),