diff --git a/exercises/8-embedded/3-async-on-embedded/1-compass/src/dial.rs b/exercises/8-embedded/3-async-on-embedded/1-compass/src/dial.rs index d282e1f..8d625dd 100644 --- a/exercises/8-embedded/3-async-on-embedded/1-compass/src/dial.rs +++ b/exercises/8-embedded/3-async-on-embedded/1-compass/src/dial.rs @@ -123,15 +123,17 @@ impl Dial { self.cols.iter_mut().for_each(|c| c.set_high()); } - pub fn light_only(&mut self, dir: Direction) { + pub fn set_light_direction(&mut self, dir: Direction) { let (row, col) = dir.led_index(); self.clear(); self.rows[row].set_high(); self.cols[col].set_low(); } - /// Operate the dial. This function is useful for running + /// Operate the dial autonomously. This function is useful for running /// in a separate task. + /// + /// Useful for exercise 8.1.2 pub async fn run( mut self, receiver: embassy_sync::channel::Receiver<'_, NoopRawMutex, Direction, 4>, diff --git a/exercises/8-embedded/3-async-on-embedded/1-compass/src/main.rs b/exercises/8-embedded/3-async-on-embedded/1-compass/src/main.rs index ed958f2..059a0dc 100644 --- a/exercises/8-embedded/3-async-on-embedded/1-compass/src/main.rs +++ b/exercises/8-embedded/3-async-on-embedded/1-compass/src/main.rs @@ -3,7 +3,6 @@ use dial::{dir_channel, Dial, Direction}; use embassy_nrf::{self as hal, peripherals::TWISPI0, twim::Twim}; -use embassy_time::Delay; use embedded_hal_async::delay::DelayNs; use hal::twim; use lsm303agr::{interface::I2cInterface, mode::MagOneShot, Lsm303agr, MagnetometerId}; @@ -19,7 +18,6 @@ hal::bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(s: embassy_executor::Spawner) -> ! { - // Init RTT control block rtt_init_print!(); @@ -31,21 +29,27 @@ async fn main(s: embassy_executor::Spawner) -> ! { let config = twim::Config::default(); let twim0 = Twim::new(dp.TWISPI0, Irqs, dp.P0_16, dp.P0_08, config); + let delay = embassy_time::Delay; let dial: Dial = todo!("Initialize Dial"); - let mut sensor: Lsm303agr>, MagOneShot> = todo!("Initialize LSM303AGR driver given the twim0 peripheral"); + let mut sensor: Lsm303agr>, MagOneShot> = + todo!("Initialize LSM303AGR driver given the twim0 peripheral"); let id: MagnetometerId = todo!("Read the magnetometer ID using the driver"); rprintln!("{:#02x?}", id); todo!("Initialize the driver"); todo!("Set magnetometer mode to high resolution and output data rate to 100Hz"); - todo!("Change the magnetometer to continuous mode"); todo!("Enable magnetometer offset cancellation"); loop { todo!("Read data and update the dial accordingly"); + + // Steps: + // - Read the MagneticField data + // - Convert to a direction using: Direction::from(mag_data) + // - Update the dial: dial.set_light_direction(direction) } } diff --git a/slides/8_1-embedded-ecosystem.md b/slides/8_1-embedded-ecosystem.md index 6fe263d..e1eb876 100644 --- a/slides/8_1-embedded-ecosystem.md +++ b/slides/8_1-embedded-ecosystem.md @@ -90,7 +90,6 @@ layout: three-slots - Similar to CMSIS register definitions ::right:: - ## `cortex-m-rt`
@@ -133,7 +132,7 @@ WDT->CONFIG.bit.PER = WDT_CONFIG_PER_16; let dp = atmsamd21e::Peripherals::take().unwrap(); let is_8_cycles = dp.WDT.CONFIG.read().per().is_8(); -dp.WDT.CONFIG.modify(|_, w| w.per()._8()); +dp.WDT.CONFIG.modify(|_, w| w.per()._16()); ``` --- @@ -183,31 +182,27 @@ layout: full --- ```rust -hal::bind_interrupts!(struct Irqs { - SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; -}); - -#[cortex_m_rt::entry] -fn main() -> ! { - // Init RTT control block - rtt_init_print!(); - - let _cp = cortex_m::Peripherals::take().unwrap(); - // Use ``dp` to get a handle to the peripherals - let dp = hal::init(Default::default()); +//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. - rprintln!("Starting"); +#![no_std] +#![no_main] - let config = twim::Config::default(); - let mut twim0 = Twim::new(dp.TWISPI0, Irqs, dp.P0_03, dp.P0_04, config); +use embassy_rp::uart; +use {defmt_rtt as _, panic_probe as _}; - rprintln!("Reading..."); +#[cortex_m_rt::entry] +fn main() -> ! { + // Init the hal + let p = embassy_rp::init(Default::default()); - let mut buf = [0u8; 16]; - twim0.blocking_write_read(0xAB, &mut [0x00], &mut buf).unwrap(); + // Init the uart + let config = uart::Config::default(); + let mut uart = uart::Uart::new_with_rtscts_blocking(p.UART0, p.PIN_0, p.PIN_1, p.PIN_3, p.PIN_2, config); - rprintln!("Read: {:02x?}", buf); - exit(); + loop { + uart.blocking_write("hello there!\r\n".as_bytes()).unwrap(); + cortex_m::asm::delay(1_000_000); + } } ``` @@ -246,6 +241,7 @@ layout: with-footer --- layout: with-footer --- + # `embedded-hal` The glue of the entire ecosytem @@ -253,7 +249,7 @@ The glue of the entire ecosytem
-### SPI example trait: +### SPI trait: ```rust pub trait SpiDevice: ErrorType { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error>; @@ -300,31 +296,6 @@ layout: with-footer ---- -layout: with-footer ---- - -# Typestate - -State encoded in the *type* of the variable - -```rust -// https://github.com/embassy-rs/embassy/blob/main/examples/nrf52840/src/bin/blinky.rs - -use nrf52833_hal::gpio::{Pin, p0::P0_04, Input, PullDown, Output, PushPull}; - -/// Take an nRF pin. -/// It must be: -/// - Port 0 pin 4 (Compile time constant) -/// - Configured as input -fn read_status(pin: Input<'_, P0_04>) -> bool {} - -/// Take an nRF pin. -/// It must be: -/// - Any port and pin (Runtime variable) -/// - Configured as output -fn set_led_level(pin: Output<'_, AnyPin>, enabled: bool) {} -``` --- layout: with-footer @@ -332,6 +303,8 @@ layout: with-footer # Runtimes +


+ diff --git a/slides/8_2-portable-drivers.md b/slides/8_2-portable-drivers.md index a1b51f2..8c632d7 100644 --- a/slides/8_2-portable-drivers.md +++ b/slides/8_2-portable-drivers.md @@ -74,30 +74,35 @@ Fill-in functions - ✅ Efficient - ❌ Inconvenient -::right:: +

```c -#include - -// TODO Change to your spi type +// TODO: Change to your spi type #define SPI_TYPE void -static SPI_TYPE* spi = NULL; -void init(SPI_TYPE* spi_instance); +void enable_cs(SPI_TYPE* spi) { /* TODO */ } +void disable_cs(SPI_TYPE* spi) { /* TODO */ } -static void enable_cs(SPI_TYPE* spi) {/* Todo */ } +char transfer(SPI_TYPE* spi, char value) { + // TODO +} -static void disable_cs(SPI_TYPE* spi){/* Todo */ } +``` -static char transfer(SPI_TYPE* spim, char value) -{ - // Todo: Transfer 'value' over the bus and return the response -} +::right:: + +`driver.h` + +```c +// Hold spi context +static SPI_TYPE* spi = NULL; +// Init the driver +void init(SPI_TYPE* spi_instance); char example() { enable_cs(spi); - transfer(spi, 0xDE); - char result = transfer(spi, 0xAD); + transfer(spi, 123); + char result = transfer(spi, 124); disable_cs(spi); return result; @@ -105,10 +110,65 @@ char example() { ``` --- -layout: full +layout: two-cols --- - +https://godbolt.org/z/E3d6MT413 + +```c +typedef struct Spi { + unsigned int cs; + unsigned int write; + unsigned int read; +} Spi_t; + +#define SPI_TYPE Spi_t + +static void enable_cs(volatile SPI_TYPE* spi) { + spi->cs = 1; +} +static void disable_cs(volatile SPI_TYPE* spi) { + spi->cs = 0; +} +static char transfer( + volatile SPI_TYPE* spi, + char value +) { + spi->write = value; + return spi->read; +} +``` + +::right:: + +```c +char example() { + enable_cs(spi); + transfer(spi, 123); + char result = transfer(spi, 124); + disable_cs(spi); + + return result; +} +``` + +```asm +example: + movs r0, #0 + movs r1, #1 + str r1, [r0] + movs r1, #4 + movs r2, #123 + str r2, [r1] + movs r2, #8 + ldr r3, [r2] + movs r3, #124 + str r3, [r1] + ldr r1, [r2] + str r0, [r0] + uxtb r0, r1 + bx lr +``` --- layout: two-cols @@ -120,42 +180,47 @@ Function pointers - ❌ Inefficient - ✅ Convenient -::right:: - ```c -#include - typedef void (*EnableCs)(); typedef void (*DisableCs)(); typedef char (*SpiTransfer)(char); -static EnableCs enable_cs = NULL; -static DisableCs disable_cs = NULL; -static SpiTransfer spi_transfer = NULL; - -void init( - EnableCs init_enable, - DisableCs init_disable, - SpiTransfer init_transfer, -) { - enable_cs = init_enable; - disable_cs = init_disable; - spi_transfer = init_transfer; -} -char example() { - enable_cs(); - spi_transfer(0xDE); - char result = spi_transfer(0xAD); - disable_cs(); + +typedef struct Spi { + EnableCs enable_cs; + DisableCs disable_cs; + SpiTransfer spi_transfer; +} Spi_t; + +char example(Spi_t spi) { + spi.enable_cs(); + spi.spi_transfer(123); + char result = spi.spi_transfer(124); + spi.disable_cs(); + return result; } ``` ---- -layout: full ---- - - +::right:: +https://godbolt.org/z/d43Tad96j + +```asm +example: + push {r4, r5, r7, lr} + add r7, sp, #8 + mov r5, r2 + mov r4, r1 + blx r0 ; function call + movs r0, #123 + blx r5 + movs r0, #124 + blx r5 ; function call + mov r5, r0 + blx r4 ; function call + mov r0, r5 + pop {r4, r5, r7, pc} +``` --- layout: two-cols --- @@ -167,32 +232,46 @@ Link-time binding - 🟠 Somewhat convenient - ❌ Error-prone -::right:: - ```c -#include - extern void enable_cs(); extern void disable_cs(); extern char spi_transfer(char value); char example() { enable_cs(); - spi_transfer(0xDE); - char result = spi_transfer(0xAD); + spi_transfer(123); + char result = spi_transfer(124); disable_cs(); + return result; } ``` ---- -layout: full ---- +::right:: - +https://godbolt.org/z/hjesx8qoW + +```asm +example: + ldr r0, .LCPI0_0 + movs r1, #1 + str r1, [r0] + movs r1, #123 + str r1, [r0, #4] + ldr r1, [r0, #8] + movs r1, #124 + str r1, [r0, #4] + ldr r1, [r0, #8] + movs r2, #0 + str r2, [r0] + uxtb r0, r1 + bx lr +.LCPI0_0: + .long 1000000 +``` --- -layout: two-cols +layout: full --- # Abstraction in Rust @@ -202,27 +281,18 @@ Traits & Generics - ✅ Efficient - ✅ Convenient -::right:: - +Reminder (simplified): ```rust -/// My fictional SPI peripheral -struct SpiPeripheral; - -impl SpiPeripheral { - #[inline] - fn enable_cs(&mut self) { - unsafe { ptr::write_volatile(0x2000000 as *mut u8, 0) } - } - - #[inline] - fn disable_cs(&mut self) { - unsafe { ptr::write_volatile(0x2000000 as *mut u8, 1) } - } +pub trait SpiDevice { + fn transaction(&mut self, operations: &mut [Operation<'_>]) -> Result<(), ()>; } -impl spi::ErrorType for SpiPeripheral { - // SPI operations never fail on this peripheral - type Error = core::convert::Infallible; +pub enum Operation<'a> { + Read(&'a mut [u8]), + Write(&'a [u8]), + Transfer(&'a mut [u8], &'a [u8]), + TransferInPlace(&'a mut [u8]), + DelayNs(u32), } ``` @@ -231,40 +301,35 @@ layout: full --- ```rust -impl SpiDevice for SpiPeripheral { - #[inline] - fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { - self.enable_cs(); - for op in operations.iter_mut() { - match op { - spi::Operation::Write(w) => unsafe { - w.into_iter() - .for_each(|word| ptr::write_volatile(0x2000001 as *mut u8, *word)) - }, - spi::Operation::Transfer(r, w) => unsafe { - let len = w.len().max(r.len()); - let it = w.into_iter().chain(repeat(&0x00)); - let it = it - .zip(r.into_iter().map(Some).chain(repeat_with(|| None))).take(len); - it.for_each(|(w, r)| { - ptr::write_volatile(0x2000001 as *mut u8, *w); - r.map(|r| *r = ptr::read_volatile(0x2000001 as *mut u8)); - }); - }, - _ => todo!(), - } - } - self.disable_cs(); - Ok(()) - } +pub fn example(spi: &mut Spi) -> u8 { + let mut buf = [0]; + + spi.transaction(&mut [ + Operation::Write(&[123]), + Operation::Transfer(&mut buf, &[124]), + ]).unwrap(); + buf[0] } ``` ---- -layout: full ---- - - +https://godbolt.org/z/xv33T6e6c + +```asm +example::example_instance::h0a336da11b699c51: + ldr r1, .LCPI1_0 + movs r0, #1 + strb r0, [r1] + movs r0, #123 + strb r0, [r1, #4] + movs r0, #124 + strb r0, [r1, #4] + ldrb r0, [r1, #8] + movs r2, #0 + strb r2, [r1] + bx lr +.LCPI1_0: + .long 1000000 +``` --- layout: with-footer @@ -298,6 +363,18 @@ layout: with-footer
🔸
Bare metal
+ interrupts
+--- +layout: with-footer +--- +# Applied + + + + --- layout: with-footer @@ -315,7 +392,7 @@ layout: with-footer - Common interface export --- -layout: two-cols +layout: full --- # Low level driver in C @@ -323,8 +400,6 @@ layout: two-cols - Enum & `#define` for definitions - Functions for read/write -::right:: - ```c enum Reg { @@ -332,8 +407,6 @@ enum Reg REG_IER = 0x02, REG_IDR = 0x03, REG_ISR = 0x04, - REG_PIN = 0x05, - REG_PORT = 0x06, }; #define ID_MANUFACTURER_MASK 0xF0 @@ -353,19 +426,10 @@ static char read_register(enum Reg address) layout: full --- - - ---- -layout: two-cols ---- - # Low level driver in Rust -- Enum for definitions -- Device struct represents chip instance -- Functions for read/write - -::right:: +- Enum & `const` for definitions +- Functions for read/write based on shared trait ```rust use embedded_hal::spi::SpiDevice; @@ -376,18 +440,15 @@ pub enum Register { Ier = 0x02, Idr = 0x03, Isr = 0x04, - Pin = 0x05, - Port = 0x06, } -pub struct Device { - spi: SPI, -} +const ID_MANUFACTURER_MASK: u8 = 0xF0; +const ID_MANUFACTURER_POS: u8 = 4; -impl Device { - pub fn new(spi: SPI) -> Self { - Self { spi } - } +pub fn read_register(spi: &mut Spi, address: Register) -> u8 { + let mut buffer = [address as u8, 0]; + spi.transfer_in_place(&mut buffer).unwrap(); + buffer[1] } ``` @@ -395,37 +456,31 @@ impl Device { layout: full --- - - ---- -layout: with-footer ---- - -# Possibilities in Rust +# Low level driver in Rust -Much more is possible: +- Ideomatic: Device driver is a struct ```rust -pub fn example() { - let mut device = Device::new(Spi); +pub struct Device { + interface: SPI, +} - let manufacturer = device.id().read().manufacturer(); +impl Device { + pub fn new(interface: SPI) -> Self { + Self { interface } + } - if manufacturer != 0 { - device.port().modify(|_, w| w.enable_7(true)); + pub fn read_register(&mut self, address: Register) -> u8 { + let mut buffer = [address as u8, 0]; + self.interface.transfer_in_place(&mut buffer).unwrap(); + buffer[1] } } -``` -```c -void example() { - char manufacturer = (read_register(REG_ID) & ID_MANUFACTURER_MASK) >> ID_MANUFACTURER_POS; - - if (manufacturer != 0) { - char reg = read_register(REG_PORT); - reg |= 0x80; - write_register(REG_PORT, reg); - } +fn foo() { + let interface = // Init interface + let mut my_device = Device::new(interface); + let value = my_device.read_register(Register::Id); } ``` @@ -439,6 +494,16 @@ Add typestate to high-level driver - Allows compiler to check valid use of driver +Usage: +```rust +let mut device: Device<_, Idle> = + Device::new(/* spi interface */); + +device.wait_ready(); +// ^^^^^^^^^^ Compile error +// `wait_ready` not implemented for `Device<_, Idle>` +``` + ::right:: ```rust @@ -450,7 +515,6 @@ pub struct Device { _state: PhantomData, } - impl Device { pub fn send(self, data: &[u8]) -> Device @@ -460,7 +524,7 @@ impl Device { } impl Device { - pub async fn wait_ready(self) + pub fn wait_ready(self) -> Device { todo!("Block until done, go to Idle state") } @@ -475,12 +539,13 @@ layout: with-footer Much more is possible: - `embedded-hal` -- `radio` -- `embedded-nal` -- `usb-device` +- `embedded-io` +- `embedded-storage` - `embedded-graphics` +- `embedded-nal` - `accelerometer` -- `embedded-storage` +- `usb-device` +- `radio` --- layout: with-footer diff --git a/slides/images/8_2-c-fill-in-light.png b/slides/images/8_2-c-fill-in-light.png deleted file mode 100644 index 6102dce..0000000 Binary files a/slides/images/8_2-c-fill-in-light.png and /dev/null differ diff --git a/slides/images/8_2-c-function-pointers-light.png b/slides/images/8_2-c-function-pointers-light.png deleted file mode 100644 index ab85dfb..0000000 Binary files a/slides/images/8_2-c-function-pointers-light.png and /dev/null differ diff --git a/slides/images/8_2-c-link-binding-light.png b/slides/images/8_2-c-link-binding-light.png deleted file mode 100644 index 03d93c4..0000000 Binary files a/slides/images/8_2-c-link-binding-light.png and /dev/null differ diff --git a/slides/images/8_2-embedded-hal.png b/slides/images/8_2-embedded-hal.png new file mode 100644 index 0000000..07bd0f5 Binary files /dev/null and b/slides/images/8_2-embedded-hal.png differ diff --git a/slides/images/8_2-low-level-c-driver-light.png b/slides/images/8_2-low-level-c-driver-light.png deleted file mode 100644 index 7d830ac..0000000 Binary files a/slides/images/8_2-low-level-c-driver-light.png and /dev/null differ diff --git a/slides/images/8_2-low-level-rust-driver-light.png b/slides/images/8_2-low-level-rust-driver-light.png deleted file mode 100644 index 4e13b40..0000000 Binary files a/slides/images/8_2-low-level-rust-driver-light.png and /dev/null differ diff --git a/slides/images/8_2-rust-light.png b/slides/images/8_2-rust-light.png deleted file mode 100644 index 33d26bd..0000000 Binary files a/slides/images/8_2-rust-light.png and /dev/null differ