Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a HAL implementation for SPI master #13

Merged
merged 64 commits into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
08ed9e3
Updated readme with latest supported hardware
jonahbron Jun 27, 2019
2136e7c
Merge remote-tracking branch 'Rahix/master'
jonahbron Jul 2, 2019
823a5da
Added skeleton of SPI implementor macro
jonahbron Jul 3, 2019
18bb855
Fleshed out theoretical steps to read/write to SPI
jonahbron Jul 3, 2019
936bc8a
Fleshed out more details of SPI macro in comments
jonahbron Jul 3, 2019
24570cb
Temporarily moved to a non-macro implementation of SPI to make debugg…
jonahbron Jul 3, 2019
dd7cea9
Accepted peripheral in constructor and added data writing
jonahbron Jul 4, 2019
46a0cb3
Unwrapped secondary_select state changes
jonahbron Jul 5, 2019
5af1622
Finished fleshing out SPI implementation (testing pending)
jonahbron Jul 5, 2019
1dd920a
Refactored SPI code to be simpler, added doc comments
jonahbron Jul 6, 2019
79f5b68
Made simple SPI proof-of-concept for Uno
jonahbron Jul 12, 2019
1b4f916
Moved POC for SPI code back into atmega328p SPI library, added exampl…
jonahbron Jul 12, 2019
a796048
Removed POC example
jonahbron Jul 12, 2019
d2b922f
Added comments to SPI example
jonahbron Jul 12, 2019
021998b
Added better documentation to the SPI library
jonahbron Jul 12, 2019
87bec23
Moved SPI library into macro and invoked from atmega328p
jonahbron Jul 12, 2019
a09cf20
Removed commented-out manual implementation
jonahbron Jul 12, 2019
f160871
Added SPI support to ATMega32u4/Leonardo
jonahbron Jul 12, 2019
b8bf624
Updated readme with status of SPI
jonahbron Jul 12, 2019
311e775
Fixed pin comments in examples
jonahbron Jul 12, 2019
b54001e
Removed changes to cargo that were necessary to implement SPI manually
jonahbron Jul 12, 2019
787846d
Added SCLK pin to SPI constructor to allow output mode enforcement, u…
jonahbron Jul 20, 2019
1ce5a7e
Made SPI settings public so they can be changed by user
jonahbron Jul 20, 2019
7bfcdcc
Since SPI takes ownership of pins and so cannot share, removed redund…
jonahbron Jul 20, 2019
604414c
Merge branch 'master' of github.com:Rahix/avr-hal
jonahbron Oct 23, 2019
bd13c70
Wrapped newly unsafe function in an unsafe block
jonahbron Oct 23, 2019
5233da4
Moved SPI chip implementations to lib file, maintaining the same modu…
jonahd-g Feb 3, 2020
9c3bdcc
Removed alias, value cannot be moved twice
jonahd-g Feb 3, 2020
864f42f
Replaced empty SpiError enum with void::Void
jonahd-g Feb 3, 2020
103783c
Replaced bespoke polarity/phase enums with Embedded HAL's standard eq…
jonahd-g Feb 3, 2020
854ca0a
Made release() SPI method disable device before releasing peripheral …
jonahd-g Feb 3, 2020
bcc913f
Renamed piso/posi to miso/mosi to maintain consistency with external …
jonahd-g Feb 3, 2020
0878ef3
Merge pull request #1 from jonahd-google/master
jonahbron Feb 3, 2020
4101196
Removed unused SpiError enum
jonahd-g Feb 3, 2020
60db66d
Merge pull request #2 from jonahd-google/master
jonahbron Feb 3, 2020
ecea704
Imporoved wording of SPI constructor docs
jonahd-g Feb 6, 2020
f4cd0ed
Renamed ICSP pins on Leonardo
jonahd-g Feb 6, 2020
e2d46cb
Moved Settings outside of macro
jonahd-g Feb 6, 2020
74c11d0
Fixed docs for SPI example
jonahd-g Feb 6, 2020
2fcd7a8
Removed unused SpiError enum
jonahd-g Feb 6, 2020
1af06a6
Removed mention of USB since Leonardo has no USB
jonahd-g Feb 6, 2020
5902653
Merge branch 'master' of github.com:jonahd-google/avr-hal
jonahd-g Feb 6, 2020
9af129c
Fixed path to device in Uno SPI example
jonahd-g Feb 6, 2020
a31779b
Merge pull request #3 from jonahd-google/master
jonahbron Feb 6, 2020
beab667
Fixed name of pins in Leonardo SPI example
jonahd-g Apr 11, 2020
fa22d56
Added general-purpose derivable traits to settings structs/enums
jonahd-g Apr 11, 2020
248f4d1
Changed to proper use of NB to enable the program to do other things …
jonahd-g Apr 11, 2020
cdc4de5
Merge branch 'master' of github.com:Rahix/avr-hal
jonahd-g Apr 11, 2020
2cae2ce
Merge pull request #4 from jonahd-google/master
jonahbron Apr 11, 2020
faa58de
Fixed write-in-progress flag clearing
jonahd-g Apr 12, 2020
03ffc32
Updated SPI Feedback examples to correctly block the FullDuplex methods
jonahd-g Apr 12, 2020
32f1055
Merge pull request #5 from jonahd-google/master
jonahbron Apr 12, 2020
3d26634
Improved SPI feedback examples to print transmitted character to seri…
jonahd-g Apr 12, 2020
5a1de82
Merge pull request #6 from jonahd-google/master
jonahbron Apr 12, 2020
486cbd7
Ran cargo fmt on SPI examples
jonahd-g Apr 12, 2020
9c0a1ff
Merge pull request #7 from jonahd-google/master
jonahbron Apr 12, 2020
0700adc
Removed derived traits from macro definition
jonahd-g Apr 14, 2020
73e2668
Merge branch 'master' of github.com:Rahix/avr-hal
jonahd-g Apr 14, 2020
a04fbee
Simplified SPI feedback examples to use ufmt again
jonahd-g Apr 14, 2020
fa1da88
Merge pull request #8 from jonahd-google/master
jonahbron Apr 14, 2020
204fbfa
Changed implementation of SPI to match nb::Result contract correctly:…
jonahd-g Apr 14, 2020
7907026
Merge pull request #9 from jonahd-google/master
jonahbron Apr 14, 2020
bb50c07
Removed debug trait from SPI
jonahd-g Apr 14, 2020
be3bffb
Merge pull request #10 from jonahd-google/master
jonahbron Apr 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ The following peripherals are supported in `avr-hal-generic`:
- [x] A spinning delay implementation
- [x] `PORTx` peripherals as digital IO (v2)
- [x] A TWI based I2C implementation
- [ ] SPI primary-mode implementation

