Skip to content

Instantly share code, notes, and snippets.

@dflemstr
Created February 20, 2020 21:53
Show Gist options
  • Save dflemstr/a51798ef7ec70138ea89c5f574829dc3 to your computer and use it in GitHub Desktop.
Save dflemstr/a51798ef7ec70138ea89c5f574829dc3 to your computer and use it in GitHub Desktop.
//! Demonstrates an SPI master. We try to read data from
//! an Adafruit AirLift.
#![no_std]
#![no_main]
extern crate panic_halt;
use bsp::hal::gpio::IntoGpio;
use core::fmt::Debug;
use embedded_hal::blocking::spi::{Transfer, Write};
use embedded_hal::digital::v2::InputPin;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::spi::FullDuplex;
use teensy4_bsp as bsp;
const START_CMD: u8 = 0xe0;
const END_CMD: u8 = 0xee;
const ERR_CMD: u8 = 0xef;
const REPLY_FLAG: u8 = 1 << 7;
const GET_FW_VERSION_CMD: u8 = 0x37;
const GET_CONN_STATUS_CMD: u8 = 0x20;
const SET_PIN_MODE: u8 = 0x50;
const SET_DIGITAL_WRITE: u8 = 0x51;
const SET_ANALOG_WRITE: u8 = 0x52;
const LOW: u8 = 0x00;
const HIGH: u8 = 0x01;
const INPUT: u8 = 0x00;
const OUTPUT: u8 = 0x01;
const INPUT_PULLUP: u8 = 0x02;
#[bsp::rt::entry]
fn main() -> ! {
let mut peripherals = bsp::Peripherals::take().unwrap();
peripherals.usb.init(Default::default());
bsp::delay(5000);
log::info!("Enabling SPI clocks...");
let (_, _, _, spi4_builder) = peripherals.spi.clock(
&mut peripherals.ccm.handle,
bsp::hal::ccm::spi::ClockSelect::Pll3Pfd0,
bsp::hal::ccm::spi::PrescalarSelect::LPSPI_PODF_2,
);
log::info!("Constructing SPI instance on pins 11, 12 and 13...");
let mut spi = spi4_builder.build(
peripherals.pins.p11.alt3(),
peripherals.pins.p12.alt3(),
peripherals.pins.p13.alt3(),
);
let mut esp_gpio0 = peripherals.pins.p2.into_gpio().output();
let esp_busy = peripherals.pins.p3.into_gpio();
let mut esp_rst = peripherals.pins.p4.into_gpio().output();
let mut esp_cs = peripherals.pins.p5.into_gpio().output();
esp_gpio0.set_high().unwrap();
esp_rst.set_low().unwrap();
bsp::delay(100);
esp_rst.set_high().unwrap();
log::info!("Starting I/O loop...");
let mut buffer = [0; 1024];
loop {
send_cmd(
&mut spi,
&esp_busy,
&mut esp_cs,
&get_connection_status_cmd(),
);
let mut resp = [0];
if wait_response(
&mut spi,
&esp_busy,
&mut esp_cs,
GET_CONN_STATUS_CMD,
&mut resp,
) {
if resp[0] == 255 {
log::error!("No module detected");
}
if resp[0] == 0 {
break;
}
}
bsp::delay(1000);
}
log::info!("Configure remote pins");
send_cmd(&mut spi, &esp_busy, &mut esp_cs, &pin_mode_cmd(25, OUTPUT));
while !wait_response(&mut spi, &esp_busy, &mut esp_cs, SET_PIN_MODE, &mut [0]) {}
send_cmd(&mut spi, &esp_busy, &mut esp_cs, &pin_mode_cmd(26, OUTPUT));
while !wait_response(&mut spi, &esp_busy, &mut esp_cs, SET_PIN_MODE, &mut [0]) {}
send_cmd(&mut spi, &esp_busy, &mut esp_cs, &pin_mode_cmd(27, OUTPUT));
while !wait_response(&mut spi, &esp_busy, &mut esp_cs, SET_PIN_MODE, &mut [0]) {}
loop {
send_cmd(&mut spi, &esp_busy, &mut esp_cs, &analog_write_cmd(25, 128));
while !wait_response(&mut spi, &esp_busy, &mut esp_cs, SET_ANALOG_WRITE, &mut [0]) {}
send_cmd(&mut spi, &esp_busy, &mut esp_cs, &analog_write_cmd(26, 128));
while !wait_response(&mut spi, &esp_busy, &mut esp_cs, SET_ANALOG_WRITE, &mut [0]) {}
send_cmd(&mut spi, &esp_busy, &mut esp_cs, &analog_write_cmd(27, 128));
while !wait_response(&mut spi, &esp_busy, &mut esp_cs, SET_ANALOG_WRITE, &mut [0]) {}
bsp::delay(1000);
}
}
fn get_connection_status_cmd() -> [u8; 4] {
[START_CMD, GET_CONN_STATUS_CMD & !REPLY_FLAG, 0, END_CMD]
}
fn analog_write_cmd(pin: u8, value: u8) -> [u8; 8] {
[
START_CMD,
SET_ANALOG_WRITE & !REPLY_FLAG,
2,
1,
pin,
1,
value,
END_CMD,
]
}
fn pin_mode_cmd(pin: u8, mode: u8) -> [u8; 8] {
[
START_CMD,
SET_PIN_MODE & !REPLY_FLAG,
2,
1,
pin,
1,
mode,
END_CMD,
]
}
fn send_cmd<W, BSY, CS>(spi: &mut W, esp_busy: &BSY, esp_cs: &mut CS, cmd: &[u8])
where
W: Write<u8>,
<W as Write<u8>>::Error: Debug,
BSY: InputPin,
BSY::Error: Debug,
CS: OutputPin,
CS::Error: Debug,
{
log::debug!("Waiting for ESP to be ready for request");
while esp_busy.is_high().unwrap() {}
esp_cs.set_low().unwrap();
spi.write(cmd).unwrap();
esp_cs.set_high().unwrap();
if cmd.len() > 32 {
log::debug!("Sent cmd (truncated): {:02x?}...", &cmd[..32]);
} else {
log::debug!("Sent cmd: {:02x?}", cmd);
}
}
fn read<W>(spi: &mut W) -> u8
where
W: FullDuplex<u8>,
<W as FullDuplex<u8>>::Error: Debug,
{
nb::block!(spi.send(0x00)).unwrap(); // dummy
let data = nb::block!(spi.read()).unwrap();
log::trace!("read {:02x}", data);
data
}
fn wait_response<W, BSY, CS>(
spi: &mut W,
esp_busy: &BSY,
esp_cs: &mut CS,
cmd: u8,
param: &mut [u8],
) -> bool
where
W: FullDuplex<u8>,
<W as FullDuplex<u8>>::Error: Debug,
BSY: InputPin,
BSY::Error: Debug,
CS: OutputPin,
CS::Error: Debug,
{
log::debug!("Waiting for ESP to be ready for response");
while esp_busy.is_high().unwrap() {}
esp_cs.set_low().unwrap();
loop {
let mut num_attempts = 0;
loop {
let data = read(spi);
num_attempts += 1;
if data == START_CMD {
break;
}
if data == ERR_CMD {
log::warn!("Received error response");
esp_cs.set_high().unwrap();
return false;
}
if num_attempts >= 256 {
log::warn!("Timed out");
esp_cs.set_high().unwrap();
return false;
}
}
let data = read(spi);
let expected = cmd | REPLY_FLAG;
if data == expected {
break;
} else {
log::warn!("Reply command: expected {:x} but got {:x}", expected, data);
esp_cs.set_high().unwrap();
return false;
}
}
let data = read(spi) as usize;
let expected = param.len();
if data != expected {
log::warn!("Reply len: expected {:x} but got {:x}", expected, data);
esp_cs.set_high().unwrap();
return false;
}
for b in param {
*b = read(spi);
}
let data = read(spi);
if data != END_CMD {
log::warn!("Reply end command: expected END_CMD but got {:x}", data);
esp_cs.set_high().unwrap();
return false;
}
esp_cs.set_high().unwrap();
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment