Compare commits

...

7 Commits

Author SHA1 Message Date
Astro 82b6e8e179 main: rm stale coda, poll adc, broadcast over tcp 2019-08-08 02:26:53 +02:00
Astro 057ddbdbf6 add ad7172 adc 2019-08-08 02:26:53 +02:00
Astro 4437a4195e delay: is unsafe 2019-08-08 02:26:53 +02:00
Astro 44f48f6e0f main: construct softspi 2019-08-08 02:26:53 +02:00
Astro 9c7ca0df87 add board::delay 2019-08-08 02:26:53 +02:00
Astro f04bbd8726 softspi: add SyncSoftSpi 2019-08-08 02:26:53 +02:00
Astro 4bf52b093e implement softspi 2019-08-08 02:26:53 +02:00
7 changed files with 383 additions and 94 deletions

1
firmware/Cargo.lock generated
View File

@ -120,6 +120,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)",
"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)",
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -13,6 +13,7 @@ cortex-m-rt = "0.6"
crc = { version = "1.7", default-features = false }
tm4c129x = { version = "0.8", features = ["rt"] }
embedded-hal = { version = "0.2", features = ["unproven"] }
nb = "0.1"
[dependencies.smoltcp]
git = "https://github.com/m-labs/smoltcp"

99
firmware/src/ad7172.rs Normal file
View File

@ -0,0 +1,99 @@
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::blocking::spi::Transfer;
#[allow(unused)]
#[repr(u8)]
pub enum Register {
Status = 0x00,
AdcMode = 0x01,
IfMode = 0x02,
RegCheck = 0x03,
Data = 0x04,
GpioCon = 0x06,
Id = 0x07,
Ch0 = 0x10,
Ch1 = 0x11,
Ch2 = 0x12,
Ch3 = 0x13,
SetupCon0 = 0x20,
SetupCon1 = 0x21,
SetupCon2 = 0x22,
SetupCon3 = 0x23,
FiltCon0 = 0x28,
FiltCon1 = 0x29,
FiltCon2 = 0x2A,
FiltCon3 = 0x2B,
Offset0 = 0x30,
Offset1 = 0x31,
Offset2 = 0x32,
Offset3 = 0x33,
Gain0 = 0x38,
Gain1 = 0x39,
Gain2 = 0x3A,
Gain3 = 0x3B,
}
pub struct Adc<SPI: Transfer<u8>, NSS: OutputPin> {
spi: SPI,
nss: NSS,
}
impl<SPI: Transfer<u8>, NSS: OutputPin> Adc<SPI, NSS> {
pub fn new(spi: SPI, nss: NSS) -> Result<Self, SPI::Error> {
let mut adc = Adc { spi, nss};
let mut buf = [0, 0, 0];
adc.write_reg(Register::AdcMode, &mut buf)?;
let mut buf = [0, 1, 0];
adc.write_reg(Register::IfMode, &mut buf)?;
let mut buf = [0, 0, 0];
adc.write_reg(Register::GpioCon, &mut buf)?;
Ok(adc)
}
/// Returns the channel the data is from
pub fn data_ready(&mut self) -> Option<u8> {
let mut buf = [0u8; 2];
match self.read_reg(Register::Status, &mut buf) {
Err(_) => None,
Ok(()) => {
if buf[1] & 0x80 == 0 {
None
} else {
Some(buf[1] & 0x3)
}
}
}
}
/// Get data
pub fn read_data(&mut self) -> Result<u32, SPI::Error> {
let mut buf = [0u8; 4];
self.read_reg(Register::Data, &mut buf)?;
let result =
(u32::from(buf[1]) << 16) |
(u32::from(buf[2]) << 8) |
u32::from(buf[3]);
Ok(result)
}
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], SPI::Error> {
let _ = self.nss.set_low();
let result = self.spi.transfer(words);
let _ = self.nss.set_high();
result
}
fn read_reg(&mut self, reg: Register, buffer: &'_ mut [u8]) -> Result<(), SPI::Error> {
buffer[0] = reg as u8;
self.transfer(buffer)?;
Ok(())
}
fn write_reg(&mut self, reg: Register, buffer: &'_ mut [u8]) -> Result<(), SPI::Error> {
buffer[0] = 0x40 | (reg as u8);
self.transfer(buffer)?;
Ok(())
}
}

