Compare commits

..

3 Commits

6 changed files with 328 additions and 19 deletions

35
firmware/Cargo.lock generated
View File

@ -137,6 +137,7 @@ dependencies = [
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"logos 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=cd893e6)",
"tm4c129x 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -157,6 +158,26 @@ name = "libm"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "logos"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"logos-derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "logos-derive"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "managed"
version = "0.5.1"
@ -209,6 +230,11 @@ name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex-syntax"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.2.3"
@ -285,6 +311,11 @@ name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vcell"
version = "0.1.2"
@ -342,6 +373,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
"checksum logos 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f494e22d293fa05db60b3fd95fb30e9409feb5672b56ce6f250f99d9fbae6b93"
"checksum logos-derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "13ff1b1068db09ee21d12baf55eccc0900a781a735273e0a606f6f4fbb32a322"
"checksum managed 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43e2737ecabe4ae36a68061398bf27d2bfd0763f4c3c837a398478459494c4b7"
"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
@ -350,6 +383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
@ -360,6 +394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum tm4c129x 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d430ed4ed06dd9fff3d4517a37343e1b53789218f2f608bf1e0432f67abf624"
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"

View File

@ -19,6 +19,7 @@ cortex-m-semihosting = "0.3"
byteorder = { version = "1.3", default-features = false }
bit_field = "0.10"
bare-metal = "0.2"
logos = { version = "~0.9", default-features = false, features = ["export_derive"] }
[dependencies.smoltcp]
git = "https://github.com/m-labs/smoltcp"

View File

@ -14,19 +14,25 @@ pub trait RegisterData {
macro_rules! def_reg {
($Reg: ident, $reg: ident, $addr: expr, $size: expr) => {
/// AD7172 register
pub struct $Reg;
impl Register for $Reg {
/// Register contents
type Data = $reg::Data;
/// Register address
fn address(&self) -> u8 {
$addr
}
}
mod $reg {
/// Register contents
pub struct Data(pub [u8; $size]);
impl super::RegisterData for Data {
/// Generate zeroed register contents
fn empty() -> Self {
Data([0; $size])
}
/// Borrow for SPI transfer
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
@ -190,7 +196,7 @@ impl setup_con::Data {
reg_bits!(ref_sel, set_ref_sel, 1, 4..=5, RefSource, "Select reference source for conversion");
}
def_reg!(FiltCon, u8, filt_con, 0x80, 2);
def_reg!(FiltCon, u8, filt_con, 0x28, 2);
impl filt_con::Data {
reg_bit!(sinc3_map, 0, 7, "If set, mapping of filter register changes to directly program the decimation rate of the sinc3 filter");
reg_bit!(enh_filt_en, set_enh_filt_en, 0, 3, "Enable postfilters for enhanced 50Hz and 60Hz rejection");
@ -198,3 +204,31 @@ impl filt_con::Data {
reg_bits!(order, set_order, 1, 5..=6, DigitalFilterOrder, "order of the digital filter that processes the modulator data");
reg_bits!(odr, set_odr, 1, 0..=4, "Output data rate");
}
def_reg!(Offset, u8, offset, 0x30, 3);
impl offset::Data {
pub fn offset(&self) -> u32 {
(u32::from(self.0[0]) << 16) |
(u32::from(self.0[1]) << 8) |
u32::from(self.0[2])
}
pub fn set_offset(&mut self, value: u32) {
self.0[0] = (value >> 16) as u8;
self.0[1] = (value >> 8) as u8;
self.0[2] = value as u8;
}
}
def_reg!(Gain, u8, gain, 0x38, 3);
impl gain::Data {
pub fn gain(&self) -> u32 {
(u32::from(self.0[0]) << 16) |
(u32::from(self.0[1]) << 8) |
u32::from(self.0[2])
}
pub fn set_gain(&mut self, value: u32) {
self.0[0] = (value >> 16) as u8;
self.0[1] = (value >> 8) as u8;
self.0[2] = value as u8;
}
}

View File

@ -0,0 +1,85 @@
use logos::Logos;
use super::session::ReportMode;
#[derive(Logos, Debug, PartialEq)]
enum Token {
#[end]
End,
#[error]
Error,
#[token = "Quit"]
Quit,
#[token = "show"]
Show,
#[token = "channel"]
Channel,
#[token = "report"]
Report,
#[token = "mode"]
Mode,
#[token = "off"]
Off,
#[token = "once"]
Once,
#[token = "continuous"]
Continuous,
#[regex = "[0-9]+"]
Number,
}
#[derive(Debug)]
pub enum Error {
Parser,
UnexpectedEnd,
UnexpectedToken(Token),
}
#[derive(Debug)]
pub enum ShowCommand {
ReportMode,
}
#[derive(Debug)]
pub enum Command {
Quit,
Show(ShowCommand),
Report(ReportMode),
}
impl Command {
pub fn parse(input: &str) -> Result<Self, Error> {
let mut lexer = Token::lexer(input);
macro_rules! choice {
[$($token: tt => $block: stmt,)*] => {
match lexer.token {
$(
Token::$token => {
lexer.advance();
$block
}
)*
Token::End => Err(Error::UnexpectedEnd),
_ => Err(Error::UnexpectedToken(lexer.token))
}
}
}
choice![
Quit => Ok(Command::Quit),
Report => choice![
Mode => choice![
End => Ok(Command::Show(ShowCommand::ReportMode)),
Off => Ok(Command::Report(ReportMode::Off)),
Once => Ok(Command::Report(ReportMode::Once)),
Continuous => Ok(Command::Report(ReportMode::Continuous)),
],
End => Ok(Command::Report(ReportMode::Once)),
],
]
}
}

View File

@ -34,6 +34,10 @@ pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
mod board;
use self::board::{gpio::Gpio, systick::get_time};
mod ethmac;
mod command_parser;
use command_parser::{Command, ShowCommand};
mod session;
use self::session::{Session, SessionOutput};
mod ad7172;
pub struct UART0;
@ -129,6 +133,7 @@ fn main() -> ! {
writeln!(stdout, "Corrupt ADC id: {:04X}", id).unwrap(),
};
}
let mut hardware_addr = EthernetAddress(board::get_mac_address());
if hardware_addr.is_multicast() {
println!("programmed MAC address is invalid, using default");
@ -166,21 +171,20 @@ fn main() -> ! {
create_socket!(sockets, tcp_rx_storage5, tcp_tx_storage5, tcp_handle5);
create_socket!(sockets, tcp_rx_storage6, tcp_tx_storage6, tcp_handle6);
create_socket!(sockets, tcp_rx_storage7, tcp_tx_storage7, tcp_handle7);
let handles = [
tcp_handle0,
tcp_handle1,
tcp_handle2,
tcp_handle3,
tcp_handle4,
tcp_handle5,
tcp_handle6,
tcp_handle7,
let mut sessions_handles = [
(Session::new(), tcp_handle0),
(Session::new(), tcp_handle1),
(Session::new(), tcp_handle2),
(Session::new(), tcp_handle3),
(Session::new(), tcp_handle4),
(Session::new(), tcp_handle5),
(Session::new(), tcp_handle6),
(Session::new(), tcp_handle7),
];
let mut read_times = [0, 0];
let mut data = None;
// if a socket has sent the latest data
let mut socket_pending = [false; 8];
loop {
let _ = adc.data_ready()
.and_then(|channel|
@ -190,8 +194,8 @@ fn main() -> ! {
read_times[0] = read_times[1];
read_times[1] = now;
data = Some((now, Ok((channel, new_data))));
for p in socket_pending.iter_mut() {
*p = true;
for (session, _) in sessions_handles.iter_mut() {
session.set_report_pending();
}
})
).unwrap_or(Ok(()))
@ -199,17 +203,43 @@ fn main() -> ! {
.map_err(|e| {
let now = get_time();
data = Some((now, Err(e)));
for p in socket_pending.iter_mut() {
*p = true;
for (session, _) in sessions_handles.iter_mut() {
session.set_report_pending();
}
});
for (&tcp_handle, pending) in handles.iter().zip(socket_pending.iter_mut()) {
let socket = &mut *sockets.get::<TcpSocket>(tcp_handle);
for (session, tcp_handle) in sessions_handles.iter_mut() {
let socket = &mut *sockets.get::<TcpSocket>(*tcp_handle);
if !socket.is_open() {
if session.is_dirty() {
// Reset a previously uses session/socket
*session = Session::new();
}
socket.listen(23).unwrap()
}
if socket.may_send() && *pending {
if socket.may_recv() && socket.may_send() {
let command = socket.recv(|buf| session.feed(buf));
match command {
Ok(SessionOutput::Nothing) => {}
Ok(SessionOutput::Command(Command::Quit)) =>
socket.close(),
Ok(SessionOutput::Command(Command::Report(mode))) => {
let _ = writeln!(socket, "Report mode: {:?}", mode);
}
Ok(SessionOutput::Command(Command::Show(ShowCommand::ReportMode))) => {
let _ = writeln!(socket, "Report mode: {:?}", session.report_mode());
}
Ok(SessionOutput::Command(command)) => {
let _ = writeln!(socket, "Not implemented: {:?}", command);
}
Ok(SessionOutput::Error(e)) => {
let _ = writeln!(socket, "Command error: {:?}", e);
}
Err(_) => {}
}
}
if socket.may_send() && session.is_report_pending() {
match &data {
Some((time, Ok((channel, input)))) => {
let interval = read_times[1] - read_times[0];
@ -223,7 +253,7 @@ fn main() -> ! {
}
None => {}
}
*pending = false;
session.mark_report_sent();
}
}
match iface.poll(&mut sockets, Instant::from_millis((get_time() / 1000) as i64)) {

124
firmware/src/session.rs Normal file
View File

@ -0,0 +1,124 @@
use super::command_parser::{Command, Error as ParserError};
const MAX_LINE_LEN: usize = 64;
struct LineReader {
buf: [u8; MAX_LINE_LEN],
pos: usize,
}
impl LineReader {
pub fn new() -> Self {
LineReader {
buf: [0; MAX_LINE_LEN],
pos: 0,
}
}
pub fn feed(&mut self, c: u8) -> Option<&str> {
if c == 13 || c == 10 {
// Enter
if self.pos > 0 {
let len = self.pos;
self.pos = 0;
core::str::from_utf8(&self.buf[..len]).ok()
} else {
None
}
} else if self.pos < self.buf.len() {
// Add input
self.buf[self.pos] = c;
self.pos += 1;
None
} else {
// Buffer is full, ignore
None
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ReportMode {
Off,
Once,
Continuous,
}
pub enum SessionOutput {
Nothing,
Command(Command),
Error(ParserError),
}
impl From<Result<Command, ParserError>> for SessionOutput {
fn from(input: Result<Command, ParserError>) -> Self {
input.map(SessionOutput::Command)
.unwrap_or_else(SessionOutput::Error)
}
}
pub struct Session {
reader: LineReader,
report_mode: ReportMode,
report_pending: bool,
}
impl Session {
pub fn new() -> Self {
Session {
reader: LineReader::new(),
report_mode: ReportMode::Off,
report_pending: false,
}
}
pub fn is_dirty(&self) -> bool {
self.reader.pos > 0
}
pub fn report_mode(&self) -> ReportMode {
self.report_mode
}
pub fn set_report_pending(&mut self) {
self.report_pending = true;
}
pub fn is_report_pending(&self) -> bool {
match self.report_mode {
ReportMode::Off => false,
_ => self.report_pending,
}
}
pub fn mark_report_sent(&mut self) {
self.report_pending = false;
match self.report_mode {
ReportMode::Once =>
self.report_mode = ReportMode::Off,
_ => {}
}
}
pub fn feed(&mut self, buf: &[u8]) -> (usize, SessionOutput) {
let mut buf_bytes = 0;
for (i, b) in buf.iter().enumerate() {
buf_bytes = i + 1;
match self.reader.feed(*b) {
Some(line) => {
let command = Command::parse(line);
match command {
Ok(Command::Report(mode)) => {
self.report_mode = mode;
}
_ => {}
}
return (buf_bytes, command.into());
}
None => {}
}
}
(buf_bytes, SessionOutput::Nothing)
}
}