### HAL Status
The chip-HAL crates currently support the following peripherals:
Expand Down
1 change: 1 addition & 0 deletions avr-hal-generic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod delay;
pub mod port;
pub mod serial;
pub mod i2c;
pub mod spi;

/// Prelude containing all HAL traits
pub mod prelude {
Expand Down
185 changes: 185 additions & 0 deletions avr-hal-generic/src/spi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//! SPI Implementation

/// Error type emitted by Spi in the event of a critical failure. Errors have
/// no information attached.
#[derive(Debug, Clone, Copy)]
pub enum SpiError {}
jonahbron marked this conversation as resolved.
Show resolved Hide resolved

/// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits.
pub enum SerialClockRate {
OscfOver2,
OscfOver4,
OscfOver8,
OscfOver16,
OscfOver32,
OscfOver64,
OscfOver128,
}
jonahbron marked this conversation as resolved.
Show resolved Hide resolved

/// Order of data transmission, either MSB first or LSB first
pub enum DataOrder {
MostSignificantFirst,
LeastSignificantFirst,
}

/// Polarity of clock (whether SCLK idles at low state or high state)
pub enum SerialClockPolarity {
IdleHigh,
IdleLow,
}

/// Clock sampling phase (check at leading or trailing edge of signal)
pub enum SerialClockPhase {
SampleLeading,
SampleTrailing,
}
jonahbron marked this conversation as resolved.
Show resolved Hide resolved

/// Implement traits for a SPI interface
#[macro_export]
macro_rules! impl_spi {
(
$(#[$spi_attr:meta])*
pub struct $Spi:ident {
peripheral: $SPI:ty,
pins: {
posi: $POSI:ident,
piso: $PISO:ident,
}
}
) => {
type POSI = $POSI<mode::Output>;
type PISO = $PISO<mode::Input<mode::PullUp>>;

/// Settings to pass to Spi.
///
/// Easiest way to initialize is with
/// `Settings::default()`. Otherwise can be instantiated with alternate
/// settings directly.
pub struct Settings {
data_order: DataOrder,
clock: SerialClockRate,
clock_polarity: SerialClockPolarity,
clock_phase: SerialClockPhase,
}

impl Default for Settings {
fn default() -> Self {
Settings {
data_order: DataOrder::MostSignificantFirst,
clock: SerialClockRate::OscfOver4,
clock_polarity: SerialClockPolarity::IdleLow,
clock_phase: SerialClockPhase::SampleTrailing,
}
}
}

/// Behavior for a SPI interface.
///
/// Stores the SPI peripheral for register access. In addition, it takes
/// ownership of the POSI and PISO pins to ensure they are in the correct mode.
/// Instantiate with the `new` method.
$(#[$spi_attr])*
pub struct $Spi {
peripheral: $SPI,
posi: POSI,
piso: PISO,
settings: Settings,
}

/// Implementation-specific behavior of the struct, including setup/tear-down
impl $Spi {
/// Instantiate an SPI with the registers, POSI/PISO pins, and settings
pub fn new(peripheral: $SPI, posi: POSI, piso: PISO, settings: Settings) -> $Spi {
Spi {
peripheral,
posi,
piso,
settings,
}
}

/// Release ownership of the peripheral and pins. Instance can no-longer
/// be used after this is invoked.
pub fn release(self) -> ($SPI, POSI, PISO) {
(self.peripheral, self.posi, self.piso)
}

/// Write a byte to the data register, which begins transmission
/// automatically
fn write(&self, byte: u8) {
self.peripheral.spdr.write(|w| w.bits(byte));
}

/// Loop forever, checking the transmission complete bit until it is set
fn block_until_transfer_complete(&self) {
while self.peripheral.spsr.read().spif().bit_is_clear() { }
}

/// Sets up the control/status registers with the right settings for this secondary device
fn setup(&self) {
// set up control register
self.peripheral.spcr.write(|w| {
// enable SPI
w.spe().set_bit();
// Set to primary mode
w.mstr().set_bit();
// set up data order control bit
match self.settings.data_order {
DataOrder::MostSignificantFirst => w.dord().clear_bit(),
DataOrder::LeastSignificantFirst => w.dord().set_bit(),
};
// set up polarity control bit
match self.settings.clock_polarity {
SerialClockPolarity::IdleHigh => w.cpol().set_bit(),
SerialClockPolarity::IdleLow => w.cpol().clear_bit(),
};
// set up phase control bit
match self.settings.clock_phase {
SerialClockPhase::SampleLeading => w.cpha().clear_bit(),
SerialClockPhase::SampleTrailing => w.cpha().set_bit(),
};
// set up clock rate control bit
match self.settings.clock {
SerialClockRate::OscfOver2 => w.spr().val_0x00(),
SerialClockRate::OscfOver4 => w.spr().val_0x00(),
SerialClockRate::OscfOver8 => w.spr().val_0x01(),
SerialClockRate::OscfOver16 => w.spr().val_0x01(),
SerialClockRate::OscfOver32 => w.spr().val_0x02(),
SerialClockRate::OscfOver64 => w.spr().val_0x02(),
SerialClockRate::OscfOver128 => w.spr().val_0x03(),
}
});
// set up 2x clock rate status bit
self.peripheral.spsr.write(|w| match self.settings.clock {
SerialClockRate::OscfOver2 => w.spi2x().set_bit(),
SerialClockRate::OscfOver4 => w.spi2x().clear_bit(),
SerialClockRate::OscfOver8 => w.spi2x().set_bit(),
SerialClockRate::OscfOver16 => w.spi2x().clear_bit(),
SerialClockRate::OscfOver32 => w.spi2x().set_bit(),
SerialClockRate::OscfOver64 => w.spi2x().clear_bit(),
SerialClockRate::OscfOver128 => w.spi2x().set_bit(),
});
}
}

/// FullDuplex trait implementation, allowing this struct to be provided to
/// drivers that require it for operation. Only 8-bit word size is supported
/// for now.
impl $crate::hal::spi::FullDuplex<u8> for $Spi {
type Error = $crate::spi::SpiError;

/// Sets up the device for transmission and sends the data
fn send(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> {
self.setup();
self.write(byte);
self.block_until_transfer_complete();
Ok(())
}

/// Reads and returns the response in the data register
fn read(&mut self) -> $crate::nb::Result<u8, Self::Error> {
Ok(self.peripheral.spdr.read().bits())
}
jonahbron marked this conversation as resolved.
Show resolved Hide resolved
}
};
}
55 changes: 55 additions & 0 deletions boards/arduino-uno/examples/uno-spi-feedback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! This example demonstrates how to set up a SPI interface and communicate over it.
//! The physical hardware configuation consists of connecting a jumper directly from pin `~11` to
//! pin `~12`.
//!
//! Once this program is written to the board, the serial output can be accessed with
//!
//! ```
//! sudo screen /tty/ACM0 57600
jonahbron marked this conversation as resolved.
Show resolved Hide resolved
//! ```
//!
//! You should see it output the line `data: 15` repeatedly (aka 0b00001111)

#![no_std]
#![no_main]
#![feature(proc_macro_hygiene)]
extern crate panic_halt;
use arduino_uno::prelude::*;
use arduino_uno::spi::{Spi,Settings};
#[no_mangle]
pub extern fn main() -> ! {
let dp = arduino_uno::Peripherals::take().unwrap();
let mut delay = arduino_uno::Delay::new();
let mut pins = arduino_uno::Pins::new(
dp.PORTB,
dp.PORTC,
dp.PORTD,
);
// set up serial interface for text output
let mut serial = arduino_uno::Serial::new(
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
);

pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode
// create SPI interface
let mut spi = Spi::new(
dp.SPI,
pins.d11.into_output(&mut pins.ddr),
pins.d12.into_pull_up_input(&mut pins.ddr),
Settings::default(),
jonahbron marked this conversation as resolved.
Show resolved Hide resolved
);

loop {
// Send a byte
spi.send(0b00001111).unwrap();
// Because PISO is connected to POSI, the read data should be the same
let data = spi.read().unwrap();

ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap();
delay.delay_ms(1000);
}
}

1 change: 1 addition & 0 deletions boards/arduino-uno/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod pins;
pub use atmega328p_hal::atmega328p;
pub use crate::atmega328p::Peripherals;
pub use atmega328p_hal::prelude;
pub use atmega328p_hal::spi;
pub use crate::pins::*;

pub type Delay = hal::delay::Delay<hal::clock::MHz16>;
Expand Down
5 changes: 5 additions & 0 deletions chips/atmega328p-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ edition = "2018"

[dependencies]
avr-hal-generic = { path = "../../avr-hal-generic/" }
nb = "0.1.2"

[dependencies.embedded-hal]
version = "0.2.3"
features = ["unproven"]

[dependencies.avr-device]
git = "https://github.com/Rahix/avr-device"
Expand Down
1 change: 1 addition & 0 deletions chips/atmega328p-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub use avr_hal::clock;
pub use avr_hal::delay;

pub mod port;
pub mod spi;

pub mod prelude {
pub use crate::avr_hal::prelude::*;
Expand Down
Loading