View File

@ -0,0 +1,76 @@
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m::peripheral::SYST;
use tm4c129x::CorePeripherals;
/// precision internal oscillator
const PIOSC: u32 = 16_000_000;
pub struct Delay {
syst: SYST,
}
impl Delay {
/// unsafe: must only be used once to avoid concurrent use of systick
pub unsafe fn new() -> Self {
let mut syst = CorePeripherals::steal().SYST;
// PIOSC
syst.set_clock_source(SystClkSource::External);
syst.disable_interrupt();
Delay { syst }
}
}
impl DelayMs<u32> for Delay {
fn delay_ms(&mut self, ms: u32) {
self.delay_us(ms * 1_000);
}
}
impl DelayMs<u16> for Delay {
fn delay_ms(&mut self, ms: u16) {
self.delay_ms(u32::from(ms));
}
}
impl DelayMs<u8> for Delay {
fn delay_ms(&mut self, ms: u8) {
self.delay_ms(u32::from(ms));
}
}
impl DelayUs<u32> for Delay {
fn delay_us(&mut self, us: u32) {
// The SysTick Reload Value register supports values between 1 and 0x00FFFFFF.
const MAX_RVR: u32 = 0x00FF_FFFF;
let mut total_rvr = us * (PIOSC / 1_000_000);
while total_rvr != 0 {
let current_rvr = total_rvr.min(MAX_RVR);
self.syst.set_reload(current_rvr);
self.syst.clear_current();
self.syst.enable_counter();
// Update the tracking variable while we are waiting...
total_rvr -= current_rvr;
while !self.syst.has_wrapped() {}
self.syst.disable_counter();
}
}
}
impl DelayUs<u16> for Delay {
fn delay_us(&mut self, us: u16) {
self.delay_us(u32::from(us))
}
}
impl DelayUs<u8> for Delay {
fn delay_us(&mut self, us: u8) {
self.delay_us(u32::from(us))
}
}

View File

@ -1,7 +1,9 @@
use cortex_m;
use tm4c129x;
mod gpio;
pub mod gpio;
pub mod softspi;
pub mod delay;
const LED1: u8 = 0x10; // PK4

View File

