From 08ed9e329987389766d86db4c410e98812ca72cb Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Thu, 27 Jun 2019 11:57:59 -0500 Subject: [PATCH 01/49] Updated readme with latest supported hardware --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index de65b45f54..569c772147 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,11 @@ The following peripherals are supported in `avr-hal-generic`: ### HAL Status The chip-HAL crates currently support the following peripherals: +* [`atmega328p-hal`]('./chips/atmega328p-hal) + - [x] Spinning Delay + - [x] `PORTB`, `PORTC`, `PORTD` as digital IO (v2) + - [x] `USART0` for serial communication + - [x] I2C using `TWI` * [`atmega32u4-hal`](./chips/atmega32u4-hal) - [x] Spinning Delay - [x] `PORTB`, `PORTC`, `PORTD`, `PORTE`, `PORTF` as digital IO (v2) @@ -85,6 +90,9 @@ In `boards/` there are crates for the following hardware. Please note that this * [Arduino Leonardo](./boards/arduino-leonardo) - [Website](https://www.arduino.cc/en/Main/Arduino_BoardLeonardo) - Support for basic digital IO +* [Arduino Uno](./boards/arduino-uno) + - [Website](https://store.arduino.cc/usa/arduino-uno-rev3) + - Support for basic digital IO * [Adafruit Trinket (3V3 or 5V)](./boards/trinket) (**not** PRO!) - [Website](https://learn.adafruit.com/introducing-trinket) - Support for basic digital IO From 823a5da96ffad6c2bb1dffd964ee53ecaff40b91 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Tue, 2 Jul 2019 19:50:53 -0500 Subject: [PATCH 02/49] Added skeleton of SPI implementor macro --- README.md | 1 + avr-hal-generic/src/spi.rs | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 avr-hal-generic/src/spi.rs diff --git a/README.md b/README.md index 569c772147..7e8ebf218b 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs new file mode 100644 index 0000000000..327bac6646 --- /dev/null +++ b/avr-hal-generic/src/spi.rs @@ -0,0 +1,55 @@ +//! SPI Implementation + +/// SPI Error +#[derive(Debug, Clone, Copy)] +pub enum Error { } + +/// Implement traits for a SPI interface +#[macro_export] +macro_rules! impl_spi { + ( + $(#[$spi_attr:meta])* + pub struct $Spi:ident { + peripheral: $SPI:ty, + pins: { + clock: $clockmod:ident::$CLOCK:ident, + piso: $pisomod:ident::$PISO:ident, + posi: $posimod:ident::$POSI:ident, + }, + registers: { + control: $control:ident, + status: $status:ident, + data: $data:ident, + }, + } + ) => { + $(#[$spi_attr])* + pub struct $Spi { + // TODO add necessary properties + } + + impl $Spi + { + // TODO add settings arguments besides secondary select (optional?) + /// Initialize the SPI peripheral + pub fn new(ss: $crate::hal::digital::v2::OutputPin) -> $Spi { + // TODO actually set up SPI peripheral + $Spi {} + } + } + + impl $crate::hal::spi::FullDuplex for $Spi { + type Error = $crate::spi::Error; + + fn write(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> { + // TODO implement write + Ok(()) + } + + fn read(&mut self) -> $crate::nb::Result { + // TODO implement read + Ok(0) + } + } + }; +} From 18bb85561904db00b2b52d348925f3ab35948e6c Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Tue, 2 Jul 2019 22:55:20 -0500 Subject: [PATCH 03/49] Fleshed out theoretical steps to read/write to SPI --- avr-hal-generic/src/spi.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 327bac6646..3db0b406d1 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -12,6 +12,7 @@ macro_rules! impl_spi { pub struct $Spi:ident { peripheral: $SPI:ty, pins: { + // Might not need references to these pins? Seems like r/w and clock are handled by hardware. clock: $clockmod:ident::$CLOCK:ident, piso: $pisomod:ident::$PISO:ident, posi: $posimod:ident::$POSI:ident, @@ -33,7 +34,8 @@ macro_rules! impl_spi { // TODO add settings arguments besides secondary select (optional?) /// Initialize the SPI peripheral pub fn new(ss: $crate::hal::digital::v2::OutputPin) -> $Spi { - // TODO actually set up SPI peripheral + // pull SS high + // store secondary-select, control, status, and register pins to struct $Spi {} } } @@ -42,12 +44,28 @@ macro_rules! impl_spi { type Error = $crate::spi::Error; fn write(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> { - // TODO implement write + // I think it would be best to set all control bits for every write. This way the user can have + // multiple Spi instances that communicate with different secondaries with no problem, even if they + // each have different settings. + + // pull SS low + // set SPIE (SPI enable) control bit to 1 + // set MSTR (primary/secondary select) control bit to 1 + + // set DORD (data order) control bit to user-defined setting (default 1) + // set CPOL (clock polarity) control bit to user-defined setting (default 0) + // set CPHA (clock phase) control bit to user-defined setting (default 0) + // set SPR (clock speed) control bits to user-defined setting (default 3) + // set SPIX2 (x2 clock speed) status bit to user-defined setting (default 0) + + // set $data to byte + + // pull SS high Ok(()) } fn read(&mut self) -> $crate::nb::Result { - // TODO implement read + // return and dereference $data Ok(0) } } From 936bc8adbadfe43db5f118fc8019cb869ea627a6 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Tue, 2 Jul 2019 23:44:52 -0500 Subject: [PATCH 04/49] Fleshed out more details of SPI macro in comments --- avr-hal-generic/src/spi.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 3db0b406d1..f23da65384 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -13,9 +13,9 @@ macro_rules! impl_spi { peripheral: $SPI:ty, pins: { // Might not need references to these pins? Seems like r/w and clock are handled by hardware. - clock: $clockmod:ident::$CLOCK:ident, - piso: $pisomod:ident::$PISO:ident, - posi: $posimod:ident::$POSI:ident, + clock: $CLOCK:ident, + piso: $PISO:ident, + posi: $POSI:ident, }, registers: { control: $control:ident, @@ -47,8 +47,10 @@ macro_rules! impl_spi { // I think it would be best to set all control bits for every write. This way the user can have // multiple Spi instances that communicate with different secondaries with no problem, even if they // each have different settings. + // make sure the entire control register is set in one instruction for efficiency + // registers have modify/read/write/reset methods - // pull SS low + // pull SS (instance of embedded_hal::serial::v2::OutputPin) low // set SPIE (SPI enable) control bit to 1 // set MSTR (primary/secondary select) control bit to 1 From 24570cbf23acd7494acd3eb00f78805225277c6b Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Wed, 3 Jul 2019 13:14:28 -0500 Subject: [PATCH 05/49] Temporarily moved to a non-macro implementation of SPI to make debugging easier. Skeleton now compiles --- avr-hal-generic/src/spi.rs | 135 +++++++++++++++++--------------- chips/atmega328p-hal/Cargo.toml | 5 ++ chips/atmega328p-hal/src/lib.rs | 1 + chips/atmega328p-hal/src/spi.rs | 67 ++++++++++++++++ 4 files changed, 143 insertions(+), 65 deletions(-) create mode 100644 chips/atmega328p-hal/src/spi.rs diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index f23da65384..f6b1ccfce1 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -1,75 +1,80 @@ -//! SPI Implementation +// //! SPI Implementation -/// SPI Error -#[derive(Debug, Clone, Copy)] -pub enum Error { } +// /// SPI Error +// #[derive(Debug, Clone, Copy)] +// pub enum Error { } -/// Implement traits for a SPI interface -#[macro_export] -macro_rules! impl_spi { - ( - $(#[$spi_attr:meta])* - pub struct $Spi:ident { - peripheral: $SPI:ty, - pins: { - // Might not need references to these pins? Seems like r/w and clock are handled by hardware. - clock: $CLOCK:ident, - piso: $PISO:ident, - posi: $POSI:ident, - }, - registers: { - control: $control:ident, - status: $status:ident, - data: $data:ident, - }, - } - ) => { - $(#[$spi_attr])* - pub struct $Spi { - // TODO add necessary properties - } +// /// Implement traits for a SPI interface +// #[macro_export] +// macro_rules! impl_spi { +// ( +// $(#[$spi_attr:meta])* +// pub struct $Spi:ident { +// peripheral: $SPI:ty, +// pins: { +// // Might not need references to these pins? Seems like r/w and clock are handled by hardware. +// clock: $CLOCK:ident, +// piso: $PISO:ident, +// posi: $POSI:ident, +// }, +// registers: { +// control: $control:ident, +// status: $status:ident, +// data: $data:ident, +// }, +// } +// ) => { +// $(#[$spi_attr])* +// pub struct $Spi { +// ss: $crate::hal::digital::v2::OutputPin, +// // TODO add necessary properties +// } - impl $Spi - { - // TODO add settings arguments besides secondary select (optional?) - /// Initialize the SPI peripheral - pub fn new(ss: $crate::hal::digital::v2::OutputPin) -> $Spi { - // pull SS high - // store secondary-select, control, status, and register pins to struct - $Spi {} - } - } +// impl $Spi +// { +// // TODO add settings arguments besides secondary select (optional?) +// /// Initialize the SPI peripheral +// pub fn new(ss: $crate::hal::digital::v2::OutputPin) -> $Spi { +// // pull SS high +// // store secondary-select, control, status, and register pins to struct +// $Spi {} +// } +// } - impl $crate::hal::spi::FullDuplex for $Spi { - type Error = $crate::spi::Error; +// impl $crate::hal::spi::FullDuplex for $Spi { +// type Error = $crate::spi::Error; - fn write(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> { - // I think it would be best to set all control bits for every write. This way the user can have - // multiple Spi instances that communicate with different secondaries with no problem, even if they - // each have different settings. - // make sure the entire control register is set in one instruction for efficiency - // registers have modify/read/write/reset methods +// fn write(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> { +// // I think it would be best to set all control bits for every write. This way the user can have +// // multiple Spi instances that communicate with different secondaries with no problem, even if they +// // each have different settings. +// // make sure the entire control register is set in one instruction for efficiency +// // registers have modify/read/write/reset methods - // pull SS (instance of embedded_hal::serial::v2::OutputPin) low - // set SPIE (SPI enable) control bit to 1 - // set MSTR (primary/secondary select) control bit to 1 +// // open communication with secondary via secondary-select pin +// self.ss.set_low(); - // set DORD (data order) control bit to user-defined setting (default 1) - // set CPOL (clock polarity) control bit to user-defined setting (default 0) - // set CPHA (clock phase) control bit to user-defined setting (default 0) - // set SPR (clock speed) control bits to user-defined setting (default 3) - // set SPIX2 (x2 clock speed) status bit to user-defined setting (default 0) +// // pull SS (instance of embedded_hal::serial::v2::OutputPin) low +// // set SPIE (SPI enable) control bit to 1 +// // set MSTR (primary/secondary select) control bit to 1 - // set $data to byte +// // set DORD (data order) control bit to user-defined setting (default 1) +// // set CPOL (clock polarity) control bit to user-defined setting (default 0) +// // set CPHA (clock phase) control bit to user-defined setting (default 0) +// // set SPR (clock speed) control bits to user-defined setting (default 3) +// // set SPIX2 (x2 clock speed) status bit to user-defined setting (default 0) - // pull SS high - Ok(()) - } +// // set $data to byte - fn read(&mut self) -> $crate::nb::Result { - // return and dereference $data - Ok(0) - } - } - }; -} +// // close communication with secondary via secondary-select pin +// self.ss.set_high(); +// Ok(()) +// } + +// fn read(&mut self) -> $crate::nb::Result { +// // return and dereference $data +// Ok(0) +// } +// } +// }; +// } diff --git a/chips/atmega328p-hal/Cargo.toml b/chips/atmega328p-hal/Cargo.toml index eb04dccc45..1840dbe987 100644 --- a/chips/atmega328p-hal/Cargo.toml +++ b/chips/atmega328p-hal/Cargo.toml @@ -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" diff --git a/chips/atmega328p-hal/src/lib.rs b/chips/atmega328p-hal/src/lib.rs index 4f2375f338..6e67097acd 100644 --- a/chips/atmega328p-hal/src/lib.rs +++ b/chips/atmega328p-hal/src/lib.rs @@ -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::*; diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs new file mode 100644 index 0000000000..fbf22dbe59 --- /dev/null +++ b/chips/atmega328p-hal/src/spi.rs @@ -0,0 +1,67 @@ + + +use embedded_hal as hal; +use nb; + +#[derive(Debug, Clone, Copy)] +pub enum Error { } + +pub struct Spi where + SS: hal::digital::v2::OutputPin +{ + ss: SS, + // TODO add necessary properties +} + +impl Spi where + SS: hal::digital::v2::OutputPin +{ + // TODO add settings arguments besides secondary select (optional?) + /// Initialize the SPI peripheral + pub fn new(mut ss: SS) -> Spi { + // start by closing communication with secondary + ss.set_high(); + // TODO control, status, and register pins to struct + Spi { + ss: ss + } + } +} + +impl hal::spi::FullDuplex for Spi where + SS: hal::digital::v2::OutputPin +{ + type Error = Error; + + fn send(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + // I think it would be best to set all control bits for every write. This way the user can have + // multiple Spi instances that communicate with different secondaries with no problem, even if they + // each have different settings. + // make sure the entire control register is set in one instruction for efficiency + // registers have modify/read/write/reset methods + + // open communication with secondary via secondary-select pin + self.ss.set_low(); + + // pull SS (instance of embedded_hal::serial::v2::OutputPin) low + // set SPIE (SPI enable) control bit to 1 + // set MSTR (primary/secondary select) control bit to 1 + + // set DORD (data order) control bit to user-defined setting (default 1) + // set CPOL (clock polarity) control bit to user-defined setting (default 0) + // set CPHA (clock phase) control bit to user-defined setting (default 0) + // set SPR (clock speed) control bits to user-defined setting (default 3) + // set SPIX2 (x2 clock speed) status bit to user-defined setting (default 0) + + // set $data to byte + + // close communication with secondary via secondary-select pin + self.ss.set_high(); + Ok(()) + } + + fn read(&mut self) -> nb::Result { + // return and dereference $data + Ok(0) + } +} \ No newline at end of file From dd7cea91c649723cc7f0758f263602f91655ee2a Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Thu, 4 Jul 2019 13:24:06 -0500 Subject: [PATCH 06/49] Accepted peripheral in constructor and added data writing --- chips/atmega328p-hal/src/spi.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index fbf22dbe59..bdf77bed01 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -3,33 +3,37 @@ use embedded_hal as hal; use nb; +use crate::atmega328p::SPI; + #[derive(Debug, Clone, Copy)] pub enum Error { } pub struct Spi where - SS: hal::digital::v2::OutputPin + SS: hal::digital::v2::OutputPin, { - ss: SS, + peripheral: SPI, + secondary_select: SS, // TODO add necessary properties } impl Spi where - SS: hal::digital::v2::OutputPin + SS: hal::digital::v2::OutputPin, { // TODO add settings arguments besides secondary select (optional?) /// Initialize the SPI peripheral - pub fn new(mut ss: SS) -> Spi { + pub fn new(peripheral: SPI, mut secondary_select: SS) -> Spi { // start by closing communication with secondary - ss.set_high(); + secondary_select.set_high(); // TODO control, status, and register pins to struct Spi { - ss: ss + peripheral, + secondary_select, } } } impl hal::spi::FullDuplex for Spi where - SS: hal::digital::v2::OutputPin + SS: hal::digital::v2::OutputPin, { type Error = Error; @@ -41,7 +45,7 @@ impl hal::spi::FullDuplex for Spi where // registers have modify/read/write/reset methods // open communication with secondary via secondary-select pin - self.ss.set_low(); + self.secondary_select.set_low(); // pull SS (instance of embedded_hal::serial::v2::OutputPin) low // set SPIE (SPI enable) control bit to 1 @@ -53,10 +57,13 @@ impl hal::spi::FullDuplex for Spi where // set SPR (clock speed) control bits to user-defined setting (default 3) // set SPIX2 (x2 clock speed) status bit to user-defined setting (default 0) - // set $data to byte + // write byte to data register which triggers transmission + self.peripheral.spdr.write(|w| w.bits(byte)); + + // TODO wait until send complete bit is set // close communication with secondary via secondary-select pin - self.ss.set_high(); + self.secondary_select.set_high(); Ok(()) } From 46a0cb3dcbd8a214e01e05e18507079026a6869e Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Thu, 4 Jul 2019 23:20:54 -0500 Subject: [PATCH 07/49] Unwrapped secondary_select state changes --- chips/atmega328p-hal/src/spi.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index bdf77bed01..a82daef060 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -2,14 +2,13 @@ use embedded_hal as hal; use nb; - use crate::atmega328p::SPI; #[derive(Debug, Clone, Copy)] -pub enum Error { } +pub enum SpiError { } pub struct Spi where - SS: hal::digital::v2::OutputPin, + SS: hal::digital::v2::OutputPin, { peripheral: SPI, secondary_select: SS, @@ -17,13 +16,13 @@ pub struct Spi where } impl Spi where - SS: hal::digital::v2::OutputPin, + SS: hal::digital::v2::OutputPin, { // TODO add settings arguments besides secondary select (optional?) /// Initialize the SPI peripheral pub fn new(peripheral: SPI, mut secondary_select: SS) -> Spi { // start by closing communication with secondary - secondary_select.set_high(); + secondary_select.set_high().unwrap(); // TODO control, status, and register pins to struct Spi { peripheral, @@ -33,9 +32,9 @@ impl Spi where } impl hal::spi::FullDuplex for Spi where - SS: hal::digital::v2::OutputPin, + SS: hal::digital::v2::OutputPin, { - type Error = Error; + type Error = SpiError; fn send(&mut self, byte: u8) -> nb::Result<(), Self::Error> { // I think it would be best to set all control bits for every write. This way the user can have @@ -45,9 +44,8 @@ impl hal::spi::FullDuplex for Spi where // registers have modify/read/write/reset methods // open communication with secondary via secondary-select pin - self.secondary_select.set_low(); + self.secondary_select.set_low().unwrap(); - // pull SS (instance of embedded_hal::serial::v2::OutputPin) low // set SPIE (SPI enable) control bit to 1 // set MSTR (primary/secondary select) control bit to 1 @@ -63,7 +61,7 @@ impl hal::spi::FullDuplex for Spi where // TODO wait until send complete bit is set // close communication with secondary via secondary-select pin - self.secondary_select.set_high(); + self.secondary_select.set_high().unwrap(); Ok(()) } From 5af1622a4e6a89c0d7a60bf91f065fc670448aa3 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 5 Jul 2019 12:39:31 -0500 Subject: [PATCH 08/49] Finished fleshing out SPI implementation (testing pending) --- chips/atmega328p-hal/src/spi.rs | 100 ++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 18 deletions(-) diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index a82daef060..0aac5557f7 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -7,11 +7,45 @@ use crate::atmega328p::SPI; #[derive(Debug, Clone, Copy)] pub enum SpiError { } +/// Oscillator Clock Frequency division options +pub enum SerialClockRate { + OscfOver2, + OscfOver4, + OscfOver8, + OscfOver16, + OscfOver32, + OscfOver64, + OscfOver128, +} + +pub enum DataOrder { + MostSignificantFirst, + LeastSignificantFirst, +} + +pub enum SerialClockPolarity { + IdleHigh, + IdleLow, +} + +pub enum SerialClockPhase { + SampleLeading, + SampleTrailing, +} + +pub struct Settings { + data_order: DataOrder, + clock: SerialClockRate, + clock_polarity: SerialClockPolarity, + clock_phase: SerialClockPhase, +} + pub struct Spi where SS: hal::digital::v2::OutputPin, { peripheral: SPI, secondary_select: SS, + settings: Settings, // TODO add necessary properties } @@ -20,13 +54,14 @@ impl Spi where { // TODO add settings arguments besides secondary select (optional?) /// Initialize the SPI peripheral - pub fn new(peripheral: SPI, mut secondary_select: SS) -> Spi { + pub fn new(peripheral: SPI, mut secondary_select: SS, settings: Settings) -> Spi { // start by closing communication with secondary secondary_select.set_high().unwrap(); // TODO control, status, and register pins to struct Spi { peripheral, secondary_select, + settings, } } } @@ -36,37 +71,66 @@ impl hal::spi::FullDuplex for Spi where { type Error = SpiError; + /// Sets up control/status register before writing data to ensure settings are always correct fn send(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - // I think it would be best to set all control bits for every write. This way the user can have - // multiple Spi instances that communicate with different secondaries with no problem, even if they - // each have different settings. - // make sure the entire control register is set in one instruction for efficiency - // registers have modify/read/write/reset methods - // open communication with secondary via secondary-select pin self.secondary_select.set_low().unwrap(); - // set SPIE (SPI enable) control bit to 1 - // set MSTR (primary/secondary select) control bit to 1 - - // set DORD (data order) control bit to user-defined setting (default 1) - // set CPOL (clock polarity) control bit to user-defined setting (default 0) - // set CPHA (clock phase) control bit to user-defined setting (default 0) - // set SPR (clock speed) control bits to user-defined setting (default 3) - // set SPIX2 (x2 clock speed) status bit to user-defined setting (default 0) + // set up control register + self.peripheral.spcr.write(|w| { + w + .spe().set_bit()// enable SPI + .mstr().set_bit();// set to primary mode + // 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(), + }); // write byte to data register which triggers transmission self.peripheral.spdr.write(|w| w.bits(byte)); - // TODO wait until send complete bit is set + // wait until transmission is complete + while self.peripheral.spsr.read().spif().bit_is_clear() { } // close communication with secondary via secondary-select pin + self.secondary_select.set_high().unwrap(); Ok(()) } fn read(&mut self) -> nb::Result { - // return and dereference $data - Ok(0) + Ok(self.peripheral.spdr.read().bits()) } } \ No newline at end of file From 1dd920a48fb0a7cf7bf6ee24c8f79b7f8610347f Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 6 Jul 2019 02:09:26 -0500 Subject: [PATCH 09/49] Refactored SPI code to be simpler, added doc comments --- chips/atmega328p-hal/src/spi.rs | 94 +++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index 0aac5557f7..b901e1057a 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -4,9 +4,6 @@ use embedded_hal as hal; use nb; use crate::atmega328p::SPI; -#[derive(Debug, Clone, Copy)] -pub enum SpiError { } - /// Oscillator Clock Frequency division options pub enum SerialClockRate { OscfOver2, @@ -18,21 +15,25 @@ pub enum SerialClockRate { OscfOver128, } +/// Order of data transmission, either MSB first or LSB first pub enum DataOrder { MostSignificantFirst, LeastSignificantFirst, } +/// Polarity of clock (rising edge is tick or falling edge) pub enum SerialClockPolarity { IdleHigh, IdleLow, } +/// Clock sampling phase (check at leading or trailing edge of signal) pub enum SerialClockPhase { SampleLeading, SampleTrailing, } +/// Settings to pass to Spi. Easiest way to initialize is with `Settings::default()` pub struct Settings { data_order: DataOrder, clock: SerialClockRate, @@ -40,42 +41,63 @@ pub struct Settings { clock_phase: SerialClockPhase, } -pub struct Spi where - SS: hal::digital::v2::OutputPin, +impl Default for Settings { + fn default() -> Settings { + Settings { + data_order: DataOrder::MostSignificantFirst, + clock: SerialClockRate::OscfOver4, + clock_polarity: SerialClockPolarity::IdleLow, + clock_phase: SerialClockPhase::SampleLeading, + } + } +} + +/// Behavior for a SPI interface. Stores the SPI peripheral along with a secondary-select pin and the settings +pub struct Spi where + SS: hal::digital::v2::OutputPin { peripheral: SPI, secondary_select: SS, settings: Settings, - // TODO add necessary properties } -impl Spi where - SS: hal::digital::v2::OutputPin, +/// General SPI methods for reading/wrigint +impl Spi where + SS: hal::digital::v2::OutputPin { - // TODO add settings arguments besides secondary select (optional?) - /// Initialize the SPI peripheral - pub fn new(peripheral: SPI, mut secondary_select: SS, settings: Settings) -> Spi { - // start by closing communication with secondary - secondary_select.set_high().unwrap(); - // TODO control, status, and register pins to struct - Spi { + /// Instantiate an Spi interface + pub fn new(peripheral: SPI, secondary_select: SS, settings: Settings) -> Result, OutputPinError> { + let mut instance = Spi { peripheral, secondary_select, settings, - } + }; + instance.disable_secondary()?; + Ok(instance) } -} -impl hal::spi::FullDuplex for Spi where - SS: hal::digital::v2::OutputPin, -{ - type Error = SpiError; + /// Write a byte to the data register and begin transmission + fn write(&self, byte: u8) { + self.peripheral.spdr.write(|w| w.bits(byte)); + } - /// Sets up control/status register before writing data to ensure settings are always correct - fn send(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - // open communication with secondary via secondary-select pin - self.secondary_select.set_low().unwrap(); + /// Enable the secondary by settings its pin to low + fn enable_secondary(&mut self) -> Result<(), OutputPinError> { + self.secondary_select.set_low() + } + /// Disable the secondary by settings its pin to high + fn disable_secondary(&mut self) -> Result<(), OutputPinError> { + self.secondary_select.set_high() + } + + /// Loop and keep checking that the SPI transmission is complete, returning when it has + 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| { w @@ -117,19 +139,25 @@ impl hal::spi::FullDuplex for Spi where SerialClockRate::OscfOver64 => w.spi2x().clear_bit(), SerialClockRate::OscfOver128 => w.spi2x().set_bit(), }); + } +} - // write byte to data register which triggers transmission - self.peripheral.spdr.write(|w| w.bits(byte)); - - // wait until transmission is complete - while self.peripheral.spsr.read().spif().bit_is_clear() { } - - // close communication with secondary via secondary-select pin +impl hal::spi::FullDuplex for Spi where + SS: hal::digital::v2::OutputPin +{ + type Error = OutputPinError; - self.secondary_select.set_high().unwrap(); + /// Sets up the device for transmission and sends the data + fn send(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + self.setup(); + self.enable_secondary()?; + self.write(byte); + self.block_until_transfer_complete(); + self.disable_secondary()?; Ok(()) } + /// Reads and returns the response in the data register fn read(&mut self) -> nb::Result { Ok(self.peripheral.spdr.read().bits()) } From 79f5b6813475374b41ba38ba753d53f7fc81bea1 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Thu, 11 Jul 2019 21:47:58 -0500 Subject: [PATCH 10/49] Made simple SPI proof-of-concept for Uno --- .../arduino-uno/examples/uno-spi-feedback.rs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 boards/arduino-uno/examples/uno-spi-feedback.rs diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs new file mode 100644 index 0000000000..e74d0d4bc3 --- /dev/null +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(proc_macro_hygiene)] +extern crate panic_halt; +use arduino_uno::prelude::*; +#[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, + ); + pins.d10.into_output(&mut pins.ddr);// POSI pin must be made an ouptput + pins.d11.into_output(&mut pins.ddr);// secondary select pin must be made an output + let mut serial = arduino_uno::Serial::new( + dp.USART0, + pins.d0, + pins.d1.into_output(&mut pins.ddr), + 57600, + ); + dp.SPI.spcr.write(|w| { + w.spie().clear_bit(); + w.spe().set_bit();// must enable SPI + w.dord().clear_bit(); + w.mstr().set_bit();// must set to primary mode + w.cpol().clear_bit(); + w.cpha().clear_bit(); + w.spr().val_0x00() + }); + dp.SPI.spsr.write(|w| w.spi2x().clear_bit()); + + loop { + dp.SPI.spdr.write(|w| w.bits(0b10101010)); + while dp.SPI.spsr.read().spif().bit_is_clear() {} + let read_data = dp.SPI.spdr.read().bits(); + + ufmt::uwriteln!(&mut serial, "data: {}\r", read_data).unwrap(); + delay.delay_ms(1000); + } +} + From 1b4f91687d78eb08e271d6e964fbba6e8d25d897 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 09:08:56 -0500 Subject: [PATCH 11/49] Moved POC for SPI code back into atmega328p SPI library, added example that uses the library --- .../arduino-uno/examples/uno-spi-feedback.rs | 28 ++++----- boards/arduino-uno/examples/uno-spi-poc.rs | 43 ++++++++++++++ boards/arduino-uno/src/lib.rs | 1 + chips/atmega328p-hal/src/spi.rs | 58 +++++++++---------- 4 files changed, 82 insertions(+), 48 deletions(-) create mode 100644 boards/arduino-uno/examples/uno-spi-poc.rs diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index e74d0d4bc3..d943c20006 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -3,6 +3,7 @@ #![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(); @@ -12,31 +13,26 @@ pub extern fn main() -> ! { dp.PORTC, dp.PORTD, ); - pins.d10.into_output(&mut pins.ddr);// POSI pin must be made an ouptput - pins.d11.into_output(&mut pins.ddr);// secondary select pin must be made an output let mut serial = arduino_uno::Serial::new( dp.USART0, pins.d0, pins.d1.into_output(&mut pins.ddr), 57600, ); - dp.SPI.spcr.write(|w| { - w.spie().clear_bit(); - w.spe().set_bit();// must enable SPI - w.dord().clear_bit(); - w.mstr().set_bit();// must set to primary mode - w.cpol().clear_bit(); - w.cpha().clear_bit(); - w.spr().val_0x00() - }); - dp.SPI.spsr.write(|w| w.spi2x().clear_bit()); + + pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode + 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(), + ); loop { - dp.SPI.spdr.write(|w| w.bits(0b10101010)); - while dp.SPI.spsr.read().spif().bit_is_clear() {} - let read_data = dp.SPI.spdr.read().bits(); + spi.send(0b00001111).unwrap(); + let data = spi.read().unwrap(); - ufmt::uwriteln!(&mut serial, "data: {}\r", read_data).unwrap(); + ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap(); delay.delay_ms(1000); } } diff --git a/boards/arduino-uno/examples/uno-spi-poc.rs b/boards/arduino-uno/examples/uno-spi-poc.rs new file mode 100644 index 0000000000..e74d0d4bc3 --- /dev/null +++ b/boards/arduino-uno/examples/uno-spi-poc.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(proc_macro_hygiene)] +extern crate panic_halt; +use arduino_uno::prelude::*; +#[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, + ); + pins.d10.into_output(&mut pins.ddr);// POSI pin must be made an ouptput + pins.d11.into_output(&mut pins.ddr);// secondary select pin must be made an output + let mut serial = arduino_uno::Serial::new( + dp.USART0, + pins.d0, + pins.d1.into_output(&mut pins.ddr), + 57600, + ); + dp.SPI.spcr.write(|w| { + w.spie().clear_bit(); + w.spe().set_bit();// must enable SPI + w.dord().clear_bit(); + w.mstr().set_bit();// must set to primary mode + w.cpol().clear_bit(); + w.cpha().clear_bit(); + w.spr().val_0x00() + }); + dp.SPI.spsr.write(|w| w.spi2x().clear_bit()); + + loop { + dp.SPI.spdr.write(|w| w.bits(0b10101010)); + while dp.SPI.spsr.read().spif().bit_is_clear() {} + let read_data = dp.SPI.spdr.read().bits(); + + ufmt::uwriteln!(&mut serial, "data: {}\r", read_data).unwrap(); + delay.delay_ms(1000); + } +} + diff --git a/boards/arduino-uno/src/lib.rs b/boards/arduino-uno/src/lib.rs index 2fb05e2a0e..e5b9b6b219 100644 --- a/boards/arduino-uno/src/lib.rs +++ b/boards/arduino-uno/src/lib.rs @@ -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; diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index b901e1057a..77d7ae02ce 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -3,6 +3,13 @@ use embedded_hal as hal; use nb; use crate::atmega328p::SPI; +use crate::port::{portb,mode}; + +type POSI = portb::PB3; +type PISO = portb::PB4>; + +#[derive(Debug, Clone, Copy)] +pub enum SpiError {} /// Oscillator Clock Frequency division options pub enum SerialClockRate { @@ -47,33 +54,33 @@ impl Default for Settings { data_order: DataOrder::MostSignificantFirst, clock: SerialClockRate::OscfOver4, clock_polarity: SerialClockPolarity::IdleLow, - clock_phase: SerialClockPhase::SampleLeading, + clock_phase: SerialClockPhase::SampleTrailing, } } } /// Behavior for a SPI interface. Stores the SPI peripheral along with a secondary-select pin and the settings -pub struct Spi where - SS: hal::digital::v2::OutputPin -{ +pub struct Spi { peripheral: SPI, - secondary_select: SS, + posi: POSI, + piso: PISO, settings: Settings, } -/// General SPI methods for reading/wrigint -impl Spi where - SS: hal::digital::v2::OutputPin -{ +/// General SPI methods for reading/wrighting +impl Spi { /// Instantiate an Spi interface - pub fn new(peripheral: SPI, secondary_select: SS, settings: Settings) -> Result, OutputPinError> { - let mut instance = Spi { + pub fn new(peripheral: SPI, posi: POSI, piso: PISO, settings: Settings) -> Spi { + Spi { peripheral, - secondary_select, + posi, + piso, settings, - }; - instance.disable_secondary()?; - Ok(instance) + } + } + + pub fn release(self) -> (SPI, POSI, PISO) { + (self.peripheral, self.posi, self.piso) } /// Write a byte to the data register and begin transmission @@ -81,16 +88,6 @@ impl Spi where self.peripheral.spdr.write(|w| w.bits(byte)); } - /// Enable the secondary by settings its pin to low - fn enable_secondary(&mut self) -> Result<(), OutputPinError> { - self.secondary_select.set_low() - } - - /// Disable the secondary by settings its pin to high - fn disable_secondary(&mut self) -> Result<(), OutputPinError> { - self.secondary_select.set_high() - } - /// Loop and keep checking that the SPI transmission is complete, returning when it has fn block_until_transfer_complete(&self) { while self.peripheral.spsr.read().spif().bit_is_clear() { } @@ -142,18 +139,14 @@ impl Spi where } } -impl hal::spi::FullDuplex for Spi where - SS: hal::digital::v2::OutputPin -{ - type Error = OutputPinError; +impl hal::spi::FullDuplex for Spi { + type Error = SpiError; /// Sets up the device for transmission and sends the data fn send(&mut self, byte: u8) -> nb::Result<(), Self::Error> { self.setup(); - self.enable_secondary()?; self.write(byte); self.block_until_transfer_complete(); - self.disable_secondary()?; Ok(()) } @@ -161,4 +154,5 @@ impl hal::spi::FullDuplex for Spi wh fn read(&mut self) -> nb::Result { Ok(self.peripheral.spdr.read().bits()) } -} \ No newline at end of file +} + From a796048b0df52d4ecaea1d5a31f0ba00de8b5345 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 09:09:37 -0500 Subject: [PATCH 12/49] Removed POC example --- boards/arduino-uno/examples/uno-spi-poc.rs | 43 ---------------------- 1 file changed, 43 deletions(-) delete mode 100644 boards/arduino-uno/examples/uno-spi-poc.rs diff --git a/boards/arduino-uno/examples/uno-spi-poc.rs b/boards/arduino-uno/examples/uno-spi-poc.rs deleted file mode 100644 index e74d0d4bc3..0000000000 --- a/boards/arduino-uno/examples/uno-spi-poc.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![no_std] -#![no_main] -#![feature(proc_macro_hygiene)] -extern crate panic_halt; -use arduino_uno::prelude::*; -#[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, - ); - pins.d10.into_output(&mut pins.ddr);// POSI pin must be made an ouptput - pins.d11.into_output(&mut pins.ddr);// secondary select pin must be made an output - let mut serial = arduino_uno::Serial::new( - dp.USART0, - pins.d0, - pins.d1.into_output(&mut pins.ddr), - 57600, - ); - dp.SPI.spcr.write(|w| { - w.spie().clear_bit(); - w.spe().set_bit();// must enable SPI - w.dord().clear_bit(); - w.mstr().set_bit();// must set to primary mode - w.cpol().clear_bit(); - w.cpha().clear_bit(); - w.spr().val_0x00() - }); - dp.SPI.spsr.write(|w| w.spi2x().clear_bit()); - - loop { - dp.SPI.spdr.write(|w| w.bits(0b10101010)); - while dp.SPI.spsr.read().spif().bit_is_clear() {} - let read_data = dp.SPI.spdr.read().bits(); - - ufmt::uwriteln!(&mut serial, "data: {}\r", read_data).unwrap(); - delay.delay_ms(1000); - } -} - From d2b922fe66150febfb56716992954f1b78847ab3 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 09:16:58 -0500 Subject: [PATCH 13/49] Added comments to SPI example --- boards/arduino-uno/examples/uno-spi-feedback.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index d943c20006..b3f96633f5 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -1,3 +1,15 @@ +//! 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 +//! ``` +//! +//! You should see it output the line `data: 15` repeatedly (aka 0b00001111) + #![no_std] #![no_main] #![feature(proc_macro_hygiene)] @@ -13,6 +25,7 @@ pub extern fn main() -> ! { dp.PORTC, dp.PORTD, ); + // set up serial interface for text output let mut serial = arduino_uno::Serial::new( dp.USART0, pins.d0, @@ -21,6 +34,7 @@ pub extern fn main() -> ! { ); 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), @@ -29,7 +43,9 @@ pub extern fn main() -> ! { ); 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(); From 021998b18c900571f224b6ff33d5cbdb7e4516f1 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 11:26:11 -0500 Subject: [PATCH 14/49] Added better documentation to the SPI library --- chips/atmega328p-hal/src/spi.rs | 63 ++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index 77d7ae02ce..a6d49f6a5d 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -1,4 +1,25 @@ - +//! Implementation of the Rust Embedded-HAL SPI FullDuplex trait for AVR. +//! +//! The interface can be instantiated with the `new` method, and used directly +//! or passed into a driver. Example usage: +//! +//! ``` +//! pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode +//! // create SPI interface +//! let mut spi = Spi::new( +//! dp.SPI,// SPI peripheral +//! pins.d11.into_output(&mut pins.ddr),// MOSI output pin +//! pins.d12.into_pull_up_input(&mut pins.ddr),// MISO input pin +//! Settings::default(), +//! ); +//! +//! // Send a byte +//! let sent = 0b10101010; +//! spi.send(sent).unwrap(); +//! let response = spi.read().unwrap(); +//! ``` +//! In the example above, all of the settings are left at the default. You can +//! also instantiate a Settings object with the other options available. use embedded_hal as hal; use nb; @@ -8,10 +29,12 @@ use crate::port::{portb,mode}; type POSI = portb::PB3; type PISO = portb::PB4>; +/// Error type emitted by Spi in the event of a critical failure. Errors have +/// no information attached. #[derive(Debug, Clone, Copy)] pub enum SpiError {} -/// Oscillator Clock Frequency division options +/// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits. pub enum SerialClockRate { OscfOver2, OscfOver4, @@ -28,7 +51,7 @@ pub enum DataOrder { LeastSignificantFirst, } -/// Polarity of clock (rising edge is tick or falling edge) +/// Polarity of clock (whether SCLK idles at low state or high state) pub enum SerialClockPolarity { IdleHigh, IdleLow, @@ -40,7 +63,11 @@ pub enum SerialClockPhase { SampleTrailing, } -/// Settings to pass to Spi. Easiest way to initialize is with `Settings::default()` +/// 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, @@ -59,7 +86,11 @@ impl Default for Settings { } } -/// Behavior for a SPI interface. Stores the SPI peripheral along with a secondary-select pin and the settings +/// 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. pub struct Spi { peripheral: SPI, posi: POSI, @@ -67,9 +98,9 @@ pub struct Spi { settings: Settings, } -/// General SPI methods for reading/wrighting +/// Implementation-specific behavior of the struct, including setup/tear-down impl Spi { - /// Instantiate an Spi interface + /// 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, @@ -79,16 +110,19 @@ impl Spi { } } + /// 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 and begin transmission + /// 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 and keep checking that the SPI transmission is complete, returning when it has + /// 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() { } } @@ -97,9 +131,10 @@ impl Spi { fn setup(&self) { // set up control register self.peripheral.spcr.write(|w| { - w - .spe().set_bit()// enable SPI - .mstr().set_bit();// set to primary mode + // 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(), @@ -139,6 +174,9 @@ impl Spi { } } +/// 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 hal::spi::FullDuplex for Spi { type Error = SpiError; @@ -155,4 +193,3 @@ impl hal::spi::FullDuplex for Spi { Ok(self.peripheral.spdr.read().bits()) } } - From 87bec23caea475b57654e4837a5ae8b4a06fa658 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 12:59:31 -0500 Subject: [PATCH 15/49] Moved SPI library into macro and invoked from atmega328p --- avr-hal-generic/src/lib.rs | 1 + avr-hal-generic/src/spi.rs | 265 +++++++++++++++++-------- chips/atmega328p-hal/src/spi.rs | 341 +++++++++++++++++--------------- 3 files changed, 364 insertions(+), 243 deletions(-) diff --git a/avr-hal-generic/src/lib.rs b/avr-hal-generic/src/lib.rs index 80078a0c78..2514fbe112 100644 --- a/avr-hal-generic/src/lib.rs +++ b/avr-hal-generic/src/lib.rs @@ -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 { diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index f6b1ccfce1..6c36380f18 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -1,80 +1,185 @@ -// //! SPI Implementation - -// /// SPI Error -// #[derive(Debug, Clone, Copy)] -// pub enum Error { } - -// /// Implement traits for a SPI interface -// #[macro_export] -// macro_rules! impl_spi { -// ( -// $(#[$spi_attr:meta])* -// pub struct $Spi:ident { -// peripheral: $SPI:ty, -// pins: { -// // Might not need references to these pins? Seems like r/w and clock are handled by hardware. -// clock: $CLOCK:ident, -// piso: $PISO:ident, -// posi: $POSI:ident, -// }, -// registers: { -// control: $control:ident, -// status: $status:ident, -// data: $data:ident, -// }, -// } -// ) => { -// $(#[$spi_attr])* -// pub struct $Spi { -// ss: $crate::hal::digital::v2::OutputPin, -// // TODO add necessary properties -// } - -// impl $Spi -// { -// // TODO add settings arguments besides secondary select (optional?) -// /// Initialize the SPI peripheral -// pub fn new(ss: $crate::hal::digital::v2::OutputPin) -> $Spi { -// // pull SS high -// // store secondary-select, control, status, and register pins to struct -// $Spi {} -// } -// } - -// impl $crate::hal::spi::FullDuplex for $Spi { -// type Error = $crate::spi::Error; - -// fn write(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> { -// // I think it would be best to set all control bits for every write. This way the user can have -// // multiple Spi instances that communicate with different secondaries with no problem, even if they -// // each have different settings. -// // make sure the entire control register is set in one instruction for efficiency -// // registers have modify/read/write/reset methods - -// // open communication with secondary via secondary-select pin -// self.ss.set_low(); - -// // pull SS (instance of embedded_hal::serial::v2::OutputPin) low -// // set SPIE (SPI enable) control bit to 1 -// // set MSTR (primary/secondary select) control bit to 1 - -// // set DORD (data order) control bit to user-defined setting (default 1) -// // set CPOL (clock polarity) control bit to user-defined setting (default 0) -// // set CPHA (clock phase) control bit to user-defined setting (default 0) -// // set SPR (clock speed) control bits to user-defined setting (default 3) -// // set SPIX2 (x2 clock speed) status bit to user-defined setting (default 0) - -// // set $data to byte - -// // close communication with secondary via secondary-select pin -// self.ss.set_high(); -// Ok(()) -// } - -// fn read(&mut self) -> $crate::nb::Result { -// // return and dereference $data -// Ok(0) -// } -// } -// }; -// } +//! 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 {} + +/// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits. +pub enum SerialClockRate { + OscfOver2, + OscfOver4, + OscfOver8, + OscfOver16, + OscfOver32, + OscfOver64, + OscfOver128, +} + +/// 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, +} + +/// 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; + type PISO = $PISO>; + + /// 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 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 { + Ok(self.peripheral.spdr.read().bits()) + } + } + }; +} diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index a6d49f6a5d..066d68d2ec 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -21,175 +21,190 @@ //! In the example above, all of the settings are left at the default. You can //! also instantiate a Settings object with the other options available. -use embedded_hal as hal; -use nb; -use crate::atmega328p::SPI; -use crate::port::{portb,mode}; - -type POSI = portb::PB3; -type PISO = portb::PB4>; - -/// Error type emitted by Spi in the event of a critical failure. Errors have -/// no information attached. -#[derive(Debug, Clone, Copy)] -pub enum SpiError {} - -/// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits. -pub enum SerialClockRate { - OscfOver2, - OscfOver4, - OscfOver8, - OscfOver16, - OscfOver32, - OscfOver64, - OscfOver128, -} - -/// Order of data transmission, either MSB first or LSB first -pub enum DataOrder { - MostSignificantFirst, - LeastSignificantFirst, -} +extern crate avr_hal_generic as avr_hal; -/// Polarity of clock (whether SCLK idles at low state or high state) -pub enum SerialClockPolarity { - IdleHigh, - IdleLow, -} +pub use avr_hal::spi::*; -/// Clock sampling phase (check at leading or trailing edge of signal) -pub enum SerialClockPhase { - SampleLeading, - SampleTrailing, -} +// use nb; +// use crate::atmega328p::SPI; +use crate::port::{portb::PB3,portb::PB4,mode}; -/// 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() -> Settings { - Settings { - data_order: DataOrder::MostSignificantFirst, - clock: SerialClockRate::OscfOver4, - clock_polarity: SerialClockPolarity::IdleLow, - clock_phase: SerialClockPhase::SampleTrailing, +avr_hal::impl_spi! { + pub struct Spi { + peripheral: crate::atmega328p::SPI, + pins: { + posi: PB3, + piso: PB4, } } } -/// 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. -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 hal::spi::FullDuplex for Spi { - type Error = SpiError; - - /// Sets up the device for transmission and sends the data - fn send(&mut self, byte: u8) -> 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) -> nb::Result { - Ok(self.peripheral.spdr.read().bits()) - } -} +// type POSI = portb::PB3; +// type PISO = portb::PB4>; + +// /// Error type emitted by Spi in the event of a critical failure. Errors have +// /// no information attached. +// #[derive(Debug, Clone, Copy)] +// pub enum SpiError {} + +// /// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits. +// pub enum SerialClockRate { +// OscfOver2, +// OscfOver4, +// OscfOver8, +// OscfOver16, +// OscfOver32, +// OscfOver64, +// OscfOver128, +// } + +// /// 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, +// } + +// /// 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() -> Settings { +// 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. +// 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 hal::spi::FullDuplex for Spi { +// type Error = SpiError; + +// /// Sets up the device for transmission and sends the data +// fn send(&mut self, byte: u8) -> 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) -> nb::Result { +// Ok(self.peripheral.spdr.read().bits()) +// } +// } From a09cf207f0062ebfa2d04399a7e5ecf181c41dc4 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 13:53:41 -0500 Subject: [PATCH 16/49] Removed commented-out manual implementation --- chips/atmega328p-hal/src/spi.rs | 169 -------------------------------- 1 file changed, 169 deletions(-) diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index 066d68d2ec..f0a5abde93 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -39,172 +39,3 @@ avr_hal::impl_spi! { } } } - - -// type POSI = portb::PB3; -// type PISO = portb::PB4>; - -// /// Error type emitted by Spi in the event of a critical failure. Errors have -// /// no information attached. -// #[derive(Debug, Clone, Copy)] -// pub enum SpiError {} - -// /// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits. -// pub enum SerialClockRate { -// OscfOver2, -// OscfOver4, -// OscfOver8, -// OscfOver16, -// OscfOver32, -// OscfOver64, -// OscfOver128, -// } - -// /// 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, -// } - -// /// 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() -> Settings { -// 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. -// 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 hal::spi::FullDuplex for Spi { -// type Error = SpiError; - -// /// Sets up the device for transmission and sends the data -// fn send(&mut self, byte: u8) -> 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) -> nb::Result { -// Ok(self.peripheral.spdr.read().bits()) -// } -// } From f160871c1489c129b1bcea15030b04b7047d2a6c Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 14:15:06 -0500 Subject: [PATCH 17/49] Added SPI support to ATMega32u4/Leonardo --- .../examples/leonardo-spi-feedback.rs | 56 +++++++++++++++++++ boards/arduino-leonardo/src/lib.rs | 1 + boards/arduino-leonardo/src/pins.rs | 5 ++ chips/atmega328p-hal/src/spi.rs | 3 - chips/atmega32u4-hal/src/lib.rs | 1 + chips/atmega32u4-hal/src/spi.rs | 38 +++++++++++++ 6 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 boards/arduino-leonardo/examples/leonardo-spi-feedback.rs create mode 100644 chips/atmega32u4-hal/src/spi.rs diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs new file mode 100644 index 0000000000..84b7301038 --- /dev/null +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -0,0 +1,56 @@ +//! 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 +//! ``` +//! +//! 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_leonardo::prelude::*; +use arduino_leonardo::spi::{Spi,Settings}; +#[no_mangle] +pub extern fn main() -> ! { + let dp = arduino_leonardo::Peripherals::take().unwrap(); + let mut delay = arduino_leonardo::Delay::new(); + let mut pins = arduino_leonardo::Pins::new( + dp.PORTB, + dp.PORTC, + dp.PORTD, + dp.PORTE, + ); + + let mut serial = arduino_leonardo::Serial::new( + dp.USART1, + 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.icsp10.into_output(&mut pins.ddr), + pins.icsp11.into_pull_up_input(&mut pins.ddr), + Settings::default(), + ); + + 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); + } +} + diff --git a/boards/arduino-leonardo/src/lib.rs b/boards/arduino-leonardo/src/lib.rs index 8b9d6e84ef..d150822191 100644 --- a/boards/arduino-leonardo/src/lib.rs +++ b/boards/arduino-leonardo/src/lib.rs @@ -7,6 +7,7 @@ mod pins; pub use atmega32u4_hal::atmega32u4; pub use crate::atmega32u4::Peripherals; pub use atmega32u4_hal::prelude; +pub use atmega32u4_hal::spi; pub use crate::pins::*; pub type Delay = hal::delay::Delay; diff --git a/boards/arduino-leonardo/src/pins.rs b/boards/arduino-leonardo/src/pins.rs index 543cabf530..9562d2d228 100644 --- a/boards/arduino-leonardo/src/pins.rs +++ b/boards/arduino-leonardo/src/pins.rs @@ -93,5 +93,10 @@ avr_hal_generic::impl_board_pins! { /// /// Led for indicating outbound data pub led_tx: portd::pd5::PD5, + + // ICSP MOSI pin + pub icsp10: portb::pb2::PB2, + // ICSP MISO pin + pub icsp11: portb::pb3::PB3, } } diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index f0a5abde93..9323cf0443 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -24,9 +24,6 @@ extern crate avr_hal_generic as avr_hal; pub use avr_hal::spi::*; - -// use nb; -// use crate::atmega328p::SPI; use crate::port::{portb::PB3,portb::PB4,mode}; diff --git a/chips/atmega32u4-hal/src/lib.rs b/chips/atmega32u4-hal/src/lib.rs index 16192a1c8f..63d7d175f1 100644 --- a/chips/atmega32u4-hal/src/lib.rs +++ b/chips/atmega32u4-hal/src/lib.rs @@ -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::*; diff --git a/chips/atmega32u4-hal/src/spi.rs b/chips/atmega32u4-hal/src/spi.rs new file mode 100644 index 0000000000..a5f3c33644 --- /dev/null +++ b/chips/atmega32u4-hal/src/spi.rs @@ -0,0 +1,38 @@ +//! Implementation of the Rust Embedded-HAL SPI FullDuplex trait for AVR. +//! +//! The interface can be instantiated with the `new` method, and used directly +//! or passed into a driver. Example usage: +//! +//! ``` +//! pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode +//! // create SPI interface +//! let mut spi = Spi::new( +//! dp.SPI,// SPI peripheral +//! pins.d11.into_output(&mut pins.ddr),// MOSI output pin +//! pins.d12.into_pull_up_input(&mut pins.ddr),// MISO input pin +//! Settings::default(), +//! ); +//! +//! // Send a byte +//! let sent = 0b10101010; +//! spi.send(sent).unwrap(); +//! let response = spi.read().unwrap(); +//! ``` +//! In the example above, all of the settings are left at the default. You can +//! also instantiate a Settings object with the other options available. + +extern crate avr_hal_generic as avr_hal; + +pub use avr_hal::spi::*; +use crate::port::{portb::PB2,portb::PB3,mode}; + + +avr_hal::impl_spi! { + pub struct Spi { + peripheral: crate::atmega32u4::SPI, + pins: { + posi: PB2, + piso: PB3, + } + } +} From b8bf6246e814d88a3d7ad2b5bf30cafc985c5a6b Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 14:16:37 -0500 Subject: [PATCH 18/49] Updated readme with status of SPI --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7e8ebf218b..855ca3dcbc 100644 --- a/README.md +++ b/README.md @@ -67,7 +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 +- [X] SPI primary-mode implementation ### HAL Status The chip-HAL crates currently support the following peripherals: @@ -76,11 +76,13 @@ The chip-HAL crates currently support the following peripherals: - [x] `PORTB`, `PORTC`, `PORTD` as digital IO (v2) - [x] `USART0` for serial communication - [x] I2C using `TWI` + - [x] SPI * [`atmega32u4-hal`](./chips/atmega32u4-hal) - [x] Spinning Delay - [x] `PORTB`, `PORTC`, `PORTD`, `PORTE`, `PORTF` as digital IO (v2) - [x] `USART1` for serial communication - [x] I2C using `TWI` + - [x] SPI * [`attiny85-hal`](./chips/attiny85-hal) - [x] Spinning Delay - [x] `PORTB` as digital IO (v2) @@ -90,10 +92,10 @@ In `boards/` there are crates for the following hardware. Please note that this * [Arduino Leonardo](./boards/arduino-leonardo) - [Website](https://www.arduino.cc/en/Main/Arduino_BoardLeonardo) - - Support for basic digital IO + - Support for basic digital IO and SPI * [Arduino Uno](./boards/arduino-uno) - [Website](https://store.arduino.cc/usa/arduino-uno-rev3) - - Support for basic digital IO + - Support for basic digital IO and SPI * [Adafruit Trinket (3V3 or 5V)](./boards/trinket) (**not** PRO!) - [Website](https://learn.adafruit.com/introducing-trinket) - Support for basic digital IO From 311e775f41c2a6c25ccf2632b85f7bfc5a018f7f Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 14:19:28 -0500 Subject: [PATCH 19/49] Fixed pin comments in examples --- .../arduino-leonardo/examples/leonardo-spi-feedback.rs | 9 +++++---- boards/arduino-uno/examples/uno-spi-feedback.rs | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index 84b7301038..1f8221bdf9 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -1,8 +1,9 @@ -//! 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`. +//! 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 ICSP pin 10 to ICSP pin 11. //! -//! Once this program is written to the board, the serial output can be accessed with +//! Once this program is written to the board, the serial output can be accessed +//! with //! //! ``` //! sudo screen /tty/ACM0 57600 diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index b3f96633f5..b424bd9859 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -1,8 +1,9 @@ -//! 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`. +//! 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 +//! Once this program is written to the board, the serial output can be +//! accessed with //! //! ``` //! sudo screen /tty/ACM0 57600 From b54001e1ad089056e70b5d1094f331d82dbdc9e0 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Fri, 12 Jul 2019 14:22:06 -0500 Subject: [PATCH 20/49] Removed changes to cargo that were necessary to implement SPI manually --- chips/atmega328p-hal/Cargo.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/chips/atmega328p-hal/Cargo.toml b/chips/atmega328p-hal/Cargo.toml index 1840dbe987..eb04dccc45 100644 --- a/chips/atmega328p-hal/Cargo.toml +++ b/chips/atmega328p-hal/Cargo.toml @@ -6,11 +6,6 @@ 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" From 787846d8c258bef83fd53b89d12473aca04a3e2f Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 20 Jul 2019 09:54:35 -0500 Subject: [PATCH 21/49] Added SCLK pin to SPI constructor to allow output mode enforcement, updated all deps and examples --- avr-hal-generic/src/spi.rs | 15 +++++++++++---- .../examples/leonardo-spi-feedback.rs | 3 ++- boards/arduino-leonardo/src/pins.rs | 19 +++++++++++++++---- .../arduino-uno/examples/uno-spi-feedback.rs | 1 + chips/atmega328p-hal/Cargo.toml | 1 - chips/atmega328p-hal/src/spi.rs | 4 +++- chips/atmega32u4-hal/src/spi.rs | 3 ++- 7 files changed, 34 insertions(+), 12 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 6c36380f18..dc8310cf73 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -42,11 +42,13 @@ macro_rules! impl_spi { pub struct $Spi:ident { peripheral: $SPI:ty, pins: { + sclk: $SCLK:ident, posi: $POSI:ident, piso: $PISO:ident, } } ) => { + type SCLK = $SCLK; type POSI = $POSI; type PISO = $PISO>; @@ -81,6 +83,7 @@ macro_rules! impl_spi { $(#[$spi_attr])* pub struct $Spi { peripheral: $SPI, + sclk: SCLK, posi: POSI, piso: PISO, settings: Settings, @@ -88,10 +91,14 @@ macro_rules! impl_spi { /// 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 { + /// Instantiate an SPI with the registers, SCLK/POSI/PISO pins, and settings + /// + /// The pins are not actually used directly, but they are accepted in order to enforce + /// that they are in the correct mode. + pub fn new(peripheral: $SPI, sclk: SCLK, posi: POSI, piso: PISO, settings: Settings) -> $Spi { Spi { peripheral, + sclk, posi, piso, settings, @@ -100,8 +107,8 @@ macro_rules! impl_spi { /// 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) + pub fn release(self) -> ($SPI, SCLK, POSI, PISO) { + (self.peripheral, self.sclk, self.posi, self.piso) } /// Write a byte to the data register, which begins transmission diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index 1f8221bdf9..fe5e33557b 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -35,10 +35,11 @@ pub extern fn main() -> ! { 57600, ); - pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode + pins.cs.into_output(&mut pins.ddr);// SS must be set to output mode // create SPI interface let mut spi = Spi::new( dp.SPI, + pins.icsp9.into_output(&mut pins.ddr), pins.icsp10.into_output(&mut pins.ddr), pins.icsp11.into_pull_up_input(&mut pins.ddr), Settings::default(), diff --git a/boards/arduino-leonardo/src/pins.rs b/boards/arduino-leonardo/src/pins.rs index 9562d2d228..ca65696785 100644 --- a/boards/arduino-leonardo/src/pins.rs +++ b/boards/arduino-leonardo/src/pins.rs @@ -87,16 +87,27 @@ avr_hal_generic::impl_board_pins! { pub d13: portc::pc7::PC7, /// `RX` /// - /// Led for indicating inbound data + /// Led for indicating inbound data. Alias of CS pin. pub led_rx: portb::pb0::PB0, /// `TX` /// /// Led for indicating outbound data pub led_tx: portd::pd5::PD5, - - // ICSP MOSI pin + /// `CS` + /// + /// Chip-select pin. Alias + pub cs: portb::pb0::PB0, + /// `SCLK` + /// + /// ICSP SCLK pin + pub icsp9: portb::pb1::PB1, + /// `MOSI` + /// + /// ICSP MOSI pin pub icsp10: portb::pb2::PB2, - // ICSP MISO pin + /// `MISO` + /// + /// ICSP MISO pin pub icsp11: portb::pb3::PB3, } } diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index b424bd9859..0ab78220eb 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -38,6 +38,7 @@ pub extern fn main() -> ! { // create SPI interface let mut spi = Spi::new( dp.SPI, + pins.d13.into_output(&mut pins.ddr), pins.d11.into_output(&mut pins.ddr), pins.d12.into_pull_up_input(&mut pins.ddr), Settings::default(), diff --git a/chips/atmega328p-hal/Cargo.toml b/chips/atmega328p-hal/Cargo.toml index eb04dccc45..a6fb914da4 100644 --- a/chips/atmega328p-hal/Cargo.toml +++ b/chips/atmega328p-hal/Cargo.toml @@ -10,4 +10,3 @@ avr-hal-generic = { path = "../../avr-hal-generic/" } [dependencies.avr-device] git = "https://github.com/Rahix/avr-device" features = ["atmega328p"] - diff --git a/chips/atmega328p-hal/src/spi.rs b/chips/atmega328p-hal/src/spi.rs index 9323cf0443..194915ba06 100644 --- a/chips/atmega328p-hal/src/spi.rs +++ b/chips/atmega328p-hal/src/spi.rs @@ -8,6 +8,7 @@ //! // create SPI interface //! let mut spi = Spi::new( //! dp.SPI,// SPI peripheral +//! pins.d13.into_output(&mut pins.ddr),// SCLK //! pins.d11.into_output(&mut pins.ddr),// MOSI output pin //! pins.d12.into_pull_up_input(&mut pins.ddr),// MISO input pin //! Settings::default(), @@ -24,13 +25,14 @@ extern crate avr_hal_generic as avr_hal; pub use avr_hal::spi::*; -use crate::port::{portb::PB3,portb::PB4,mode}; +use crate::port::{portb::PB3,portb::PB4,portb::PB5,mode}; avr_hal::impl_spi! { pub struct Spi { peripheral: crate::atmega328p::SPI, pins: { + sclk: PB5, posi: PB3, piso: PB4, } diff --git a/chips/atmega32u4-hal/src/spi.rs b/chips/atmega32u4-hal/src/spi.rs index a5f3c33644..ebff3a8852 100644 --- a/chips/atmega32u4-hal/src/spi.rs +++ b/chips/atmega32u4-hal/src/spi.rs @@ -24,13 +24,14 @@ extern crate avr_hal_generic as avr_hal; pub use avr_hal::spi::*; -use crate::port::{portb::PB2,portb::PB3,mode}; +use crate::port::{portb::PB1,portb::PB2,portb::PB3,mode}; avr_hal::impl_spi! { pub struct Spi { peripheral: crate::atmega32u4::SPI, pins: { + sclk: PB1, posi: PB2, piso: PB3, } From 1ce5a7e669e42c119efc0444a23914d0d2585c1f Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 20 Jul 2019 09:55:12 -0500 Subject: [PATCH 22/49] Made SPI settings public so they can be changed by user --- avr-hal-generic/src/spi.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index dc8310cf73..ce5efd8e31 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -58,10 +58,10 @@ macro_rules! impl_spi { /// `Settings::default()`. Otherwise can be instantiated with alternate /// settings directly. pub struct Settings { - data_order: DataOrder, - clock: SerialClockRate, - clock_polarity: SerialClockPolarity, - clock_phase: SerialClockPhase, + pub data_order: DataOrder, + pub clock: SerialClockRate, + pub clock_polarity: SerialClockPolarity, + pub clock_phase: SerialClockPhase, } impl Default for Settings { From 7bfcdcc9186561463450b71fc506b652bc3228eb Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 20 Jul 2019 09:55:59 -0500 Subject: [PATCH 23/49] Since SPI takes ownership of pins and so cannot share, removed redundent setup calls --- avr-hal-generic/src/spi.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index ce5efd8e31..65e5f8b894 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -96,13 +96,15 @@ macro_rules! impl_spi { /// The pins are not actually used directly, but they are accepted in order to enforce /// that they are in the correct mode. pub fn new(peripheral: $SPI, sclk: SCLK, posi: POSI, piso: PISO, settings: Settings) -> $Spi { - Spi { + let spi = Spi { peripheral, sclk, posi, piso, settings, - } + }; + spi.setup(); + spi } /// Release ownership of the peripheral and pins. Instance can no-longer @@ -177,7 +179,6 @@ macro_rules! impl_spi { /// 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(()) From bd13c70417da2abbb1d67fd59a2cb039c5fbe3d3 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Tue, 22 Oct 2019 23:57:54 -0500 Subject: [PATCH 24/49] Wrapped newly unsafe function in an unsafe block --- avr-hal-generic/src/spi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 65e5f8b894..de8440a742 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -116,7 +116,7 @@ macro_rules! impl_spi { /// Write a byte to the data register, which begins transmission /// automatically fn write(&self, byte: u8) { - self.peripheral.spdr.write(|w| w.bits(byte)); + self.peripheral.spdr.write(|w| unsafe { w.bits(byte) }); } /// Loop forever, checking the transmission complete bit until it is set From 5233da40176be76438f83429ec06d9a5418f6a9b Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sun, 2 Feb 2020 17:10:11 -0800 Subject: [PATCH 25/49] Moved SPI chip implementations to lib file, maintaining the same module location --- avr-hal-generic/src/spi.rs | 12 +++++----- chips/atmega328p-hal/src/lib.rs | 41 ++++++++++++++++++++++++++++++++- chips/atmega32u4-hal/src/lib.rs | 40 +++++++++++++++++++++++++++++++- chips/atmega32u4-hal/src/spi.rs | 39 ------------------------------- 4 files changed, 85 insertions(+), 47 deletions(-) delete mode 100644 chips/atmega32u4-hal/src/spi.rs diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index de8440a742..7a0f9b60b0 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -42,15 +42,15 @@ macro_rules! impl_spi { pub struct $Spi:ident { peripheral: $SPI:ty, pins: { - sclk: $SCLK:ident, - posi: $POSI:ident, - piso: $PISO:ident, + sclk: $sclkmod:ident::$SCLK:ident, + posi: $posimod:ident::$POSI:ident, + piso: $pisomod:ident::$PISO:ident, } } ) => { - type SCLK = $SCLK; - type POSI = $POSI; - type PISO = $PISO>; + type SCLK = $sclkmod::$SCLK<$crate::port::mode::Output>; + type POSI = $posimod::$POSI<$crate::port::mode::Output>; + type PISO = $pisomod::$PISO<$crate::port::mode::Input<$crate::port::mode::PullUp>>; /// Settings to pass to Spi. /// diff --git a/chips/atmega328p-hal/src/lib.rs b/chips/atmega328p-hal/src/lib.rs index 6e67097acd..0c87958752 100644 --- a/chips/atmega328p-hal/src/lib.rs +++ b/chips/atmega328p-hal/src/lib.rs @@ -8,7 +8,6 @@ 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::*; @@ -47,6 +46,46 @@ pub mod i2c { } } +pub mod spi { + //! Implementation of the Rust Embedded-HAL SPI FullDuplex trait for AVR. + //! + //! The interface can be instantiated with the `new` method, and used directly + //! or passed into a driver. Example usage: + //! + //! ``` + //! pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode + //! // create SPI interface + //! let mut spi = Spi::new( + //! dp.SPI,// SPI peripheral + //! pins.d13.into_output(&mut pins.ddr),// SCLK + //! pins.d11.into_output(&mut pins.ddr),// MOSI output pin + //! pins.d12.into_pull_up_input(&mut pins.ddr),// MISO input pin + //! Settings::default(), + //! ); + //! + //! // Send a byte + //! let sent = 0b10101010; + //! spi.send(sent).unwrap(); + //! let response = spi.read().unwrap(); + //! ``` + //! In the example above, all of the settings are left at the default. You can + //! also instantiate a Settings object with the other options available. + + use crate::port::portb; + pub use avr_hal::spi::*; + + avr_hal::impl_spi! { + pub struct Spi { + peripheral: crate::atmega328p::SPI, + pins: { + sclk: portb::PB5, + posi: portb::PB3, + piso: portb::PB4, + } + } + } +} + /// Serial interface using USART pub mod usart { use crate::port::portd; diff --git a/chips/atmega32u4-hal/src/lib.rs b/chips/atmega32u4-hal/src/lib.rs index 63d7d175f1..a0b61292be 100644 --- a/chips/atmega32u4-hal/src/lib.rs +++ b/chips/atmega32u4-hal/src/lib.rs @@ -8,7 +8,6 @@ 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::*; @@ -47,6 +46,45 @@ pub mod i2c { } } +pub mod spi { + //! Implementation of the Rust Embedded-HAL SPI FullDuplex trait for AVR. + //! + //! The interface can be instantiated with the `new` method, and used directly + //! or passed into a driver. Example usage: + //! + //! ``` + //! pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode + //! // create SPI interface + //! let mut spi = Spi::new( + //! dp.SPI,// SPI peripheral + //! pins.d11.into_output(&mut pins.ddr),// MOSI output pin + //! pins.d12.into_pull_up_input(&mut pins.ddr),// MISO input pin + //! Settings::default(), + //! ); + //! + //! // Send a byte + //! let sent = 0b10101010; + //! spi.send(sent).unwrap(); + //! let response = spi.read().unwrap(); + //! ``` + //! In the example above, all of the settings are left at the default. You can + //! also instantiate a Settings object with the other options available. + + pub use avr_hal::spi::*; + use crate::port::portb; + + avr_hal::impl_spi! { + pub struct Spi { + peripheral: crate::atmega32u4::SPI, + pins: { + sclk: portb::PB1, + posi: portb::PB2, + piso: portb::PB3, + } + } + } +} + /// Serial interface using USART pub mod usart { use crate::port::portd; diff --git a/chips/atmega32u4-hal/src/spi.rs b/chips/atmega32u4-hal/src/spi.rs deleted file mode 100644 index ebff3a8852..0000000000 --- a/chips/atmega32u4-hal/src/spi.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Implementation of the Rust Embedded-HAL SPI FullDuplex trait for AVR. -//! -//! The interface can be instantiated with the `new` method, and used directly -//! or passed into a driver. Example usage: -//! -//! ``` -//! pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode -//! // create SPI interface -//! let mut spi = Spi::new( -//! dp.SPI,// SPI peripheral -//! pins.d11.into_output(&mut pins.ddr),// MOSI output pin -//! pins.d12.into_pull_up_input(&mut pins.ddr),// MISO input pin -//! Settings::default(), -//! ); -//! -//! // Send a byte -//! let sent = 0b10101010; -//! spi.send(sent).unwrap(); -//! let response = spi.read().unwrap(); -//! ``` -//! In the example above, all of the settings are left at the default. You can -//! also instantiate a Settings object with the other options available. - -extern crate avr_hal_generic as avr_hal; - -pub use avr_hal::spi::*; -use crate::port::{portb::PB1,portb::PB2,portb::PB3,mode}; - - -avr_hal::impl_spi! { - pub struct Spi { - peripheral: crate::atmega32u4::SPI, - pins: { - sclk: PB1, - posi: PB2, - piso: PB3, - } - } -} From 9c3bdcc481e2fe67a7fbd5cbf1fecb9168be4f32 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sun, 2 Feb 2020 17:14:23 -0800 Subject: [PATCH 26/49] Removed alias, value cannot be moved twice --- boards/arduino-leonardo/src/pins.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/boards/arduino-leonardo/src/pins.rs b/boards/arduino-leonardo/src/pins.rs index ca65696785..6b38cd4448 100644 --- a/boards/arduino-leonardo/src/pins.rs +++ b/boards/arduino-leonardo/src/pins.rs @@ -87,16 +87,12 @@ avr_hal_generic::impl_board_pins! { pub d13: portc::pc7::PC7, /// `RX` /// - /// Led for indicating inbound data. Alias of CS pin. + /// Led for indicating inbound data. Also the CS pin. pub led_rx: portb::pb0::PB0, /// `TX` /// /// Led for indicating outbound data pub led_tx: portd::pd5::PD5, - /// `CS` - /// - /// Chip-select pin. Alias - pub cs: portb::pb0::PB0, /// `SCLK` /// /// ICSP SCLK pin From 864f42f27568059a7e766e03e74206cb98952eaf Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sun, 2 Feb 2020 17:23:20 -0800 Subject: [PATCH 27/49] Replaced empty SpiError enum with void::Void --- avr-hal-generic/src/spi.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 7a0f9b60b0..b969ff3cec 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -48,6 +48,9 @@ macro_rules! impl_spi { } } ) => { + + use $crate::void::Void; + type SCLK = $sclkmod::$SCLK<$crate::port::mode::Output>; type POSI = $posimod::$POSI<$crate::port::mode::Output>; type PISO = $pisomod::$PISO<$crate::port::mode::Input<$crate::port::mode::PullUp>>; @@ -175,7 +178,7 @@ macro_rules! impl_spi { /// drivers that require it for operation. Only 8-bit word size is supported /// for now. impl $crate::hal::spi::FullDuplex for $Spi { - type Error = $crate::spi::SpiError; + type Error = $crate::void::Void; /// Sets up the device for transmission and sends the data fn send(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> { From 103783c53f26063a26e9aed31419eb9333d3b7b1 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sun, 2 Feb 2020 17:49:02 -0800 Subject: [PATCH 28/49] Replaced bespoke polarity/phase enums with Embedded HAL's standard equivalents --- avr-hal-generic/src/spi.rs | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index b969ff3cec..ea2b5c4aa5 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -22,18 +22,6 @@ pub enum DataOrder { 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, -} - /// Implement traits for a SPI interface #[macro_export] macro_rules! impl_spi { @@ -50,6 +38,7 @@ macro_rules! impl_spi { ) => { use $crate::void::Void; + use $crate::hal::spi; type SCLK = $sclkmod::$SCLK<$crate::port::mode::Output>; type POSI = $posimod::$POSI<$crate::port::mode::Output>; @@ -63,8 +52,7 @@ macro_rules! impl_spi { pub struct Settings { pub data_order: DataOrder, pub clock: SerialClockRate, - pub clock_polarity: SerialClockPolarity, - pub clock_phase: SerialClockPhase, + pub mode: spi::Mode, } impl Default for Settings { @@ -72,8 +60,7 @@ macro_rules! impl_spi { Settings { data_order: DataOrder::MostSignificantFirst, clock: SerialClockRate::OscfOver4, - clock_polarity: SerialClockPolarity::IdleLow, - clock_phase: SerialClockPhase::SampleTrailing, + mode: spi::Mode { polarity: spi::Polarity::IdleLow, phase: spi::Phase::CaptureOnSecondTransition }, } } } @@ -141,14 +128,14 @@ macro_rules! impl_spi { 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(), + match self.settings.mode.polarity { + spi::Polarity::IdleHigh => w.cpol().set_bit(), + spi::Polarity::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(), + match self.settings.mode.phase { + spi::Phase::CaptureOnFirstTransition => w.cpha().clear_bit(), + spi::Phase::CaptureOnSecondTransition => w.cpha().set_bit(), }; // set up clock rate control bit match self.settings.clock { From 854ca0a6e6d943c16a3f98786adc6d83218ddf85 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sun, 2 Feb 2020 17:56:56 -0800 Subject: [PATCH 29/49] Made release() SPI method disable device before releasing peripheral ownership --- avr-hal-generic/src/spi.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index ea2b5c4aa5..f2f01ea949 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -97,9 +97,13 @@ macro_rules! impl_spi { spi } - /// Release ownership of the peripheral and pins. Instance can no-longer - /// be used after this is invoked. + /// Disable the SPI device and release ownership of the peripheral + /// and pins. Instance can no-longer be used after this is + /// invoked. pub fn release(self) -> ($SPI, SCLK, POSI, PISO) { + self.peripheral.spcr.write(|w| { + w.spe().clear_bit() + }); (self.peripheral, self.sclk, self.posi, self.piso) } From bcc913fd4b8dd3911330a4bc5ed76d65fb84f80d Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sun, 2 Feb 2020 18:01:51 -0800 Subject: [PATCH 30/49] Renamed piso/posi to miso/mosi to maintain consistency with external projects --- avr-hal-generic/src/spi.rs | 26 +++++++++---------- .../examples/leonardo-spi-feedback.rs | 2 +- .../arduino-uno/examples/uno-spi-feedback.rs | 2 +- chips/atmega328p-hal/src/lib.rs | 4 +-- chips/atmega32u4-hal/src/lib.rs | 4 +-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index f2f01ea949..2de2e97a13 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -31,8 +31,8 @@ macro_rules! impl_spi { peripheral: $SPI:ty, pins: { sclk: $sclkmod:ident::$SCLK:ident, - posi: $posimod:ident::$POSI:ident, - piso: $pisomod:ident::$PISO:ident, + mosi: $mosimod:ident::$MOSI:ident, + miso: $misomod:ident::$MISO:ident, } } ) => { @@ -41,8 +41,8 @@ macro_rules! impl_spi { use $crate::hal::spi; type SCLK = $sclkmod::$SCLK<$crate::port::mode::Output>; - type POSI = $posimod::$POSI<$crate::port::mode::Output>; - type PISO = $pisomod::$PISO<$crate::port::mode::Input<$crate::port::mode::PullUp>>; + type MOSI = $mosimod::$MOSI<$crate::port::mode::Output>; + type MISO = $misomod::$MISO<$crate::port::mode::Input<$crate::port::mode::PullUp>>; /// Settings to pass to Spi. /// @@ -68,29 +68,29 @@ macro_rules! impl_spi { /// 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. + /// ownership of the MOSI and MISO pins to ensure they are in the correct mode. /// Instantiate with the `new` method. $(#[$spi_attr])* pub struct $Spi { peripheral: $SPI, sclk: SCLK, - posi: POSI, - piso: PISO, + mosi: MOSI, + miso: MISO, settings: Settings, } /// Implementation-specific behavior of the struct, including setup/tear-down impl $Spi { - /// Instantiate an SPI with the registers, SCLK/POSI/PISO pins, and settings + /// Instantiate an SPI with the registers, SCLK/MOSI/MISO pins, and settings /// /// The pins are not actually used directly, but they are accepted in order to enforce /// that they are in the correct mode. - pub fn new(peripheral: $SPI, sclk: SCLK, posi: POSI, piso: PISO, settings: Settings) -> $Spi { + pub fn new(peripheral: $SPI, sclk: SCLK, mosi: MOSI, miso: MISO, settings: Settings) -> $Spi { let spi = Spi { peripheral, sclk, - posi, - piso, + mosi, + miso, settings, }; spi.setup(); @@ -100,11 +100,11 @@ macro_rules! impl_spi { /// Disable the SPI device and release ownership of the peripheral /// and pins. Instance can no-longer be used after this is /// invoked. - pub fn release(self) -> ($SPI, SCLK, POSI, PISO) { + pub fn release(self) -> ($SPI, SCLK, MOSI, MISO) { self.peripheral.spcr.write(|w| { w.spe().clear_bit() }); - (self.peripheral, self.sclk, self.posi, self.piso) + (self.peripheral, self.sclk, self.mosi, self.miso) } /// Write a byte to the data register, which begins transmission diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index fe5e33557b..72cd9514b7 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -48,7 +48,7 @@ pub extern fn main() -> ! { loop { // Send a byte spi.send(0b00001111).unwrap(); - // Because PISO is connected to POSI, the read data should be the same + // Because MISO is connected to MOSI, the read data should be the same let data = spi.read().unwrap(); ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap(); diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index 0ab78220eb..1342653fe6 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -47,7 +47,7 @@ pub extern fn main() -> ! { loop { // Send a byte spi.send(0b00001111).unwrap(); - // Because PISO is connected to POSI, the read data should be the same + // Because MISO is connected to MOSI, the read data should be the same let data = spi.read().unwrap(); ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap(); diff --git a/chips/atmega328p-hal/src/lib.rs b/chips/atmega328p-hal/src/lib.rs index 0c87958752..85a681cf93 100644 --- a/chips/atmega328p-hal/src/lib.rs +++ b/chips/atmega328p-hal/src/lib.rs @@ -79,8 +79,8 @@ pub mod spi { peripheral: crate::atmega328p::SPI, pins: { sclk: portb::PB5, - posi: portb::PB3, - piso: portb::PB4, + mosi: portb::PB3, + miso: portb::PB4, } } } diff --git a/chips/atmega32u4-hal/src/lib.rs b/chips/atmega32u4-hal/src/lib.rs index a0b61292be..46710eddff 100644 --- a/chips/atmega32u4-hal/src/lib.rs +++ b/chips/atmega32u4-hal/src/lib.rs @@ -78,8 +78,8 @@ pub mod spi { peripheral: crate::atmega32u4::SPI, pins: { sclk: portb::PB1, - posi: portb::PB2, - piso: portb::PB3, + mosi: portb::PB2, + miso: portb::PB3, } } } From 4101196500c95a2c2217ede494094bc840edb28d Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist <59749951+jonahd-google@users.noreply.github.com> Date: Mon, 3 Feb 2020 14:38:45 -0800 Subject: [PATCH 31/49] Removed unused SpiError enum --- avr-hal-generic/src/spi.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 2de2e97a13..fd24af946a 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -1,10 +1,5 @@ //! 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 {} - /// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits. pub enum SerialClockRate { OscfOver2, From ecea7044ebf90ec0957d622f4426a9ff9e7e58b4 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Wed, 5 Feb 2020 19:05:00 -0800 Subject: [PATCH 32/49] Imporoved wording of SPI constructor docs --- avr-hal-generic/src/spi.rs | 5 +++-- chips/atmega328p-hal/Cargo.toml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 2de2e97a13..4b12e122ac 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -83,8 +83,9 @@ macro_rules! impl_spi { impl $Spi { /// Instantiate an SPI with the registers, SCLK/MOSI/MISO pins, and settings /// - /// The pins are not actually used directly, but they are accepted in order to enforce - /// that they are in the correct mode. + /// The pins are not actually used directly, but they are moved into the struct in + /// order to enforce that they are in the correct mode, and cannot be used by anyone + /// else while SPI is active. pub fn new(peripheral: $SPI, sclk: SCLK, mosi: MOSI, miso: MISO, settings: Settings) -> $Spi { let spi = Spi { peripheral, diff --git a/chips/atmega328p-hal/Cargo.toml b/chips/atmega328p-hal/Cargo.toml index a6fb914da4..eb04dccc45 100644 --- a/chips/atmega328p-hal/Cargo.toml +++ b/chips/atmega328p-hal/Cargo.toml @@ -10,3 +10,4 @@ avr-hal-generic = { path = "../../avr-hal-generic/" } [dependencies.avr-device] git = "https://github.com/Rahix/avr-device" features = ["atmega328p"] + From f4cd0ed5051ceecbef4a141b3711cd295fe16293 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Wed, 5 Feb 2020 19:09:57 -0800 Subject: [PATCH 33/49] Renamed ICSP pins on Leonardo --- boards/arduino-leonardo/src/pins.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/boards/arduino-leonardo/src/pins.rs b/boards/arduino-leonardo/src/pins.rs index 6b38cd4448..b6b0a4b766 100644 --- a/boards/arduino-leonardo/src/pins.rs +++ b/boards/arduino-leonardo/src/pins.rs @@ -96,14 +96,14 @@ avr_hal_generic::impl_board_pins! { /// `SCLK` /// /// ICSP SCLK pin - pub icsp9: portb::pb1::PB1, + pub sck: portb::pb1::PB1, /// `MOSI` /// /// ICSP MOSI pin - pub icsp10: portb::pb2::PB2, + pub mosi: portb::pb2::PB2, /// `MISO` /// /// ICSP MISO pin - pub icsp11: portb::pb3::PB3, + pub miso: portb::pb3::PB3, } } From e2d46cbe57b19e37ece1f3fe054ce98f875679e9 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Wed, 5 Feb 2020 19:19:30 -0800 Subject: [PATCH 34/49] Moved Settings outside of macro --- avr-hal-generic/src/spi.rs | 45 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 4b12e122ac..ac0c290860 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -1,5 +1,7 @@ //! SPI Implementation +pub use embedded_hal::spi; + /// Error type emitted by Spi in the event of a critical failure. Errors have /// no information attached. #[derive(Debug, Clone, Copy)] @@ -22,6 +24,27 @@ pub enum DataOrder { LeastSignificantFirst, } +/// Settings to pass to Spi. +/// +/// Easiest way to initialize is with +/// `Settings::default()`. Otherwise can be instantiated with alternate +/// settings directly. +pub struct Settings { + pub data_order: DataOrder, + pub clock: SerialClockRate, + pub mode: spi::Mode, +} + +impl Default for Settings { + fn default() -> Self { + Settings { + data_order: DataOrder::MostSignificantFirst, + clock: SerialClockRate::OscfOver4, + mode: spi::Mode { polarity: spi::Polarity::IdleLow, phase: spi::Phase::CaptureOnSecondTransition }, + } + } +} + /// Implement traits for a SPI interface #[macro_export] macro_rules! impl_spi { @@ -39,32 +62,12 @@ macro_rules! impl_spi { use $crate::void::Void; use $crate::hal::spi; + pub use avr_hal::spi::*; type SCLK = $sclkmod::$SCLK<$crate::port::mode::Output>; type MOSI = $mosimod::$MOSI<$crate::port::mode::Output>; type MISO = $misomod::$MISO<$crate::port::mode::Input<$crate::port::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 { - pub data_order: DataOrder, - pub clock: SerialClockRate, - pub mode: spi::Mode, - } - - impl Default for Settings { - fn default() -> Self { - Settings { - data_order: DataOrder::MostSignificantFirst, - clock: SerialClockRate::OscfOver4, - mode: spi::Mode { polarity: spi::Polarity::IdleLow, phase: spi::Phase::CaptureOnSecondTransition }, - } - } - } - /// Behavior for a SPI interface. /// /// Stores the SPI peripheral for register access. In addition, it takes From 74c11d0485304c9a40b79c7cf3a010724ddb7524 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Wed, 5 Feb 2020 19:20:44 -0800 Subject: [PATCH 35/49] Fixed docs for SPI example --- boards/arduino-uno/examples/uno-spi-feedback.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index 1342653fe6..7cf3aea4c2 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -6,7 +6,7 @@ //! accessed with //! //! ``` -//! sudo screen /tty/ACM0 57600 +//! sudo screen /tty/ttyACM0 57600 //! ``` //! //! You should see it output the line `data: 15` repeatedly (aka 0b00001111) From 2fcd7a8e5c3fdffd6cad84eb3598ef23a3cde3cd Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Wed, 5 Feb 2020 19:21:13 -0800 Subject: [PATCH 36/49] Removed unused SpiError enum --- avr-hal-generic/src/spi.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index ac0c290860..d7fdb2f9fc 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -2,11 +2,6 @@ pub use embedded_hal::spi; -/// Error type emitted by Spi in the event of a critical failure. Errors have -/// no information attached. -#[derive(Debug, Clone, Copy)] -pub enum SpiError {} - /// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits. pub enum SerialClockRate { OscfOver2, From 1af06a6a1ce37c699fc010ca0031f9a4712d0ff8 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Wed, 5 Feb 2020 19:22:52 -0800 Subject: [PATCH 37/49] Removed mention of USB since Leonardo has no USB --- .../examples/leonardo-spi-feedback.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index 72cd9514b7..a2e98a0081 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -2,14 +2,9 @@ //! over it. The physical hardware configuation consists of connecting a //! jumper directly from ICSP pin 10 to ICSP pin 11. //! -//! Once this program is written to the board, the serial output can be accessed -//! with -//! -//! ``` -//! sudo screen /tty/ACM0 57600 -//! ``` -//! -//! You should see it output the line `data: 15` repeatedly (aka 0b00001111) +//! Once this program is written to the board, you can use the board's serial +//! connection to see the output. You should see it output the line +//! `data: 15` repeatedly (aka 0b00001111) #![no_std] #![no_main] From 9af129ce9c5c420318d893a4719b326bb3d14f29 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Wed, 5 Feb 2020 20:20:49 -0800 Subject: [PATCH 38/49] Fixed path to device in Uno SPI example --- boards/arduino-uno/examples/uno-spi-feedback.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index 7cf3aea4c2..12120390bf 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -6,7 +6,7 @@ //! accessed with //! //! ``` -//! sudo screen /tty/ttyACM0 57600 +//! sudo screen /dev/ttyACM0 57600 //! ``` //! //! You should see it output the line `data: 15` repeatedly (aka 0b00001111) From beab6671b11308cc000161dff684f62ab2a65517 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 11 Apr 2020 11:14:03 -0700 Subject: [PATCH 39/49] Fixed name of pins in Leonardo SPI example --- boards/arduino-leonardo/examples/leonardo-spi-feedback.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index a2e98a0081..118e8e2f83 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -30,13 +30,13 @@ pub extern fn main() -> ! { 57600, ); - pins.cs.into_output(&mut pins.ddr);// SS must be set to output mode + pins.led_rx.into_output(&mut pins.ddr);// SS must be set to output mode // create SPI interface let mut spi = Spi::new( dp.SPI, - pins.icsp9.into_output(&mut pins.ddr), - pins.icsp10.into_output(&mut pins.ddr), - pins.icsp11.into_pull_up_input(&mut pins.ddr), + pins.sck.into_output(&mut pins.ddr), + pins.mosi.into_output(&mut pins.ddr), + pins.miso.into_pull_up_input(&mut pins.ddr), Settings::default(), ); From fa22d56b1470aba262b8a635747ddc4f5e675259 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 11 Apr 2020 11:43:53 -0700 Subject: [PATCH 40/49] Added general-purpose derivable traits to settings structs/enums --- avr-hal-generic/src/spi.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index d7fdb2f9fc..d43c9cd2dc 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -3,6 +3,7 @@ pub use embedded_hal::spi; /// Oscillator Clock Frequency division options. Controls both SPR and SPI2X register bits. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SerialClockRate { OscfOver2, OscfOver4, @@ -14,6 +15,7 @@ pub enum SerialClockRate { } /// Order of data transmission, either MSB first or LSB first +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DataOrder { MostSignificantFirst, LeastSignificantFirst, @@ -24,6 +26,7 @@ pub enum DataOrder { /// Easiest way to initialize is with /// `Settings::default()`. Otherwise can be instantiated with alternate /// settings directly. +#[derive(Clone, PartialEq, Eq)] pub struct Settings { pub data_order: DataOrder, pub clock: SerialClockRate, @@ -35,7 +38,10 @@ impl Default for Settings { Settings { data_order: DataOrder::MostSignificantFirst, clock: SerialClockRate::OscfOver4, - mode: spi::Mode { polarity: spi::Polarity::IdleLow, phase: spi::Phase::CaptureOnSecondTransition }, + mode: spi::Mode { + polarity: spi::Polarity::IdleLow, + phase: spi::Phase::CaptureOnSecondTransition, + }, } } } @@ -44,7 +50,7 @@ impl Default for Settings { #[macro_export] macro_rules! impl_spi { ( - $(#[$spi_attr:meta])* + $(#[$spi_attr:meta, derive(Clone, Debug, Eq)])* pub struct $Spi:ident { peripheral: $SPI:ty, pins: { @@ -68,7 +74,7 @@ macro_rules! impl_spi { /// Stores the SPI peripheral for register access. In addition, it takes /// ownership of the MOSI and MISO pins to ensure they are in the correct mode. /// Instantiate with the `new` method. - $(#[$spi_attr])* + $(#[$spi_attr, derive(Clone, Debug, Eq)])* pub struct $Spi { peripheral: $SPI, sclk: SCLK, From 248f4d164d1e71e05100ce4922723fa248c5f5d6 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 11 Apr 2020 14:54:41 -0700 Subject: [PATCH 41/49] Changed to proper use of NB to enable the program to do other things while waiting for a transfer to complete --- avr-hal-generic/src/spi.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index d43c9cd2dc..0517b7b811 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -81,6 +81,7 @@ macro_rules! impl_spi { mosi: MOSI, miso: MISO, settings: Settings, + is_write_in_progress: bool, } /// Implementation-specific behavior of the struct, including setup/tear-down @@ -97,6 +98,7 @@ macro_rules! impl_spi { mosi, miso, settings, + is_write_in_progress: false, }; spi.setup(); spi @@ -118,9 +120,13 @@ macro_rules! impl_spi { self.peripheral.spdr.write(|w| unsafe { 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() { } + /// Check if write flag is set, and return a WouldBlock error if it is not. + fn assert_write_complete(&self) -> $crate::nb::Result<(), $crate::void::Void> { + if self.peripheral.spsr.read().spif().bit_is_set() { + Ok(()) + } else { + Err($crate::nb::Error::WouldBlock) + } } /// Sets up the control/status registers with the right settings for this secondary device @@ -178,8 +184,12 @@ macro_rules! impl_spi { /// Sets up the device for transmission and sends the data fn send(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> { - self.write(byte); - self.block_until_transfer_complete(); + if !self.is_write_in_progress { + self.is_write_in_progress = true; + self.write(byte); + } + self.assert_write_complete()?; + self.is_write_in_progress = true; Ok(()) } From faa58de5393e552c9d0a7a17fd6ef9d275e594f0 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 11 Apr 2020 17:11:21 -0700 Subject: [PATCH 42/49] Fixed write-in-progress flag clearing --- avr-hal-generic/src/spi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 0517b7b811..43a59f0c82 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -189,7 +189,7 @@ macro_rules! impl_spi { self.write(byte); } self.assert_write_complete()?; - self.is_write_in_progress = true; + self.is_write_in_progress = false; Ok(()) } From 03ffc326f564e9035bc85219087b051452f6520b Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 11 Apr 2020 17:30:55 -0700 Subject: [PATCH 43/49] Updated SPI Feedback examples to correctly block the FullDuplex methods --- .../examples/leonardo-spi-feedback.rs | 20 +++++++++++++------ .../arduino-uno/examples/uno-spi-feedback.rs | 17 +++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index 118e8e2f83..d6b3ab2b64 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -3,8 +3,11 @@ //! jumper directly from ICSP pin 10 to ICSP pin 11. //! //! Once this program is written to the board, you can use the board's serial -//! connection to see the output. You should see it output the line -//! `data: 15` repeatedly (aka 0b00001111) +//! connection to see the output. +//! +//! As long as the jumper is in place, you should repeatedly get the output +//! "Correct value transmitted!". Try disconnecting it while running to see +//! what changes. #![no_std] #![no_main] @@ -12,6 +15,7 @@ extern crate panic_halt; use arduino_leonardo::prelude::*; use arduino_leonardo::spi::{Spi,Settings}; +use nb::block; #[no_mangle] pub extern fn main() -> ! { let dp = arduino_leonardo::Peripherals::take().unwrap(); @@ -42,11 +46,15 @@ pub extern fn main() -> ! { loop { // Send a byte - spi.send(0b00001111).unwrap(); - // Because MISO is connected to MOSI, the read data should be the same - let data = spi.read().unwrap(); + block!(spi.send(0b00001111)).unwrap(); + let data = block!(spi.read()).unwrap(); - ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap(); + // Because MISO is connected to MOSI, the read data should be the same + if data == 0b00001111 { + ufmt::uwriteln!(&mut serial, "Correct value transmitted!\r").unwrap(); + } else { + ufmt::uwriteln!(&mut serial, "Data not fed back. Make sure you have a jumper between the MISO and MOSI pins.\r").unwrap(); + } delay.delay_ms(1000); } } diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index 12120390bf..2abde34128 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -9,7 +9,9 @@ //! sudo screen /dev/ttyACM0 57600 //! ``` //! -//! You should see it output the line `data: 15` repeatedly (aka 0b00001111) +//! As long as the jumper is in place, you should repeatedly get the output +//! "Correct value transmitted!". Try disconnecting it while running to see +//! what changes. #![no_std] #![no_main] @@ -17,6 +19,7 @@ extern crate panic_halt; use arduino_uno::prelude::*; use arduino_uno::spi::{Spi,Settings}; +use nb::block; #[no_mangle] pub extern fn main() -> ! { let dp = arduino_uno::Peripherals::take().unwrap(); @@ -46,11 +49,15 @@ pub extern fn main() -> ! { loop { // Send a byte - spi.send(0b00001111).unwrap(); - // Because MISO is connected to MOSI, the read data should be the same - let data = spi.read().unwrap(); + block!(spi.send(0b00001111)).unwrap(); + let data = block!(spi.read()).unwrap(); - ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap(); + // Because MISO is connected to MOSI, the read data should be the same + if data == 0b00001111 { + ufmt::uwriteln!(&mut serial, "Correct value transmitted!\r").unwrap(); + } else { + ufmt::uwriteln!(&mut serial, "Data not fed back. Make sure you have a jumper between pins 11 and 12.\r").unwrap(); + } delay.delay_ms(1000); } } From 3d26634fe9453c896418c2e4227e4ac236a33240 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 11 Apr 2020 18:38:37 -0700 Subject: [PATCH 44/49] Improved SPI feedback examples to print transmitted character to serial output --- .../examples/leonardo-spi-feedback.rs | 14 ++++++++------ boards/arduino-uno/examples/uno-spi-feedback.rs | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index d6b3ab2b64..4aa39d4f1a 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -46,14 +46,16 @@ pub extern fn main() -> ! { loop { // Send a byte - block!(spi.send(0b00001111)).unwrap(); - let data = block!(spi.read()).unwrap(); + block!(spi.send(64)).unwrap(); + let data: u8 = block!(spi.read()).unwrap(); - // Because MISO is connected to MOSI, the read data should be the same - if data == 0b00001111 { - ufmt::uwriteln!(&mut serial, "Correct value transmitted!\r").unwrap(); + // Input in pull-up mode, so always reading high if no connection + if data != 0b11111111 { + ufmt::uwrite!(&mut serial, "Character fed back: ").unwrap(); + block!(serial.write(data)).unwrap(); + ufmt::uwrite!(&mut serial, "\r\n").unwrap(); } else { - ufmt::uwriteln!(&mut serial, "Data not fed back. Make sure you have a jumper between the MISO and MOSI pins.\r").unwrap(); + ufmt::uwriteln!(&mut serial, "Character not fed back. Make sure you have a jumper between the MISO and MOSI pins.\r").unwrap(); } delay.delay_ms(1000); } diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index 2abde34128..327ada379d 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -49,14 +49,16 @@ pub extern fn main() -> ! { loop { // Send a byte - block!(spi.send(0b00001111)).unwrap(); - let data = block!(spi.read()).unwrap(); + block!(spi.send(64)).unwrap(); + let data: u8 = block!(spi.read()).unwrap(); - // Because MISO is connected to MOSI, the read data should be the same - if data == 0b00001111 { - ufmt::uwriteln!(&mut serial, "Correct value transmitted!\r").unwrap(); + // Input in pull-up mode, so always reading high if no connection + if data != 0b11111111 { + ufmt::uwrite!(&mut serial, "Character fed back: ").unwrap(); + block!(serial.write(data)).unwrap(); + ufmt::uwrite!(&mut serial, "\r\n").unwrap(); } else { - ufmt::uwriteln!(&mut serial, "Data not fed back. Make sure you have a jumper between pins 11 and 12.\r").unwrap(); + ufmt::uwriteln!(&mut serial, "Character not fed back. Make sure you have a jumper between pins 11 and 12.\r").unwrap(); } delay.delay_ms(1000); } From 486cbd765df047f678e03cb47c8c7863254ba3f3 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Sat, 11 Apr 2020 18:49:14 -0700 Subject: [PATCH 45/49] Ran cargo fmt on SPI examples --- .../examples/leonardo-spi-feedback.rs | 17 +++++--------- .../arduino-uno/examples/uno-spi-feedback.rs | 22 +++++++++---------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index 4aa39d4f1a..ad3d1ed772 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -14,18 +14,13 @@ #![feature(proc_macro_hygiene)] extern crate panic_halt; use arduino_leonardo::prelude::*; -use arduino_leonardo::spi::{Spi,Settings}; +use arduino_leonardo::spi::{Settings, Spi}; use nb::block; #[no_mangle] -pub extern fn main() -> ! { +pub extern "C" fn main() -> ! { let dp = arduino_leonardo::Peripherals::take().unwrap(); let mut delay = arduino_leonardo::Delay::new(); - let mut pins = arduino_leonardo::Pins::new( - dp.PORTB, - dp.PORTC, - dp.PORTD, - dp.PORTE, - ); + let mut pins = arduino_leonardo::Pins::new(dp.PORTB, dp.PORTC, dp.PORTD, dp.PORTE); let mut serial = arduino_leonardo::Serial::new( dp.USART1, @@ -34,8 +29,9 @@ pub extern fn main() -> ! { 57600, ); - pins.led_rx.into_output(&mut pins.ddr);// SS must be set to output mode - // create SPI interface + pins.led_rx.into_output(&mut pins.ddr); // SS must be set to output mode. + + // Create SPI interface. let mut spi = Spi::new( dp.SPI, pins.sck.into_output(&mut pins.ddr), @@ -60,4 +56,3 @@ pub extern fn main() -> ! { delay.delay_ms(1000); } } - diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index 327ada379d..735f429b62 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -18,17 +18,13 @@ #![feature(proc_macro_hygiene)] extern crate panic_halt; use arduino_uno::prelude::*; -use arduino_uno::spi::{Spi,Settings}; +use arduino_uno::spi::{Settings, Spi}; use nb::block; #[no_mangle] -pub extern fn main() -> ! { +pub extern "C" 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, - ); + 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, @@ -37,8 +33,9 @@ pub extern fn main() -> ! { 57600, ); - pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode - // create SPI interface + 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.d13.into_output(&mut pins.ddr), @@ -58,9 +55,12 @@ pub extern fn main() -> ! { block!(serial.write(data)).unwrap(); ufmt::uwrite!(&mut serial, "\r\n").unwrap(); } else { - ufmt::uwriteln!(&mut serial, "Character not fed back. Make sure you have a jumper between pins 11 and 12.\r").unwrap(); + ufmt::uwriteln!( + &mut serial, + "Character not fed back. Make sure you have a jumper between pins 11 and 12.\r" + ) + .unwrap(); } delay.delay_ms(1000); } } - From 0700adc2ab28b7c76572a85c85b1e77084b3c923 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Mon, 13 Apr 2020 21:04:55 -0700 Subject: [PATCH 46/49] Removed derived traits from macro definition --- avr-hal-generic/src/spi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 43a59f0c82..a0a5d0c682 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -50,7 +50,7 @@ impl Default for Settings { #[macro_export] macro_rules! impl_spi { ( - $(#[$spi_attr:meta, derive(Clone, Debug, Eq)])* + $(#[$spi_attr:meta])* pub struct $Spi:ident { peripheral: $SPI:ty, pins: { From a04fbee7ee752f2f20271239738dadc02f65f500 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Mon, 13 Apr 2020 21:53:39 -0700 Subject: [PATCH 47/49] Simplified SPI feedback examples to use ufmt again --- .../examples/leonardo-spi-feedback.rs | 22 ++++++------------ .../arduino-uno/examples/uno-spi-feedback.rs | 23 +++++-------------- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs index ad3d1ed772..f548d043dd 100644 --- a/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs +++ b/boards/arduino-leonardo/examples/leonardo-spi-feedback.rs @@ -3,11 +3,9 @@ //! jumper directly from ICSP pin 10 to ICSP pin 11. //! //! Once this program is written to the board, you can use the board's serial -//! connection to see the output. -//! -//! As long as the jumper is in place, you should repeatedly get the output -//! "Correct value transmitted!". Try disconnecting it while running to see -//! what changes. +//! connection to see the output. You should see it output the line +//! `data: 15` repeatedly (aka 0b00001111). If the output you see is +//! `data: 255`, you may need to check your jumper. #![no_std] #![no_main] @@ -42,17 +40,11 @@ pub extern "C" fn main() -> ! { loop { // Send a byte - block!(spi.send(64)).unwrap(); - let data: u8 = block!(spi.read()).unwrap(); + block!(spi.send(0b00001111)).unwrap(); + // Because MISO is connected to MOSI, the read data should be the same + let data = block!(spi.read()).unwrap(); - // Input in pull-up mode, so always reading high if no connection - if data != 0b11111111 { - ufmt::uwrite!(&mut serial, "Character fed back: ").unwrap(); - block!(serial.write(data)).unwrap(); - ufmt::uwrite!(&mut serial, "\r\n").unwrap(); - } else { - ufmt::uwriteln!(&mut serial, "Character not fed back. Make sure you have a jumper between the MISO and MOSI pins.\r").unwrap(); - } + ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap(); delay.delay_ms(1000); } } diff --git a/boards/arduino-uno/examples/uno-spi-feedback.rs b/boards/arduino-uno/examples/uno-spi-feedback.rs index 735f429b62..02193b8603 100644 --- a/boards/arduino-uno/examples/uno-spi-feedback.rs +++ b/boards/arduino-uno/examples/uno-spi-feedback.rs @@ -9,9 +9,8 @@ //! sudo screen /dev/ttyACM0 57600 //! ``` //! -//! As long as the jumper is in place, you should repeatedly get the output -//! "Correct value transmitted!". Try disconnecting it while running to see -//! what changes. +//! You should see it output the line `data: 15` repeatedly (aka 0b00001111). +//! If the output you see is `data: 255`, you may need to check your jumper. #![no_std] #![no_main] @@ -46,21 +45,11 @@ pub extern "C" fn main() -> ! { loop { // Send a byte - block!(spi.send(64)).unwrap(); - let data: u8 = block!(spi.read()).unwrap(); + block!(spi.send(0b00001111)).unwrap(); + // Because MISO is connected to MOSI, the read data should be the same + let data = block!(spi.read()).unwrap(); - // Input in pull-up mode, so always reading high if no connection - if data != 0b11111111 { - ufmt::uwrite!(&mut serial, "Character fed back: ").unwrap(); - block!(serial.write(data)).unwrap(); - ufmt::uwrite!(&mut serial, "\r\n").unwrap(); - } else { - ufmt::uwriteln!( - &mut serial, - "Character not fed back. Make sure you have a jumper between pins 11 and 12.\r" - ) - .unwrap(); - } + ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap(); delay.delay_ms(1000); } } From 204fbfa92cc243819d473f027bbaab0b545fca49 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Mon, 13 Apr 2020 22:10:11 -0700 Subject: [PATCH 48/49] Changed implementation of SPI to match nb::Result contract correctly: send does not block unless another write is already pending --- avr-hal-generic/src/spi.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index a0a5d0c682..35ba3f9f1c 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -115,18 +115,22 @@ macro_rules! impl_spi { } /// Write a byte to the data register, which begins transmission - /// automatically - fn write(&self, byte: u8) { + /// automatically. + fn write(&mut self, byte: u8) { + self.is_write_in_progress = true; self.peripheral.spdr.write(|w| unsafe { w.bits(byte) }); } /// Check if write flag is set, and return a WouldBlock error if it is not. - fn assert_write_complete(&self) -> $crate::nb::Result<(), $crate::void::Void> { - if self.peripheral.spsr.read().spif().bit_is_set() { - Ok(()) - } else { - Err($crate::nb::Error::WouldBlock) + fn flush(&mut self) -> $crate::nb::Result<(), $crate::void::Void> { + if self.is_write_in_progress { + if self.peripheral.spsr.read().spif().bit_is_set() { + self.is_write_in_progress = false; + } else { + return Err($crate::nb::Error::WouldBlock); + } } + Ok(()) } /// Sets up the control/status registers with the right settings for this secondary device @@ -184,17 +188,14 @@ macro_rules! impl_spi { /// Sets up the device for transmission and sends the data fn send(&mut self, byte: u8) -> $crate::nb::Result<(), Self::Error> { - if !self.is_write_in_progress { - self.is_write_in_progress = true; - self.write(byte); - } - self.assert_write_complete()?; - self.is_write_in_progress = false; + self.flush()?; + self.write(byte); Ok(()) } /// Reads and returns the response in the data register fn read(&mut self) -> $crate::nb::Result { + self.flush()?; Ok(self.peripheral.spdr.read().bits()) } } From bb50c07c684df02afb31978b2c06ec03f4e19f57 Mon Sep 17 00:00:00 2001 From: Jonah Dahlquist Date: Mon, 13 Apr 2020 22:15:25 -0700 Subject: [PATCH 49/49] Removed debug trait from SPI --- avr-hal-generic/src/spi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 35ba3f9f1c..4666e7935f 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -74,7 +74,7 @@ macro_rules! impl_spi { /// Stores the SPI peripheral for register access. In addition, it takes /// ownership of the MOSI and MISO pins to ensure they are in the correct mode. /// Instantiate with the `new` method. - $(#[$spi_attr, derive(Clone, Debug, Eq)])* + $(#[$spi_attr])* pub struct $Spi { peripheral: $SPI, sclk: SCLK,