@ -0,0 +1,149 @@
use embedded_hal::spi::FullDuplex;
use embedded_hal::digital::v2::{InputPin, OutputPin};
use embedded_hal::blocking::spi::Transfer;
use embedded_hal::blocking::delay::DelayUs;
use nb::Error::WouldBlock;
/// Bit-banged SPI
pub struct SoftSpi<SCK, MOSI, MISO> {
sck: SCK,
mosi: MOSI,
miso: MISO,
state: State,
input: Option<u8>,
}
#[derive(PartialEq)]
enum State {
Idle,
Transfer {
clock_phase: bool,
mask: u8,
output: u8,
input: u8,
},
}
impl<SCK: OutputPin, MOSI: OutputPin, MISO: InputPin> SoftSpi<SCK, MOSI, MISO> {
pub fn new(mut sck: SCK, mut mosi: MOSI, miso: MISO) -> Self {
let _ = sck.set_high();
let _ = mosi.set_low();
SoftSpi {
sck, mosi, miso,
state: State::Idle,
input: None,
}
}
/// Call this at twice the data rate
pub fn tick(&mut self) {
match self.state {
State::Idle => {}
State::Transfer { clock_phase: false,
mask, output, input } => {
if output & mask != 0 {
let _ = self.mosi.set_high();
} else {
let _ = self.mosi.set_low();
}
let _ = self.sck.set_low();
self.state = State::Transfer {
clock_phase: true,
mask, output, input,
};
}
State::Transfer { clock_phase: true,
mask, output, mut input } => {
let _ = self.sck.set_high();
if self.miso.is_high().unwrap_or(false) {
input |= mask;
}
if mask != 1 {
self.state = State::Transfer {
clock_phase: false,
mask: mask >> 1,
output, input,
};
} else {
self.input = Some(input);
self.state = State::Idle;
}
}
}
}
pub fn run<F: FnMut()>(&mut self, delay: &'_ mut F) {
while self.state != State::Idle {
self.tick();
delay();
}
}
}
impl<SCK: OutputPin, MOSI: OutputPin, MISO: InputPin> FullDuplex<u8> for SoftSpi<SCK, MOSI, MISO> {
type Error = ();
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
match self.input.take() {
Some(input) =>
Ok(input),
None if self.state == State::Idle =>
Err(nb::Error::Other(())),
None =>
Err(WouldBlock),
}
}
fn send(&mut self, output: u8) -> Result<(), nb::Error<Self::Error>> {
match self.state {
State::Idle => {
self.state = State::Transfer {
clock_phase: false,
mask: 0x80,
output,
input: 0,
};
Ok(())
}
_ => Err(WouldBlock)
}
}
}
pub struct SyncSoftSpi<'d, SCK: OutputPin, MOSI: OutputPin, MISO: InputPin, D: FnMut()> {
spi: SoftSpi<SCK, MOSI, MISO>,
delay: &'d mut D,
}
impl<'d, SCK: OutputPin, MOSI: OutputPin, MISO: InputPin, D: FnMut()> SyncSoftSpi<'d, SCK, MOSI, MISO, D> {
pub fn new(spi: SoftSpi<SCK, MOSI, MISO>, delay: &'d mut D) -> Self {
SyncSoftSpi { spi, delay }
}
fn retry<R, E, F>(&mut self, f: &F) -> Result<R, E>
where
F: Fn(&'_ mut SoftSpi<SCK, MOSI, MISO>) -> Result<R, nb::Error<E>>
{
loop {
match f(&mut self.spi) {
Ok(r) => return Ok(r),
Err(nb::Error::Other(e)) => return Err(e),
Err(WouldBlock) => self.spi.run(self.delay),
}
}
}
}
impl<'d, SCK: OutputPin, MOSI: OutputPin, MISO: InputPin, D: FnMut()> Transfer<u8> for SyncSoftSpi<'d, SCK, MOSI, MISO, D> {
// TODO: proper type
type Error = ();
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
for b in words.iter_mut() {
self.retry(&|spi| spi.send(*b))?;
*b = self.retry(&|spi| spi.read())?;
}
Ok(words)
}
}

View File

@ -11,9 +11,12 @@ extern crate tm4c129x;
extern crate smoltcp;
extern crate crc;
extern crate embedded_hal;
extern crate nb;
use core::cell::{Cell, RefCell};
use core::fmt;
use core::fmt::{self, Write};
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal::blocking::delay::DelayUs;
use cortex_m::interrupt::Mutex;
use smoltcp::time::Instant;
use smoltcp::wire::EthernetAddress;
@ -43,18 +46,11 @@ pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
#[macro_use]
mod board;
use board::gpio::Gpio;
mod eeprom;
mod config;
mod ethmac;
static ADC_IRQ_COUNT: Mutex<Cell<u64>> = Mutex::new(Cell::new(0));
fn get_time_ms() -> u64 {
let adc_irq_count = cortex_m::interrupt::free(|cs| {
ADC_IRQ_COUNT.borrow(cs).get()
});
adc_irq_count*24/125
}
mod ad7172;
pub struct UART0;
@ -111,6 +107,21 @@ fn main() -> ! {
| |
|_|
"#);
let mut delay = unsafe { board::delay::Delay::new() };
// CSn
let pb4 = board::gpio::PB4.into_output();
// SCLK
let pb5 = board::gpio::PB5.into_output();
// MOSI
let pe4 = board::gpio::PE4.into_output();
// MISO
let pe5 = board::gpio::PE5.into_input();
let mut delay_fn = || delay.delay_us(1u32);
let spi = board::softspi::SyncSoftSpi::new(
board::softspi::SoftSpi::new(pb5, pe4, pe5),
&mut delay_fn
);
let mut adc = ad7172::Adc::new(spi, pb4).unwrap();
let mut hardware_addr = EthernetAddress(board::get_mac_address());
if hardware_addr.is_multicast() {
@ -149,98 +160,48 @@ 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 = [
(http::Request::new(), tcp_handle0),
(http::Request::new(), tcp_handle1),
(http::Request::new(), tcp_handle2),
(http::Request::new(), tcp_handle3),
(http::Request::new(), tcp_handle4),
(http::Request::new(), tcp_handle5),
(http::Request::new(), tcp_handle6),
(http::Request::new(), tcp_handle7),
];*/
/*board::set_hv_pwm(board::PWM_LOAD / 2);
board::set_fv_pwm(board::PWM_LOAD / 2);
board::set_fbv_pwm(board::PWM_LOAD / 2);*/
board::start_adc();
let mut fast_blink_count = 0;
let mut next_blink = 0;
let mut led_state = true;
let mut latch_reset_time = None;
let mut time = 0i64;
let mut data = 0;
let mut socket_pending = [false; 8];
loop {
let time = get_time_ms();
/*for &mut(ref mut request, tcp_handle) in sessions.iter_mut() {
adc.data_ready()
.map(|channel| {
adc.read_data()
.map(|new_data| {
data = new_data;
if channel == 0 {
for p in socket_pending.iter_mut() {
*p = true;
}
}
})
});
for (&tcp_handle, pending) in (handles.iter().zip(socket_pending.iter_mut())) {
let socket = &mut *sockets.get::<TcpSocket>(tcp_handle);
if !socket.is_open() {
socket.listen(80).unwrap()
socket.listen(23).unwrap()
}
if socket.may_recv() {
match socket.recv(|data| (data.len(), request.input(data))).unwrap() {
Ok(true) => {
if socket.can_send() {
pages::serve(socket, &request, &mut config, &LOOP_ANODE, &LOOP_CATHODE, &ELECTROMETER);
}
request.reset();
socket.close();
}
Ok(false) => (),
Err(err) => {
println!("failed HTTP request: {}", err);
request.reset();
socket.close();
}
}
} else if socket.may_send() {
request.reset();
socket.close();
if socket.may_send() && *pending {
let _ = writeln!(socket, "{}\r", data);
*pending = false;
}
}*/
match iface.poll(&mut sockets, Instant::from_millis(time as i64)) {
}
match iface.poll(&mut sockets, Instant::from_millis(time)) {
Ok(_) => (),
Err(e) => println!("poll error: {}", e)
}
board::process_errors();
if board::error_latched() {
match latch_reset_time {
None => {
println!("Protection latched");
latch_reset_time = Some(time + 5000);
}
Some(t) => if time > t {
latch_reset_time = None;
cortex_m::interrupt::free(|cs| {
board::reset_error();
});
println!("Protection reset");
}
}
}
time += 1;
}
}
interrupt!(ADC0SS0, adc0_ss0);
fn adc0_ss0() {
cortex_m::interrupt::free(|cs| {
let adc0 = unsafe { &*tm4c129x::ADC0::ptr() };
if adc0.ostat.read().ov0().bit() {
panic!("ADC FIFO overflowed")
}
adc0.isc.write(|w| w.in0().bit(true));
let ic_sample = adc0.ssfifo0.read().data().bits();
let fbi_sample = adc0.ssfifo0.read().data().bits();
let fv_sample = adc0.ssfifo0.read().data().bits();
let fd_sample = adc0.ssfifo0.read().data().bits();
let av_sample = adc0.ssfifo0.read().data().bits();
let fbv_sample = adc0.ssfifo0.read().data().bits();
let adc_irq_count = ADC_IRQ_COUNT.borrow(cs);
adc_irq_count.set(adc_irq_count.get() + 1);
});
}