From aa09b013897b381ee1aae1065a5d5fc09c8c3ae3 Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Mon, 17 Oct 2022 01:51:38 -0500 Subject: [PATCH 01/82] update to be compatible with latest nightly --- .cargo/config | 5 +++++ Cargo.toml | 7 ++++--- src/clock.rs | 9 +++++++-- src/lib.rs | 4 +++- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.cargo/config b/.cargo/config index 1abfebf..8e8a65d 100644 --- a/.cargo/config +++ b/.cargo/config @@ -14,3 +14,8 @@ rustflags = [ [build] target = "msp430-none-elf" + +[unstable] +# MSP430 doesn't come with libcore compiled already. But when it does, this +# key can be removed. +build-std = ["core"] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 4c131a7..b15a242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.rs/msp430fr2x5x-hal" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -msp430 = "0.2.2" +msp430 = "0.4.0" nb = "0.1.2" [dependencies.void] @@ -24,13 +24,14 @@ version = "0.2.3" features = ["unproven"] [dependencies.msp430fr2355] +path = "../msp430fr2355" version = "0.4.2" features = ["rt"] [dev-dependencies] -panic-msp430 = "0.2.0" +panic-msp430 = "0.4.0" panic-never = "0.1.0" -msp430-rt = "0.2.0" +msp430-rt = "0.4.0" [profile.release] lto = "fat" diff --git a/src/clock.rs b/src/clock.rs index 6b2d80c..b36ef9d 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -14,6 +14,8 @@ use pac::cs::csctl1::DCORSEL_A; use pac::cs::csctl4::{SELA_A, SELMS_A}; pub use pac::cs::csctl5::{DIVM_A as MclkDiv, DIVS_A as SmclkDiv}; +use crate::asm; + /// REFOCLK frequency pub const REFOCLK: u16 = 32768; /// VLOCLK frequency @@ -258,13 +260,16 @@ impl ClockConfig { #[inline(always)] fn fll_off() { const FLAG: u8 = 1 << 6; - unsafe { llvm_asm!("bis.b $0, SR" :: "i"(FLAG) : "memory" : "volatile") }; + // unsafe { asm!("bis.b $0, SR" :: "i"(FLAG) : "memory" : "volatile") }; + // TODO what does "i"(FLAG) do? + unsafe { asm!("bis.b $0, SR", options(nomem, preserves_flags)) }; } #[inline(always)] fn fll_on() { const FLAG: u8 = 1 << 6; - unsafe { llvm_asm!("bic.b $0, SR" :: "i"(FLAG) : "memory" : "volatile") }; + // unsafe { asm!("bic.b $0, SR" :: "i"(FLAG) : "memory" : "volatile") }; + unsafe { asm!("bic.b $0, SR", options(nomem, preserves_flags)) }; } impl ClockConfig { diff --git a/src/lib.rs b/src/lib.rs index 8346da4..bf32dd0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,8 +24,10 @@ #![no_std] #![allow(incomplete_features)] // Enable specialization without warnings #![feature(specialization)] -#![feature(llvm_asm)] #![deny(missing_docs)] +#![feature(asm_experimental_arch)] + +use core::arch::asm; pub mod batch_gpio; pub mod capture; From e64afa2236441e83b9c6e2dd6f678e69c204ed84 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Mon, 17 Oct 2022 16:35:38 -0500 Subject: [PATCH 02/82] Fix asm statements --- src/clock.rs | 5 +++-- src/lib.rs | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index b36ef9d..03a5600 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -262,14 +262,15 @@ fn fll_off() { const FLAG: u8 = 1 << 6; // unsafe { asm!("bis.b $0, SR" :: "i"(FLAG) : "memory" : "volatile") }; // TODO what does "i"(FLAG) do? - unsafe { asm!("bis.b $0, SR", options(nomem, preserves_flags)) }; + // "i"(FLAG) is llvm syntax for FLAG being an immediate value in this instruction + unsafe { asm!("bis.b {num}, SR", num= const FLAG, options(nomem, preserves_flags)) }; } #[inline(always)] fn fll_on() { const FLAG: u8 = 1 << 6; // unsafe { asm!("bic.b $0, SR" :: "i"(FLAG) : "memory" : "volatile") }; - unsafe { asm!("bic.b $0, SR", options(nomem, preserves_flags)) }; + unsafe { asm!("bic.b {num}, SR", num= const FLAG, options(nomem, preserves_flags)) }; } impl ClockConfig { diff --git a/src/lib.rs b/src/lib.rs index bf32dd0..a1cfd43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ #![feature(specialization)] #![deny(missing_docs)] #![feature(asm_experimental_arch)] +#![feature(asm_const)] use core::arch::asm; From 0426b9370978b698573b3409d6634370c7b3a30f Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Wed, 25 Jan 2023 03:31:02 -0600 Subject: [PATCH 03/82] compiling now --- Cargo.toml | 2 +- src/clock.rs | 2 -- src/lib.rs | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc37d99..334fea4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ features = ["unproven"] [dependencies.msp430fr2355] path = "../msp430fr2355" -version = "0.4.2" +version = "0.5.2" features = ["rt"] void = { version = "1.0.2", default-features = false } embedded-hal = { version = "0.2.7", features = ["unproven"] } diff --git a/src/clock.rs b/src/clock.rs index 874d5c8..90e7bb5 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -16,8 +16,6 @@ use pac::cs::csctl1::DCORSEL_A; use pac::cs::csctl4::{SELA_A, SELMS_A}; pub use pac::cs::csctl5::{DIVM_A as MclkDiv, DIVS_A as SmclkDiv}; -use crate::asm; - /// REFOCLK frequency pub const REFOCLK: u16 = 32768; /// VLOCLK frequency diff --git a/src/lib.rs b/src/lib.rs index 9f816bb..3d0801f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,11 +26,8 @@ #![feature(specialization)] #![feature(asm_experimental_arch)] #![deny(missing_docs)] -#![feature(asm_experimental_arch)] #![feature(asm_const)] -use core::arch::asm; - pub mod batch_gpio; pub mod capture; pub mod clock; From fcd83bbc4cdae66c88f473522f99acdec0f5b786 Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Wed, 25 Jan 2023 14:01:27 -0600 Subject: [PATCH 04/82] compiling now --- Cargo.toml | 15 +++------------ rust-toolchain | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 334fea4..138a1d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,22 +12,13 @@ documentation = "https://docs.rs/msp430fr2x5x-hal" [dependencies] msp430 = "0.4.0" nb = "0.1.3" - -[dependencies.void] -version = "1.0.2" -default-features = false - -[dependencies.embedded-hal] -version = "0.2.3" -features = ["unproven"] +void = { version = "1.0.2", default-features = false } +embedded-hal = { version = "0.2.7", features = ["unproven"] } [dependencies.msp430fr2355] path = "../msp430fr2355" version = "0.5.2" -features = ["rt"] -void = { version = "1.0.2", default-features = false } -embedded-hal = { version = "0.2.7", features = ["unproven"] } -msp430fr2355 = { version = "0.5.2", features = ["rt", "critical-section"] } +features = ["rt", "critical-section"] [dev-dependencies] panic-msp430 = "0.4.0" diff --git a/rust-toolchain b/rust-toolchain index bf867e0..07ade69 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly +nightly \ No newline at end of file From 1d648b7d5943393a5fa36614d40461450e5819b7 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Fri, 27 Jan 2023 16:45:19 -0600 Subject: [PATCH 05/82] i2c work --- src/hw_traits/eusci.rs | 546 +++++++++++++++++++++++++++++++++++------ src/i2c.rs | 96 ++++++++ src/lib.rs | 3 + src/serial.rs | 4 +- 4 files changed, 576 insertions(+), 73 deletions(-) create mode 100644 src/i2c.rs diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 0798cf6..deb1c4b 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -1,13 +1,92 @@ use super::Steal; use msp430fr2355 as pac; +use crate::pac::e_usci_b0::ucb0ctlw1::{UCCLTO_A, UCASTP_A}; +use crate::pac::e_usci_b0::ucb0statw::{UCSCLLOW_A, UCGC_A, UCBBUSY_A}; +use crate::pac::e_usci_b0::ucb0ctlw0::{UCMODE_A}; +use crate::pac::e_usci_b0::ucb0ie::{UCBIT9IE_A, UCTXIE3_A, UCRXIE3_A, UCTXIE2_A, UCRXIE2_A, + UCTXIE1_A, UCRXIE1_A, UCCLTOIE_A, UCBCNTIE_A, UCNACKIE_A, + UCALIE_A, UCSTPIE_A, UCSTTIE_A, UCTXIE0_A, UCRXIE0_A}; +use crate::pac::e_usci_b0::ucb0ifg::{UCBIT9IFG_A, UCTXIFG3_A, UCRXIFG3_A, UCTXIFG2_A, UCRXIFG2_A, + UCTXIFG1_A, UCRXIFG1_A, UCCLTOIFG_A, UCBCNTIFG_A, UCNACKIFG_A, + UCALIFG_A, UCSTPIFG_A, UCSTTIFG_A, UCTXIFG0_A, UCRXIFG0_A}; +use crate::pac::e_usci_b0::ucb0iv::UCIV_A; + + +macro_rules! from_u8 { + ($type: ty) => { + impl From for $type { + #[inline(always)] + fn from(variant: u8) -> Self { + <$type>::from(variant) + } + } + }; +} + +/// Defines a macro to +/// Also +macro_rules! reg_struct { + ( + pub struct $struct_name : ident, $macro_rd : ident, $macro_wr : ident { + flags{ + $(pub $bool_name : ident : bool,)* + } + vals{ + $(pub $val_name : ident : $val_type : ty : $size : ty,)* + } + + } + ) => { + pub struct $struct_name { + $(pub $bool_name : bool,)* + $(pub $val_name : $val_type ,)* + } + macro_rules! $macro_rd { + ($reader : expr) => { + $struct_name{ + $($bool_name : $reader.$bool_name().bit(),)* + $($val_name : <$val_type>::from(<$size>::from($reader.$val_name().variant())),)* + } + }; + } + + macro_rules! $macro_wr { + ($reg : expr) => { |w| { + w$(.$bool_name().bit($reg.$bool_name))* + $(.$val_name().bits($reg.$val_name as $size))* + }}; + } + }; +} + pub enum Ucssel { Uclk, Aclk, Smclk, } -pub struct UcxCtl0 { +from_u8!(Ucssel); + +pub enum Ucmode { + ThreePinSPI, + FourPinSPI_1, + FourPinSPI_0, + I2CMode, +} + +from_u8!(Ucmode); + +pub enum Ucglit { + Max50ns, + Max25ns, + Max12_5ns, + Max6_25ns, +} + +from_u8!(Ucglit); + +pub struct UcaCtlw0{ pub ucpen: bool, pub ucpar: bool, pub ucmsb: bool, @@ -17,7 +96,95 @@ pub struct UcxCtl0 { pub ucrxeie: bool, } -pub trait EUsci: Steal { +reg_struct! { +pub struct UcbCtlw0, UcbCtlw0_rd, UcbCtlw0_wr { + flags{ + pub uca10: bool, + pub ucsla10: bool, + pub ucmm: bool, + pub ucmst: bool, + pub ucsync: bool, + pub uctxack: bool, + pub uctr: bool, + pub uctxnack: bool, + pub uctxstp: bool, + pub uctxstt: bool, + pub ucswrst: bool, + } + vals{ + pub ucmode: Ucmode : u8, + pub ucssel: Ucssel : u8, + } +} +} + +pub struct UcbCtlw1 { + pub ucetxint: bool, + pub ucclto: UCCLTO_A, + pub ucstpnack: bool, + pub ucswack: bool, + pub ucastp: UCASTP_A, + pub ucglit: Ucglit, +} + +pub struct UcbStatw { + pub ucbcnt: u8, + pub ucscllow: UCSCLLOW_A, + pub ucgc: UCGC_A, + pub ucbbusy: UCBBUSY_A, +} + +pub struct UcbI2coa { + /// ucgcen is only available for i2c0a0 + pub ucgcen: bool, + pub ucoaen: bool, + /// 10 bits + pub i2coa0: u16, +} + +pub struct UcbIe { + pub ucbit9ie: bool, + pub uctxie3: bool, + pub ucrxie3: bool, + pub uctxie2: bool, + pub ucrxie2: bool, + pub uctxie1: bool, + pub ucrxie1: bool, + pub uccltoie: bool, + pub ucbcntie: bool, + pub ucnackie: bool, + pub ucalie: bool, + pub ucstpie: bool, + pub ucsttie: bool, + pub uctxie0: bool, + pub ucrxie0: bool, +} + +pub struct UcbIFG { + pub ucbit9ifg: bool, + pub uctxifg3: bool, + pub ucrxifg3: bool, + pub uctxifg2: bool, + pub ucrxifg2: bool, + pub uctxifg1: bool, + pub ucrxifg1: bool, + pub uccltoifg: bool, + pub ucbcntifg: bool, + pub ucnackifg: bool, + pub ucalifg: bool, + pub ucstpifg: bool, + pub ucsttifg: bool, + pub uctxifg0: bool, + pub ucrxifg0: bool, +} + +pub trait EUsci : Steal { + +} + +pub trait EUsciUart: EUsci { + type Statw: UartUcxStatw; + fn ctl0_reset(&self); // only call while in reset state @@ -26,33 +193,84 @@ pub trait EUsci: Steal { // only call while in reset state fn loopback(&self, loopback: bool); - fn rx_rd(&self) -> u8; + fn txifg_rd(&self) -> bool; + fn rxifg_rd(&self) -> bool; + fn rx_rd(&self) -> u8; fn tx_wr(&self, val: u8); + fn iv_rd(&self) -> u16; + + // only call while in reset state + fn ctl0_settings(&self, reg: UcaCtlw0); + + fn mctlw_settings(&self, ucos16: bool, ucbrs: u8, ucbrf: u8); + + fn statw_rd(&self) -> ::Statw; + fn txie_set(&self); fn txie_clear(&self); fn rxie_set(&self); fn rxie_clear(&self); +} - fn txifg_rd(&self) -> bool; - fn rxifg_rd(&self) -> bool; +pub trait EUsciI2C: EUsci { - fn iv_rd(&self) -> u16; -} + // Read or write to UCSWRST + fn ctw0_rd_rst(&self) -> bool; + fn ctw0_wr_rst(&self, bit:bool); -pub trait EUsciUart: EUsci { - type Statw: UcaxStatw; + // Modify only when UCSWRST = 1 + fn ctw0_rd(&self) -> UcbCtlw0; + fn ctw0_wr(&self, reg:UcbCtlw0); - // only call while in reset state - fn ctl0_settings(&self, reg: UcxCtl0); + // Modify only when UCSWRST = 1 + fn ctw1_rd(&self) -> UcbCtlw1; + fn ctw1_wr(&self, reg:UcbCtlw1); - fn mctlw_settings(&self, ucos16: bool, ucbrs: u8, ucbrf: u8); + // Modify only when UCSWRST = 1 + fn brw_rd(&self) -> u16; + fn brw_wr(&self, val:u16); + + fn statw_rd(&self) -> UcbStatw; + + // Modify only when UCSWRST = 1 + fn tbcnt_rd(&self) -> u16; + fn tbcnt_wr(&self, val:u16); + + fn ucrxbuf_rd(&self) -> u8; + fn uctxbuf_wr(&self, val: u8); + + // Modify only when UCSWRST = 1 + // the which parameter is used to select one of the 4 registers + fn i2coa_rd(&self, which:u8) -> UcbI2coa; + fn i2coa_wr(&self, which:u8, reg:UcbI2coa); + + fn addrx_rd(&self) -> u16; + + // Modify only when UCSWRST = 1 + fn addrmask_rd(&self) -> u16; + fn addrmask_wr(&self, val:u16); + + fn i2csa_rd(&self) -> u16; + fn i2csa_wr(&self, val:u16); + + fn ucbie_rd(&self) -> UcbIe; + fn ucboe_wr(&self, reg:UcbIe); + //some common bitwise operations for this register + fn ucboe_rmw_or(&self, reg:UcbIe); + fn ucboe_rmw_and(&self, reg:UcbIe); + + fn ifg_rd(&self) -> UcbIFG; + fn ifg_wr(&self, reg: UcbIFG); + fn iv_rd(&self) -> UCIV_A; +} + +pub trait UcxStatw { - fn statw_rd(&self) -> Self::Statw; } -pub trait UcaxStatw { +pub trait UartUcxStatw : UcxStatw { fn ucfe(&self) -> bool; fn ucoe(&self) -> bool; fn ucpe(&self) -> bool; @@ -60,10 +278,14 @@ pub trait UcaxStatw { fn ucbusy(&self) -> bool; } -macro_rules! eusci_a_impl { - ($EUsci:ident, $eusci:ident, $ucaxctlw0:ident, $ucaxctlw1:ident, $ucaxbrw:ident, $ucaxmctlw:ident, - $ucaxstatw:ident, $ucaxrxbuf:ident, $ucaxtxbuf:ident, $ucaxie:ident, $ucaxifg:ident, - $ucaxiv:ident, $Statw:ty) => { +pub trait I2CUcxStatw : UcxStatw { + +} + +macro_rules! eusci_impl { + ($EUsci:ident, $eusci:ident, $ucxctlw0:ident, $ucxctlw1:ident, $ucxbrw:ident, + $ucxstatw:ident, $ucxrxbuf:ident, $ucxtxbuf:ident, $ucxie:ident, $ucxifg:ident, + $ucxiv:ident, $Statw:ty) => { impl Steal for pac::$EUsci { #[inline(always)] unsafe fn steal() -> Self { @@ -72,30 +294,73 @@ macro_rules! eusci_a_impl { } impl EUsci for pac::$EUsci { - #[inline(always)] - fn ctl0_reset(&self) { - self.$ucaxctlw0().write(|w| w.ucswrst().set_bit()); - } - #[inline(always)] - fn brw_settings(&self, ucbr: u16) { - self.$ucaxbrw().write(|w| unsafe { w.bits(ucbr) }); - } + } + + impl UcxStatw for $Statw { + + } + } +} + +macro_rules! eusci_a_impl { + ($EUsci:ident, $eusci:ident, $ucaxctlw0:ident, $ucaxctlw1:ident, $ucaxbrw:ident, $ucaxmctlw:ident, + $ucaxstatw:ident, $ucaxrxbuf:ident, $ucaxtxbuf:ident, $ucaxie:ident, $ucaxifg:ident, + $ucaxiv:ident, $Statw:ty) => { + + eusci_impl!( + $EUsci, + $eusci, + $ucaxctlw0, + $ucaxctlw1, + $ucaxbrw, + $ucaxstatw, + $ucaxrxbuf, + $ucaxtxbuf, + $ucaxie, + $ucaxifg, + $ucaxiv, + $Statw + ); + + impl EUsciUart for pac::$EUsci { + type Statw = $Statw; #[inline(always)] - fn loopback(&self, loopback: bool) { - self.$ucaxstatw().write(|w| w.uclisten().bit(loopback)); + fn ctl0_settings(&self, reg: UcaCtlw0) { + self.$ucaxctlw0().write(|w| { + w.ucpen() + .bit(reg.ucpen) + .ucpar() + .bit(reg.ucpar) + .ucmsb() + .bit(reg.ucmsb) + .uc7bit() + .bit(reg.uc7bit) + .ucspb() + .bit(reg.ucspb) + .ucssel() + .bits(reg.ucssel as u8) + .ucrxeie() + .bit(reg.ucrxeie) + }); } #[inline(always)] - fn rx_rd(&self) -> u8 { - self.$ucaxrxbuf().read().ucrxbuf().bits() + fn mctlw_settings(&self, ucos16: bool, ucbrs: u8, ucbrf: u8) { + self.$ucaxmctlw.write(|w| unsafe { + w.ucos16() + .bit(ucos16) + .ucbrs() + .bits(ucbrs) + .ucbrf() + .bits(ucbrf) + }); } #[inline(always)] - fn tx_wr(&self, bits: u8) { - self.$ucaxtxbuf() - .write(|w| unsafe { w.uctxbuf().bits(bits) }); + fn statw_rd(&self) -> ::Statw { + self.$ucaxstatw().read() } #[inline(always)] @@ -119,63 +384,48 @@ macro_rules! eusci_a_impl { } #[inline(always)] - fn txifg_rd(&self) -> bool { - self.$ucaxifg().read().uctxifg().bit() + fn ctl0_reset(&self) { + self.$ucaxctlw0().write(|w| w.ucswrst().set_bit()); } #[inline(always)] - fn rxifg_rd(&self) -> bool { - self.$ucaxifg().read().ucrxifg().bit() + fn brw_settings(&self, ucbr: u16) { + self.$ucaxbrw().write(|w| unsafe { w.bits(ucbr) }); } #[inline(always)] - fn iv_rd(&self) -> u16 { - self.$ucaxiv().read().bits() + fn loopback(&self, loopback: bool) { + self.$ucaxstatw().write(|w| w.uclisten().bit(loopback)); } - } - impl EUsciUart for pac::$EUsci { - type Statw = $Statw; + #[inline(always)] + fn rx_rd(&self) -> u8 { + self.$ucaxrxbuf().read().ucrxbuf().bits() + } #[inline(always)] - fn ctl0_settings(&self, reg: UcxCtl0) { - self.$ucaxctlw0().write(|w| { - w.ucpen() - .bit(reg.ucpen) - .ucpar() - .bit(reg.ucpar) - .ucmsb() - .bit(reg.ucmsb) - .uc7bit() - .bit(reg.uc7bit) - .ucspb() - .bit(reg.ucspb) - .ucssel() - .bits(reg.ucssel as u8) - .ucrxeie() - .bit(reg.ucrxeie) - }); + fn tx_wr(&self, bits: u8) { + self.$ucaxtxbuf() + .write(|w| unsafe { w.uctxbuf().bits(bits) }); } #[inline(always)] - fn mctlw_settings(&self, ucos16: bool, ucbrs: u8, ucbrf: u8) { - self.$ucaxmctlw.write(|w| unsafe { - w.ucos16() - .bit(ucos16) - .ucbrs() - .bits(ucbrs) - .ucbrf() - .bits(ucbrf) - }); + fn txifg_rd(&self) -> bool { + self.$ucaxifg().read().uctxifg().bit() } #[inline(always)] - fn statw_rd(&self) -> Self::Statw { - self.$ucaxstatw().read() + fn rxifg_rd(&self) -> bool { + self.$ucaxifg().read().ucrxifg().bit() + } + + #[inline(always)] + fn iv_rd(&self) -> u16 { + self.$ucaxiv().read().bits() } } - impl UcaxStatw for $Statw { + impl UartUcxStatw for $Statw { #[inline(always)] fn ucfe(&self) -> bool { self.ucfe().bit() @@ -201,9 +451,115 @@ macro_rules! eusci_a_impl { self.ucbusy().bit() } } + }; } +macro_rules! eusci_b_impl { + ($EUsci:ident, $eusci:ident, $ucbxctlw0:ident, $ucbxctlw1:ident, $ucbxbrw:ident, + $ucbxstatw:ident, $ucbxtbcnt:ident, $ucbxrxbuf:ident, $ucbxtxbuf:ident, $ucbxi2coa0:ident, + $ucbxi2coa1:ident, $ucbxi2coa2:ident, $ucbxi2coa3:ident, $ucbxaddrx:ident, $ucbxaddrmask:ident, + $ucbxi2csa:ident, $ucbxie:ident, + $ucbxifg:ident, $ucbxiv:ident, $Statw:ty) => { + eusci_impl!( + $EUsci, + $eusci, + $ucbxctlw0, + $ucbxctlw1, + $ucbxbrw, + $ucbxstatw, + $ucbxrxbuf, + $ucbxtxbuf, + $ucbxie, + $ucbxifg, + $ucbxiv, + $Statw + ); + + + + impl EUsciI2C for pac::$EUsci { + #[inline(always)] + fn ctw0_rd_rst(&self) -> bool{ + self.$ucbxctlw0().read().ucswrst().bit() + } + #[inline(always)] + fn ctw0_wr_rst(&self, bit:bool){ + self.$ucbxctlw0().write(|w| unsafe{w.ucswrst().bit(bit)}) + } + + #[inline(always)] + fn ctw0_rd(&self) -> UcbCtlw0{ + let content = self.$ucbxctlw0().read(); + UcbCtlw0_rd! {content} + } + + #[inline(always)] + fn ctw0_wr(&self, reg:UcbCtlw0){ + self.$ucbxctlw0().write(UcbCtlw0_wr! {reg}); + } + + #[inline(always)] + fn ctw1_rd(&self) -> UcbCtlw1; + #[inline(always)] + fn ctw1_wr(&self, reg:UcbCtlw1); + + #[inline(always)] + fn brw_rd(&self) -> u16; + #[inline(always)] + fn brw_wr(&self, val:u16); + + #[inline(always)] + fn statw_rd(&self) -> UcbStatw; + + #[inline(always)] + fn tbcnt_rd(&self) -> u16; + #[inline(always)] + fn tbcnt_wr(&self, val:u16); + + #[inline(always)] + fn ucrxbuf_rd(&self) -> u8; + #[inline(always)] + fn uctxbuf_wr(&self, val: u8); + + #[inline(always)] + fn i2coa_rd(&self, which:u8) -> UcbI2coa; + #[inline(always)] + fn i2coa_wr(&self, which:u8, reg:UcbI2coa); + + #[inline(always)] + fn addrx_rd(&self) -> u16; + + #[inline(always)] + fn addrmask_rd(&self) -> u16; + #[inline(always)] + fn addrmask_wr(&self, val:u16); + + #[inline(always)] + fn i2csa_rd(&self) -> u16; + #[inline(always)] + fn i2csa_wr(&self, val:u16); + + #[inline(always)] + fn ucbie_rd(&self) -> UcbIe; + #[inline(always)] + fn ucboe_wr(&self, reg:UcbIe); + + #[inline(always)] + fn ucboe_rmw_or(&self, reg:UcbIe); + #[inline(always)] + fn ucboe_rmw_and(&self, reg:UcbIe); + + #[inline(always)] + fn ifg_rd(&self) -> UcbIFG; + #[inline(always)] + fn ifg_wr(&self, reg: UcbIFG); + #[inline(always)] + fn iv_rd(&self) -> UCIV_A; + } + }; +} + eusci_a_impl!( E_USCI_A0, e_usci_a0, @@ -235,3 +591,51 @@ eusci_a_impl!( uca1iv, pac::e_usci_a1::uca1statw::R ); + +eusci_b_impl!( + E_USCI_B0, + e_usci_b0, + ucb0ctlw0, + ucb0ctlw1, + ucb0brw, + ucb0statw, + ucb0tbcnt, + ucb0rxbuf, + ucb0txbuf, + ucb0i2coa0, + ucb0i2coa1, + ucb0i2coa2, + ucb0i2coa3, + ucb0addrx, + ucb0addrmask, + ucb0i2csa, + ucb0ie, + ucb0ifg, + ucb0iv, + pac::e_usci_b0::ucb0statw::R +); + +eusci_b_impl!( + E_USCI_B1, + e_usci_b1, + ucb1ctlw0, + ucb1ctlw1, + ucb1brw, + ucb1statw, + ucb1tbcnt, + ucb1rxbuf, + ucb1txbuf, + ucb1i2coa0, + ucb1i2coa1, + ucb1i2coa2, + ucb1i2coa3, + ucb1addrx, + ucb1addrmask, + ucb1i2csa, + ucb1ie, + ucb1ifg, + ucb1iv, + pac::e_usci_b1::ucb1statw::R +); + + diff --git a/src/i2c.rs b/src/i2c.rs new file mode 100644 index 0000000..9ac0258 --- /dev/null +++ b/src/i2c.rs @@ -0,0 +1,96 @@ +//! I2C +//! +//! Peripherals eUSCI_B0 and eUSCI_B1 can be used for I2C communication. +//! +//! Pins used: +//! +//! eUSCI_B0: {SCL:P1.3, SDA:P1.2} +//! +//! eUSCI_B1: {SCL:P4.7, SDA:P4.6} +//! + +use crate::{ + gpio::{Alternate1, Pin, P1, Pin2, Pin3, Pin6, Pin7}, + hal::blocking::i2c::{Read, Write, WriteRead}, + hw_traits::eusci::{EUsciI2C}, + pac, +}; + +/// Configure bus to use 7bit or 10bit I2C slave addressing mode +#[derive(Clone, Copy)] +pub enum AddressingMode { + /// 7 Bit addressing mode + SevenBit, + /// 10 bit addressing mode + TenBit, +} + +/// Configures bus to act as master or slave I2C device +#[derive(Clone, Copy)] +pub enum MasterSlaveMode { + ///Device acts as master, sends data to slaves. + MasterReceiver, + ///Device acts as master, requests data from slaves. + MasterTransmitter, + /// Device acts as slave. + /// Automatically acts as receiver or transmitter depending on R/W bit from master. + Slave, +} + +/// Configures the automatic glitch filter on the SDA and SCL lines +#[derive(Clone, Copy)] +pub enum GlitchFilter { + ///Pulses of maximum 50-ns length are filtered. + Max50ns, + ///Pulses of maximum 25-ns length are filtered. + Max25ns, + ///Pulses of maximum 12.5-ns length are filtered. + Max12_5ns, + ///Pulses of maximum 6.25-ns length are filtered. + Max6_25ns, +} + +///Struct used to configure a I2C bus +pub struct I2CBusConfig { + usci: USCI, + addressing_mode: AddressingMode, + master_slave_mode: MasterSlaveMode, + glitch_filter: GlitchFilter, + +} + +/// Marks a usci capable of I2C communication +pub trait EUsciI2CBus : EUsciI2C{ + /// I2C SCL + type ClockPin; + /// I2C SDA + type DataPin; +} + +impl EUsciI2CBus for pac::E_USCI_B0 { + type ClockPin = UsciB0SCLPin; + type DataPin = UsciB0SDAPin; +} + +/// I2C SCL pin for eUSCI B0 +pub struct UsciB0SCLPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB0SCLPin { + UsciB0SCLPin + } +} + +/// I2C SDA pin for eUSCI B0 +pub struct UsciB0SDAPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB0SDAPin { + UsciB0SDAPin + } +} + + +impl I2CBusConfig{ + +} diff --git a/src/lib.rs b/src/lib.rs index 3d0801f..b109b49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,4 +44,7 @@ pub mod watchdog; mod hw_traits; mod util; +pub mod i2c; + pub use msp430fr2355 as pac; +pub use embedded_hal as hal; diff --git a/src/serial.rs b/src/serial.rs index 2dca4ef..e3a8d49 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -8,7 +8,7 @@ use crate::clock::{Aclk, Clock, Smclk}; use crate::gpio::{Alternate1, Pin, Pin1, Pin2, Pin3, Pin5, Pin6, Pin7, P1, P4}; -use crate::hw_traits::eusci::{EUsciUart, UcaxStatw, Ucssel, UcxCtl0}; +use crate::hw_traits::eusci::{EUsciUart, UartUcxStatw, Ucssel, UcaCtlw0}; use core::marker::PhantomData; use embedded_hal::serial::{Read, Write}; use msp430fr2355 as pac; @@ -402,7 +402,7 @@ impl SerialConfig { usci.brw_settings(baud_config.br); usci.mctlw_settings(baud_config.ucos16, baud_config.brs, baud_config.brf); usci.loopback(self.loopback.to_bool()); - usci.ctl0_settings(UcxCtl0 { + usci.ctl0_settings(UcaCtlw0 { ucpen: self.parity.ucpen(), ucpar: self.parity.ucpar(), ucmsb: self.order.to_bool(), From 0ada7e4a5d4373e66253d5aabc47fd24a3103efe Mon Sep 17 00:00:00 2001 From: rbneiman Date: Sat, 28 Jan 2023 21:08:57 -0600 Subject: [PATCH 06/82] i2c work --- src/hw_traits/eusci.rs | 398 ++++++++++++++++++++++++++++------------- 1 file changed, 276 insertions(+), 122 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index deb1c4b..04a327c 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -1,17 +1,6 @@ use super::Steal; use msp430fr2355 as pac; -use crate::pac::e_usci_b0::ucb0ctlw1::{UCCLTO_A, UCASTP_A}; -use crate::pac::e_usci_b0::ucb0statw::{UCSCLLOW_A, UCGC_A, UCBBUSY_A}; -use crate::pac::e_usci_b0::ucb0ctlw0::{UCMODE_A}; -use crate::pac::e_usci_b0::ucb0ie::{UCBIT9IE_A, UCTXIE3_A, UCRXIE3_A, UCTXIE2_A, UCRXIE2_A, - UCTXIE1_A, UCRXIE1_A, UCCLTOIE_A, UCBCNTIE_A, UCNACKIE_A, - UCALIE_A, UCSTPIE_A, UCSTTIE_A, UCTXIE0_A, UCRXIE0_A}; -use crate::pac::e_usci_b0::ucb0ifg::{UCBIT9IFG_A, UCTXIFG3_A, UCRXIFG3_A, UCTXIFG2_A, UCRXIFG2_A, - UCTXIFG1_A, UCRXIFG1_A, UCCLTOIFG_A, UCBCNTIFG_A, UCNACKIFG_A, - UCALIFG_A, UCSTPIFG_A, UCSTTIFG_A, UCTXIFG0_A, UCRXIFG0_A}; -use crate::pac::e_usci_b0::ucb0iv::UCIV_A; - macro_rules! from_u8 { ($type: ty) => { @@ -24,68 +13,102 @@ macro_rules! from_u8 { }; } -/// Defines a macro to -/// Also +/// Defines macros for a register associated struct to make reading/writing to this struct's +/// register a lot less tedious. +/// +/// The $macro_rd and $macro_wr inputs are needed due to a limitation in Rust's macro parsing macro_rules! reg_struct { ( pub struct $struct_name : ident, $macro_rd : ident, $macro_wr : ident { - flags{ + $(flags{ $(pub $bool_name : ident : bool,)* - } - vals{ + })? + $(enums{ $(pub $val_name : ident : $val_type : ty : $size : ty,)* - } - + })? + $(ints{ + $(pub $int_name : ident : $int_size : ty,)* + })? } ) => { pub struct $struct_name { - $(pub $bool_name : bool,)* - $(pub $val_name : $val_type ,)* + $($(pub $bool_name : bool,)*)? + $($(pub $val_name : $val_type ,)*)? + $($(pub $int_name : $int_size ,)*)? } macro_rules! $macro_rd { ($reader : expr) => { $struct_name{ - $($bool_name : $reader.$bool_name().bit(),)* - $($val_name : <$val_type>::from(<$size>::from($reader.$val_name().variant())),)* + $($($bool_name : $reader.$bool_name().bit(),)*)? + $($($val_name : <$val_type>::from(<$size>::from($reader.$val_name().variant())),)*)? + $($($int_name : <$int_size>::from($reader.$int_name().bits()),)*)? } }; } macro_rules! $macro_wr { - ($reg : expr) => { |w| { - w$(.$bool_name().bit($reg.$bool_name))* - $(.$val_name().bits($reg.$val_name as $size))* + ($reg : expr) => { |w| unsafe { + w$($(.$bool_name().bit($reg.$bool_name))*)? + $($(.$val_name().bits($reg.$val_name as $size))*)? + $($(.$int_name().bits($reg.$int_name as $int_size))*)? }}; } }; } pub enum Ucssel { - Uclk, - Aclk, - Smclk, + Uclk = 0, + Aclk = 1, + Smclk = 2, } - from_u8!(Ucssel); pub enum Ucmode { - ThreePinSPI, - FourPinSPI_1, - FourPinSPI_0, - I2CMode, + ThreePinSPI = 0, + FourPinSPI_1 = 1, + FourPinSPI_0 = 2, + I2CMode = 3, } - from_u8!(Ucmode); pub enum Ucglit { - Max50ns, - Max25ns, - Max12_5ns, - Max6_25ns, + Max50ns = 0, + Max25ns = 1, + Max12_5ns = 2, + Max6_25ns = 3, } - from_u8!(Ucglit); +/// Clock low timeout select +pub enum Ucclto { + /// Disable clock low time-out counter + Ucclto_00b = 0, + /// 135000 MODCLK cycles (approximately 28 ms) + Ucclto_01b = 1, + /// 150000 MODCLK cycles (approximately 31 ms) + Ucclto_10b = 2, + /// = 165000 MODCLK cycles (approximately 34 ms) + Ucclto_11b = 3, +} +from_u8!(Ucclto); + +/// Automatic STOP condition generation. In slave mode, only settings 00b and 01b +/// are available. +pub enum Ucastp { + /// No automatic STOP generation. The STOP condition is generated after + /// the user sets the UCTXSTP bit. The value in UCBxTBCNT is a don't care. + Ucastp_00b = 0, + /// UCBCNTIFG is set with the byte counter reaches the threshold defined in + /// UCBxTBCNT + Ucastp_01b = 1, + /// A STOP condition is generated automatically after the byte counter value + /// reached UCBxTBCNT. UCBCNTIFG is set with the byte counter reaching the + /// threshold. + Ucastp_10b = 2, +} +from_u8!(Ucastp); + + pub struct UcaCtlw0{ pub ucpen: bool, pub ucpar: bool, @@ -111,71 +134,93 @@ pub struct UcbCtlw0, UcbCtlw0_rd, UcbCtlw0_wr { pub uctxstt: bool, pub ucswrst: bool, } - vals{ + enums{ pub ucmode: Ucmode : u8, pub ucssel: Ucssel : u8, } } } -pub struct UcbCtlw1 { - pub ucetxint: bool, - pub ucclto: UCCLTO_A, - pub ucstpnack: bool, - pub ucswack: bool, - pub ucastp: UCASTP_A, - pub ucglit: Ucglit, +// from_u8!(UCCLTO_A); +// from_u8!(UCASTP_A); + +reg_struct! { +pub struct UcbCtlw1, UcbCtlw1_rd, UcbCtlw1_wr { + flags{ + pub ucetxint: bool, + pub ucstpnack: bool, + pub ucswack: bool, + } + enums{ + pub ucclto: Ucclto : u8, + pub ucastp: Ucastp : u8, + pub ucglit: Ucglit : u8, + } +} } -pub struct UcbStatw { - pub ucbcnt: u8, - pub ucscllow: UCSCLLOW_A, - pub ucgc: UCGC_A, - pub ucbbusy: UCBBUSY_A, +reg_struct! { +pub struct UcbStatw, UcbStatw_rd, UcbStatw_wr{ + flags{ + pub ucscllow: bool, + pub ucgc: bool, + pub ucbbusy: bool, + } + ints { + pub ucbcnt: u8, + } +} } -pub struct UcbI2coa { - /// ucgcen is only available for i2c0a0 +// in order to avoid 4 separate structs, I manually implemented the macro for these registers +pub struct UcbI2coa{ pub ucgcen: bool, pub ucoaen: bool, - /// 10 bits pub i2coa0: u16, } -pub struct UcbIe { - pub ucbit9ie: bool, - pub uctxie3: bool, - pub ucrxie3: bool, - pub uctxie2: bool, - pub ucrxie2: bool, - pub uctxie1: bool, - pub ucrxie1: bool, - pub uccltoie: bool, - pub ucbcntie: bool, - pub ucnackie: bool, - pub ucalie: bool, - pub ucstpie: bool, - pub ucsttie: bool, - pub uctxie0: bool, - pub ucrxie0: bool, +reg_struct! { +pub struct UcbIe, UcbIe_rd, UcbIe_wr { + flags{ + pub ucbit9ie: bool, + pub uctxie3: bool, + pub ucrxie3: bool, + pub uctxie2: bool, + pub ucrxie2: bool, + pub uctxie1: bool, + pub ucrxie1: bool, + pub uccltoie: bool, + pub ucbcntie: bool, + pub ucnackie: bool, + pub ucalie: bool, + pub ucstpie: bool, + pub ucsttie: bool, + pub uctxie0: bool, + pub ucrxie0: bool, + } +} } -pub struct UcbIFG { - pub ucbit9ifg: bool, - pub uctxifg3: bool, - pub ucrxifg3: bool, - pub uctxifg2: bool, - pub ucrxifg2: bool, - pub uctxifg1: bool, - pub ucrxifg1: bool, - pub uccltoifg: bool, - pub ucbcntifg: bool, - pub ucnackifg: bool, - pub ucalifg: bool, - pub ucstpifg: bool, - pub ucsttifg: bool, - pub uctxifg0: bool, - pub ucrxifg0: bool, +reg_struct! { +pub struct UcbIFG, UcbIFG_rd, UcbIFG_wr{ + flags{ + pub ucbit9ifg: bool, + pub uctxifg3: bool, + pub ucrxifg3: bool, + pub uctxifg2: bool, + pub ucrxifg2: bool, + pub uctxifg1: bool, + pub ucrxifg1: bool, + pub uccltoifg: bool, + pub ucbcntifg: bool, + pub ucnackifg: bool, + pub ucalifg: bool, + pub ucstpifg: bool, + pub ucsttifg: bool, + pub uctxifg0: bool, + pub ucrxifg0: bool, + } +} } pub trait EUsci : Steal { @@ -249,21 +294,21 @@ pub trait EUsciI2C: EUsci { fn addrx_rd(&self) -> u16; // Modify only when UCSWRST = 1 - fn addrmask_rd(&self) -> u16; - fn addrmask_wr(&self, val:u16); + fn addmask_rd(&self) -> u16; + fn addmask_wr(&self, val:u16); fn i2csa_rd(&self) -> u16; fn i2csa_wr(&self, val:u16); fn ucbie_rd(&self) -> UcbIe; - fn ucboe_wr(&self, reg:UcbIe); + fn ucbie_wr(&self, reg:UcbIe); //some common bitwise operations for this register - fn ucboe_rmw_or(&self, reg:UcbIe); - fn ucboe_rmw_and(&self, reg:UcbIe); + fn ucbie_rmw_or(&self, reg:UcbIe); + fn ucbie_rmw_and(&self, reg:UcbIe); fn ifg_rd(&self) -> UcbIFG; fn ifg_wr(&self, reg: UcbIFG); - fn iv_rd(&self) -> UCIV_A; + fn iv_rd(&self) -> u16; } pub trait UcxStatw { @@ -458,7 +503,7 @@ macro_rules! eusci_a_impl { macro_rules! eusci_b_impl { ($EUsci:ident, $eusci:ident, $ucbxctlw0:ident, $ucbxctlw1:ident, $ucbxbrw:ident, $ucbxstatw:ident, $ucbxtbcnt:ident, $ucbxrxbuf:ident, $ucbxtxbuf:ident, $ucbxi2coa0:ident, - $ucbxi2coa1:ident, $ucbxi2coa2:ident, $ucbxi2coa3:ident, $ucbxaddrx:ident, $ucbxaddrmask:ident, + $ucbxi2coa1:ident, $ucbxi2coa2:ident, $ucbxi2coa3:ident, $ucbxaddrx:ident, $ucbxaddmask:ident, $ucbxi2csa:ident, $ucbxie:ident, $ucbxifg:ident, $ucbxiv:ident, $Statw:ty) => { eusci_impl!( @@ -500,62 +545,171 @@ macro_rules! eusci_b_impl { } #[inline(always)] - fn ctw1_rd(&self) -> UcbCtlw1; + fn ctw1_rd(&self) -> UcbCtlw1{ + let content = self.$ucbxctlw1.read(); + UcbCtlw1_rd! {content} + } + #[inline(always)] - fn ctw1_wr(&self, reg:UcbCtlw1); + fn ctw1_wr(&self, reg:UcbCtlw1){ + self.$ucbxctlw1.write(UcbCtlw1_wr! {reg}); + } #[inline(always)] - fn brw_rd(&self) -> u16; + fn brw_rd(&self) -> u16{ + self.$ucbxbrw().read().bits() + } #[inline(always)] - fn brw_wr(&self, val:u16); + fn brw_wr(&self, val:u16){ + self.$ucbxbrw().write(|w| unsafe { w.bits(val) }); + } #[inline(always)] - fn statw_rd(&self) -> UcbStatw; + fn statw_rd(&self) -> UcbStatw{ + let content = self.$ucbxstatw().read(); + UcbStatw_rd! {content} + } #[inline(always)] - fn tbcnt_rd(&self) -> u16; + fn tbcnt_rd(&self) -> u16{ + self.$ucbxtbcnt.read().bits() + } #[inline(always)] - fn tbcnt_wr(&self, val:u16); + fn tbcnt_wr(&self, val:u16){ + self.$ucbxtbcnt.write(|w| unsafe { w.bits(val) }); + } #[inline(always)] - fn ucrxbuf_rd(&self) -> u8; + fn ucrxbuf_rd(&self) -> u8{ + self.$ucbxrxbuf().read().bits() as u8 + } #[inline(always)] - fn uctxbuf_wr(&self, val: u8); + fn uctxbuf_wr(&self, val: u8){ + self.$ucbxrxbuf().write(|w| unsafe { w.bits(val as u16) }); + } - #[inline(always)] - fn i2coa_rd(&self, which:u8) -> UcbI2coa; - #[inline(always)] - fn i2coa_wr(&self, which:u8, reg:UcbI2coa); + fn i2coa_rd(&self, which:u8) -> UcbI2coa{ + match which { + 1 => { + let content = self.$ucbxi2coa1.read(); + UcbI2coa{ + ucgcen : false, + ucoaen : content.ucoaen().bit(), + i2coa0 : ::from(content.i2coa1().bits()), + } + } + 2 => { + let content = self.$ucbxi2coa2.read(); + UcbI2coa{ + ucgcen : false, + ucoaen : content.ucoaen().bit(), + i2coa0 : ::from(content.i2coa2().bits()), + } + } + 3=>{ + let content = self.$ucbxi2coa3.read(); + UcbI2coa{ + ucgcen : false, + ucoaen : content.ucoaen().bit(), + i2coa0 : ::from(content.i2coa3().bits()), + } + } + _ =>{ + let content = self.$ucbxi2coa0.read(); + UcbI2coa{ + ucgcen : content.ucgcen().bit(), + ucoaen : content.ucoaen().bit(), + i2coa0 : ::from(content.i2coa0().bits()), + } + } + } + } + + fn i2coa_wr(&self, which:u8, reg:UcbI2coa){ + match which { + 1 => { + self.$ucbxi2coa1.write(|w| unsafe { + w.ucoaen().bit(reg.ucoaen) + .i2coa1().bits(reg.i2coa0 as u16) + }); + } + 2 => { + self.$ucbxi2coa2.write(|w| unsafe { + w.ucoaen().bit(reg.ucoaen) + .i2coa2().bits(reg.i2coa0 as u16) + }); + } + 3=>{ + self.$ucbxi2coa3.write(|w| unsafe { + w.ucoaen().bit(reg.ucoaen) + .i2coa3().bits(reg.i2coa0 as u16) + }); + } + _ =>{ + self.$ucbxi2coa0.write(|w| unsafe { + w.ucgcen().bit(reg.ucgcen) + .ucoaen().bit(reg.ucoaen) + .i2coa0().bits(reg.i2coa0 as u16) + }); + } + } + } #[inline(always)] - fn addrx_rd(&self) -> u16; + fn addrx_rd(&self) -> u16{ + self.$ucbxaddrx.read().bits() + } #[inline(always)] - fn addrmask_rd(&self) -> u16; + fn addmask_rd(&self) -> u16{ + self.$ucbxaddmask.read().bits() + } #[inline(always)] - fn addrmask_wr(&self, val:u16); + fn addmask_wr(&self, val:u16){ + self.$ucbxaddmask.write(|w| unsafe { w.bits(val) }); + } #[inline(always)] - fn i2csa_rd(&self) -> u16; + fn i2csa_rd(&self) -> u16{ + self.$ucbxi2csa.read().bits() + } #[inline(always)] - fn i2csa_wr(&self, val:u16); + fn i2csa_wr(&self, val:u16){ + self.$ucbxi2csa.write(|w| unsafe { w.bits(val) }); + } #[inline(always)] - fn ucbie_rd(&self) -> UcbIe; + fn ucbie_rd(&self) -> UcbIe{ + let content = self.$ucbxie().read(); + UcbIe_rd! {content} + } #[inline(always)] - fn ucboe_wr(&self, reg:UcbIe); - + fn ucbie_wr(&self, reg:UcbIe){ + self.$ucbxie().write(UcbIe_wr! {reg}); + } #[inline(always)] - fn ucboe_rmw_or(&self, reg:UcbIe); + fn ucbie_rmw_or(&self, reg:UcbIe){ + //TODO + } #[inline(always)] - fn ucboe_rmw_and(&self, reg:UcbIe); + fn ucbie_rmw_and(&self, reg:UcbIe){ + //TODO + } #[inline(always)] - fn ifg_rd(&self) -> UcbIFG; + fn ifg_rd(&self) -> UcbIFG{ + let content = self.$ucbxifg().read(); + UcbIFG_rd! {content} + } #[inline(always)] - fn ifg_wr(&self, reg: UcbIFG); + fn ifg_wr(&self, reg: UcbIFG){ + self.$ucbxifg().write(UcbIFG_wr! {reg}); + } + #[inline(always)] - fn iv_rd(&self) -> UCIV_A; + fn iv_rd(&self) -> u16{ + self.$ucbxiv().read().bits() + } } }; } @@ -607,7 +761,7 @@ eusci_b_impl!( ucb0i2coa2, ucb0i2coa3, ucb0addrx, - ucb0addrmask, + ucb0addmask, ucb0i2csa, ucb0ie, ucb0ifg, @@ -630,7 +784,7 @@ eusci_b_impl!( ucb1i2coa2, ucb1i2coa3, ucb1addrx, - ucb1addrmask, + ucb1addmask, ucb1i2csa, ucb1ie, ucb1ifg, From d0608b276270adbd4a25376d1b917a74cb26b85a Mon Sep 17 00:00:00 2001 From: rbneiman Date: Mon, 20 Feb 2023 22:14:46 -0600 Subject: [PATCH 07/82] i2c work --- src/hw_traits/eusci.rs | 200 ++++++++++++++--- src/i2c.rs | 478 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 622 insertions(+), 56 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 04a327c..fc9454c 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -17,25 +17,40 @@ macro_rules! from_u8 { /// register a lot less tedious. /// /// The $macro_rd and $macro_wr inputs are needed due to a limitation in Rust's macro parsing +/// where it isn't able to create new tokens. macro_rules! reg_struct { ( + $(#[$attr:meta])* pub struct $struct_name : ident, $macro_rd : ident, $macro_wr : ident { $(flags{ - $(pub $bool_name : ident : bool,)* + $(pub $bool_name : ident : bool, $(#[$f_attr:meta])*)* })? $(enums{ - $(pub $val_name : ident : $val_type : ty : $size : ty,)* + $(pub $val_name : ident : $val_type : ty : $size : ty, $(#[$e_attr:meta])*)* })? $(ints{ - $(pub $int_name : ident : $int_size : ty,)* + $(pub $int_name : ident : $int_size : ty, $(#[$i_attr:meta])*)* })? } ) => { + $(#[$attr:meta])* pub struct $struct_name { - $($(pub $bool_name : bool,)*)? - $($(pub $val_name : $val_type ,)*)? - $($(pub $int_name : $int_size ,)*)? + $($(pub $bool_name : bool, $(#[$f_attr])*)*)? + $($(pub $val_name : $val_type , $(#[$e_attr])*)*)? + $($(pub $int_name : $int_size , $(#[$i_attr])*)*)? } + + // + // impl $struct_name{ + // fn default()->Self{ + // $struct_name{ + // $($($bool_name : false,)*)? + // $($($val_name : 0.into(),)*)? + // $($($int_name : 0,)*)? + // } + // } + // } + macro_rules! $macro_rd { ($reader : expr) => { $struct_name{ @@ -56,6 +71,7 @@ macro_rules! reg_struct { }; } +#[derive(Copy, Clone)] pub enum Ucssel { Uclk = 0, Aclk = 1, @@ -63,6 +79,7 @@ pub enum Ucssel { } from_u8!(Ucssel); +#[derive(Copy, Clone)] pub enum Ucmode { ThreePinSPI = 0, FourPinSPI_1 = 1, @@ -71,6 +88,7 @@ pub enum Ucmode { } from_u8!(Ucmode); +#[derive(Copy, Clone)] pub enum Ucglit { Max50ns = 0, Max25ns = 1, @@ -80,6 +98,7 @@ pub enum Ucglit { from_u8!(Ucglit); /// Clock low timeout select +#[derive(Copy, Clone)] pub enum Ucclto { /// Disable clock low time-out counter Ucclto_00b = 0, @@ -94,11 +113,12 @@ from_u8!(Ucclto); /// Automatic STOP condition generation. In slave mode, only settings 00b and 01b /// are available. +#[derive(Copy, Clone)] pub enum Ucastp { /// No automatic STOP generation. The STOP condition is generated after /// the user sets the UCTXSTP bit. The value in UCBxTBCNT is a don't care. Ucastp_00b = 0, - /// UCBCNTIFG is set with the byte counter reaches the threshold defined in + /// UCBCNTIFG is set when the byte counter reaches the threshold defined in /// UCBxTBCNT Ucastp_01b = 1, /// A STOP condition is generated automatically after the byte counter value @@ -260,6 +280,22 @@ pub trait EUsciUart: EUsci { } pub trait EUsciI2C: EUsci { + type IfgOut : I2CUcbIfg_out; + + fn transmit_ack(&self); + fn transmit_nack(&self); + fn transmit_start(&self); + fn transmit_stop(&self); + + fn uctxstp_rd(&self) -> bool; + + fn set_ucsla10(&self, bit:bool); + fn set_uctr(&self, bit:bool); + + fn txifg0_rd(&self) -> bool; + fn rxifg0_rd(&self) -> bool; + + //Register read/write functions // Read or write to UCSWRST fn ctw0_rd_rst(&self) -> bool; @@ -267,11 +303,11 @@ pub trait EUsciI2C: EUsci { // Modify only when UCSWRST = 1 fn ctw0_rd(&self) -> UcbCtlw0; - fn ctw0_wr(&self, reg:UcbCtlw0); + fn ctw0_wr(&self, reg:&UcbCtlw0); // Modify only when UCSWRST = 1 fn ctw1_rd(&self) -> UcbCtlw1; - fn ctw1_wr(&self, reg:UcbCtlw1); + fn ctw1_wr(&self, reg:&UcbCtlw1); // Modify only when UCSWRST = 1 fn brw_rd(&self) -> u16; @@ -289,7 +325,7 @@ pub trait EUsciI2C: EUsci { // Modify only when UCSWRST = 1 // the which parameter is used to select one of the 4 registers fn i2coa_rd(&self, which:u8) -> UcbI2coa; - fn i2coa_wr(&self, which:u8, reg:UcbI2coa); + fn i2coa_wr(&self, which:u8, reg:&UcbI2coa); fn addrx_rd(&self) -> u16; @@ -300,14 +336,14 @@ pub trait EUsciI2C: EUsci { fn i2csa_rd(&self) -> u16; fn i2csa_wr(&self, val:u16); - fn ucbie_rd(&self) -> UcbIe; - fn ucbie_wr(&self, reg:UcbIe); + fn ie_rd(&self) -> UcbIe; + fn ie_wr(&self, reg:&UcbIe); //some common bitwise operations for this register - fn ucbie_rmw_or(&self, reg:UcbIe); - fn ucbie_rmw_and(&self, reg:UcbIe); + fn ie_rmw_or(&self, reg:&UcbIe); + fn ie_rmw_and(&self, reg:&UcbIe); - fn ifg_rd(&self) -> UcbIFG; - fn ifg_wr(&self, reg: UcbIFG); + fn ifg_rd(&self) -> Self::IfgOut; + fn ifg_wr(&self, reg:&UcbIFG); fn iv_rd(&self) -> u16; } @@ -323,8 +359,21 @@ pub trait UartUcxStatw : UcxStatw { fn ucbusy(&self) -> bool; } -pub trait I2CUcxStatw : UcxStatw { - +pub trait I2CUcbIfg_out { + /// Byte counter interrupt flag + fn ucbcntifg(&self) -> bool; + /// Not-acknowledge received interrupt flag + fn ucnackifg(&self) -> bool; + /// Arbitration lost interrupt flag + fn ucalifg(&self) -> bool; + /// STOP condition interrupt flag + fn ucstpifg(&self) -> bool; + /// START condition interrupt flag + fn ucsttifg(&self) -> bool; + /// eUSCI_B transmit interrupt flag 0. (UCBxTXBUF is empty) + fn uctxifg0(&self) -> bool; + /// eUSCI_B receive interrupt flag 0. (complete character present in UCBxRXBUF) + fn ucrxifg0(&self) -> bool; } macro_rules! eusci_impl { @@ -505,7 +554,7 @@ macro_rules! eusci_b_impl { $ucbxstatw:ident, $ucbxtbcnt:ident, $ucbxrxbuf:ident, $ucbxtxbuf:ident, $ucbxi2coa0:ident, $ucbxi2coa1:ident, $ucbxi2coa2:ident, $ucbxi2coa3:ident, $ucbxaddrx:ident, $ucbxaddmask:ident, $ucbxi2csa:ident, $ucbxie:ident, - $ucbxifg:ident, $ucbxiv:ident, $Statw:ty) => { + $ucbxifg:ident, $ucbxiv:ident, $Statw:ty, $Ifg:ty) => { eusci_impl!( $EUsci, $eusci, @@ -524,15 +573,63 @@ macro_rules! eusci_b_impl { impl EUsciI2C for pac::$EUsci { + type IfgOut = $Ifg; + #[inline(always)] fn ctw0_rd_rst(&self) -> bool{ self.$ucbxctlw0().read().ucswrst().bit() } + #[inline(always)] fn ctw0_wr_rst(&self, bit:bool){ self.$ucbxctlw0().write(|w| unsafe{w.ucswrst().bit(bit)}) } + #[inline(always)] + fn transmit_ack(&self){ + self.$ucbxctlw0().write(|w| unsafe{w.uctxack().bit(true)}) + } + + #[inline(always)] + fn transmit_nack(&self){ + self.$ucbxctlw0().write(|w| unsafe{w.uctxnack().bit(true)}) + } + + #[inline(always)] + fn transmit_start(&self){ + self.$ucbxctlw0().write(|w| unsafe{w.uctxstt().bit(true)}) + } + + #[inline(always)] + fn transmit_stop(&self){ + self.$ucbxctlw0().write(|w| unsafe{w.uctxstp().bit(true)}) + } + + #[inline(always)] + fn uctxstp_rd(&self) -> bool{ + self.$ucbxctlw0().read().uctxstp().bit() + } + + #[inline(always)] + fn set_ucsla10(&self, bit:bool){ + self.$ucbxctlw0().write(|w| unsafe{w.ucsla10().bit(bit)}) + } + + #[inline(always)] + fn set_uctr(&self, bit:bool){ + self.$ucbxctlw0().write(|w| unsafe{w.uctr().bit(bit)}) + } + + #[inline(always)] + fn txifg0_rd(&self) -> bool{ + self.$ucbxifg().read().uctxifg0().bit() + } + + #[inline(always)] + fn rxifg0_rd(&self) -> bool{ + self.$ucbxifg().read().ucrxifg0().bit() + } + #[inline(always)] fn ctw0_rd(&self) -> UcbCtlw0{ let content = self.$ucbxctlw0().read(); @@ -540,7 +637,7 @@ macro_rules! eusci_b_impl { } #[inline(always)] - fn ctw0_wr(&self, reg:UcbCtlw0){ + fn ctw0_wr(&self, reg:&UcbCtlw0){ self.$ucbxctlw0().write(UcbCtlw0_wr! {reg}); } @@ -551,7 +648,7 @@ macro_rules! eusci_b_impl { } #[inline(always)] - fn ctw1_wr(&self, reg:UcbCtlw1){ + fn ctw1_wr(&self, reg:&UcbCtlw1){ self.$ucbxctlw1.write(UcbCtlw1_wr! {reg}); } @@ -625,7 +722,7 @@ macro_rules! eusci_b_impl { } } - fn i2coa_wr(&self, which:u8, reg:UcbI2coa){ + fn i2coa_wr(&self, which:u8, reg:&UcbI2coa){ match which { 1 => { self.$ucbxi2coa1.write(|w| unsafe { @@ -679,30 +776,30 @@ macro_rules! eusci_b_impl { } #[inline(always)] - fn ucbie_rd(&self) -> UcbIe{ + fn ie_rd(&self) -> UcbIe{ let content = self.$ucbxie().read(); UcbIe_rd! {content} } #[inline(always)] - fn ucbie_wr(&self, reg:UcbIe){ + fn ie_wr(&self, reg:&UcbIe){ self.$ucbxie().write(UcbIe_wr! {reg}); } #[inline(always)] - fn ucbie_rmw_or(&self, reg:UcbIe){ + fn ie_rmw_or(&self, reg:&UcbIe){ //TODO } #[inline(always)] - fn ucbie_rmw_and(&self, reg:UcbIe){ + fn ie_rmw_and(&self, reg:&UcbIe){ //TODO } #[inline(always)] - fn ifg_rd(&self) -> UcbIFG{ - let content = self.$ucbxifg().read(); - UcbIFG_rd! {content} + fn ifg_rd(&self) -> Self::IfgOut{ + self.$ucbxifg().read() } + #[inline(always)] - fn ifg_wr(&self, reg: UcbIFG){ + fn ifg_wr(&self, reg:&UcbIFG){ self.$ucbxifg().write(UcbIFG_wr! {reg}); } @@ -711,6 +808,43 @@ macro_rules! eusci_b_impl { self.$ucbxiv().read().bits() } } + + impl I2CUcbIfg_out for $Ifg{ + #[inline(always)] + fn ucbcntifg(&self) -> bool{ + self.ucbcntifg().bit() + } + + #[inline(always)] + fn ucnackifg(&self) -> bool{ + self.ucnackifg().bit() + } + + #[inline(always)] + fn ucalifg(&self) -> bool{ + self.ucalifg().bit() + } + + #[inline(always)] + fn ucstpifg(&self) -> bool{ + self.ucstpifg().bit() + } + + #[inline(always)] + fn ucsttifg(&self) -> bool{ + self.ucsttifg().bit() + } + + #[inline(always)] + fn uctxifg0(&self) -> bool{ + self.uctxifg0().bit() + } + + #[inline(always)] + fn ucrxifg0(&self) -> bool{ + self.ucrxifg0().bit() + } + } }; } @@ -766,7 +900,8 @@ eusci_b_impl!( ucb0ie, ucb0ifg, ucb0iv, - pac::e_usci_b0::ucb0statw::R + pac::e_usci_b0::ucb0statw::R, + pac::e_usci_b0::ucb0ifg::R ); eusci_b_impl!( @@ -789,7 +924,8 @@ eusci_b_impl!( ucb1ie, ucb1ifg, ucb1iv, - pac::e_usci_b1::ucb1statw::R + pac::e_usci_b1::ucb1statw::R, + pac::e_usci_b1::ucb1ifg::R ); diff --git a/src/i2c.rs b/src/i2c.rs index 9ac0258..bf82c6d 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -9,54 +9,86 @@ //! eUSCI_B1: {SCL:P4.7, SDA:P4.6} //! +use core::marker::PhantomData; use crate::{ - gpio::{Alternate1, Pin, P1, Pin2, Pin3, Pin6, Pin7}, - hal::blocking::i2c::{Read, Write, WriteRead}, - hw_traits::eusci::{EUsciI2C}, - pac, + gpio::{Alternate1, Pin, P1, P4, Pin2, Pin3, Pin6, Pin7}, + hal::blocking::i2c::{Read, Write, WriteRead, WriteIter, Operation, TransactionalIter, + SevenBitAddress, TenBitAddress, Transactional}, + hw_traits::eusci::{EUsciI2C, UcbCtlw0, UcbCtlw1, UcbI2coa, UcbIe, UcbIFG, UcbStatw, Ucssel, + Ucmode, Ucglit, Ucclto, Ucastp}, + pac }; +use crate::hw_traits::eusci::I2CUcbIfg_out; /// Configure bus to use 7bit or 10bit I2C slave addressing mode #[derive(Clone, Copy)] -pub enum AddressingMode { +enum AddressingMode { /// 7 Bit addressing mode - SevenBit, + SevenBit = 0, /// 10 bit addressing mode - TenBit, + TenBit = 1, } -/// Configures bus to act as master or slave I2C device +impl From for bool { + fn from(f: AddressingMode) -> bool { + match f { + AddressingMode::SevenBit => false, + AddressingMode::TenBit => true, + } + } +} + +/// Configure between master receiver and master transmitter modes #[derive(Clone, Copy)] -pub enum MasterSlaveMode { - ///Device acts as master, sends data to slaves. - MasterReceiver, - ///Device acts as master, requests data from slaves. - MasterTransmitter, - /// Device acts as slave. - /// Automatically acts as receiver or transmitter depending on R/W bit from master. - Slave, +enum TransmissionMode { + /// master receiver mode + Receive = 0, + /// master transmitter mode + Transmit = 1, +} + +impl From for bool { + fn from(f: TransmissionMode) -> bool { + match f { + TransmissionMode::Receive => false, + TransmissionMode::Transmit => true, + } + } } -/// Configures the automatic glitch filter on the SDA and SCL lines +/// Configure the automatic glitch filter on the SDA and SCL lines #[derive(Clone, Copy)] pub enum GlitchFilter { ///Pulses of maximum 50-ns length are filtered. - Max50ns, + Max50ns = 0, ///Pulses of maximum 25-ns length are filtered. - Max25ns, + Max25ns = 1, ///Pulses of maximum 12.5-ns length are filtered. - Max12_5ns, + Max12_5ns = 2, ///Pulses of maximum 6.25-ns length are filtered. - Max6_25ns, + Max6_25ns = 3, +} + +impl From for Ucglit { + fn from(f: GlitchFilter) -> Ucglit { + Ucglit::from(f as u8) + } } ///Struct used to configure a I2C bus pub struct I2CBusConfig { usci: USCI, - addressing_mode: AddressingMode, - master_slave_mode: MasterSlaveMode, - glitch_filter: GlitchFilter, + baud_rate: u16, + // Register configs + ctlw0: UcbCtlw0, + ctlw1: UcbCtlw1, + i2coa0: UcbI2coa, + i2coa1: UcbI2coa, + i2coa2: UcbI2coa, + i2coa3: UcbI2coa, + ie: UcbIe, + ifg: UcbIFG, } /// Marks a usci capable of I2C communication @@ -72,6 +104,11 @@ impl EUsciI2CBus for pac::E_USCI_B0 { type DataPin = UsciB0SDAPin; } +impl EUsciI2CBus for pac::E_USCI_B1 { + type ClockPin = UsciB1SCLPin; + type DataPin = UsciB1SDAPin; +} + /// I2C SCL pin for eUSCI B0 pub struct UsciB0SCLPin; impl Into for Pin> { @@ -90,7 +127,400 @@ impl Into for Pin> { } } +/// I2C SCL pin for eUSCI B1 +pub struct UsciB1SCLPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB1SCLPin { + UsciB1SCLPin + } +} + +/// I2C SDA pin for eUSCI B1 +pub struct UsciB1SDAPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB1SDAPin { + UsciB1SDAPin + } +} impl I2CBusConfig{ + /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode + pub fn new( + usci: USCI, + clk_source: Ucssel, + baud_rate: u16, + )->Self{ + + let ctlw0 = UcbCtlw0{ + uca10: false, + ucsla10: false, + ucmm: false, + ucmst: true, + ucsync: true, + uctxack: false, + uctr: false, + uctxnack: false, + uctxstp: false, + uctxstt: false, + ucswrst: true, + ucmode: Ucmode::I2CMode, + ucssel: clk_source, + }; + + let ctlw1 = UcbCtlw1{ + ucetxint: false, + ucstpnack: false, + ucswack: false, + ucclto: Ucclto::Ucclto_00b, + ucastp: Ucastp::Ucastp_00b, + ucglit: Ucglit::Max6_25ns, + }; + + let i2coa0 = UcbI2coa{ + ucgcen: false, + ucoaen: false, + i2coa0: 0, + }; + + let i2coa1 = UcbI2coa{ + ucgcen: false, + ucoaen: false, + i2coa0: 0, + }; + + let i2coa2 = UcbI2coa{ + ucgcen: false, + ucoaen: false, + i2coa0: 0, + }; + + let i2coa3 = UcbI2coa{ + ucgcen: false, + ucoaen: false, + i2coa0: 0, + }; + + let ie = UcbIe{ + ucbit9ie: false, + uctxie3: false, + ucrxie3: false, + uctxie2: false, + ucrxie2: false, + uctxie1: false, + ucrxie1: false, + uccltoie: false, + ucbcntie: false, + ucnackie: false, + ucalie: false, + ucstpie: false, + ucsttie: false, + uctxie0: false, + ucrxie0: false, + }; + + let ifg = UcbIFG{ + ucbit9ifg: false, + uctxifg3: false, + ucrxifg3: false, + uctxifg2: false, + ucrxifg2: false, + uctxifg1: false, + ucrxifg1: false, + uccltoifg: false, + ucbcntifg: false, + ucnackifg: false, + ucalifg: false, + ucstpifg: false, + ucsttifg: false, + uctxifg0: false, + ucrxifg0: false, + }; + + I2CBusConfig{ + usci, + baud_rate: baud_rate, + ctlw0: ctlw0, + ctlw1: ctlw1, + i2coa0: i2coa0, + i2coa1: i2coa1, + i2coa2: i2coa2, + i2coa3: i2coa3, + ie: ie, + ifg: ifg, + } + } + + /// Configures the glitch filter length for the SDA and SCL lines + #[inline(always)] + pub fn set_deglitch_time(&mut self, deglitch_time:GlitchFilter){ + self.ctlw1.ucglit = deglitch_time.into(); + } + + /// Performs hardware confiuration and creates SDL pin + pub fn configure(&self) -> SDL{ + self.usci.ctw0_wr_rst(true); + + self.usci.ctw0_wr(&self.ctlw0); + self.usci.ctw1_wr(&self.ctlw1); + self.usci.i2coa_wr(0, &self.i2coa0); + self.usci.i2coa_wr(1, &self.i2coa1); + self.usci.i2coa_wr(2, &self.i2coa2); + self.usci.i2coa_wr(3, &self.i2coa3); + self.usci.ie_wr(&self.ie); + self.usci.ifg_wr(&self.ifg); + + self.usci.brw_wr(self.baud_rate); + self.usci.tbcnt_wr(0); + + self.usci.ctw0_wr_rst(false); + + SDL(PhantomData) + } + +} + +/// I2C data pin +pub struct SDL(PhantomData); + +/// I2C transmit/receive errors +#[derive(Clone, Copy)] +pub enum I2CErr{ + /// Function not implemented + Unimplemented = 0, + /// Address was never acknolwedged by slave + GotNACK, + /// Device lost arbitration + ArbitrationLost, + +} + +impl SDL{ + + #[inline(always)] + fn set_addressing_mode(&mut self, mode:AddressingMode){ + let usci = unsafe { USCI::steal() }; + usci.set_ucsla10(mode.into()) + } + + #[inline(always)] + fn set_transmission_mode(&mut self, mode:TransmissionMode){ + let usci = unsafe { USCI::steal() }; + usci.set_uctr(mode.into()) + } + + /// Blocking read + fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), I2CErr>{ + let usci = unsafe { USCI::steal() }; + usci.i2csa_wr(address); + usci.transmit_start(); + let mut ifg = usci.ifg_rd(); + while ifg.ucsttifg() {ifg = usci.ifg_rd();} + if ifg.ucnackifg() { + Err::<(), I2CErr>(I2CErr::GotNACK); + } + + for i in 0 .. buffer.len()-2 { + while !ifg.ucrxifg0() { + ifg = usci.ifg_rd(); + + } + + buffer[i] = usci.ucrxbuf_rd(); + } + usci.transmit_stop(); + while !ifg.ucstpifg() {ifg = usci.ifg_rd();} + buffer[buffer.len()-1] = usci.ucrxbuf_rd(); + + Ok(()) + } + /// Blocking write + fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), I2CErr>{ + let usci = unsafe { USCI::steal() }; + usci.i2csa_wr(address); + usci.transmit_start(); + let mut ifg = usci.ifg_rd(); + + while !ifg.uctxifg0() {ifg = usci.ifg_rd();} + usci.uctxbuf_wr(bytes[0]); + + while ifg.ucsttifg() {ifg = usci.ifg_rd();} + if ifg.ucnackifg() { + Err::<(), I2CErr>(I2CErr::GotNACK); + } + + for i in 1 .. bytes.len()-1 { + while !ifg.uctxifg0() {ifg = usci.ifg_rd();} + usci.uctxbuf_wr(bytes[i]); + } + usci.transmit_stop(); + while !ifg.ucstpifg() {ifg = usci.ifg_rd();} + + Ok(()) + } + + fn writeIter(&mut self, address: u16, bytes: B) -> Result<(), I2CErr> + where + B: IntoIterator{ + + Err(I2CErr::Unimplemented) + } + + /// blocking write then blocking read + fn write_read( + &mut self, + address: u16, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), I2CErr>{ + self.set_transmission_mode(TransmissionMode::Transmit); + let result = self.read(address, buffer); + if result.is_err() { + return result; + } + self.set_transmission_mode(TransmissionMode::Receive); + self.write(address, bytes) + } + + fn exec<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) + -> Result<(), I2CErr>{ + Err(I2CErr::Unimplemented) + } + + fn exec_iter<'a, O>(&mut self, address: u16, operations: O) -> Result<(), I2CErr> + where + O: IntoIterator>{ + Err(I2CErr::Unimplemented) + } } + + +impl Read for SDL{ + type Error = I2CErr; + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::SevenBit); + self.set_transmission_mode(TransmissionMode::Receive); + SDL::read(self, address as u16, buffer) + } +} + +impl Read for SDL{ + type Error = I2CErr; + fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::TenBit); + self.set_transmission_mode(TransmissionMode::Receive); + SDL::read(self, address, buffer) + } +} + +impl Write for SDL{ + type Error = I2CErr; + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::SevenBit); + self.set_transmission_mode(TransmissionMode::Transmit); + SDL::write(self, address as u16, bytes) + } +} + +impl Write for SDL{ + type Error = I2CErr; + fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::TenBit); + self.set_transmission_mode(TransmissionMode::Transmit); + SDL::write(self, address, bytes) + } +} + +impl WriteIter for SDL{ + type Error = I2CErr; + fn write(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator{ + self.set_addressing_mode(AddressingMode::SevenBit); + self.set_transmission_mode(TransmissionMode::Transmit); + SDL::writeIter(self, address as u16, bytes) + } +} + +impl WriteIter for SDL{ + type Error = I2CErr; + fn write(&mut self, address: u16, bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator{ + self.set_addressing_mode(AddressingMode::TenBit); + self.set_transmission_mode(TransmissionMode::Transmit); + SDL::writeIter(self, address, bytes) + } +} + +impl WriteRead for SDL{ + type Error = I2CErr; + fn write_read( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::SevenBit); + SDL::write_read(self, address as u16, bytes, buffer) + } +} + +impl WriteRead for SDL{ + type Error = I2CErr; + fn write_read( + &mut self, + address: u16, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::TenBit); + SDL::write_read(self, address, bytes, buffer) + } +} + +impl Transactional for SDL{ + type Error = I2CErr; + fn exec<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) + -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::SevenBit); + SDL::exec(self, address as u16, operations) + } +} + +impl Transactional for SDL{ + type Error = I2CErr; + fn exec<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) + -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::TenBit); + SDL::exec(self, address, operations) + } +} + +impl TransactionalIter for SDL { + type Error = I2CErr; + fn exec_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>{ + self.set_addressing_mode(AddressingMode::SevenBit); + SDL::exec_iter(self, address as u16, operations) + } +} + +impl TransactionalIter for SDL { + type Error = I2CErr; + fn exec_iter<'a, O>(&mut self, address: u16, operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>{ + self.set_addressing_mode(AddressingMode::TenBit); + SDL::exec_iter(self, address, operations) + } +} + + + + + + From 483c682522282959f26ada8b2251dde2b3db8281 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Sun, 26 Feb 2023 18:15:21 -0600 Subject: [PATCH 08/82] increased delay to avoid nasty bug --- src/clock.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/clock.rs b/src/clock.rs index 90e7bb5..cac5957 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -275,6 +275,13 @@ impl ClockConfig { // Run FLL configuration procedure from the user's guide if we are using DCO if let MclkSel::Dcoclk(target_freq) = self.mclk.0 { fll_off(); + msp430::asm::nop(); + msp430::asm::nop(); + msp430::asm::nop(); + msp430::asm::nop(); + msp430::asm::nop(); + msp430::asm::nop(); + self.periph.csctl3.write(|w| w.selref().refoclk()); self.periph.csctl0.write(|w| unsafe { w.bits(0) }); self.periph @@ -286,6 +293,10 @@ impl ClockConfig { ._1() }); + msp430::asm::nop(); + msp430::asm::nop(); + msp430::asm::nop(); + msp430::asm::nop(); msp430::asm::nop(); msp430::asm::nop(); msp430::asm::nop(); From 8b7e3c7869fecc6b1db9c0bc491a30c7f72cd2e9 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Sun, 26 Feb 2023 20:34:48 -0600 Subject: [PATCH 09/82] i2c fixes --- src/hw_traits/eusci.rs | 15 +-- src/i2c.rs | 208 ++++++++++++++++++++++++----------------- 2 files changed, 132 insertions(+), 91 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index fc9454c..e17c514 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -34,6 +34,7 @@ macro_rules! reg_struct { } ) => { $(#[$attr:meta])* + #[derive(Copy, Clone)] pub struct $struct_name { $($(pub $bool_name : bool, $(#[$f_attr])*)*)? $($(pub $val_name : $val_type , $(#[$e_attr])*)*)? @@ -582,27 +583,27 @@ macro_rules! eusci_b_impl { #[inline(always)] fn ctw0_wr_rst(&self, bit:bool){ - self.$ucbxctlw0().write(|w| unsafe{w.ucswrst().bit(bit)}) + self.$ucbxctlw0().modify(|_, w| unsafe{w.ucswrst().bit(bit)}) } #[inline(always)] fn transmit_ack(&self){ - self.$ucbxctlw0().write(|w| unsafe{w.uctxack().bit(true)}) + self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxack().bit(true)}) } #[inline(always)] fn transmit_nack(&self){ - self.$ucbxctlw0().write(|w| unsafe{w.uctxnack().bit(true)}) + self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxnack().bit(true)}) } #[inline(always)] fn transmit_start(&self){ - self.$ucbxctlw0().write(|w| unsafe{w.uctxstt().bit(true)}) + self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxstt().bit(true)}) } #[inline(always)] fn transmit_stop(&self){ - self.$ucbxctlw0().write(|w| unsafe{w.uctxstp().bit(true)}) + self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxstp().bit(true)}) } #[inline(always)] @@ -612,12 +613,12 @@ macro_rules! eusci_b_impl { #[inline(always)] fn set_ucsla10(&self, bit:bool){ - self.$ucbxctlw0().write(|w| unsafe{w.ucsla10().bit(bit)}) + self.$ucbxctlw0().modify(|_, w| unsafe{w.ucsla10().bit(bit)}) } #[inline(always)] fn set_uctr(&self, bit:bool){ - self.$ucbxctlw0().write(|w| unsafe{w.uctr().bit(bit)}) + self.$ucbxctlw0().modify(|_, w| unsafe{w.uctr().bit(bit)}) } #[inline(always)] diff --git a/src/i2c.rs b/src/i2c.rs index bf82c6d..6152332 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -18,6 +18,7 @@ use crate::{ Ucmode, Ucglit, Ucclto, Ucastp}, pac }; +use crate::clock::{Aclk, Smclk}; use crate::hw_traits::eusci::I2CUcbIfg_out; /// Configure bus to use 7bit or 10bit I2C slave addressing mode @@ -149,7 +150,6 @@ impl I2CBusConfig{ /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode pub fn new( usci: USCI, - clk_source: Ucssel, baud_rate: u16, )->Self{ @@ -166,7 +166,7 @@ impl I2CBusConfig{ uctxstt: false, ucswrst: true, ucmode: Ucmode::I2CMode, - ucssel: clk_source, + ucssel: Ucssel::Smclk, }; let ctlw1 = UcbCtlw1{ @@ -252,22 +252,41 @@ impl I2CBusConfig{ } } + /// Configures this peripheral to use smclk + #[inline] + pub fn use_smclk(&mut self, smclk:&Smclk){ + self.ctlw0.ucssel = Ucssel::Smclk; + } + + /// Configures this peripheral to use aclk + #[inline] + pub fn use_aclk(&mut self, aclk:&Aclk){ + self.ctlw0.ucssel = Ucssel::Aclk; + } + /// Configures the glitch filter length for the SDA and SCL lines #[inline(always)] pub fn set_deglitch_time(&mut self, deglitch_time:GlitchFilter){ self.ctlw1.ucglit = deglitch_time.into(); } - /// Performs hardware confiuration and creates SDL pin - pub fn configure(&self) -> SDL{ + /// Performs hardware configuration and creates the SDL pin + pub fn sdl, D: Into>(&self, _scl: C, _sdl: D) -> SDL{ + self.configure(); + SDL(PhantomData) + } + + /// Performs hardware confiuration + #[inline] + fn configure(&self){ self.usci.ctw0_wr_rst(true); self.usci.ctw0_wr(&self.ctlw0); self.usci.ctw1_wr(&self.ctlw1); - self.usci.i2coa_wr(0, &self.i2coa0); - self.usci.i2coa_wr(1, &self.i2coa1); - self.usci.i2coa_wr(2, &self.i2coa2); - self.usci.i2coa_wr(3, &self.i2coa3); + // self.usci.i2coa_wr(0, &self.i2coa0); + // self.usci.i2coa_wr(1, &self.i2coa1); + // self.usci.i2coa_wr(2, &self.i2coa2); + // self.usci.i2coa_wr(3, &self.i2coa3); self.usci.ie_wr(&self.ie); self.usci.ifg_wr(&self.ifg); @@ -275,8 +294,6 @@ impl I2CBusConfig{ self.usci.tbcnt_wr(0); self.usci.ctw0_wr_rst(false); - - SDL(PhantomData) } } @@ -313,25 +330,33 @@ impl SDL{ /// Blocking read fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), I2CErr>{ let usci = unsafe { USCI::steal() }; + usci.i2csa_wr(address); + self.set_transmission_mode(TransmissionMode::Receive); + self.set_addressing_mode(AddressingMode::SevenBit); usci.transmit_start(); + let mut ifg = usci.ifg_rd(); while ifg.ucsttifg() {ifg = usci.ifg_rd();} if ifg.ucnackifg() { - Err::<(), I2CErr>(I2CErr::GotNACK); + return Err::<(), I2CErr>(I2CErr::GotNACK); } - - for i in 0 .. buffer.len()-2 { + for i in 0 .. buffer.len()-1 { while !ifg.ucrxifg0() { ifg = usci.ifg_rd(); - } - + if ifg.ucnackifg() { + return Err::<(), I2CErr>(I2CErr::GotNACK); + } buffer[i] = usci.ucrxbuf_rd(); } usci.transmit_stop(); - while !ifg.ucstpifg() {ifg = usci.ifg_rd();} + while !ifg.ucrxifg0() {ifg = usci.ifg_rd();} + if ifg.ucnackifg() { + return Err::<(), I2CErr>(I2CErr::GotNACK); + } buffer[buffer.len()-1] = usci.ucrxbuf_rd(); + while !ifg.ucstpifg() {ifg = usci.ifg_rd();} Ok(()) } @@ -339,24 +364,39 @@ impl SDL{ /// Blocking write fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), I2CErr>{ let usci = unsafe { USCI::steal() }; + usci.i2csa_wr(address); + self.set_addressing_mode(AddressingMode::SevenBit); + self.set_transmission_mode(TransmissionMode::Transmit); usci.transmit_start(); - let mut ifg = usci.ifg_rd(); - while !ifg.uctxifg0() {ifg = usci.ifg_rd();} - usci.uctxbuf_wr(bytes[0]); - - while ifg.ucsttifg() {ifg = usci.ifg_rd();} + let mut ifg = usci.ifg_rd(); if ifg.ucnackifg() { - Err::<(), I2CErr>(I2CErr::GotNACK); + return Err::<(), I2CErr>(I2CErr::GotNACK); + } + while !ifg.uctxifg0() { + ifg = usci.ifg_rd(); + if ifg.ucnackifg() { + return Err::<(), I2CErr>(I2CErr::GotNACK); + } } - for i in 1 .. bytes.len()-1 { - while !ifg.uctxifg0() {ifg = usci.ifg_rd();} + for i in 0 .. bytes.len()-1 { usci.uctxbuf_wr(bytes[i]); + ifg = usci.ifg_rd(); + if ifg.ucnackifg() { + return Err::<(), I2CErr>(I2CErr::GotNACK); + } + while !ifg.uctxifg0() { + ifg = usci.ifg_rd(); + if ifg.ucnackifg() { + return Err::<(), I2CErr>(I2CErr::GotNACK); + } + } } + usci.uctxbuf_wr(bytes[bytes.len()-1]); usci.transmit_stop(); - while !ifg.ucstpifg() {ifg = usci.ifg_rd();} + while ifg.ucstpifg() {ifg = usci.ifg_rd();} Ok(()) } @@ -369,20 +409,20 @@ impl SDL{ } /// blocking write then blocking read - fn write_read( - &mut self, - address: u16, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), I2CErr>{ - self.set_transmission_mode(TransmissionMode::Transmit); - let result = self.read(address, buffer); - if result.is_err() { - return result; - } - self.set_transmission_mode(TransmissionMode::Receive); - self.write(address, bytes) - } + // fn write_read( + // &mut self, + // address: u16, + // bytes: &[u8], + // buffer: &mut [u8], + // ) -> Result<(), I2CErr>{ + // self.set_transmission_mode(TransmissionMode::Transmit); + // let result = self.read(address, buffer); + // if result.is_err() { + // return result; + // } + // self.set_transmission_mode(TransmissionMode::Receive); + // self.write(address, bytes) + // } fn exec<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) -> Result<(), I2CErr>{ @@ -400,38 +440,38 @@ impl SDL{ impl Read for SDL{ type Error = I2CErr; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error>{ - self.set_addressing_mode(AddressingMode::SevenBit); - self.set_transmission_mode(TransmissionMode::Receive); + // self.set_addressing_mode(AddressingMode::SevenBit); + // self.set_transmission_mode(TransmissionMode::Receive); SDL::read(self, address as u16, buffer) } } -impl Read for SDL{ - type Error = I2CErr; - fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error>{ - self.set_addressing_mode(AddressingMode::TenBit); - self.set_transmission_mode(TransmissionMode::Receive); - SDL::read(self, address, buffer) - } -} +// impl Read for SDL{ +// type Error = I2CErr; +// fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error>{ +// self.set_addressing_mode(AddressingMode::TenBit); +// self.set_transmission_mode(TransmissionMode::Receive); +// SDL::read(self, address, buffer) +// } +// } impl Write for SDL{ type Error = I2CErr; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error>{ - self.set_addressing_mode(AddressingMode::SevenBit); - self.set_transmission_mode(TransmissionMode::Transmit); + // self.set_addressing_mode(AddressingMode::SevenBit); + // self.set_transmission_mode(TransmissionMode::Transmit); SDL::write(self, address as u16, bytes) } } -impl Write for SDL{ - type Error = I2CErr; - fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error>{ - self.set_addressing_mode(AddressingMode::TenBit); - self.set_transmission_mode(TransmissionMode::Transmit); - SDL::write(self, address, bytes) - } -} +// impl Write for SDL{ +// type Error = I2CErr; +// fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error>{ +// self.set_addressing_mode(AddressingMode::TenBit); +// self.set_transmission_mode(TransmissionMode::Transmit); +// SDL::write(self, address, bytes) +// } +// } impl WriteIter for SDL{ type Error = I2CErr; @@ -455,31 +495,31 @@ impl WriteIter for SDL{ } } -impl WriteRead for SDL{ - type Error = I2CErr; - fn write_read( - &mut self, - address: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error>{ - self.set_addressing_mode(AddressingMode::SevenBit); - SDL::write_read(self, address as u16, bytes, buffer) - } -} - -impl WriteRead for SDL{ - type Error = I2CErr; - fn write_read( - &mut self, - address: u16, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error>{ - self.set_addressing_mode(AddressingMode::TenBit); - SDL::write_read(self, address, bytes, buffer) - } -} +// impl WriteRead for SDL{ +// type Error = I2CErr; +// fn write_read( +// &mut self, +// address: u8, +// bytes: &[u8], +// buffer: &mut [u8], +// ) -> Result<(), Self::Error>{ +// self.set_addressing_mode(AddressingMode::SevenBit); +// SDL::write_read(self, address as u16, bytes, buffer) +// } +// } +// +// impl WriteRead for SDL{ +// type Error = I2CErr; +// fn write_read( +// &mut self, +// address: u16, +// bytes: &[u8], +// buffer: &mut [u8], +// ) -> Result<(), Self::Error>{ +// self.set_addressing_mode(AddressingMode::TenBit); +// SDL::write_read(self, address, bytes, buffer) +// } +// } impl Transactional for SDL{ type Error = I2CErr; From 4ad192a795e02554b1a97923d6438317baaf7843 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Mon, 27 Feb 2023 01:05:28 -0600 Subject: [PATCH 10/82] I2C works --- src/hw_traits/eusci.rs | 8 ++++++- src/i2c.rs | 47 +++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index e17c514..52769cf 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -288,6 +288,7 @@ pub trait EUsciI2C: EUsci { fn transmit_start(&self); fn transmit_stop(&self); + fn uctxstt_rd(&self) -> bool; fn uctxstp_rd(&self) -> bool; fn set_ucsla10(&self, bit:bool); @@ -606,6 +607,11 @@ macro_rules! eusci_b_impl { self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxstp().bit(true)}) } + #[inline(always)] + fn uctxstt_rd(&self) -> bool{ + self.$ucbxctlw0().read().uctxstp().bit() + } + #[inline(always)] fn uctxstp_rd(&self) -> bool{ self.$ucbxctlw0().read().uctxstp().bit() @@ -683,7 +689,7 @@ macro_rules! eusci_b_impl { } #[inline(always)] fn uctxbuf_wr(&self, val: u8){ - self.$ucbxrxbuf().write(|w| unsafe { w.bits(val as u16) }); + self.$ucbxtxbuf().write(|w| unsafe { w.bits(val as u16) }); } fn i2coa_rd(&self, which:u8) -> UcbI2coa{ diff --git a/src/i2c.rs b/src/i2c.rs index 6152332..9a1a8da 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -10,6 +10,9 @@ //! use core::marker::PhantomData; +use msp430::asm; +use msp430::interrupt::CriticalSection; +use msp430fr2355::interrupt; use crate::{ gpio::{Alternate1, Pin, P1, P4, Pin2, Pin3, Pin6, Pin7}, hal::blocking::i2c::{Read, Write, WriteRead, WriteIter, Operation, TransactionalIter, @@ -283,10 +286,10 @@ impl I2CBusConfig{ self.usci.ctw0_wr(&self.ctlw0); self.usci.ctw1_wr(&self.ctlw1); - // self.usci.i2coa_wr(0, &self.i2coa0); - // self.usci.i2coa_wr(1, &self.i2coa1); - // self.usci.i2coa_wr(2, &self.i2coa2); - // self.usci.i2coa_wr(3, &self.i2coa3); + self.usci.i2coa_wr(0, &self.i2coa0); + self.usci.i2coa_wr(1, &self.i2coa1); + self.usci.i2coa_wr(2, &self.i2coa2); + self.usci.i2coa_wr(3, &self.i2coa3); self.usci.ie_wr(&self.ie); self.usci.ifg_wr(&self.ifg); @@ -361,6 +364,11 @@ impl SDL{ Ok(()) } + + // #[interrupt] + // unsafe fn UCRXIE0(cs: CriticalSection){ + // + // } /// Blocking write fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), I2CErr>{ let usci = unsafe { USCI::steal() }; @@ -371,32 +379,33 @@ impl SDL{ usci.transmit_start(); let mut ifg = usci.ifg_rd(); + while !ifg.uctxifg0() {ifg = usci.ifg_rd();} + // usci.uctxbuf_wr(bytes[0]); + + while usci.uctxstt_rd() {asm::nop();} + + ifg = usci.ifg_rd(); if ifg.ucnackifg() { + usci.transmit_stop(); + while usci.uctxstp_rd() {asm::nop();} return Err::<(), I2CErr>(I2CErr::GotNACK); } - while !ifg.uctxifg0() { - ifg = usci.ifg_rd(); - if ifg.ucnackifg() { - return Err::<(), I2CErr>(I2CErr::GotNACK); - } - } - for i in 0 .. bytes.len()-1 { + for i in 0 .. bytes.len() { usci.uctxbuf_wr(bytes[i]); ifg = usci.ifg_rd(); - if ifg.ucnackifg() { - return Err::<(), I2CErr>(I2CErr::GotNACK); - } while !ifg.uctxifg0() { ifg = usci.ifg_rd(); - if ifg.ucnackifg() { - return Err::<(), I2CErr>(I2CErr::GotNACK); - } + } + if ifg.ucnackifg() { + usci.transmit_stop(); + while usci.uctxstp_rd() {asm::nop();} + return Err::<(), I2CErr>(I2CErr::GotNACK); } } - usci.uctxbuf_wr(bytes[bytes.len()-1]); + // usci.uctxbuf_wr(bytes[bytes.len()-1]); usci.transmit_stop(); - while ifg.ucstpifg() {ifg = usci.ifg_rd();} + while usci.uctxstp_rd() {asm::nop();} Ok(()) } From af60637ab10f91eb139b6d2558ff01768e5339f0 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Mon, 27 Feb 2023 14:36:16 -0600 Subject: [PATCH 11/82] driver cleanup --- src/i2c.rs | 74 +++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index 9a1a8da..5ca105d 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -82,7 +82,7 @@ impl From for Ucglit { ///Struct used to configure a I2C bus pub struct I2CBusConfig { usci: USCI, - baud_rate: u16, + divisor: u16, // Register configs ctlw0: UcbCtlw0, @@ -153,7 +153,6 @@ impl I2CBusConfig{ /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode pub fn new( usci: USCI, - baud_rate: u16, )->Self{ let ctlw0 = UcbCtlw0{ @@ -243,7 +242,7 @@ impl I2CBusConfig{ I2CBusConfig{ usci, - baud_rate: baud_rate, + divisor: 1, ctlw0: ctlw0, ctlw1: ctlw1, i2coa0: i2coa0, @@ -257,14 +256,16 @@ impl I2CBusConfig{ /// Configures this peripheral to use smclk #[inline] - pub fn use_smclk(&mut self, smclk:&Smclk){ + pub fn use_smclk(&mut self, smclk:&Smclk, clk_divisor:u16){ self.ctlw0.ucssel = Ucssel::Smclk; + self.divisor = clk_divisor; } /// Configures this peripheral to use aclk #[inline] - pub fn use_aclk(&mut self, aclk:&Aclk){ + pub fn use_aclk(&mut self, aclk:&Aclk, clk_divisor:u16){ self.ctlw0.ucssel = Ucssel::Aclk; + self.divisor = clk_divisor; } /// Configures the glitch filter length for the SDA and SCL lines @@ -293,7 +294,7 @@ impl I2CBusConfig{ self.usci.ie_wr(&self.ie); self.usci.ifg_wr(&self.ifg); - self.usci.brw_wr(self.baud_rate); + self.usci.brw_wr(self.divisor); self.usci.tbcnt_wr(0); self.usci.ctw0_wr_rst(false); @@ -313,7 +314,6 @@ pub enum I2CErr{ GotNACK, /// Device lost arbitration ArbitrationLost, - } impl SDL{ @@ -335,31 +335,28 @@ impl SDL{ let usci = unsafe { USCI::steal() }; usci.i2csa_wr(address); - self.set_transmission_mode(TransmissionMode::Receive); - self.set_addressing_mode(AddressingMode::SevenBit); usci.transmit_start(); + while usci.uctxstt_rd() {asm::nop();} + let mut ifg = usci.ifg_rd(); - while ifg.ucsttifg() {ifg = usci.ifg_rd();} if ifg.ucnackifg() { + usci.transmit_stop(); + while usci.uctxstp_rd() {asm::nop();} return Err::<(), I2CErr>(I2CErr::GotNACK); } + for i in 0 .. buffer.len()-1 { while !ifg.ucrxifg0() { ifg = usci.ifg_rd(); } - if ifg.ucnackifg() { - return Err::<(), I2CErr>(I2CErr::GotNACK); - } buffer[i] = usci.ucrxbuf_rd(); } usci.transmit_stop(); while !ifg.ucrxifg0() {ifg = usci.ifg_rd();} - if ifg.ucnackifg() { - return Err::<(), I2CErr>(I2CErr::GotNACK); - } buffer[buffer.len()-1] = usci.ucrxbuf_rd(); - while !ifg.ucstpifg() {ifg = usci.ifg_rd();} + + while usci.uctxstp_rd() {asm::nop();} Ok(()) } @@ -374,13 +371,10 @@ impl SDL{ let usci = unsafe { USCI::steal() }; usci.i2csa_wr(address); - self.set_addressing_mode(AddressingMode::SevenBit); - self.set_transmission_mode(TransmissionMode::Transmit); usci.transmit_start(); let mut ifg = usci.ifg_rd(); while !ifg.uctxifg0() {ifg = usci.ifg_rd();} - // usci.uctxbuf_wr(bytes[0]); while usci.uctxstt_rd() {asm::nop();} @@ -449,38 +443,38 @@ impl SDL{ impl Read for SDL{ type Error = I2CErr; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error>{ - // self.set_addressing_mode(AddressingMode::SevenBit); - // self.set_transmission_mode(TransmissionMode::Receive); + self.set_addressing_mode(AddressingMode::SevenBit); + self.set_transmission_mode(TransmissionMode::Receive); SDL::read(self, address as u16, buffer) } } -// impl Read for SDL{ -// type Error = I2CErr; -// fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error>{ -// self.set_addressing_mode(AddressingMode::TenBit); -// self.set_transmission_mode(TransmissionMode::Receive); -// SDL::read(self, address, buffer) -// } -// } +impl Read for SDL{ + type Error = I2CErr; + fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::TenBit); + self.set_transmission_mode(TransmissionMode::Receive); + SDL::read(self, address, buffer) + } +} impl Write for SDL{ type Error = I2CErr; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error>{ - // self.set_addressing_mode(AddressingMode::SevenBit); - // self.set_transmission_mode(TransmissionMode::Transmit); + self.set_addressing_mode(AddressingMode::SevenBit); + self.set_transmission_mode(TransmissionMode::Transmit); SDL::write(self, address as u16, bytes) } } -// impl Write for SDL{ -// type Error = I2CErr; -// fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error>{ -// self.set_addressing_mode(AddressingMode::TenBit); -// self.set_transmission_mode(TransmissionMode::Transmit); -// SDL::write(self, address, bytes) -// } -// } +impl Write for SDL{ + type Error = I2CErr; + fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::TenBit); + self.set_transmission_mode(TransmissionMode::Transmit); + SDL::write(self, address, bytes) + } +} impl WriteIter for SDL{ type Error = I2CErr; From d9f3b5cd11346d3ed55862d2e8ff1cfdfde5193d Mon Sep 17 00:00:00 2001 From: rbneiman Date: Mon, 27 Feb 2023 17:43:02 -0600 Subject: [PATCH 12/82] driver cleanup --- src/i2c.rs | 83 +++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index 5ca105d..a651ba5 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -361,11 +361,6 @@ impl SDL{ Ok(()) } - - // #[interrupt] - // unsafe fn UCRXIE0(cs: CriticalSection){ - // - // } /// Blocking write fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), I2CErr>{ let usci = unsafe { USCI::steal() }; @@ -412,20 +407,20 @@ impl SDL{ } /// blocking write then blocking read - // fn write_read( - // &mut self, - // address: u16, - // bytes: &[u8], - // buffer: &mut [u8], - // ) -> Result<(), I2CErr>{ - // self.set_transmission_mode(TransmissionMode::Transmit); - // let result = self.read(address, buffer); - // if result.is_err() { - // return result; - // } - // self.set_transmission_mode(TransmissionMode::Receive); - // self.write(address, bytes) - // } + fn write_read( + &mut self, + address: u16, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), I2CErr>{ + self.set_transmission_mode(TransmissionMode::Transmit); + let result = self.read(address, buffer); + if result.is_err() { + return result; + } + self.set_transmission_mode(TransmissionMode::Receive); + self.write(address, bytes) + } fn exec<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) -> Result<(), I2CErr>{ @@ -498,31 +493,31 @@ impl WriteIter for SDL{ } } -// impl WriteRead for SDL{ -// type Error = I2CErr; -// fn write_read( -// &mut self, -// address: u8, -// bytes: &[u8], -// buffer: &mut [u8], -// ) -> Result<(), Self::Error>{ -// self.set_addressing_mode(AddressingMode::SevenBit); -// SDL::write_read(self, address as u16, bytes, buffer) -// } -// } -// -// impl WriteRead for SDL{ -// type Error = I2CErr; -// fn write_read( -// &mut self, -// address: u16, -// bytes: &[u8], -// buffer: &mut [u8], -// ) -> Result<(), Self::Error>{ -// self.set_addressing_mode(AddressingMode::TenBit); -// SDL::write_read(self, address, bytes, buffer) -// } -// } +impl WriteRead for SDL{ + type Error = I2CErr; + fn write_read( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::SevenBit); + SDL::write_read(self, address as u16, bytes, buffer) + } +} + +impl WriteRead for SDL{ + type Error = I2CErr; + fn write_read( + &mut self, + address: u16, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error>{ + self.set_addressing_mode(AddressingMode::TenBit); + SDL::write_read(self, address, bytes, buffer) + } +} impl Transactional for SDL{ type Error = I2CErr; From 61202f300fc7fff8e01b42cf2583e6d9282147ba Mon Sep 17 00:00:00 2001 From: rbneiman Date: Mon, 27 Feb 2023 23:15:46 -0600 Subject: [PATCH 13/82] SPI configuration --- src/hw_traits/eusci.rs | 241 ++++++++++++++++++++++++++-------- src/i2c.rs | 10 +- src/lib.rs | 1 + src/spi.rs | 289 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 481 insertions(+), 60 deletions(-) create mode 100644 src/spi.rs diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 52769cf..2230c32 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -41,17 +41,7 @@ macro_rules! reg_struct { $($(pub $int_name : $int_size , $(#[$i_attr])*)*)? } - // - // impl $struct_name{ - // fn default()->Self{ - // $struct_name{ - // $($($bool_name : false,)*)? - // $($($val_name : 0.into(),)*)? - // $($($int_name : 0,)*)? - // } - // } - // } - + #[allow(unused_macros)] macro_rules! $macro_rd { ($reader : expr) => { $struct_name{ @@ -62,6 +52,7 @@ macro_rules! reg_struct { }; } + #[allow(unused_macros)] macro_rules! $macro_wr { ($reg : expr) => { |w| unsafe { w$($(.$bool_name().bit($reg.$bool_name))*)? @@ -83,8 +74,8 @@ from_u8!(Ucssel); #[derive(Copy, Clone)] pub enum Ucmode { ThreePinSPI = 0, - FourPinSPI_1 = 1, - FourPinSPI_0 = 2, + FourPinSPI1 = 1, + FourPinSPI0 = 2, I2CMode = 3, } from_u8!(Ucmode); @@ -102,13 +93,13 @@ from_u8!(Ucglit); #[derive(Copy, Clone)] pub enum Ucclto { /// Disable clock low time-out counter - Ucclto_00b = 0, + Ucclto00b = 0, /// 135000 MODCLK cycles (approximately 28 ms) - Ucclto_01b = 1, + Ucclto01b = 1, /// 150000 MODCLK cycles (approximately 31 ms) - Ucclto_10b = 2, + Ucclto10b = 2, /// = 165000 MODCLK cycles (approximately 34 ms) - Ucclto_11b = 3, + Ucclto11b = 3, } from_u8!(Ucclto); @@ -118,14 +109,14 @@ from_u8!(Ucclto); pub enum Ucastp { /// No automatic STOP generation. The STOP condition is generated after /// the user sets the UCTXSTP bit. The value in UCBxTBCNT is a don't care. - Ucastp_00b = 0, + Ucastp00b = 0, /// UCBCNTIFG is set when the byte counter reaches the threshold defined in /// UCBxTBCNT - Ucastp_01b = 1, + Ucastp01b = 1, /// A STOP condition is generated automatically after the byte counter value /// reached UCBxTBCNT. UCBCNTIFG is set with the byte counter reaching the /// threshold. - Ucastp_10b = 2, + Ucastp10b = 2, } from_u8!(Ucastp); @@ -244,6 +235,26 @@ pub struct UcbIFG, UcbIFG_rd, UcbIFG_wr{ } } +reg_struct!{ +pub struct UcxSpiCtw0, UcxSpiCtw0_rd, UcxSpiCtw0_wr{ + flags{ + pub ucckph: bool, + pub ucckpl: bool, + pub ucmsb: bool, + pub uc7bit: bool, + pub ucmst: bool, + pub ucsync: bool, + pub ucstem: bool, + pub ucswrst: bool, + } + enums{ + pub ucmode: Ucmode : u8, + pub ucssel: Ucssel : u8, + } +} +} + + pub trait EUsci : Steal { } @@ -281,7 +292,7 @@ pub trait EUsciUart: EUsci { } pub trait EUsciI2C: EUsci { - type IfgOut : I2CUcbIfg_out; + type IfgOut : I2CUcbIfgOut; fn transmit_ack(&self); fn transmit_nack(&self); @@ -340,20 +351,39 @@ pub trait EUsciI2C: EUsci { fn ie_rd(&self) -> UcbIe; fn ie_wr(&self, reg:&UcbIe); - //some common bitwise operations for this register - fn ie_rmw_or(&self, reg:&UcbIe); - fn ie_rmw_and(&self, reg:&UcbIe); fn ifg_rd(&self) -> Self::IfgOut; fn ifg_wr(&self, reg:&UcbIFG); fn iv_rd(&self) -> u16; } -pub trait UcxStatw { +pub trait EusciSPI : EUsci{ + type Statw : SpiStatw; + + fn ctw0_wr_rst(&self, bit:bool); + + fn ctw0_wr(&self, reg:&UcxSpiCtw0); + + fn brw_wr(&self, val:u16); + + fn statw_rd(&self) -> Self::Statw; + + fn uclisten_set(&self, bit:bool); + fn rxbuf_rd(&self) -> u16; + + fn txbuf_wr(&self, val:u16); + + fn transmit_interrupt_set(&self, bit:bool); + + fn receive_interrupt_set(&self, bit:bool); + + fn transmit_flag(&self) -> bool; + + fn receive_flag(&self) -> bool; } -pub trait UartUcxStatw : UcxStatw { +pub trait UartUcxStatw { fn ucfe(&self) -> bool; fn ucoe(&self) -> bool; fn ucpe(&self) -> bool; @@ -361,7 +391,14 @@ pub trait UartUcxStatw : UcxStatw { fn ucbusy(&self) -> bool; } -pub trait I2CUcbIfg_out { +pub trait SpiStatw{ + fn uclisten(&self) -> bool; + fn ucfe(&self) -> bool; + fn ucoe(&self) -> bool; + // fn ucbusy1(&self) -> bool; +} + +pub trait I2CUcbIfgOut { /// Byte counter interrupt flag fn ucbcntifg(&self) -> bool; /// Not-acknowledge received interrupt flag @@ -381,7 +418,8 @@ pub trait I2CUcbIfg_out { macro_rules! eusci_impl { ($EUsci:ident, $eusci:ident, $ucxctlw0:ident, $ucxctlw1:ident, $ucxbrw:ident, $ucxstatw:ident, $ucxrxbuf:ident, $ucxtxbuf:ident, $ucxie:ident, $ucxifg:ident, - $ucxiv:ident, $Statw:ty) => { + $ucxiv:ident, $StatwSpi:ty) => { + impl Steal for pac::$EUsci { #[inline(always)] unsafe fn steal() -> Self { @@ -393,30 +431,111 @@ macro_rules! eusci_impl { } - impl UcxStatw for $Statw { + impl EusciSPI for pac::$EUsci { + type Statw = $StatwSpi; + + #[inline(always)] + fn ctw0_wr_rst(&self, bit:bool){ + self.$ucxctlw0().modify(|_, w| unsafe{w.ucswrst().bit(bit)}) + } + + #[inline(always)] + fn ctw0_wr(&self, reg:&UcxSpiCtw0){ + self.$ucxctlw0().write(UcxSpiCtw0_wr! {reg}); + } + + #[inline(always)] + fn brw_wr(&self, val:u16){ + self.$ucxbrw().write(|w| unsafe{w.bits(val)}); + } + + #[inline(always)] + fn statw_rd(&self) -> Self::Statw{ + self.$ucxstatw().read() + } + + #[inline(always)] + fn uclisten_set(&self, bit:bool){ + self.$ucxstatw().modify(|_, w| unsafe{w.uclisten().bit(bit)}) + } + + #[inline(always)] + fn rxbuf_rd(&self) -> u16{ + self.$ucxrxbuf().read().bits() + } + + #[inline(always)] + fn txbuf_wr(&self, val:u16){ + self.$ucxtxbuf().write(|w| unsafe { w.bits(val as u16) }); + } + + #[inline(always)] + fn transmit_interrupt_set(&self, bit:bool){ + self.$ucxie().modify(|_, w| unsafe{w.uctxie().bit(bit)}); + } + + #[inline(always)] + fn receive_interrupt_set(&self, bit:bool){ + self.$ucxie().modify(|_, w| unsafe{w.ucrxie().bit(bit)}); + } + + #[inline(always)] + fn transmit_flag(&self) -> bool{ + self.$ucxifg().read().uctxifg().bit() + } + + #[inline(always)] + fn receive_flag(&self) -> bool{ + self.$ucxifg().read().ucrxifg().bit() + } + } + + impl SpiStatw for $StatwSpi{ + #[inline(always)] + fn uclisten(&self) -> bool{ + self.uclisten().bit() + } + + #[inline(always)] + fn ucfe(&self) -> bool{ + self.ucfe().bit() + } + + #[inline(always)] + fn ucoe(&self) -> bool{ + self.ucoe().bit() + } + + // #[inline(always)] + // fn ucbusy1(&self) -> bool{ + // self.ucbusy().bit() + // } } + } } macro_rules! eusci_a_impl { ($EUsci:ident, $eusci:ident, $ucaxctlw0:ident, $ucaxctlw1:ident, $ucaxbrw:ident, $ucaxmctlw:ident, $ucaxstatw:ident, $ucaxrxbuf:ident, $ucaxtxbuf:ident, $ucaxie:ident, $ucaxifg:ident, - $ucaxiv:ident, $Statw:ty) => { + $ucaxiv:ident, $Statw:ty, + $StatwSpi:ty, + $ucaxctlw0spi:ident, $ucaxstatwspi:ident, $ucaxiespi:ident, $ucaxifgspi:ident) => { eusci_impl!( $EUsci, $eusci, - $ucaxctlw0, + $ucaxctlw0spi, $ucaxctlw1, $ucaxbrw, - $ucaxstatw, + $ucaxstatwspi, $ucaxrxbuf, $ucaxtxbuf, - $ucaxie, - $ucaxifg, + $ucaxiespi, + $ucaxifgspi, $ucaxiv, - $Statw + $StatwSpi ); impl EUsciUart for pac::$EUsci { @@ -556,20 +675,22 @@ macro_rules! eusci_b_impl { $ucbxstatw:ident, $ucbxtbcnt:ident, $ucbxrxbuf:ident, $ucbxtxbuf:ident, $ucbxi2coa0:ident, $ucbxi2coa1:ident, $ucbxi2coa2:ident, $ucbxi2coa3:ident, $ucbxaddrx:ident, $ucbxaddmask:ident, $ucbxi2csa:ident, $ucbxie:ident, - $ucbxifg:ident, $ucbxiv:ident, $Statw:ty, $Ifg:ty) => { + $ucbxifg:ident, $ucbxiv:ident, $Statw:ty, $Ifg:ty, + $StatwSpi:ty, + $ucbxctlw0spi:ident, $ucbxstatwspi:ident, $ucbxiespi:ident, $ucbxifgspi:ident) => { eusci_impl!( $EUsci, $eusci, - $ucbxctlw0, + $ucbxctlw0spi, $ucbxctlw1, $ucbxbrw, - $ucbxstatw, + $ucbxstatwspi, $ucbxrxbuf, $ucbxtxbuf, - $ucbxie, - $ucbxifg, + $ucbxiespi, + $ucbxifgspi, $ucbxiv, - $Statw + $StatwSpi ); @@ -791,14 +912,6 @@ macro_rules! eusci_b_impl { fn ie_wr(&self, reg:&UcbIe){ self.$ucbxie().write(UcbIe_wr! {reg}); } - #[inline(always)] - fn ie_rmw_or(&self, reg:&UcbIe){ - //TODO - } - #[inline(always)] - fn ie_rmw_and(&self, reg:&UcbIe){ - //TODO - } #[inline(always)] fn ifg_rd(&self) -> Self::IfgOut{ @@ -816,7 +929,7 @@ macro_rules! eusci_b_impl { } } - impl I2CUcbIfg_out for $Ifg{ + impl I2CUcbIfgOut for $Ifg{ #[inline(always)] fn ucbcntifg(&self) -> bool{ self.ucbcntifg().bit() @@ -868,7 +981,12 @@ eusci_a_impl!( uca0ie, uca0ifg, uca0iv, - pac::e_usci_a0::uca0statw::R + pac::e_usci_a0::uca0statw::R, + pac::e_usci_a0::uca0statw_spi::R, + uca0ctlw0_spi, + uca0statw_spi, + uca0ie_spi, + uca0ifg_spi ); eusci_a_impl!( @@ -884,7 +1002,12 @@ eusci_a_impl!( uca1ie, uca1ifg, uca1iv, - pac::e_usci_a1::uca1statw::R + pac::e_usci_a1::uca1statw::R, + pac::e_usci_a1::uca1statw_spi::R, + uca1ctlw0_spi, + uca1statw_spi, + uca1ie_spi, + uca1ifg_spi ); eusci_b_impl!( @@ -908,7 +1031,12 @@ eusci_b_impl!( ucb0ifg, ucb0iv, pac::e_usci_b0::ucb0statw::R, - pac::e_usci_b0::ucb0ifg::R + pac::e_usci_b0::ucb0ifg::R, + pac::e_usci_b0::ucb0statw_spi::R, + ucb0ctlw0_spi, + ucb0statw_spi, + ucb0ie_spi, + ucb0ifg_spi ); eusci_b_impl!( @@ -932,7 +1060,12 @@ eusci_b_impl!( ucb1ifg, ucb1iv, pac::e_usci_b1::ucb1statw::R, - pac::e_usci_b1::ucb1ifg::R + pac::e_usci_b1::ucb1ifg::R, + pac::e_usci_b1::ucb1statw_spi::R, + ucb1ctlw0_spi, + ucb1statw_spi, + ucb1ie_spi, + ucb1ifg_spi ); diff --git a/src/i2c.rs b/src/i2c.rs index a651ba5..a87cfef 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -11,18 +11,16 @@ use core::marker::PhantomData; use msp430::asm; -use msp430::interrupt::CriticalSection; -use msp430fr2355::interrupt; use crate::{ gpio::{Alternate1, Pin, P1, P4, Pin2, Pin3, Pin6, Pin7}, hal::blocking::i2c::{Read, Write, WriteRead, WriteIter, Operation, TransactionalIter, SevenBitAddress, TenBitAddress, Transactional}, - hw_traits::eusci::{EUsciI2C, UcbCtlw0, UcbCtlw1, UcbI2coa, UcbIe, UcbIFG, UcbStatw, Ucssel, + hw_traits::eusci::{EUsciI2C, UcbCtlw0, UcbCtlw1, UcbI2coa, UcbIe, UcbIFG, Ucssel, Ucmode, Ucglit, Ucclto, Ucastp}, pac }; use crate::clock::{Aclk, Smclk}; -use crate::hw_traits::eusci::I2CUcbIfg_out; +use crate::hw_traits::eusci::I2CUcbIfgOut; /// Configure bus to use 7bit or 10bit I2C slave addressing mode #[derive(Clone, Copy)] @@ -175,8 +173,8 @@ impl I2CBusConfig{ ucetxint: false, ucstpnack: false, ucswack: false, - ucclto: Ucclto::Ucclto_00b, - ucastp: Ucastp::Ucastp_00b, + ucclto: Ucclto::Ucclto00b, + ucastp: Ucastp::Ucastp00b, ucglit: Ucglit::Max6_25ns, }; diff --git a/src/lib.rs b/src/lib.rs index b109b49..75e42bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,7 @@ mod hw_traits; mod util; pub mod i2c; +pub mod spi; pub use msp430fr2355 as pac; pub use embedded_hal as hal; diff --git a/src/spi.rs b/src/spi.rs new file mode 100644 index 0000000..40152b9 --- /dev/null +++ b/src/spi.rs @@ -0,0 +1,289 @@ +//! embedded_hal SPI implmentation + +use core::marker::PhantomData; +use msp430fr2355 as pac; +use crate::hal::{ + spi as spi_nb, + blocking::spi as spi_blocking, +}; +use crate::{ + hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel}, + gpio::{Alternate1, Pin, P1, P4, Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7}, + clock::{Smclk, Aclk} +}; + +/// Marks a eUSCI capable of SPI communication (in this case, all euscis do) +pub trait EUsciSPIBus : EusciSPI{ + /// Master In Slave Out (refered to as SOMI in datasheet) + type MISO; + /// Master Out Slave In (refered to as SIMO in datasheet) + type MOSI; + /// Serial Clock + type SCLK; + /// Slave Transmit Enable (mostly equivalent to Chip Select for single-master systems) + type STE; +} + +impl EUsciSPIBus for pac::E_USCI_A0 { + type MISO = UsciA0MISOPin; + type MOSI = UsciA0MOSIPin; + type SCLK = UsciA0SCLKPin; + type STE = UsciA0STEPin; +} + +impl EUsciSPIBus for pac::E_USCI_A1 { + type MISO = UsciA0MISOPin; + type MOSI = UsciA0MOSIPin; + type SCLK = UsciA0SCLKPin; + type STE = UsciA0STEPin; +} + +impl EUsciSPIBus for pac::E_USCI_B0 { + type MISO = UsciA0MISOPin; + type MOSI = UsciA0MOSIPin; + type SCLK = UsciA0SCLKPin; + type STE = UsciA0STEPin; +} + +impl EUsciSPIBus for pac::E_USCI_B1 { + type MISO = UsciA0MISOPin; + type MOSI = UsciA0MOSIPin; + type SCLK = UsciA0SCLKPin; + type STE = UsciA0STEPin; +} + +/// SPI MISO pin for eUSCI A0 +pub struct UsciA0MISOPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA0MISOPin { + UsciA0MISOPin + } +} + +/// SPI MOSI pin for eUSCI A0 +pub struct UsciA0MOSIPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA0MOSIPin { + UsciA0MOSIPin + } +} + +/// SPI SCLK pin for eUSCI A0 +pub struct UsciA0SCLKPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA0SCLKPin { + UsciA0SCLKPin + } +} + +/// SPI STE pin for eUSCI A0 +pub struct UsciA0STEPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA0STEPin { + UsciA0STEPin + } +} + +/// SPI MISO pin for eUSCI A1 +pub struct UsciA1MISOPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA1MISOPin { + UsciA1MISOPin + } +} + +/// SPI MOSI pin for eUSCI A1 +pub struct UsciA1MOSIPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA1MOSIPin { + UsciA1MOSIPin + } +} + +/// SPI SCLK pin for eUSCI A1 +pub struct UsciA1SCLKPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA1SCLKPin { + UsciA1SCLKPin + } +} + +/// SPI STE pin for eUSCI A1 +pub struct UsciA1STEPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA1STEPin { + UsciA1STEPin + } +} + +/// SPI MISO pin for eUSCI B0 +pub struct UsciB0MISOPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB0MISOPin { + UsciB0MISOPin + } +} + +/// SPI MOSI pin for eUSCI B0 +pub struct UsciB0MOSIPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB0MOSIPin { + UsciB0MOSIPin + } +} + +/// SPI SCLK pin for eUSCI B0 +pub struct UsciB0SCLKPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB0SCLKPin { + UsciB0SCLKPin + } +} + +/// SPI STE pin for eUSCI B0 +pub struct UsciB0STEPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB0STEPin { + UsciB0STEPin + } +} + +/// SPI MISO pin for eUSCI B1 +pub struct UsciB1MISOPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB1MISOPin { + UsciB1MISOPin + } +} + +/// SPI MOSI pin for eUSCI B1 +pub struct UsciB1MOSIPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB1MOSIPin { + UsciB1MOSIPin + } +} + +/// SPI SCLK pin for eUSCI B1 +pub struct UsciB1SCLKPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB1SCLKPin { + UsciB1SCLKPin + } +} + +/// SPI STE pin for eUSCI B1 +pub struct UsciB1STEPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB1STEPin { + UsciB1STEPin + } +} + +/// Struct used to configure a SPI bus +pub struct SPIBusConfig{ + usci: USCI, + prescaler: u16, + + // Register configs + ctlw0: UcxSpiCtw0, +} + +impl SPIBusConfig{ + /// Create a new configuration for setting up a EUSCI peripheral in SPI mode + pub fn new(usci: USCI, fourPin:bool)->Self{ + let ctlw0 = UcxSpiCtw0{ + ucckph: false, + ucckpl: false, + ucmsb: false, + uc7bit: false, + ucmst: false, + ucsync: false, + ucstem: false, + ucswrst: false, + ucmode: Ucmode::FourPinSPI0, + ucssel: Ucssel::Uclk, + }; + + SPIBusConfig{ + usci: usci, + prescaler: 0, + ctlw0: ctlw0 + } + } + + /// Configures this peripheral to use smclk + #[inline] + pub fn use_smclk(&mut self, smclk:&Smclk, clk_divisor:u16){ + self.ctlw0.ucssel = Ucssel::Smclk; + self.prescaler = clk_divisor; + } + + /// Configures this peripheral to use aclk + #[inline] + pub fn use_aclk(&mut self, aclk:&Aclk, clk_divisor:u16){ + self.ctlw0.ucssel = Ucssel::Aclk; + self.prescaler = clk_divisor; + } + + /// Performs hardware configuration and creates a 4 wire SPI bus + pub fn four_wire + , SI: Into, CLK: Into, CS: Into> + (&mut self, cs_active_high: bool, _miso: SO, _mosi: SI, _sclk: CLK, _cs:CS) + -> SPIPins{ + match cs_active_high { + true => {self.ctlw0.ucmode = Ucmode::FourPinSPI1}, + false => {self.ctlw0.ucmode = Ucmode::FourPinSPI0}, + } + self.configure_hw(); + SPIPins(PhantomData) + } + + #[inline] + fn configure_hw(&self){ + self.usci.ctw0_wr_rst(true); + + self.usci.ctw0_wr(&self.ctlw0); + self.usci.brw_wr(self.prescaler); + self.usci.uclisten_set(false); + self.usci.transmit_interrupt_set(false); + self.usci.receive_interrupt_set(false); + + self.usci.ctw0_wr_rst(false); + } + +} + +/// Represents a group of pins configured for SPI communication +pub struct SPIPins(PhantomData); + +impl SPIPins{ + +} + + + + + + + + + + + From ba9d5cceb62b178b4a33b1765aa34f2f2acf8d12 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Wed, 1 Mar 2023 16:57:24 -0600 Subject: [PATCH 14/82] SPI work --- src/hw_traits/eusci.rs | 88 +++++++---- src/i2c.rs | 14 +- src/lib.rs | 1 + src/spi.rs | 330 +++++++++++++++++++++++++++++++++-------- 4 files changed, 338 insertions(+), 95 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 2230c32..591710e 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -1,13 +1,13 @@ use super::Steal; use msp430fr2355 as pac; - +use msp430fr2355::interrupt; macro_rules! from_u8 { - ($type: ty) => { - impl From for $type { + ($typ: ty) => { + impl From for $typ { #[inline(always)] fn from(variant: u8) -> Self { - <$type>::from(variant) + variant.into() } } }; @@ -54,11 +54,11 @@ macro_rules! reg_struct { #[allow(unused_macros)] macro_rules! $macro_wr { - ($reg : expr) => { |w| unsafe { + ($reg : expr) => { |w| w$($(.$bool_name().bit($reg.$bool_name))*)? $($(.$val_name().bits($reg.$val_name as $size))*)? $($(.$int_name().bits($reg.$int_name as $int_size))*)? - }}; + }; } }; } @@ -120,6 +120,19 @@ pub enum Ucastp { } from_u8!(Ucastp); +#[derive(Copy, Clone)] +pub enum SPIInterruptVector{ + None = 0, + DataReceived = 2, + TXBufferEmpty = 4, +} +impl From for SPIInterruptVector { + #[inline(always)] + fn from(variant: u16) -> Self { + (variant & 0x4).into() + } +} + pub struct UcaCtlw0{ pub ucpen: bool, @@ -370,9 +383,9 @@ pub trait EusciSPI : EUsci{ fn uclisten_set(&self, bit:bool); - fn rxbuf_rd(&self) -> u16; + fn rxbuf_rd(&self) -> u8; - fn txbuf_wr(&self, val:u16); + fn txbuf_wr(&self, val:u8); fn transmit_interrupt_set(&self, bit:bool); @@ -381,6 +394,10 @@ pub trait EusciSPI : EUsci{ fn transmit_flag(&self) -> bool; fn receive_flag(&self) -> bool; + + fn iv_rd(&self) -> SPIInterruptVector; + + fn spi_interrupt(){} } pub trait UartUcxStatw { @@ -416,7 +433,7 @@ pub trait I2CUcbIfgOut { } macro_rules! eusci_impl { - ($EUsci:ident, $eusci:ident, $ucxctlw0:ident, $ucxctlw1:ident, $ucxbrw:ident, + ($intr_vec:ident, $EUsci:ident, $eusci:ident, $ucxctlw0:ident, $ucxctlw1:ident, $ucxbrw:ident, $ucxstatw:ident, $ucxrxbuf:ident, $ucxtxbuf:ident, $ucxie:ident, $ucxifg:ident, $ucxiv:ident, $StatwSpi:ty) => { @@ -431,13 +448,17 @@ macro_rules! eusci_impl { } + #[interrupt] + unsafe fn $intr_vec(){ + pac::$EUsci::spi_interrupt(); + } impl EusciSPI for pac::$EUsci { type Statw = $StatwSpi; #[inline(always)] fn ctw0_wr_rst(&self, bit:bool){ - self.$ucxctlw0().modify(|_, w| unsafe{w.ucswrst().bit(bit)}) + self.$ucxctlw0().modify(|_, w| w.ucswrst().bit(bit)) } #[inline(always)] @@ -457,27 +478,27 @@ macro_rules! eusci_impl { #[inline(always)] fn uclisten_set(&self, bit:bool){ - self.$ucxstatw().modify(|_, w| unsafe{w.uclisten().bit(bit)}) + self.$ucxstatw().modify(|_, w| w.uclisten().bit(bit)) } #[inline(always)] - fn rxbuf_rd(&self) -> u16{ - self.$ucxrxbuf().read().bits() + fn rxbuf_rd(&self) -> u8{ + self.$ucxrxbuf().read().ucrxbuf().bits() } #[inline(always)] - fn txbuf_wr(&self, val:u16){ - self.$ucxtxbuf().write(|w| unsafe { w.bits(val as u16) }); + fn txbuf_wr(&self, val:u8){ + self.$ucxtxbuf().write(|w| unsafe{w.uctxbuf().bits(val)}); } #[inline(always)] fn transmit_interrupt_set(&self, bit:bool){ - self.$ucxie().modify(|_, w| unsafe{w.uctxie().bit(bit)}); + self.$ucxie().modify(|_, w| w.uctxie().bit(bit)); } #[inline(always)] fn receive_interrupt_set(&self, bit:bool){ - self.$ucxie().modify(|_, w| unsafe{w.ucrxie().bit(bit)}); + self.$ucxie().modify(|_, w| w.ucrxie().bit(bit)); } #[inline(always)] @@ -489,6 +510,11 @@ macro_rules! eusci_impl { fn receive_flag(&self) -> bool{ self.$ucxifg().read().ucrxifg().bit() } + + #[inline(always)] + fn iv_rd(&self) -> SPIInterruptVector{ + SPIInterruptVector::from(self.$ucxiv().read().uciv().bits()) + } } impl SpiStatw for $StatwSpi{ @@ -517,13 +543,14 @@ macro_rules! eusci_impl { } macro_rules! eusci_a_impl { - ($EUsci:ident, $eusci:ident, $ucaxctlw0:ident, $ucaxctlw1:ident, $ucaxbrw:ident, $ucaxmctlw:ident, - $ucaxstatw:ident, $ucaxrxbuf:ident, $ucaxtxbuf:ident, $ucaxie:ident, $ucaxifg:ident, - $ucaxiv:ident, $Statw:ty, + ($intr_vec:ident,$EUsci:ident, $eusci:ident, $ucaxctlw0:ident, $ucaxctlw1:ident, $ucaxbrw:ident, + $ucaxmctlw:ident, $ucaxstatw:ident, $ucaxrxbuf:ident, $ucaxtxbuf:ident, $ucaxie:ident, + $ucaxifg:ident, $ucaxiv:ident, $Statw:ty, $StatwSpi:ty, $ucaxctlw0spi:ident, $ucaxstatwspi:ident, $ucaxiespi:ident, $ucaxifgspi:ident) => { eusci_impl!( + $intr_vec, $EUsci, $eusci, $ucaxctlw0spi, @@ -671,7 +698,7 @@ macro_rules! eusci_a_impl { } macro_rules! eusci_b_impl { - ($EUsci:ident, $eusci:ident, $ucbxctlw0:ident, $ucbxctlw1:ident, $ucbxbrw:ident, + ($intr_vec:ident, $EUsci:ident, $eusci:ident, $ucbxctlw0:ident, $ucbxctlw1:ident, $ucbxbrw:ident, $ucbxstatw:ident, $ucbxtbcnt:ident, $ucbxrxbuf:ident, $ucbxtxbuf:ident, $ucbxi2coa0:ident, $ucbxi2coa1:ident, $ucbxi2coa2:ident, $ucbxi2coa3:ident, $ucbxaddrx:ident, $ucbxaddmask:ident, $ucbxi2csa:ident, $ucbxie:ident, @@ -679,6 +706,7 @@ macro_rules! eusci_b_impl { $StatwSpi:ty, $ucbxctlw0spi:ident, $ucbxstatwspi:ident, $ucbxiespi:ident, $ucbxifgspi:ident) => { eusci_impl!( + $intr_vec, $EUsci, $eusci, $ucbxctlw0spi, @@ -705,27 +733,27 @@ macro_rules! eusci_b_impl { #[inline(always)] fn ctw0_wr_rst(&self, bit:bool){ - self.$ucbxctlw0().modify(|_, w| unsafe{w.ucswrst().bit(bit)}) + self.$ucbxctlw0().modify(|_, w| w.ucswrst().bit(bit)) } #[inline(always)] fn transmit_ack(&self){ - self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxack().bit(true)}) + self.$ucbxctlw0().modify(|_, w| w.uctxack().bit(true)) } #[inline(always)] fn transmit_nack(&self){ - self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxnack().bit(true)}) + self.$ucbxctlw0().modify(|_, w| w.uctxnack().bit(true)) } #[inline(always)] fn transmit_start(&self){ - self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxstt().bit(true)}) + self.$ucbxctlw0().modify(|_, w| w.uctxstt().bit(true)) } #[inline(always)] fn transmit_stop(&self){ - self.$ucbxctlw0().modify(|_, w| unsafe{w.uctxstp().bit(true)}) + self.$ucbxctlw0().modify(|_, w| w.uctxstp().bit(true)) } #[inline(always)] @@ -740,12 +768,12 @@ macro_rules! eusci_b_impl { #[inline(always)] fn set_ucsla10(&self, bit:bool){ - self.$ucbxctlw0().modify(|_, w| unsafe{w.ucsla10().bit(bit)}) + self.$ucbxctlw0().modify(|_, w| w.ucsla10().bit(bit)) } #[inline(always)] fn set_uctr(&self, bit:bool){ - self.$ucbxctlw0().modify(|_, w| unsafe{w.uctr().bit(bit)}) + self.$ucbxctlw0().modify(|_, w| w.uctr().bit(bit)) } #[inline(always)] @@ -969,6 +997,7 @@ macro_rules! eusci_b_impl { } eusci_a_impl!( + EUSCI_A0, E_USCI_A0, e_usci_a0, uca0ctlw0, @@ -990,6 +1019,7 @@ eusci_a_impl!( ); eusci_a_impl!( + EUSCI_A1, E_USCI_A1, e_usci_a1, uca1ctlw0, @@ -1011,6 +1041,7 @@ eusci_a_impl!( ); eusci_b_impl!( + EUSCI_B0, E_USCI_B0, e_usci_b0, ucb0ctlw0, @@ -1040,6 +1071,7 @@ eusci_b_impl!( ); eusci_b_impl!( + EUSCI_B1, E_USCI_B1, e_usci_b1, ucb1ctlw0, diff --git a/src/i2c.rs b/src/i2c.rs index a87cfef..7b61f58 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -254,14 +254,14 @@ impl I2CBusConfig{ /// Configures this peripheral to use smclk #[inline] - pub fn use_smclk(&mut self, smclk:&Smclk, clk_divisor:u16){ + pub fn use_smclk(&mut self, _smclk:&Smclk, clk_divisor:u16){ self.ctlw0.ucssel = Ucssel::Smclk; self.divisor = clk_divisor; } /// Configures this peripheral to use aclk #[inline] - pub fn use_aclk(&mut self, aclk:&Aclk, clk_divisor:u16){ + pub fn use_aclk(&mut self, _aclk:&Aclk, clk_divisor:u16){ self.ctlw0.ucssel = Ucssel::Aclk; self.divisor = clk_divisor; } @@ -397,7 +397,7 @@ impl SDL{ Ok(()) } - fn writeIter(&mut self, address: u16, bytes: B) -> Result<(), I2CErr> + fn write_iter(&mut self, _address: u16, _bytes: B) -> Result<(), I2CErr> where B: IntoIterator{ @@ -420,12 +420,12 @@ impl SDL{ self.write(address, bytes) } - fn exec<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) + fn exec<'a>(&mut self, _address: u16, _operations: &mut [Operation<'a>]) -> Result<(), I2CErr>{ Err(I2CErr::Unimplemented) } - fn exec_iter<'a, O>(&mut self, address: u16, operations: O) -> Result<(), I2CErr> + fn exec_iter<'a, O>(&mut self, _address: u16, _operations: O) -> Result<(), I2CErr> where O: IntoIterator>{ Err(I2CErr::Unimplemented) @@ -476,7 +476,7 @@ impl WriteIter for SDL{ B: IntoIterator{ self.set_addressing_mode(AddressingMode::SevenBit); self.set_transmission_mode(TransmissionMode::Transmit); - SDL::writeIter(self, address as u16, bytes) + SDL::write_iter(self, address as u16, bytes) } } @@ -487,7 +487,7 @@ impl WriteIter for SDL{ B: IntoIterator{ self.set_addressing_mode(AddressingMode::TenBit); self.set_transmission_mode(TransmissionMode::Transmit); - SDL::writeIter(self, address, bytes) + SDL::write_iter(self, address, bytes) } } diff --git a/src/lib.rs b/src/lib.rs index 75e42bb..5d339de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ #![feature(asm_experimental_arch)] #![deny(missing_docs)] #![feature(asm_const)] +#![feature(abi_msp430_interrupt)] pub mod batch_gpio; pub mod capture; diff --git a/src/spi.rs b/src/spi.rs index 40152b9..8d74a8b 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -1,55 +1,203 @@ //! embedded_hal SPI implmentation - use core::marker::PhantomData; +use core::cell::RefCell; +use embedded_hal::spi::FullDuplex; +use msp430::critical_section::with; use msp430fr2355 as pac; use crate::hal::{ - spi as spi_nb, - blocking::spi as spi_blocking, + spi::{Mode, Polarity, Phase} }; use crate::{ - hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel}, - gpio::{Alternate1, Pin, P1, P4, Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7}, + hw_traits::Steal, + hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel, SPIInterruptVector}, + gpio::{Alternate1, Pin, P1, P4, Pin1, Pin2, Pin3, Pin5, Pin6, Pin7}, clock::{Smclk, Aclk} }; +use msp430::interrupt::{Mutex}; +use nb::Error::{WouldBlock}; /// Marks a eUSCI capable of SPI communication (in this case, all euscis do) -pub trait EUsciSPIBus : EusciSPI{ +pub trait EUsciSPIBus : EusciSPIPriv{ /// Master In Slave Out (refered to as SOMI in datasheet) type MISO; /// Master Out Slave In (refered to as SIMO in datasheet) type MOSI; /// Serial Clock type SCLK; - /// Slave Transmit Enable (mostly equivalent to Chip Select for single-master systems) - type STE; + // /// Slave Transmit Enable + // type STE; +} + +#[doc(hidden)] +pub trait EusciSPIPriv : EusciSPI{ + fn get_rx_buf() -> &'static Mutex>; + fn get_tx_buf() -> &'static Mutex>; + unsafe fn spi_interrupt(); +} + +#[doc(hidden)] +pub struct QueueBuf{ + buf: [u8;16], + curr: u8, //ptr to current slot to get from + next: u8, //ptr to next available slot +} + +impl QueueBuf{ + const fn new() -> Self{ + QueueBuf{ + buf: [0;16], + curr: 0, + next: 0, + } + } + + #[inline] + fn inc(val:u8) -> u8{ + (val+1) & 0xF + } + + #[inline] + fn has_data(&self) -> bool{ + return self.curr != self.next; + } + + #[inline] + fn slots_left(&self) -> u8{ + 15u8 - (self.next - self.curr) + } + + #[inline] + fn is_full(&self) -> bool{ + return self.curr == Self::inc(self.next); + } + + #[inline] + fn is_empty(&self) -> bool{ + return self.curr == self.next; + } + + //make sure to check for fullness before calling + fn put(&mut self, val: u8){ + self.buf[self.next as usize] = val; + self.next = Self::inc(self.next); + } + + //make sure to check for data before calling + fn get(&mut self) -> u8{ + let val = self.buf[self.curr as usize]; + self.curr = Self::inc(self.curr); + val + } +} + + + +static SPI_RX_BUF_A0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); +static SPI_TX_BUF_A0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); + +impl EusciSPIPriv for pac::E_USCI_A0 { + #[inline] + fn get_rx_buf() -> &'static Mutex>{ + & SPI_RX_BUF_A0 + } + + #[inline] + fn get_tx_buf() ->&'static Mutex>{ + & SPI_TX_BUF_A0 + } + + #[inline] + unsafe fn spi_interrupt(){ + spi_interrupt_shared(pac::E_USCI_A0::steal()); + } } impl EUsciSPIBus for pac::E_USCI_A0 { type MISO = UsciA0MISOPin; type MOSI = UsciA0MOSIPin; type SCLK = UsciA0SCLKPin; - type STE = UsciA0STEPin; + // type STE = UsciA0STEPin; +} + +static SPI_RX_BUF_A1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); +static SPI_TX_BUF_A1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); + +impl EusciSPIPriv for pac::E_USCI_A1 { + #[inline] + fn get_rx_buf() -> &'static Mutex>{ + & SPI_RX_BUF_A1 + } + + #[inline] + fn get_tx_buf() -> &'static Mutex>{ + & SPI_TX_BUF_A1 + } + + #[inline] + unsafe fn spi_interrupt(){ + spi_interrupt_shared(pac::E_USCI_A1::steal()); + } } impl EUsciSPIBus for pac::E_USCI_A1 { type MISO = UsciA0MISOPin; type MOSI = UsciA0MOSIPin; type SCLK = UsciA0SCLKPin; - type STE = UsciA0STEPin; + // type STE = UsciA0STEPin; +} + +static SPI_RX_BUF_B0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); +static SPI_TX_BUF_B0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); + +impl EusciSPIPriv for pac::E_USCI_B0 { + #[inline] + fn get_rx_buf() -> &'static Mutex>{ + & SPI_RX_BUF_B0 + } + + #[inline] + fn get_tx_buf() -> &'static Mutex>{ + & SPI_TX_BUF_B0 + } + + #[inline] + unsafe fn spi_interrupt(){ + spi_interrupt_shared(pac::E_USCI_B0::steal()); + } } impl EUsciSPIBus for pac::E_USCI_B0 { type MISO = UsciA0MISOPin; type MOSI = UsciA0MOSIPin; type SCLK = UsciA0SCLKPin; - type STE = UsciA0STEPin; + // type STE = UsciA0STEPin; +} + +static SPI_RX_BUF_B1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); +static SPI_TX_BUF_B1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); + +impl EusciSPIPriv for pac::E_USCI_B1 { + #[inline] + fn get_rx_buf() -> &'static Mutex>{ + & SPI_RX_BUF_B1 + } + + #[inline] + fn get_tx_buf() -> &'static Mutex>{ + & SPI_TX_BUF_B1 + } + + #[inline] + unsafe fn spi_interrupt(){ + spi_interrupt_shared(pac::E_USCI_B1::steal()); + } } impl EUsciSPIBus for pac::E_USCI_B1 { type MISO = UsciA0MISOPin; type MOSI = UsciA0MOSIPin; type SCLK = UsciA0SCLKPin; - type STE = UsciA0STEPin; + // type STE = UsciA0STEPin; } /// SPI MISO pin for eUSCI A0 @@ -79,14 +227,14 @@ impl Into for Pin> { } } -/// SPI STE pin for eUSCI A0 -pub struct UsciA0STEPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA0STEPin { - UsciA0STEPin - } -} +// /// SPI STE pin for eUSCI A0 +// pub struct UsciA0STEPin; +// impl Into for Pin> { +// #[inline(always)] +// fn into(self) -> UsciA0STEPin { +// UsciA0STEPin +// } +// } /// SPI MISO pin for eUSCI A1 pub struct UsciA1MISOPin; @@ -115,14 +263,14 @@ impl Into for Pin> { } } -/// SPI STE pin for eUSCI A1 -pub struct UsciA1STEPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA1STEPin { - UsciA1STEPin - } -} +// /// SPI STE pin for eUSCI A1 +// pub struct UsciA1STEPin; +// impl Into for Pin> { +// #[inline(always)] +// fn into(self) -> UsciA1STEPin { +// UsciA1STEPin +// } +// } /// SPI MISO pin for eUSCI B0 pub struct UsciB0MISOPin; @@ -151,14 +299,14 @@ impl Into for Pin> { } } -/// SPI STE pin for eUSCI B0 -pub struct UsciB0STEPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB0STEPin { - UsciB0STEPin - } -} +// /// SPI STE pin for eUSCI B0 +// pub struct UsciB0STEPin; +// impl Into for Pin> { +// #[inline(always)] +// fn into(self) -> UsciB0STEPin { +// UsciB0STEPin +// } +// } /// SPI MISO pin for eUSCI B1 pub struct UsciB1MISOPin; @@ -187,14 +335,14 @@ impl Into for Pin> { } } -/// SPI STE pin for eUSCI B1 -pub struct UsciB1STEPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB1STEPin { - UsciB1STEPin - } -} +// /// SPI STE pin for eUSCI B1 +// pub struct UsciB1STEPin; +// impl Into for Pin> { +// #[inline(always)] +// fn into(self) -> UsciB1STEPin { +// UsciB1STEPin +// } +// } /// Struct used to configure a SPI bus pub struct SPIBusConfig{ @@ -207,20 +355,27 @@ pub struct SPIBusConfig{ impl SPIBusConfig{ /// Create a new configuration for setting up a EUSCI peripheral in SPI mode - pub fn new(usci: USCI, fourPin:bool)->Self{ + pub fn new(usci: USCI, mode:Mode)->Self{ let ctlw0 = UcxSpiCtw0{ - ucckph: false, - ucckpl: false, + ucckph: match mode.phase { + Phase::CaptureOnFirstTransition => true, + Phase::CaptureOnSecondTransition => false, + }, + ucckpl: match mode.polarity { + Polarity::IdleLow => false, + Polarity::IdleHigh => true, + }, ucmsb: false, uc7bit: false, - ucmst: false, + ucmst: true, ucsync: false, ucstem: false, - ucswrst: false, + ucswrst: true, ucmode: Ucmode::FourPinSPI0, ucssel: Ucssel::Uclk, }; + SPIBusConfig{ usci: usci, prescaler: 0, @@ -230,27 +385,23 @@ impl SPIBusConfig{ /// Configures this peripheral to use smclk #[inline] - pub fn use_smclk(&mut self, smclk:&Smclk, clk_divisor:u16){ + pub fn use_smclk(&mut self, _smclk:&Smclk, clk_divisor:u16){ self.ctlw0.ucssel = Ucssel::Smclk; self.prescaler = clk_divisor; } /// Configures this peripheral to use aclk #[inline] - pub fn use_aclk(&mut self, aclk:&Aclk, clk_divisor:u16){ + pub fn use_aclk(&mut self, _aclk:&Aclk, clk_divisor:u16){ self.ctlw0.ucssel = Ucssel::Aclk; self.prescaler = clk_divisor; } - /// Performs hardware configuration and creates a 4 wire SPI bus - pub fn four_wire - , SI: Into, CLK: Into, CS: Into> - (&mut self, cs_active_high: bool, _miso: SO, _mosi: SI, _sclk: CLK, _cs:CS) + /// Performs hardware configuration and creates an SPI bus + pub fn spi_pins + , SI: Into, CLK: Into> + (&mut self, _miso: SO, _mosi: SI, _sclk: CLK) -> SPIPins{ - match cs_active_high { - true => {self.ctlw0.ucmode = Ucmode::FourPinSPI1}, - false => {self.ctlw0.ucmode = Ucmode::FourPinSPI0}, - } self.configure_hw(); SPIPins(PhantomData) } @@ -266,6 +417,9 @@ impl SPIBusConfig{ self.usci.receive_interrupt_set(false); self.usci.ctw0_wr_rst(false); + + self.usci.transmit_interrupt_set(false); + self.usci.receive_interrupt_set(true); } } @@ -273,8 +427,63 @@ impl SPIBusConfig{ /// Represents a group of pins configured for SPI communication pub struct SPIPins(PhantomData); -impl SPIPins{ +/// SPI transmit/receive errors +#[derive(Clone, Copy)] +pub enum SPIErr{ + /// Function not implemented + Unimplemented = 0, +} + +#[inline] +fn spi_interrupt_shared (usci: USCI){ + with(|cs| { + match usci.iv_rd() { + SPIInterruptVector::DataReceived => { + let rx_buf = &mut *USCI::get_rx_buf().borrow_ref_mut(cs); + rx_buf.put(usci.rxbuf_rd()); + } + SPIInterruptVector::TXBufferEmpty => { + let tx_buf = &mut *USCI::get_tx_buf().borrow_ref_mut(cs); + if tx_buf.is_empty(){ + usci.transmit_interrupt_set(false); + return; + } + usci.txbuf_wr(tx_buf.get()); + usci.rxbuf_rd(); //dummy read + } + _ => {} + }; + }); +} + + +impl FullDuplex for SPIPins{ + type Error = SPIErr; + fn read(&mut self) -> nb::Result{ + with(|cs| { + let rx_buf = &mut *USCI::get_rx_buf().borrow_ref_mut(cs); + if rx_buf.has_data() { + Ok(rx_buf.get()) + }else{ + Err(WouldBlock) + } + }) + } + fn send(&mut self, word: u8) -> nb::Result<(), Self::Error>{ + with(|cs| { + let tx_buf = &mut *USCI::get_tx_buf().borrow_ref_mut(cs); + if tx_buf.is_full() { + Err(WouldBlock) + }else{ + if tx_buf.is_empty() { + let usci = unsafe {USCI::steal()}; + usci.transmit_interrupt_set(true); + } + Ok(tx_buf.put(word)) + } + }) + } } @@ -287,3 +496,4 @@ impl SPIPins{ + From d2c1a82691fff29d504cd8f017fdfb4e5cb47e62 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Thu, 2 Mar 2023 00:32:18 -0600 Subject: [PATCH 15/82] SPI work --- src/clock.rs | 14 +++- src/delay.rs | 35 +++++++++ src/hw_traits/eusci.rs | 11 ++- src/lib.rs | 1 + src/spi.rs | 166 ++++++++++++++++++++++++----------------- 5 files changed, 150 insertions(+), 77 deletions(-) create mode 100644 src/delay.rs diff --git a/src/clock.rs b/src/clock.rs index cac5957..595e3fe 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -15,6 +15,7 @@ use msp430fr2355 as pac; use pac::cs::csctl1::DCORSEL_A; use pac::cs::csctl4::{SELA_A, SELMS_A}; pub use pac::cs::csctl5::{DIVM_A as MclkDiv, DIVS_A as SmclkDiv}; +use crate::delay::Delay; /// REFOCLK frequency pub const REFOCLK: u16 = 32768; @@ -339,8 +340,9 @@ impl ClockConfig { impl ClockConfig { /// Apply clock configuration to hardware and return SMCLK and ACLK clock objects + /// Also returns delay provider #[inline] - pub fn freeze(self, fram: &mut Fram) -> (Smclk, Aclk) { + pub fn freeze(self, fram: &mut Fram) -> (Smclk, Aclk, Delay) { let mclk_freq = self.mclk.0.freq() >> (self.mclk_div as u32); unsafe { Self::configure_fram(fram, mclk_freq) }; self.configure_dco_fll(); @@ -348,19 +350,25 @@ impl ClockConfig { ( Smclk(mclk_freq >> (self.smclk.0 as u32)), Aclk(self.aclk_sel.freq()), + Delay::new(mclk_freq) ) } } impl ClockConfig { /// Apply clock configuration to hardware and return ACLK clock object, as SMCLK is disabled + /// Also returns delay provider. #[inline] - pub fn freeze(self, fram: &mut Fram) -> Aclk { + pub fn freeze(self, fram: &mut Fram) -> (Aclk, Delay) { let mclk_freq = self.mclk.0.freq() >> (self.mclk_div as u32); self.configure_dco_fll(); unsafe { Self::configure_fram(fram, mclk_freq) }; self.configure_cs(); - Aclk(self.aclk_sel.freq()) + ( + Aclk(self.aclk_sel.freq()), + Delay::new(mclk_freq) + ) + } } diff --git a/src/delay.rs b/src/delay.rs new file mode 100644 index 0000000..9d97983 --- /dev/null +++ b/src/delay.rs @@ -0,0 +1,35 @@ +//! Embedded hal delay implementation +use msp430::asm; +use crate::hal::blocking::delay::{DelayMs, DelayUs}; + + +/// Delay provider struct +pub struct Delay{ + freq: u32 +} + +impl Delay{ + #[doc(hidden)] + pub fn new(freq: u32) -> Self{ + Delay{freq} + } +} + +impl DelayMs for Delay{ + #[inline] + fn delay_ms(&mut self, ms: u8){ + self.delay_ms(ms as u16); + } +} + +impl DelayMs for Delay{ + fn delay_ms(&mut self, ms: u16){ + //TODO take into account clock freq for delay + // + for _ in 0 .. ms{ + for _ in 0 .. 200{ + asm::nop(); + } + } + } +} diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 591710e..f22f2ee 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -395,9 +395,12 @@ pub trait EusciSPI : EUsci{ fn receive_flag(&self) -> bool; - fn iv_rd(&self) -> SPIInterruptVector; + fn iv_rd(&self) -> u16; + +} - fn spi_interrupt(){} +pub trait EusciSPIInterrupter{ + unsafe fn spi_interrupt(); } pub trait UartUcxStatw { @@ -512,8 +515,8 @@ macro_rules! eusci_impl { } #[inline(always)] - fn iv_rd(&self) -> SPIInterruptVector{ - SPIInterruptVector::from(self.$ucxiv().read().uciv().bits()) + fn iv_rd(&self) -> u16{ + self.$ucxiv().read().uciv().bits() } } diff --git a/src/lib.rs b/src/lib.rs index 5d339de..8029d00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ mod util; pub mod i2c; pub mod spi; +pub mod delay; pub use msp430fr2355 as pac; pub use embedded_hal as hal; diff --git a/src/spi.rs b/src/spi.rs index 8d74a8b..cd3fab5 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -5,16 +5,18 @@ use embedded_hal::spi::FullDuplex; use msp430::critical_section::with; use msp430fr2355 as pac; use crate::hal::{ - spi::{Mode, Polarity, Phase} + spi::{Mode, Polarity, Phase}, + blocking::spi::write, }; use crate::{ - hw_traits::Steal, - hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel, SPIInterruptVector}, - gpio::{Alternate1, Pin, P1, P4, Pin1, Pin2, Pin3, Pin5, Pin6, Pin7}, - clock::{Smclk, Aclk} + hw_traits::Steal, hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel, SPIInterruptVector}, + gpio::{Alternate1, Pin, P1, P4, Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7}, + clock::{Smclk, Aclk}, + hal }; use msp430::interrupt::{Mutex}; use nb::Error::{WouldBlock}; +use crate::hw_traits::eusci::EusciSPIInterrupter; /// Marks a eUSCI capable of SPI communication (in this case, all euscis do) pub trait EUsciSPIBus : EusciSPIPriv{ @@ -24,15 +26,14 @@ pub trait EUsciSPIBus : EusciSPIPriv{ type MOSI; /// Serial Clock type SCLK; - // /// Slave Transmit Enable - // type STE; + /// Slave Transmit Enable (acts like CS) + type STE; } #[doc(hidden)] pub trait EusciSPIPriv : EusciSPI{ fn get_rx_buf() -> &'static Mutex>; fn get_tx_buf() -> &'static Mutex>; - unsafe fn spi_interrupt(); } #[doc(hidden)] @@ -106,6 +107,9 @@ impl EusciSPIPriv for pac::E_USCI_A0 { & SPI_TX_BUF_A0 } +} + +impl EusciSPIInterrupter for pac::E_USCI_A0 { #[inline] unsafe fn spi_interrupt(){ spi_interrupt_shared(pac::E_USCI_A0::steal()); @@ -116,7 +120,7 @@ impl EUsciSPIBus for pac::E_USCI_A0 { type MISO = UsciA0MISOPin; type MOSI = UsciA0MOSIPin; type SCLK = UsciA0SCLKPin; - // type STE = UsciA0STEPin; + type STE = UsciA0STEPin; } static SPI_RX_BUF_A1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); @@ -133,6 +137,9 @@ impl EusciSPIPriv for pac::E_USCI_A1 { & SPI_TX_BUF_A1 } +} + +impl EusciSPIInterrupter for pac::E_USCI_A1 { #[inline] unsafe fn spi_interrupt(){ spi_interrupt_shared(pac::E_USCI_A1::steal()); @@ -140,10 +147,10 @@ impl EusciSPIPriv for pac::E_USCI_A1 { } impl EUsciSPIBus for pac::E_USCI_A1 { - type MISO = UsciA0MISOPin; - type MOSI = UsciA0MOSIPin; - type SCLK = UsciA0SCLKPin; - // type STE = UsciA0STEPin; + type MISO = UsciA1MISOPin; + type MOSI = UsciA1MOSIPin; + type SCLK = UsciA1SCLKPin; + type STE = UsciA1STEPin; } static SPI_RX_BUF_B0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); @@ -160,6 +167,9 @@ impl EusciSPIPriv for pac::E_USCI_B0 { & SPI_TX_BUF_B0 } +} + +impl EusciSPIInterrupter for pac::E_USCI_B0 { #[inline] unsafe fn spi_interrupt(){ spi_interrupt_shared(pac::E_USCI_B0::steal()); @@ -167,10 +177,10 @@ impl EusciSPIPriv for pac::E_USCI_B0 { } impl EUsciSPIBus for pac::E_USCI_B0 { - type MISO = UsciA0MISOPin; - type MOSI = UsciA0MOSIPin; - type SCLK = UsciA0SCLKPin; - // type STE = UsciA0STEPin; + type MISO = UsciB0MISOPin; + type MOSI = UsciB0MOSIPin; + type SCLK = UsciB0SCLKPin; + type STE = UsciB0STEPin; } static SPI_RX_BUF_B1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); @@ -187,6 +197,9 @@ impl EusciSPIPriv for pac::E_USCI_B1 { & SPI_TX_BUF_B1 } +} + +impl EusciSPIInterrupter for pac::E_USCI_B1 { #[inline] unsafe fn spi_interrupt(){ spi_interrupt_shared(pac::E_USCI_B1::steal()); @@ -194,10 +207,10 @@ impl EusciSPIPriv for pac::E_USCI_B1 { } impl EUsciSPIBus for pac::E_USCI_B1 { - type MISO = UsciA0MISOPin; - type MOSI = UsciA0MOSIPin; - type SCLK = UsciA0SCLKPin; - // type STE = UsciA0STEPin; + type MISO = UsciB1MISOPin; + type MOSI = UsciB1MOSIPin; + type SCLK = UsciB1SCLKPin; + type STE = UsciB1STEPin; } /// SPI MISO pin for eUSCI A0 @@ -227,14 +240,14 @@ impl Into for Pin> { } } -// /// SPI STE pin for eUSCI A0 -// pub struct UsciA0STEPin; -// impl Into for Pin> { -// #[inline(always)] -// fn into(self) -> UsciA0STEPin { -// UsciA0STEPin -// } -// } +/// SPI STE pin for eUSCI A0 +pub struct UsciA0STEPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA0STEPin { + UsciA0STEPin + } +} /// SPI MISO pin for eUSCI A1 pub struct UsciA1MISOPin; @@ -263,14 +276,14 @@ impl Into for Pin> { } } -// /// SPI STE pin for eUSCI A1 -// pub struct UsciA1STEPin; -// impl Into for Pin> { -// #[inline(always)] -// fn into(self) -> UsciA1STEPin { -// UsciA1STEPin -// } -// } +/// SPI STE pin for eUSCI A1 +pub struct UsciA1STEPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciA1STEPin { + UsciA1STEPin + } +} /// SPI MISO pin for eUSCI B0 pub struct UsciB0MISOPin; @@ -299,14 +312,14 @@ impl Into for Pin> { } } -// /// SPI STE pin for eUSCI B0 -// pub struct UsciB0STEPin; -// impl Into for Pin> { -// #[inline(always)] -// fn into(self) -> UsciB0STEPin { -// UsciB0STEPin -// } -// } +/// SPI STE pin for eUSCI B0 +pub struct UsciB0STEPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB0STEPin { + UsciB0STEPin + } +} /// SPI MISO pin for eUSCI B1 pub struct UsciB1MISOPin; @@ -335,14 +348,14 @@ impl Into for Pin> { } } -// /// SPI STE pin for eUSCI B1 -// pub struct UsciB1STEPin; -// impl Into for Pin> { -// #[inline(always)] -// fn into(self) -> UsciB1STEPin { -// UsciB1STEPin -// } -// } +/// SPI STE pin for eUSCI B1 +pub struct UsciB1STEPin; +impl Into for Pin> { + #[inline(always)] + fn into(self) -> UsciB1STEPin { + UsciB1STEPin + } +} /// Struct used to configure a SPI bus pub struct SPIBusConfig{ @@ -368,8 +381,8 @@ impl SPIBusConfig{ ucmsb: false, uc7bit: false, ucmst: true, - ucsync: false, - ucstem: false, + ucsync: true, + ucstem: true, ucswrst: true, ucmode: Ucmode::FourPinSPI0, ucssel: Ucssel::Uclk, @@ -399,8 +412,8 @@ impl SPIBusConfig{ /// Performs hardware configuration and creates an SPI bus pub fn spi_pins - , SI: Into, CLK: Into> - (&mut self, _miso: SO, _mosi: SI, _sclk: CLK) + , SI: Into, CLK: Into, STE: Into> + (&mut self, _miso: SO, _mosi: SI, _sclk: CLK, _cs : STE) -> SPIPins{ self.configure_hw(); SPIPins(PhantomData) @@ -413,8 +426,6 @@ impl SPIBusConfig{ self.usci.ctw0_wr(&self.ctlw0); self.usci.brw_wr(self.prescaler); self.usci.uclisten_set(false); - self.usci.transmit_interrupt_set(false); - self.usci.receive_interrupt_set(false); self.usci.ctw0_wr_rst(false); @@ -427,6 +438,9 @@ impl SPIBusConfig{ /// Represents a group of pins configured for SPI communication pub struct SPIPins(PhantomData); + + + /// SPI transmit/receive errors #[derive(Clone, Copy)] pub enum SPIErr{ @@ -437,25 +451,37 @@ pub enum SPIErr{ #[inline] fn spi_interrupt_shared (usci: USCI){ with(|cs| { - match usci.iv_rd() { - SPIInterruptVector::DataReceived => { - let rx_buf = &mut *USCI::get_rx_buf().borrow_ref_mut(cs); - rx_buf.put(usci.rxbuf_rd()); - } - SPIInterruptVector::TXBufferEmpty => { - let tx_buf = &mut *USCI::get_tx_buf().borrow_ref_mut(cs); + if usci.receive_flag() { + let rx_buf = &mut *USCI::get_rx_buf().borrow_ref_mut(cs); + rx_buf.put(usci.rxbuf_rd()); + } + if usci.transmit_flag() { + let tx_buf = &mut *USCI::get_tx_buf().borrow_ref_mut(cs); + if tx_buf.has_data() { + usci.txbuf_wr(tx_buf.get()); if tx_buf.is_empty(){ usci.transmit_interrupt_set(false); return; } - usci.txbuf_wr(tx_buf.get()); - usci.rxbuf_rd(); //dummy read + // usci.rxbuf_rd(); //dummy read + }else{ + usci.transmit_interrupt_set(false); } - _ => {} - }; + } }); } +impl hal::blocking::spi::Write for SPIPins{ + type Error = SPIErr; + + fn write(&mut self, words: &[u8]) -> Result<(), SPIErr> { + for word in words { + nb::block!(self.send(*word))?; + nb::block!(self.read())?; + } + Ok(()) + } +} impl FullDuplex for SPIPins{ type Error = SPIErr; From b0d33bbf3d8851300e3b5d976f2ee8da6b3210db Mon Sep 17 00:00:00 2001 From: rbneiman Date: Thu, 2 Mar 2023 16:24:39 -0600 Subject: [PATCH 16/82] SPI works --- src/delay.rs | 2 +- src/spi.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 9d97983..493d923 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -27,7 +27,7 @@ impl DelayMs for Delay{ //TODO take into account clock freq for delay // for _ in 0 .. ms{ - for _ in 0 .. 200{ + for _ in 0 .. 800{ asm::nop(); } } diff --git a/src/spi.rs b/src/spi.rs index cd3fab5..43d3c31 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -368,7 +368,7 @@ pub struct SPIBusConfig{ impl SPIBusConfig{ /// Create a new configuration for setting up a EUSCI peripheral in SPI mode - pub fn new(usci: USCI, mode:Mode)->Self{ + pub fn new(usci: USCI, mode:Mode, msbFirst:bool)->Self{ let ctlw0 = UcxSpiCtw0{ ucckph: match mode.phase { Phase::CaptureOnFirstTransition => true, @@ -378,7 +378,7 @@ impl SPIBusConfig{ Polarity::IdleLow => false, Polarity::IdleHigh => true, }, - ucmsb: false, + ucmsb: msbFirst, uc7bit: false, ucmst: true, ucsync: true, From 409acbbbf545cb5d3850805e2418e95c744c93ec Mon Sep 17 00:00:00 2001 From: rbneiman Date: Sat, 4 Mar 2023 14:31:12 -0600 Subject: [PATCH 17/82] stream speedup --- src/hw_traits/eusci.rs | 23 ---- src/spi.rs | 243 ++++++----------------------------------- 2 files changed, 36 insertions(+), 230 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index f22f2ee..80d4e19 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -1,6 +1,5 @@ use super::Steal; use msp430fr2355 as pac; -use msp430fr2355::interrupt; macro_rules! from_u8 { ($typ: ty) => { @@ -120,19 +119,6 @@ pub enum Ucastp { } from_u8!(Ucastp); -#[derive(Copy, Clone)] -pub enum SPIInterruptVector{ - None = 0, - DataReceived = 2, - TXBufferEmpty = 4, -} -impl From for SPIInterruptVector { - #[inline(always)] - fn from(variant: u16) -> Self { - (variant & 0x4).into() - } -} - pub struct UcaCtlw0{ pub ucpen: bool, @@ -399,10 +385,6 @@ pub trait EusciSPI : EUsci{ } -pub trait EusciSPIInterrupter{ - unsafe fn spi_interrupt(); -} - pub trait UartUcxStatw { fn ucfe(&self) -> bool; fn ucoe(&self) -> bool; @@ -451,11 +433,6 @@ macro_rules! eusci_impl { } - #[interrupt] - unsafe fn $intr_vec(){ - pac::$EUsci::spi_interrupt(); - } - impl EusciSPI for pac::$EUsci { type Statw = $StatwSpi; diff --git a/src/spi.rs b/src/spi.rs index 43d3c31..deffe23 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -1,25 +1,21 @@ //! embedded_hal SPI implmentation use core::marker::PhantomData; -use core::cell::RefCell; use embedded_hal::spi::FullDuplex; -use msp430::critical_section::with; use msp430fr2355 as pac; use crate::hal::{ spi::{Mode, Polarity, Phase}, - blocking::spi::write, }; use crate::{ - hw_traits::Steal, hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel, SPIInterruptVector}, + hw_traits::Steal, + hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel}, gpio::{Alternate1, Pin, P1, P4, Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7}, clock::{Smclk, Aclk}, hal }; -use msp430::interrupt::{Mutex}; use nb::Error::{WouldBlock}; -use crate::hw_traits::eusci::EusciSPIInterrupter; /// Marks a eUSCI capable of SPI communication (in this case, all euscis do) -pub trait EUsciSPIBus : EusciSPIPriv{ +pub trait EUsciSPIBus : EusciSPI{ /// Master In Slave Out (refered to as SOMI in datasheet) type MISO; /// Master Out Slave In (refered to as SIMO in datasheet) @@ -30,92 +26,6 @@ pub trait EUsciSPIBus : EusciSPIPriv{ type STE; } -#[doc(hidden)] -pub trait EusciSPIPriv : EusciSPI{ - fn get_rx_buf() -> &'static Mutex>; - fn get_tx_buf() -> &'static Mutex>; -} - -#[doc(hidden)] -pub struct QueueBuf{ - buf: [u8;16], - curr: u8, //ptr to current slot to get from - next: u8, //ptr to next available slot -} - -impl QueueBuf{ - const fn new() -> Self{ - QueueBuf{ - buf: [0;16], - curr: 0, - next: 0, - } - } - - #[inline] - fn inc(val:u8) -> u8{ - (val+1) & 0xF - } - - #[inline] - fn has_data(&self) -> bool{ - return self.curr != self.next; - } - - #[inline] - fn slots_left(&self) -> u8{ - 15u8 - (self.next - self.curr) - } - - #[inline] - fn is_full(&self) -> bool{ - return self.curr == Self::inc(self.next); - } - - #[inline] - fn is_empty(&self) -> bool{ - return self.curr == self.next; - } - - //make sure to check for fullness before calling - fn put(&mut self, val: u8){ - self.buf[self.next as usize] = val; - self.next = Self::inc(self.next); - } - - //make sure to check for data before calling - fn get(&mut self) -> u8{ - let val = self.buf[self.curr as usize]; - self.curr = Self::inc(self.curr); - val - } -} - - - -static SPI_RX_BUF_A0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); -static SPI_TX_BUF_A0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); - -impl EusciSPIPriv for pac::E_USCI_A0 { - #[inline] - fn get_rx_buf() -> &'static Mutex>{ - & SPI_RX_BUF_A0 - } - - #[inline] - fn get_tx_buf() ->&'static Mutex>{ - & SPI_TX_BUF_A0 - } - -} - -impl EusciSPIInterrupter for pac::E_USCI_A0 { - #[inline] - unsafe fn spi_interrupt(){ - spi_interrupt_shared(pac::E_USCI_A0::steal()); - } -} - impl EUsciSPIBus for pac::E_USCI_A0 { type MISO = UsciA0MISOPin; type MOSI = UsciA0MOSIPin; @@ -123,29 +33,6 @@ impl EUsciSPIBus for pac::E_USCI_A0 { type STE = UsciA0STEPin; } -static SPI_RX_BUF_A1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); -static SPI_TX_BUF_A1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); - -impl EusciSPIPriv for pac::E_USCI_A1 { - #[inline] - fn get_rx_buf() -> &'static Mutex>{ - & SPI_RX_BUF_A1 - } - - #[inline] - fn get_tx_buf() -> &'static Mutex>{ - & SPI_TX_BUF_A1 - } - -} - -impl EusciSPIInterrupter for pac::E_USCI_A1 { - #[inline] - unsafe fn spi_interrupt(){ - spi_interrupt_shared(pac::E_USCI_A1::steal()); - } -} - impl EUsciSPIBus for pac::E_USCI_A1 { type MISO = UsciA1MISOPin; type MOSI = UsciA1MOSIPin; @@ -153,29 +40,6 @@ impl EUsciSPIBus for pac::E_USCI_A1 { type STE = UsciA1STEPin; } -static SPI_RX_BUF_B0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); -static SPI_TX_BUF_B0: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); - -impl EusciSPIPriv for pac::E_USCI_B0 { - #[inline] - fn get_rx_buf() -> &'static Mutex>{ - & SPI_RX_BUF_B0 - } - - #[inline] - fn get_tx_buf() -> &'static Mutex>{ - & SPI_TX_BUF_B0 - } - -} - -impl EusciSPIInterrupter for pac::E_USCI_B0 { - #[inline] - unsafe fn spi_interrupt(){ - spi_interrupt_shared(pac::E_USCI_B0::steal()); - } -} - impl EUsciSPIBus for pac::E_USCI_B0 { type MISO = UsciB0MISOPin; type MOSI = UsciB0MOSIPin; @@ -183,29 +47,6 @@ impl EUsciSPIBus for pac::E_USCI_B0 { type STE = UsciB0STEPin; } -static SPI_RX_BUF_B1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); -static SPI_TX_BUF_B1: Mutex> = Mutex::new(RefCell::new(QueueBuf::new())); - -impl EusciSPIPriv for pac::E_USCI_B1 { - #[inline] - fn get_rx_buf() -> &'static Mutex>{ - & SPI_RX_BUF_B1 - } - - #[inline] - fn get_tx_buf() -> &'static Mutex>{ - & SPI_TX_BUF_B1 - } - -} - -impl EusciSPIInterrupter for pac::E_USCI_B1 { - #[inline] - unsafe fn spi_interrupt(){ - spi_interrupt_shared(pac::E_USCI_B1::steal()); - } -} - impl EUsciSPIBus for pac::E_USCI_B1 { type MISO = UsciB1MISOPin; type MOSI = UsciB1MOSIPin; @@ -368,7 +209,7 @@ pub struct SPIBusConfig{ impl SPIBusConfig{ /// Create a new configuration for setting up a EUSCI peripheral in SPI mode - pub fn new(usci: USCI, mode:Mode, msbFirst:bool)->Self{ + pub fn new(usci: USCI, mode:Mode, msb_first:bool)->Self{ let ctlw0 = UcxSpiCtw0{ ucckph: match mode.phase { Phase::CaptureOnFirstTransition => true, @@ -378,7 +219,7 @@ impl SPIBusConfig{ Polarity::IdleLow => false, Polarity::IdleHigh => true, }, - ucmsb: msbFirst, + ucmsb: msb_first, uc7bit: false, ucmst: true, ucsync: true, @@ -430,7 +271,7 @@ impl SPIBusConfig{ self.usci.ctw0_wr_rst(false); self.usci.transmit_interrupt_set(false); - self.usci.receive_interrupt_set(true); + self.usci.receive_interrupt_set(false); } } @@ -439,7 +280,23 @@ impl SPIBusConfig{ pub struct SPIPins(PhantomData); +impl SPIPins{ + /// Enable or disable Rx interrupts, which fire when a byte is ready to be read + #[inline(always)] + pub fn rx_interrupt_set(&mut self, flag: bool) { + let usci = unsafe { USCI::steal() }; + usci.receive_interrupt_set(flag); + } + + /// Enable or disable Tx interrupts, which fire when the transmit buffer is empty + #[inline(always)] + pub fn tx_interrupt_set(&mut self, flag: bool) { + let usci = unsafe { USCI::steal() }; + usci.transmit_interrupt_set(flag); + } + +} /// SPI transmit/receive errors #[derive(Clone, Copy)] @@ -448,28 +305,7 @@ pub enum SPIErr{ Unimplemented = 0, } -#[inline] -fn spi_interrupt_shared (usci: USCI){ - with(|cs| { - if usci.receive_flag() { - let rx_buf = &mut *USCI::get_rx_buf().borrow_ref_mut(cs); - rx_buf.put(usci.rxbuf_rd()); - } - if usci.transmit_flag() { - let tx_buf = &mut *USCI::get_tx_buf().borrow_ref_mut(cs); - if tx_buf.has_data() { - usci.txbuf_wr(tx_buf.get()); - if tx_buf.is_empty(){ - usci.transmit_interrupt_set(false); - return; - } - // usci.rxbuf_rd(); //dummy read - }else{ - usci.transmit_interrupt_set(false); - } - } - }); -} + impl hal::blocking::spi::Write for SPIPins{ type Error = SPIErr; @@ -486,29 +322,22 @@ impl hal::blocking::spi::Write for SPIPins{ impl FullDuplex for SPIPins{ type Error = SPIErr; fn read(&mut self) -> nb::Result{ - with(|cs| { - let rx_buf = &mut *USCI::get_rx_buf().borrow_ref_mut(cs); - if rx_buf.has_data() { - Ok(rx_buf.get()) - }else{ - Err(WouldBlock) - } - }) + let usci = unsafe{USCI::steal()}; + if usci.receive_flag() { + Ok(usci.rxbuf_rd()) + }else{ + Err(WouldBlock) + } } fn send(&mut self, word: u8) -> nb::Result<(), Self::Error>{ - with(|cs| { - let tx_buf = &mut *USCI::get_tx_buf().borrow_ref_mut(cs); - if tx_buf.is_full() { - Err(WouldBlock) - }else{ - if tx_buf.is_empty() { - let usci = unsafe {USCI::steal()}; - usci.transmit_interrupt_set(true); - } - Ok(tx_buf.put(word)) - } - }) + let usci = unsafe{USCI::steal()}; + if usci.transmit_flag() { + usci.txbuf_wr(word); + Ok(()) + }else{ + Err(WouldBlock) + } } } From ec8b915b180405fb627480327bdb2d7e8f805f01 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Sun, 5 Mar 2023 16:41:53 -0600 Subject: [PATCH 18/82] interrupt handlers --- src/delay.rs | 10 +++++----- src/lib.rs | 1 - src/serial.rs | 7 +++++++ src/spi.rs | 7 ++++++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 493d923..f22c477 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -5,13 +5,15 @@ use crate::hal::blocking::delay::{DelayMs, DelayUs}; /// Delay provider struct pub struct Delay{ - freq: u32 + nops_per_ms: u16 } impl Delay{ #[doc(hidden)] pub fn new(freq: u32) -> Self{ - Delay{freq} + // ~21 nops needed per 2^20 MHz to delay 1 ms + let nops : u32 = 210 * (freq >> 20); + Delay{nops_per_ms: (nops as u16)} } } @@ -24,10 +26,8 @@ impl DelayMs for Delay{ impl DelayMs for Delay{ fn delay_ms(&mut self, ms: u16){ - //TODO take into account clock freq for delay - // for _ in 0 .. ms{ - for _ in 0 .. 800{ + for _ in 0 .. self.nops_per_ms{ asm::nop(); } } diff --git a/src/lib.rs b/src/lib.rs index 8029d00..99b2228 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,6 @@ #![feature(asm_experimental_arch)] #![deny(missing_docs)] #![feature(asm_const)] -#![feature(abi_msp430_interrupt)] pub mod batch_gpio; pub mod capture; diff --git a/src/serial.rs b/src/serial.rs index e3a8d49..44674b4 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -508,6 +508,13 @@ impl Rx { let usci = unsafe { USCI::steal() }; usci.rxie_clear(); } + + /// Reads raw value from Rx buffer with no checks for validity + #[inline(always)] + pub fn read_no_check(&mut self) -> u8{ + let usci = unsafe { USCI::steal() }; + usci.rx_rd() + } } /// Serial receive errors diff --git a/src/spi.rs b/src/spi.rs index deffe23..98222ce 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -295,7 +295,12 @@ impl SPIPins{ usci.transmit_interrupt_set(flag); } - + /// Writes raw value to Tx buffer with no checks for validity + #[inline(always)] + pub fn write_no_check(&mut self, val: u8){ + let usci = unsafe { USCI::steal() }; + usci.txbuf_wr(val) + } } /// SPI transmit/receive errors From aab3fe12ec635f91526090d3605f29230cf89592 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Mon, 6 Mar 2023 13:51:35 -0600 Subject: [PATCH 19/82] uart baud calculation improvements --- src/serial.rs | 94 +++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index 44674b4..68919e4 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -306,7 +306,6 @@ struct BaudConfig { ucos16: bool, } -#[inline] fn calculate_baud_config(clk_freq: u32, bps: u32) -> BaudConfig { // Prevent division by 0 let bps = bps.max(1); @@ -337,56 +336,57 @@ fn calculate_baud_config(clk_freq: u32, bps: u32) -> BaudConfig { } } +// Data from table 22-4 of MSP430FR4xx and MSP430FR2xx family user's guide (Rev. I) +const BRS_LOOKUP_KEYS : [u16;36] = + [ + 0x0000, 0x00d9, 0x0125, 0x0156, 0x019a, + 0x0201, 0x024a, 0x02ac, 0x036f, 0x038f, + 0x0401, 0x04cd, 0x0556, 0x05b8, 0x0601, + 0x0668, 0x06dc, 0x0701, 0x0801, 0x0925, + 0x099b, 0x0a02, 0x0a4b, 0x0aab, 0x0b34, + 0x0b6f, 0x0c01, 0x0c94, 0x0cce, 0x0d55, + 0x0d8b, 0x0db7, 0x0e00, 0x0e68, 0x0eac, + 0x0edc + ]; + +const BRS_LOOKUP_VALS : [u8;36] = + [ + 0x00,0x01,0x02,0x04,0x08, + 0x10,0x20,0x11,0x21,0x22, + 0x44,0x25,0x49,0x4A,0x52, + 0x92,0x53,0x55,0xAA,0x6B, + 0xAD,0xB5,0xB6,0xD6,0xB7, + 0xBB,0xDD,0xED,0xEE,0xBF, + 0xDF,0xEF,0xF7,0xFB,0xFD, + 0xFE + ]; + +#[inline(always)] +fn binary_search_brs_table(res : u16) -> u8{ + let mut low: usize = 0; + let mut high: usize = BRS_LOOKUP_KEYS.len() - 1; + while low != high { + let mid = (low + high) >> 2; + let key = BRS_LOOKUP_KEYS[mid]; + if res == key { + return BRS_LOOKUP_VALS[mid] + }else if res > key{ + low = mid + 1; + }else{ + high = mid - 1; + } + } + return BRS_LOOKUP_VALS[low]; +} + #[inline(always)] fn lookup_brs(clk_freq: u32, bps: u32) -> u8 { let modulo = clk_freq % bps; + // 12 fractional bit fixed point result + let fixed_point_result : u32 = (modulo << 12) / bps; - // Fractional part lookup for the baud rate. Not extremely precise - if modulo * 19 < bps { - 0x0 - } else if modulo * 14 < bps { - 0x1 - } else if modulo * 12 < bps { - 0x2 - } else if modulo * 10 < bps { - 0x4 - } else if modulo * 8 < bps { - 0x8 - } else if modulo * 7 < bps { - 0x10 - } else if modulo * 6 < bps { - 0x20 - } else if modulo * 5 < bps { - 0x11 - } else if modulo * 4 < bps { - 0x22 - } else if modulo * 3 < bps { - 0x44 - } else if modulo * 11 < bps * 4 { - 0x49 - } else if modulo * 5 < bps * 2 { - 0x4A - } else if modulo * 7 < bps * 3 { - 0x92 - } else if modulo * 2 < bps { - 0x53 - } else if modulo * 7 < bps * 4 { - 0xAA - } else if modulo * 13 < bps * 8 { - 0x6B - } else if modulo * 3 < bps * 2 { - 0xAD - } else if modulo * 11 < bps * 8 { - 0xD6 - } else if modulo * 4 < bps * 3 { - 0xBB - } else if modulo * 5 < bps * 4 { - 0xDD - } else if modulo * 9 < bps * 8 { - 0xEF - } else { - 0xFD - } + // Throw away the upper bits since the fractional part is all we care about + binary_search_brs_table(fixed_point_result as u16) } impl SerialConfig { From cdffad85f49d5eea27a6452356cc02874a4dd76b Mon Sep 17 00:00:00 2001 From: rbneiman Date: Tue, 7 Mar 2023 15:15:09 -0600 Subject: [PATCH 20/82] fix bugs in my baud config fixes --- src/serial.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index 68919e4..413c2dd 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -306,6 +306,7 @@ struct BaudConfig { ucos16: bool, } +#[inline] fn calculate_baud_config(clk_freq: u32, bps: u32) -> BaudConfig { // Prevent division by 0 let bps = bps.max(1); @@ -366,12 +367,18 @@ fn binary_search_brs_table(res : u16) -> u8{ let mut low: usize = 0; let mut high: usize = BRS_LOOKUP_KEYS.len() - 1; while low != high { - let mid = (low + high) >> 2; + let mid = (low + high) >> 1; let key = BRS_LOOKUP_KEYS[mid]; if res == key { return BRS_LOOKUP_VALS[mid] + }else if high - low == 1{ + return if res < BRS_LOOKUP_KEYS[high] { + BRS_LOOKUP_VALS[low] + } else { + BRS_LOOKUP_VALS[high] + } }else if res > key{ - low = mid + 1; + low = mid; }else{ high = mid - 1; } From 45b190e5f97f677fe9cec03cfbd3054161843e55 Mon Sep 17 00:00:00 2001 From: rbneiman Date: Tue, 7 Mar 2023 15:37:40 -0600 Subject: [PATCH 21/82] stream bugs --- src/gpio.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/gpio.rs b/src/gpio.rs index f53a754..a715ddb 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -18,6 +18,7 @@ use crate::hw_traits::gpio::{GpioPeriph, IntrPeriph}; use crate::util::BitsExt; use core::marker::PhantomData; use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; +use msp430::asm; use msp430fr2355 as pac; pub use pac::{P1, P2, P3, P4, P5, P6}; @@ -359,6 +360,10 @@ impl OutputPin for Pin { #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { + //TODO figure out why this is needed for screen + for _ in 0..200{ + asm::nop(); + } let p = unsafe { PORT::steal() }; p.pxout_clear(PIN::CLR_MASK); Ok(()) @@ -366,6 +371,10 @@ impl OutputPin for Pin { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { + //TODO figure out why this is needed for screen + for _ in 0..200{ + asm::nop(); + } let p = unsafe { PORT::steal() }; p.pxout_set(PIN::SET_MASK); Ok(()) From 8e297fddc3c5eea17ba259408eebb0d2f1de2acd Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Tue, 28 Feb 2023 21:22:09 -0600 Subject: [PATCH 22/82] create adc file --- src/adc.rs | 11 +++++++++++ src/lib.rs | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/adc.rs diff --git a/src/adc.rs b/src/adc.rs new file mode 100644 index 0000000..ce8235b --- /dev/null +++ b/src/adc.rs @@ -0,0 +1,11 @@ +use msp430fr2355::ADC; + +pub struct Adc(ADC); + +impl Adc { + pub fn new(adc: ADC) -> Adc { + Adc(adc) + } + + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 99b2228..ec4ceb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,9 +25,10 @@ #![allow(incomplete_features)] // Enable specialization without warnings #![feature(specialization)] #![feature(asm_experimental_arch)] -#![deny(missing_docs)] +// #![deny(missing_docs)] #![feature(asm_const)] +pub mod adc; pub mod batch_gpio; pub mod capture; pub mod clock; From 1399e314ed8005f570abf90323299107a0abe0a5 Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Wed, 1 Mar 2023 00:29:33 -0600 Subject: [PATCH 23/82] adc stuff --- src/adc.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index ce8235b..09d2f7c 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,11 +1,58 @@ +use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; -pub struct Adc(ADC); +pub struct Adc { + adc_reg: ADC, +} -impl Adc { - pub fn new(adc: ADC) -> Adc { - Adc(adc) +impl Adc { + pub fn new(adc: ADC) -> Adc { + Adc { adc_reg: adc } } + pub fn adc_init(&self) { + self.adc_reg + .adcctl0 + .write(|w| w.adcenc().adcenc_0().adcon().adcon_0().adcsc().adcsc_0()); + self.adc_reg.adcie.write(|w| unsafe { w.bits(0) }); + self.adc_reg.adcifg.write(|w| unsafe { w.bits(0) }); + self.adc_reg.adcctl1.write(|w| unsafe { w.bits(0) }); + self.adc_reg.adcctl2.write(|w| unsafe { w.bits(0) }); + } + + pub fn adc_set_pin(&self, pin: u8) { + self.adc_reg.adcmctl0.write(|w| w.adcinch().bits(pin)); + } + + pub fn adc_enable(&self) { + self.adc_reg.adcctl0.write(|w| w.adcon().adcon_1()); + } + + pub fn adc_start_conversion(&self) { + self.adc_reg.adcctl0.write(|w| w.adcenc().adcenc_1()); + } -} \ No newline at end of file + pub fn adc_is_busy(&self) -> bool { + return self.adc_reg.adcctl1.read().adcbusy().bit_is_set(); + } + + pub fn adc_get_result(&self) -> u16 { + return self.adc_reg.adcmem0.read().bits(); + } +} + +impl OneShot, WORD, PIN> for Adc +where + WORD: From, + PIN: Channel, ID = u8>, +{ + type Error = (); + + fn read(&mut self, _pin: &mut PIN) -> nb::Result { + let chan = 1 << PIN::channel(); + //self.power_up(); + let result = 0xAA55_u16; + //self.power_down(); + Ok(result.into()) + } +} From 2b43aa98176e7cf4a88327f1ba5a057ce11eb3f6 Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Sun, 2 Apr 2023 14:38:22 -0500 Subject: [PATCH 24/82] adc modified to conform with embedded hal --- run.bat | 14 +++ src/adc.rs | 293 +++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 286 insertions(+), 21 deletions(-) create mode 100644 run.bat diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..a168780 --- /dev/null +++ b/run.bat @@ -0,0 +1,14 @@ +@REM echo off +set arg1=%1 +@REM mspdebug --allow-fw-update tilib "prog %arg1%" +@REM mspdebug --allow-fw-update tilib "run" + +@REM mspdebug --allow-fw-update tilib +@REM prog %arg1% +@REM run + +echo prog %arg1% > temp_commands.txt +echo run >> temp_commands.txt + +mspdebug --allow-fw-update tilib < temp_commands.txt +del temp_commands.txt \ No newline at end of file diff --git a/src/adc.rs b/src/adc.rs index 09d2f7c..fd33d27 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,35 +1,286 @@ -use embedded_hal::adc::{Channel, OneShot}; +use core::{u8}; +use crate::gpio::*; +use embedded_hal::{adc::{Channel, OneShot}}; use msp430fr2355::ADC; +pub enum SampleTime { + _4, + _8, + _16, + _32, + _64, + _96, + _128, + _192, + _256, + _384, + _512, + _768, + _1024, +} + +impl SampleTime { + fn adcsht(self) -> u8 { + match self { + SampleTime::_4 => 0b000, + SampleTime::_8 => 0b001, + SampleTime::_16 => 0b010, + SampleTime::_32 => 0b011, + SampleTime::_64 => 0b100, + SampleTime::_96 => 0b101, + SampleTime::_128 => 0b110, + SampleTime::_192 => 0b111, + SampleTime::_256 => 0b1000, + SampleTime::_384 => 0b1001, + SampleTime::_512 => 0b1010, + SampleTime::_768 => 0b1011, + SampleTime::_1024 => 0b1100, + } + } +} + +pub enum ClockDivider { + _1, + _2, + _3, + _4, + _5, + _6, + _7, + _8, +} + +impl ClockDivider { + fn adcdiv(self) -> u8 { + match self { + ClockDivider::_1 => 0b000, + ClockDivider::_2 => 0b001, + ClockDivider::_3 => 0b010, + ClockDivider::_4 => 0b011, + ClockDivider::_5 => 0b100, + ClockDivider::_6 => 0b101, + ClockDivider::_7 => 0b110, + ClockDivider::_8 => 0b111, + } + } +} + +pub enum ClockSource { + MODCLK, + ACLK, + SMCLK, +} + +impl ClockSource { + fn adcssel(self) -> u8 { + match self { + ClockSource::MODCLK => 0b00, + ClockSource::ACLK => 0b01, + ClockSource::SMCLK => 0b10, + } + } +} + +pub enum Predivider { + _1, + _4, + _64, +} + +impl Predivider { + fn adcpdiv(self) -> u8 { + match self { + Predivider::_1 => 0b00, + Predivider::_4 => 0b01, + Predivider::_64 => 0b10, + } + } +} + +pub enum Resolution { + _8BIT, + _10BIT, + _12BIT, +} + +impl Resolution { + fn adcres(self) -> u8 { + match self { + Resolution::_8BIT => 0b00, + Resolution::_10BIT => 0b01, + Resolution::_12BIT => 0b10, + } + } +} + +pub enum SamplingRate { + _50KSPS, + _200KSPS, +} + +impl SamplingRate { + fn adcsr(self) -> bool { + match self { + SamplingRate::_200KSPS => false, + SamplingRate::_50KSPS => true, + } + } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 0 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 1 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 2 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 3 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 4 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 5 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 6 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 7 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 8 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 9 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 10 } +} + +impl Channel> for Pin>> { + type ID = u8; + + fn channel() -> Self::ID { 11 } +} + pub struct Adc { adc_reg: ADC, } -impl Adc { - pub fn new(adc: ADC) -> Adc { - Adc { adc_reg: adc } +pub struct AdcConfig { + adc: ADC, + clock_source: ClockSource, + clock_divider: ClockDivider, + predivider: Predivider, + resolution: Resolution, + sampling_rate: SamplingRate, + sample_time: SampleTime, +} + +impl AdcConfig { + pub fn new( + adc: ADC, + clock_source: ClockSource, + clock_divider: ClockDivider, + predivider: Predivider, + resolution: Resolution, + sampling_rate: SamplingRate, + sample_time: SampleTime, + ) -> AdcConfig { + AdcConfig { + adc, + clock_source: clock_source, + clock_divider: clock_divider, + predivider: predivider, + resolution: resolution, + sampling_rate: sampling_rate, + sample_time: sample_time, + } } - pub fn adc_init(&self) { - self.adc_reg - .adcctl0 - .write(|w| w.adcenc().adcenc_0().adcon().adcon_0().adcsc().adcsc_0()); - self.adc_reg.adcie.write(|w| unsafe { w.bits(0) }); - self.adc_reg.adcifg.write(|w| unsafe { w.bits(0) }); - self.adc_reg.adcctl1.write(|w| unsafe { w.bits(0) }); - self.adc_reg.adcctl2.write(|w| unsafe { w.bits(0) }); + pub fn config_hw(self, _channel: &CHANNEL) -> Adc + where + CHANNEL: Channel, ID=u8>, { + let adc_reg = self.adc; + + // adc_reg.adcctl0.modify(|_, w| w.adcenc().adcenc_0()); + adc_reg.adcctl0.modify(|_, w| w.adcon().adcon_1()); + // adc_reg.adcctl0.modify(|_, w| w.adcsc().adcsc_0()); + + adc_reg.adcmctl0.modify(|_, w| w.adcinch().bits(CHANNEL::channel())); + + let adcsht = self.sample_time.adcsht(); + adc_reg.adcctl0.modify(|_, w| w.adcsht().bits(adcsht)); + + let adcssel = self.clock_source.adcssel(); + adc_reg.adcctl1.modify(|_, w| w.adcssel().bits(adcssel)); + + let adcdiv = self.clock_divider.adcdiv(); + adc_reg.adcctl1.modify(|_, w| w.adcdiv().bits(adcdiv)); + + let adcpdiv = self.predivider.adcpdiv(); + adc_reg.adcctl2.modify(|_, w| w.adcpdiv().bits(adcpdiv)); + + let adcres = self.resolution.adcres(); + adc_reg.adcctl2.modify(|_, w| w.adcres().bits(adcres)); + + let adcsr = self.sampling_rate.adcsr(); + adc_reg.adcctl2.modify(|_, w| w.adcsr().bit(adcsr)); + + Adc { adc_reg } } +} - pub fn adc_set_pin(&self, pin: u8) { - self.adc_reg.adcmctl0.write(|w| w.adcinch().bits(pin)); +impl Adc { + pub fn new(adc: ADC) -> Adc { + Adc { adc_reg: adc } } pub fn adc_enable(&self) { - self.adc_reg.adcctl0.write(|w| w.adcon().adcon_1()); + self.adc_reg.adcctl0.modify(|_, w| w.adcon().adcon_1().adcenc().adcenc_1()); } pub fn adc_start_conversion(&self) { - self.adc_reg.adcctl0.write(|w| w.adcenc().adcenc_1()); + // self.adc_reg + // .adcctl0 + // .modify(|_, w| w.adcenc().adcenc_1().adcon().adcon_1().adcsc().adcsc_1()); + self.adc_reg.adcctl0.write(|w| unsafe { w.bits(0x0613) }); } pub fn adc_is_busy(&self) -> bool { @@ -48,11 +299,11 @@ where { type Error = (); - fn read(&mut self, _pin: &mut PIN) -> nb::Result { - let chan = 1 << PIN::channel(); - //self.power_up(); - let result = 0xAA55_u16; - //self.power_down(); + fn read(&mut self, _pin: &mut PIN ) -> nb::Result { + self.adc_start_conversion(); + while self.adc_is_busy() {} + let result = self.adc_get_result(); + Ok(result.into()) } } From 7787faa77ad01a3dec9785fcf77005dcfb06481e Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Mon, 3 Apr 2023 03:04:54 -0500 Subject: [PATCH 25/82] adc busy stuck high. once that bug is fixed everything would be good. --- src/adc.rs | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index fd33d27..cd4eccb 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -234,16 +234,12 @@ impl AdcConfig { } } - pub fn config_hw(self, _channel: &CHANNEL) -> Adc - where - CHANNEL: Channel, ID=u8>, { + pub fn config_hw(self) -> Adc { let adc_reg = self.adc; - // adc_reg.adcctl0.modify(|_, w| w.adcenc().adcenc_0()); - adc_reg.adcctl0.modify(|_, w| w.adcon().adcon_1()); - // adc_reg.adcctl0.modify(|_, w| w.adcsc().adcsc_0()); - - adc_reg.adcmctl0.modify(|_, w| w.adcinch().bits(CHANNEL::channel())); + adc_reg.adcctl0.modify(|_, w| w.adcenc().adcenc_0() + .adcon().adcon_0() + .adcsc().adcsc_0()); let adcsht = self.sample_time.adcsht(); adc_reg.adcctl0.modify(|_, w| w.adcsht().bits(adcsht)); @@ -273,14 +269,19 @@ impl Adc { } pub fn adc_enable(&self) { - self.adc_reg.adcctl0.modify(|_, w| w.adcon().adcon_1().adcenc().adcenc_1()); + self.adc_reg.adcctl0.modify(|_, w| w.adcon().adcon_1()); + } + + pub fn adc_disable(&self) { + self.adc_reg.adcctl0.modify(|_, w| + w.adcon().adcon_0() + .adcenc().adcenc_0()); } pub fn adc_start_conversion(&self) { - // self.adc_reg - // .adcctl0 - // .modify(|_, w| w.adcenc().adcenc_1().adcon().adcon_1().adcsc().adcsc_1()); - self.adc_reg.adcctl0.write(|w| unsafe { w.bits(0x0613) }); + self.adc_reg + .adcctl0 + .modify(|_, w| w.adcenc().adcenc_1().adcsc().adcsc_1()); } pub fn adc_is_busy(&self) -> bool { @@ -290,6 +291,11 @@ impl Adc { pub fn adc_get_result(&self) -> u16 { return self.adc_reg.adcmem0.read().bits(); } + + pub fn adc_set_pin(&self, _pin: &PIN) + where PIN: Channel, ID=u8> { + self.adc_reg.adcmctl0.modify(|_, w| w.adcinch().bits(PIN::channel())); + } } impl OneShot, WORD, PIN> for Adc @@ -300,6 +306,10 @@ where type Error = (); fn read(&mut self, _pin: &mut PIN ) -> nb::Result { + self.adc_disable(); + self.adc_set_pin(_pin); + self.adc_enable(); + self.adc_start_conversion(); while self.adc_is_busy() {} let result = self.adc_get_result(); From ac25f45360153a022736583cea09427b428f0b7b Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Fri, 7 Apr 2023 19:27:21 -0500 Subject: [PATCH 26/82] fix adc --- src/adc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adc.rs b/src/adc.rs index cd4eccb..d42cc51 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -245,7 +245,7 @@ impl AdcConfig { adc_reg.adcctl0.modify(|_, w| w.adcsht().bits(adcsht)); let adcssel = self.clock_source.adcssel(); - adc_reg.adcctl1.modify(|_, w| w.adcssel().bits(adcssel)); + adc_reg.adcctl1.modify(|_, w| w.adcssel().bits(adcssel).adcshp().adcshp_1()); let adcdiv = self.clock_divider.adcdiv(); adc_reg.adcctl1.modify(|_, w| w.adcdiv().bits(adcdiv)); From 927ba101306a1da29edeaace349d166a0fb0a836 Mon Sep 17 00:00:00 2001 From: Allen Jiang Date: Fri, 7 Apr 2023 19:40:35 -0500 Subject: [PATCH 27/82] remove warnings --- src/delay.rs | 2 +- src/spi.rs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index f22c477..d6a738e 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -1,6 +1,6 @@ //! Embedded hal delay implementation use msp430::asm; -use crate::hal::blocking::delay::{DelayMs, DelayUs}; +use crate::hal::blocking::delay::{DelayMs}; /// Delay provider struct diff --git a/src/spi.rs b/src/spi.rs index 98222ce..41649c2 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -2,17 +2,14 @@ use core::marker::PhantomData; use embedded_hal::spi::FullDuplex; use msp430fr2355 as pac; -use crate::hal::{ - spi::{Mode, Polarity, Phase}, -}; +use crate::hal::spi::{Mode, Polarity, Phase}; use crate::{ - hw_traits::Steal, hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel}, gpio::{Alternate1, Pin, P1, P4, Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7}, clock::{Smclk, Aclk}, hal }; -use nb::Error::{WouldBlock}; +use nb::Error::WouldBlock; /// Marks a eUSCI capable of SPI communication (in this case, all euscis do) pub trait EUsciSPIBus : EusciSPI{ From a8dd2e1caceed0ab6b014de3cc14f92e04f5d961 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 20:47:56 +1300 Subject: [PATCH 28/82] Fix typos Fix trailing newlines, empty lines, typos --- run.bat | 2 +- rust-toolchain | 2 +- src/i2c.rs | 8 +------- src/spi.rs | 12 ------------ 4 files changed, 3 insertions(+), 21 deletions(-) diff --git a/run.bat b/run.bat index a168780..ba64884 100644 --- a/run.bat +++ b/run.bat @@ -11,4 +11,4 @@ echo prog %arg1% > temp_commands.txt echo run >> temp_commands.txt mspdebug --allow-fw-update tilib < temp_commands.txt -del temp_commands.txt \ No newline at end of file +del temp_commands.txt diff --git a/rust-toolchain b/rust-toolchain index 07ade69..bf867e0 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly \ No newline at end of file +nightly diff --git a/src/i2c.rs b/src/i2c.rs index 7b61f58..8beba67 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -278,7 +278,7 @@ impl I2CBusConfig{ SDL(PhantomData) } - /// Performs hardware confiuration + /// Performs hardware configuration #[inline] fn configure(&self){ self.usci.ctw0_wr_rst(true); @@ -554,9 +554,3 @@ impl TransactionalIter for SDL { SDL::exec_iter(self, address, operations) } } - - - - - - diff --git a/src/spi.rs b/src/spi.rs index 41649c2..7dac257 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -342,15 +342,3 @@ impl FullDuplex for SPIPins{ } } } - - - - - - - - - - - - From e2022049e209a593eb48f7a60a73d8ff27d69604 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 21:02:26 +1300 Subject: [PATCH 29/82] Make ADC read() return WouldBlock --- src/adc.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index d42cc51..79b8e08 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -201,6 +201,7 @@ impl Channel> for Pin>> { pub struct Adc { adc_reg: ADC, + is_waiting: bool, } pub struct AdcConfig { @@ -259,13 +260,13 @@ impl AdcConfig { let adcsr = self.sampling_rate.adcsr(); adc_reg.adcctl2.modify(|_, w| w.adcsr().bit(adcsr)); - Adc { adc_reg } + Adc { adc_reg, is_waiting: false } } } impl Adc { pub fn new(adc: ADC) -> Adc { - Adc { adc_reg: adc } + Adc { adc_reg: adc, is_waiting: false } } pub fn adc_enable(&self) { @@ -305,15 +306,23 @@ where { type Error = (); - fn read(&mut self, _pin: &mut PIN ) -> nb::Result { + fn read(&mut self, pin: &mut PIN ) -> nb::Result { + if self.is_waiting { + if self.adc_is_busy() { + return Err(nb::Error::WouldBlock); + } + else { + self.is_waiting = false; + return Ok(self.adc_get_result().into()); + } + } + self.adc_disable(); - self.adc_set_pin(_pin); + self.adc_set_pin(pin); self.adc_enable(); self.adc_start_conversion(); - while self.adc_is_busy() {} - let result = self.adc_get_result(); - - Ok(result.into()) + self.is_waiting = true; + Err(nb::Error::WouldBlock) } } From c91233aceef6f5e40d1d8316f8a144a2ed059cf7 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 21:07:17 +1300 Subject: [PATCH 30/82] Make AdcConfig fields public --- src/adc.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 79b8e08..489edb1 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -205,13 +205,13 @@ pub struct Adc { } pub struct AdcConfig { - adc: ADC, - clock_source: ClockSource, - clock_divider: ClockDivider, - predivider: Predivider, - resolution: Resolution, - sampling_rate: SamplingRate, - sample_time: SampleTime, + pub adc: ADC, + pub clock_source: ClockSource, + pub clock_divider: ClockDivider, + pub predivider: Predivider, + pub resolution: Resolution, + pub sampling_rate: SamplingRate, + pub sample_time: SampleTime, } impl AdcConfig { From f1d639a2809ccdb0686f4b94228ad15b9199fbce Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 21:09:34 +1300 Subject: [PATCH 31/82] Add Delay documentation Also add missing full stops to ClockConfig documentation. --- src/clock.rs | 4 ++-- src/delay.rs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 595e3fe..0123173 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -339,7 +339,7 @@ impl ClockConfig { } impl ClockConfig { - /// Apply clock configuration to hardware and return SMCLK and ACLK clock objects + /// Apply clock configuration to hardware and return SMCLK and ACLK clock objects. /// Also returns delay provider #[inline] pub fn freeze(self, fram: &mut Fram) -> (Smclk, Aclk, Delay) { @@ -356,7 +356,7 @@ impl ClockConfig { } impl ClockConfig { - /// Apply clock configuration to hardware and return ACLK clock object, as SMCLK is disabled + /// Apply clock configuration to hardware and return ACLK clock object, as SMCLK is disabled. /// Also returns delay provider. #[inline] pub fn freeze(self, fram: &mut Fram) -> (Aclk, Delay) { diff --git a/src/delay.rs b/src/delay.rs index d6a738e..d84b3b0 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -2,16 +2,15 @@ use msp430::asm; use crate::hal::blocking::delay::{DelayMs}; - /// Delay provider struct pub struct Delay{ nops_per_ms: u16 } impl Delay{ - #[doc(hidden)] + /// Create a new delay object pub fn new(freq: u32) -> Self{ - // ~21 nops needed per 2^20 MHz to delay 1 ms + // ~21 nops needed per 2^20 Hz to delay 1 ms let nops : u32 = 210 * (freq >> 20); Delay{nops_per_ms: (nops as u16)} } From 163be17c74539c0dd131c2cba43c6679531a92dc Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 21:55:51 +1300 Subject: [PATCH 32/82] Implement SPI blocking::{write, transfer} using default impl --- src/spi.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/spi.rs b/src/spi.rs index 7dac257..f661280 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -307,20 +307,6 @@ pub enum SPIErr{ Unimplemented = 0, } - - -impl hal::blocking::spi::Write for SPIPins{ - type Error = SPIErr; - - fn write(&mut self, words: &[u8]) -> Result<(), SPIErr> { - for word in words { - nb::block!(self.send(*word))?; - nb::block!(self.read())?; - } - Ok(()) - } -} - impl FullDuplex for SPIPins{ type Error = SPIErr; fn read(&mut self) -> nb::Result{ @@ -342,3 +328,7 @@ impl FullDuplex for SPIPins{ } } } + +// Implementing FullDuplex above gets us a blocking write and transfer implementation for free +impl embedded_hal::blocking::spi::write::Default for SPIPins {} +impl embedded_hal::blocking::spi::transfer::Default for SPIPins {} From 262cd408f73342e68d8c52c8c62268b99a8d1d23 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 22:31:23 +1300 Subject: [PATCH 33/82] Replace `modify` in eusci.rs with `set/clear_bits` Make separate functions for setting and clearing where possible (e.g. value known at compile time). If value is not known at compile time (e.g. function used in a public API) use a match statement to decide between set/clear. --- src/hw_traits/eusci.rs | 67 ++++++++++++++++++++++++++++++------------ src/i2c.rs | 4 +-- src/spi.rs | 6 ++-- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 80d4e19..4d1320e 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -311,7 +311,8 @@ pub trait EUsciI2C: EUsci { // Read or write to UCSWRST fn ctw0_rd_rst(&self) -> bool; - fn ctw0_wr_rst(&self, bit:bool); + fn ctw0_set_rst(&self); + fn ctw0_clear_rst(&self); // Modify only when UCSWRST = 1 fn ctw0_rd(&self) -> UcbCtlw0; @@ -359,7 +360,8 @@ pub trait EUsciI2C: EUsci { pub trait EusciSPI : EUsci{ type Statw : SpiStatw; - fn ctw0_wr_rst(&self, bit:bool); + fn ctw0_set_rst(&self); + fn ctw0_clear_rst(&self); fn ctw0_wr(&self, reg:&UcxSpiCtw0); @@ -367,7 +369,8 @@ pub trait EusciSPI : EUsci{ fn statw_rd(&self) -> Self::Statw; - fn uclisten_set(&self, bit:bool); + fn uclisten_set(&self); + fn uclisten_clear(&self); fn rxbuf_rd(&self) -> u8; @@ -437,8 +440,13 @@ macro_rules! eusci_impl { type Statw = $StatwSpi; #[inline(always)] - fn ctw0_wr_rst(&self, bit:bool){ - self.$ucxctlw0().modify(|_, w| w.ucswrst().bit(bit)) + fn ctw0_set_rst(&self){ + unsafe { self.$ucxctlw0().set_bits(|w| w.ucswrst().set_bit()) } + } + + #[inline(always)] + fn ctw0_clear_rst(&self){ + unsafe { self.$ucxctlw0().clear_bits(|w| w.ucswrst().clear_bit()) } } #[inline(always)] @@ -457,8 +465,12 @@ macro_rules! eusci_impl { } #[inline(always)] - fn uclisten_set(&self, bit:bool){ - self.$ucxstatw().modify(|_, w| w.uclisten().bit(bit)) + fn uclisten_set(&self){ + unsafe { self.$ucxstatw().set_bits(|w| w.uclisten().set_bit()) } + } + #[inline(always)] + fn uclisten_clear(&self){ + unsafe { self.$ucxstatw().clear_bits(|w| w.uclisten().clear_bit()) } } #[inline(always)] @@ -472,13 +484,19 @@ macro_rules! eusci_impl { } #[inline(always)] - fn transmit_interrupt_set(&self, bit:bool){ - self.$ucxie().modify(|_, w| w.uctxie().bit(bit)); + fn transmit_interrupt_set(&self, bit: bool){ + match bit { + true => unsafe { self.$ucxie().set_bits(|w| w.uctxie().set_bit()) }, + false => unsafe { self.$ucxie().clear_bits(|w| w.uctxie().clear_bit()) }, + } } #[inline(always)] - fn receive_interrupt_set(&self, bit:bool){ - self.$ucxie().modify(|_, w| w.ucrxie().bit(bit)); + fn receive_interrupt_set(&self, bit: bool){ + match bit { + true => unsafe { self.$ucxie().set_bits(|w| w.ucrxie().set_bit()) }, + false => unsafe { self.$ucxie().clear_bits(|w| w.ucrxie().clear_bit()) }, + } } #[inline(always)] @@ -712,28 +730,33 @@ macro_rules! eusci_b_impl { } #[inline(always)] - fn ctw0_wr_rst(&self, bit:bool){ - self.$ucbxctlw0().modify(|_, w| w.ucswrst().bit(bit)) + fn ctw0_set_rst(&self){ + unsafe { self.$ucbxctlw0().set_bits(|w| w.ucswrst().set_bit()) } + } + + #[inline(always)] + fn ctw0_clear_rst(&self){ + unsafe { self.$ucbxctlw0().clear_bits(|w| w.ucswrst().clear_bit()) } } #[inline(always)] fn transmit_ack(&self){ - self.$ucbxctlw0().modify(|_, w| w.uctxack().bit(true)) + unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxack().set_bit()) } } #[inline(always)] fn transmit_nack(&self){ - self.$ucbxctlw0().modify(|_, w| w.uctxnack().bit(true)) + unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxnack().set_bit()) } } #[inline(always)] fn transmit_start(&self){ - self.$ucbxctlw0().modify(|_, w| w.uctxstt().bit(true)) + unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxstt().set_bit()) } } #[inline(always)] fn transmit_stop(&self){ - self.$ucbxctlw0().modify(|_, w| w.uctxstp().bit(true)) + unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxstp().set_bit()) } } #[inline(always)] @@ -748,12 +771,18 @@ macro_rules! eusci_b_impl { #[inline(always)] fn set_ucsla10(&self, bit:bool){ - self.$ucbxctlw0().modify(|_, w| w.ucsla10().bit(bit)) + match bit { + true => unsafe { self.$ucbxctlw0().set_bits(|w| w.ucsla10().set_bit()) }, + false => unsafe { self.$ucbxctlw0().clear_bits(|w| w.ucsla10().clear_bit()) }, + } } #[inline(always)] fn set_uctr(&self, bit:bool){ - self.$ucbxctlw0().modify(|_, w| w.uctr().bit(bit)) + match bit { + true => unsafe { self.$ucbxctlw0().set_bits(|w| w.uctr().set_bit()) }, + false => unsafe { self.$ucbxctlw0().clear_bits(|w| w.uctr().clear_bit()) }, + } } #[inline(always)] diff --git a/src/i2c.rs b/src/i2c.rs index 8beba67..cd9dc7c 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -281,7 +281,7 @@ impl I2CBusConfig{ /// Performs hardware configuration #[inline] fn configure(&self){ - self.usci.ctw0_wr_rst(true); + self.usci.ctw0_set_rst(); self.usci.ctw0_wr(&self.ctlw0); self.usci.ctw1_wr(&self.ctlw1); @@ -295,7 +295,7 @@ impl I2CBusConfig{ self.usci.brw_wr(self.divisor); self.usci.tbcnt_wr(0); - self.usci.ctw0_wr_rst(false); + self.usci.ctw0_clear_rst(); } } diff --git a/src/spi.rs b/src/spi.rs index f661280..bc2e12c 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -259,13 +259,13 @@ impl SPIBusConfig{ #[inline] fn configure_hw(&self){ - self.usci.ctw0_wr_rst(true); + self.usci.ctw0_set_rst(); self.usci.ctw0_wr(&self.ctlw0); self.usci.brw_wr(self.prescaler); - self.usci.uclisten_set(false); + self.usci.uclisten_clear(); - self.usci.ctw0_wr_rst(false); + self.usci.ctw0_clear_rst(); self.usci.transmit_interrupt_set(false); self.usci.receive_interrupt_set(false); From f47ddac2b147c5059c690dd192305edd42def1bf Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 22:56:27 +1300 Subject: [PATCH 34/82] Add `&mut self` to modifying methods, use set/clear_bits where possible --- src/adc.rs | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 489edb1..cea14ae 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -237,11 +237,12 @@ impl AdcConfig { pub fn config_hw(self) -> Adc { let adc_reg = self.adc; - - adc_reg.adcctl0.modify(|_, w| w.adcenc().adcenc_0() - .adcon().adcon_0() - .adcsc().adcsc_0()); - + unsafe { + adc_reg.adcctl0.clear_bits(|w| w + .adcenc().clear_bit() + .adcon().clear_bit() + .adcsc().clear_bit()); + } let adcsht = self.sample_time.adcsht(); adc_reg.adcctl0.modify(|_, w| w.adcsht().bits(adcsht)); @@ -259,6 +260,7 @@ impl AdcConfig { let adcsr = self.sampling_rate.adcsr(); adc_reg.adcctl2.modify(|_, w| w.adcsr().bit(adcsr)); + Adc { adc_reg, is_waiting: false } } @@ -269,20 +271,26 @@ impl Adc { Adc { adc_reg: adc, is_waiting: false } } - pub fn adc_enable(&self) { - self.adc_reg.adcctl0.modify(|_, w| w.adcon().adcon_1()); + pub fn adc_enable(&mut self) { + unsafe {self.adc_reg.adcctl0.set_bits(|w| w.adcon().set_bit());} } - pub fn adc_disable(&self) { - self.adc_reg.adcctl0.modify(|_, w| - w.adcon().adcon_0() - .adcenc().adcenc_0()); + pub fn adc_disable(&mut self) { + unsafe { + self.adc_reg.adcctl0.clear_bits(|w| w + .adcon().clear_bit() + .adcenc().clear_bit()); + } } - pub fn adc_start_conversion(&self) { - self.adc_reg - .adcctl0 - .modify(|_, w| w.adcenc().adcenc_1().adcsc().adcsc_1()); + pub fn adc_start_conversion(&mut self) { + unsafe { + self.adc_reg.adcctl0 + .set_bits(|w| w + .adcenc().set_bit() + .adcsc().set_bit()); + } + } pub fn adc_is_busy(&self) -> bool { @@ -293,7 +301,7 @@ impl Adc { return self.adc_reg.adcmem0.read().bits(); } - pub fn adc_set_pin(&self, _pin: &PIN) + pub fn adc_set_pin(&mut self, _pin: &PIN) where PIN: Channel, ID=u8> { self.adc_reg.adcmctl0.modify(|_, w| w.adcinch().bits(PIN::channel())); } From e8bc379e4aa2d6e16dace51b04502f69b04a0c5c Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 23:22:06 +1300 Subject: [PATCH 35/82] Shorten boilerplate definitions with macros --- src/adc.rs | 90 +++++++++--------------------------- src/i2c.rs | 40 +++++++--------- src/serial.rs | 53 +++++++-------------- src/spi.rs | 125 +++++++++++--------------------------------------- 4 files changed, 82 insertions(+), 226 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index cea14ae..95cdc27 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -127,77 +127,29 @@ impl SamplingRate { } } -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 0 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 1 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 2 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 3 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 4 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 5 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 6 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 7 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 8 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 9 } -} - -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 10 } +// Pins corresponding to an ADC channel. Pin types can have `::channel()` called on them to get their ADC channel index. +macro_rules! impl_adc_channel { + ($port: ty, $pin: ty, $channel: literal ) => { + impl Channel> for Pin<$port, $pin, Alternate3>> { + type ID = u8; + + fn channel() -> Self::ID { $channel } + } + }; } -impl Channel> for Pin>> { - type ID = u8; - - fn channel() -> Self::ID { 11 } -} +impl_adc_channel!(P1, Pin0, 0); +impl_adc_channel!(P1, Pin1, 1); +impl_adc_channel!(P1, Pin2, 2); +impl_adc_channel!(P1, Pin3, 3); +impl_adc_channel!(P1, Pin4, 4); +impl_adc_channel!(P1, Pin5, 5); +impl_adc_channel!(P1, Pin6, 6); +impl_adc_channel!(P1, Pin7, 7); +impl_adc_channel!(P5, Pin0, 8); +impl_adc_channel!(P5, Pin1, 9); +impl_adc_channel!(P5, Pin2, 10); +impl_adc_channel!(P5, Pin3, 11); pub struct Adc { adc_reg: ADC, diff --git a/src/i2c.rs b/src/i2c.rs index cd9dc7c..be02f78 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -111,41 +111,33 @@ impl EUsciI2CBus for pac::E_USCI_B1 { type DataPin = UsciB1SDAPin; } +// Allows a GPIO pin to be converted into an I2C object +macro_rules! impl_i2c_pin { + ($struct_name: ident, $port: ty, $pin: ty) => { + impl Into<$struct_name> for Pin<$port, $pin, Alternate1> { + #[inline(always)] + fn into(self) -> $struct_name { + $struct_name + } + } + }; +} + /// I2C SCL pin for eUSCI B0 pub struct UsciB0SCLPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB0SCLPin { - UsciB0SCLPin - } -} +impl_i2c_pin!(UsciB0SCLPin, P1, Pin3); /// I2C SDA pin for eUSCI B0 pub struct UsciB0SDAPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB0SDAPin { - UsciB0SDAPin - } -} +impl_i2c_pin!(UsciB0SDAPin, P1, Pin2); /// I2C SCL pin for eUSCI B1 pub struct UsciB1SCLPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB1SCLPin { - UsciB1SCLPin - } -} +impl_i2c_pin!(UsciB1SCLPin, P4, Pin7); /// I2C SDA pin for eUSCI B1 pub struct UsciB1SDAPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB1SDAPin { - UsciB1SDAPin - } -} +impl_i2c_pin!(UsciB1SDAPin, P4, Pin6); impl I2CBusConfig{ /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode diff --git a/src/serial.rs b/src/serial.rs index 413c2dd..52e32a8 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -135,32 +135,28 @@ impl SerialUsci for pac::E_USCI_A0 { type RxPin = UsciA0RxPin; } +macro_rules! impl_serial_pin { + ($struct_name: ident, $port: ty, $pin: ty) => { + impl Into<$struct_name> for Pin<$port, $pin, Alternate1> { + #[inline(always)] + fn into(self) -> $struct_name { + $struct_name + } + } + }; +} + /// UCLK pin for E_USCI_A0 pub struct UsciA0ClockPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA0ClockPin { - UsciA0ClockPin - } -} +impl_serial_pin!(UsciA0ClockPin, P1, Pin5); /// Tx pin for E_USCI_A0 pub struct UsciA0TxPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA0TxPin { - UsciA0TxPin - } -} +impl_serial_pin!(UsciA0TxPin, P1, Pin7); /// Rx pin for E_USCI_A0 pub struct UsciA0RxPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA0RxPin { - UsciA0RxPin - } -} +impl_serial_pin!(UsciA0RxPin, P1, Pin6); impl SerialUsci for pac::E_USCI_A1 { type ClockPin = UsciA1ClockPin; @@ -170,30 +166,15 @@ impl SerialUsci for pac::E_USCI_A1 { /// UCLK pin for E_USCI_A1 pub struct UsciA1ClockPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA1ClockPin { - UsciA1ClockPin - } -} +impl_serial_pin!(UsciA1ClockPin, P4, Pin1); /// Tx pin for E_USCI_A1 pub struct UsciA1TxPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA1TxPin { - UsciA1TxPin - } -} +impl_serial_pin!(UsciA1TxPin, P4, Pin3); /// Rx pin for E_USCI_A1 pub struct UsciA1RxPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA1RxPin { - UsciA1RxPin - } -} +impl_serial_pin!(UsciA1RxPin, P4, Pin2); /// Typestate for a serial interface with an unspecified clock source pub struct NoClockSet { diff --git a/src/spi.rs b/src/spi.rs index bc2e12c..a0ee973 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -51,149 +51,80 @@ impl EUsciSPIBus for pac::E_USCI_B1 { type STE = UsciB1STEPin; } -/// SPI MISO pin for eUSCI A0 -pub struct UsciA0MISOPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA0MISOPin { - UsciA0MISOPin +// Allows a GPIO pin to be converted into an SPI object +macro_rules! impl_spi_pin{ + ($struct_name: ident, $port: ty, $pin: ty) => { + impl Into<$struct_name> for Pin<$port, $pin, Alternate1> { + #[inline(always)] + fn into(self) -> $struct_name { + $struct_name + } + } } } +/// SPI MISO pin for eUSCI A0 +pub struct UsciA0MISOPin; +impl_spi_pin!(UsciA0MISOPin, P1, Pin7); + /// SPI MOSI pin for eUSCI A0 pub struct UsciA0MOSIPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA0MOSIPin { - UsciA0MOSIPin - } -} +impl_spi_pin!(UsciA0MOSIPin, P1, Pin6); /// SPI SCLK pin for eUSCI A0 pub struct UsciA0SCLKPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA0SCLKPin { - UsciA0SCLKPin - } -} +impl_spi_pin!(UsciA0SCLKPin, P1, Pin5); /// SPI STE pin for eUSCI A0 pub struct UsciA0STEPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA0STEPin { - UsciA0STEPin - } -} +impl_spi_pin!(UsciA0STEPin, P1, Pin4); /// SPI MISO pin for eUSCI A1 pub struct UsciA1MISOPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA1MISOPin { - UsciA1MISOPin - } -} +impl_spi_pin!(UsciA1MISOPin, P4, Pin3); /// SPI MOSI pin for eUSCI A1 pub struct UsciA1MOSIPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA1MOSIPin { - UsciA1MOSIPin - } -} +impl_spi_pin!(UsciA1MOSIPin, P4, Pin2); /// SPI SCLK pin for eUSCI A1 pub struct UsciA1SCLKPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA1SCLKPin { - UsciA1SCLKPin - } -} - +impl_spi_pin!(UsciA1SCLKPin, P4, Pin1); /// SPI STE pin for eUSCI A1 pub struct UsciA1STEPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciA1STEPin { - UsciA1STEPin - } -} +impl_spi_pin!(UsciA1STEPin, P4, Pin0); /// SPI MISO pin for eUSCI B0 pub struct UsciB0MISOPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB0MISOPin { - UsciB0MISOPin - } -} +impl_spi_pin!(UsciB0MISOPin, P1, Pin3); /// SPI MOSI pin for eUSCI B0 pub struct UsciB0MOSIPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB0MOSIPin { - UsciB0MOSIPin - } -} +impl_spi_pin!(UsciB0MOSIPin, P1, Pin2); /// SPI SCLK pin for eUSCI B0 pub struct UsciB0SCLKPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB0SCLKPin { - UsciB0SCLKPin - } -} +impl_spi_pin!(UsciB0SCLKPin, P1, Pin1); /// SPI STE pin for eUSCI B0 pub struct UsciB0STEPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB0STEPin { - UsciB0STEPin - } -} +impl_spi_pin!(UsciB0STEPin, P1, Pin0); /// SPI MISO pin for eUSCI B1 pub struct UsciB1MISOPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB1MISOPin { - UsciB1MISOPin - } -} +impl_spi_pin!(UsciB1MISOPin, P4, Pin7); /// SPI MOSI pin for eUSCI B1 pub struct UsciB1MOSIPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB1MOSIPin { - UsciB1MOSIPin - } -} +impl_spi_pin!(UsciB1MOSIPin, P4, Pin6); /// SPI SCLK pin for eUSCI B1 pub struct UsciB1SCLKPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB1SCLKPin { - UsciB1SCLKPin - } -} +impl_spi_pin!(UsciB1SCLKPin, P4, Pin5); /// SPI STE pin for eUSCI B1 pub struct UsciB1STEPin; -impl Into for Pin> { - #[inline(always)] - fn into(self) -> UsciB1STEPin { - UsciB1STEPin - } -} +impl_spi_pin!(UsciB1STEPin, P4, Pin4); /// Struct used to configure a SPI bus pub struct SPIBusConfig{ From 8391657c4e8bd63cfd556e0b281820cb4d0020a0 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 23:30:33 +1300 Subject: [PATCH 36/82] Implement `From` rather than `Into` `Into` is automatically derived from a `From` implementation, but not the reverse. --- src/capture.rs | 6 +++--- src/i2c.rs | 4 ++-- src/serial.rs | 4 ++-- src/spi.rs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/capture.rs b/src/capture.rs index b27b186..8f62f13 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -31,10 +31,10 @@ pub enum CapTrigger { BothEdges, } -impl Into for CapTrigger { +impl From for Cm { #[inline] - fn into(self) -> Cm { - match self { + fn from(val: CapTrigger) -> Self { + match val { CapTrigger::RisingEdge => Cm::RisingEdge, CapTrigger::FallingEdge => Cm::FallingEdge, CapTrigger::BothEdges => Cm::BothEdges, diff --git a/src/i2c.rs b/src/i2c.rs index be02f78..d231c36 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -114,9 +114,9 @@ impl EUsciI2CBus for pac::E_USCI_B1 { // Allows a GPIO pin to be converted into an I2C object macro_rules! impl_i2c_pin { ($struct_name: ident, $port: ty, $pin: ty) => { - impl Into<$struct_name> for Pin<$port, $pin, Alternate1> { + impl From>> for $struct_name { #[inline(always)] - fn into(self) -> $struct_name { + fn from(_val: Pin<$port, $pin, Alternate1>) -> Self { $struct_name } } diff --git a/src/serial.rs b/src/serial.rs index 52e32a8..e85dada 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -137,9 +137,9 @@ impl SerialUsci for pac::E_USCI_A0 { macro_rules! impl_serial_pin { ($struct_name: ident, $port: ty, $pin: ty) => { - impl Into<$struct_name> for Pin<$port, $pin, Alternate1> { + impl From>> for $struct_name { #[inline(always)] - fn into(self) -> $struct_name { + fn from(_val: Pin<$port, $pin, Alternate1>) -> Self { $struct_name } } diff --git a/src/spi.rs b/src/spi.rs index a0ee973..345e9b3 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -54,9 +54,9 @@ impl EUsciSPIBus for pac::E_USCI_B1 { // Allows a GPIO pin to be converted into an SPI object macro_rules! impl_spi_pin{ ($struct_name: ident, $port: ty, $pin: ty) => { - impl Into<$struct_name> for Pin<$port, $pin, Alternate1> { + impl From>> for $struct_name { #[inline(always)] - fn into(self) -> $struct_name { + fn from(_val: Pin<$port, $pin, Alternate1>) -> Self { $struct_name } } From d609c2401867374063447a47c272d2cda8f9fba7 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 23:47:11 +1300 Subject: [PATCH 37/82] Remove redundant field names in struct initialisation --- src/adc.rs | 12 ++++++------ src/i2c.rs | 16 ++++++++-------- src/spi.rs | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 95cdc27..7461914 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -178,12 +178,12 @@ impl AdcConfig { ) -> AdcConfig { AdcConfig { adc, - clock_source: clock_source, - clock_divider: clock_divider, - predivider: predivider, - resolution: resolution, - sampling_rate: sampling_rate, - sample_time: sample_time, + clock_source, + clock_divider, + predivider, + resolution, + sampling_rate, + sample_time, } } diff --git a/src/i2c.rs b/src/i2c.rs index d231c36..5a559cf 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -233,14 +233,14 @@ impl I2CBusConfig{ I2CBusConfig{ usci, divisor: 1, - ctlw0: ctlw0, - ctlw1: ctlw1, - i2coa0: i2coa0, - i2coa1: i2coa1, - i2coa2: i2coa2, - i2coa3: i2coa3, - ie: ie, - ifg: ifg, + ctlw0, + ctlw1, + i2coa0, + i2coa1, + i2coa2, + i2coa3, + ie, + ifg, } } diff --git a/src/spi.rs b/src/spi.rs index 345e9b3..e64c9ee 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -159,9 +159,9 @@ impl SPIBusConfig{ SPIBusConfig{ - usci: usci, + usci, prescaler: 0, - ctlw0: ctlw0 + ctlw0 } } From 365e84f1d6dacefc526e97614a3c3acc68ad0077 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Fri, 20 Dec 2024 23:52:21 +1300 Subject: [PATCH 38/82] Tidy up Use '?' operator. Use iterator instead of manually indexing Remove useless conversions Remove unused imports --- src/adc.rs | 2 +- src/capture.rs | 20 ++++++++++---------- src/delay.rs | 2 +- src/i2c.rs | 9 +++------ src/spi.rs | 1 - 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 7461914..c7ab45c 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,6 +1,6 @@ use core::{u8}; use crate::gpio::*; -use embedded_hal::{adc::{Channel, OneShot}}; +use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; pub enum SampleTime { diff --git a/src/capture.rs b/src/capture.rs index 8f62f13..a7b94fe 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -206,9 +206,9 @@ impl CaptureConfig3 { pub fn commit(self) -> CaptureParts3 { let timer = self.timer; self.config.write_regs(&timer); - CCRn::::config_cap_mode(&timer, self.cap0.trigger.into(), self.cap0.select.into()); - CCRn::::config_cap_mode(&timer, self.cap1.trigger.into(), self.cap1.select.into()); - CCRn::::config_cap_mode(&timer, self.cap2.trigger.into(), self.cap2.select.into()); + CCRn::::config_cap_mode(&timer, self.cap0.trigger.into(), self.cap0.select); + CCRn::::config_cap_mode(&timer, self.cap1.trigger.into(), self.cap1.select); + CCRn::::config_cap_mode(&timer, self.cap2.trigger.into(), self.cap2.select); timer.continuous(); CaptureParts3 { @@ -313,13 +313,13 @@ impl CaptureConfig7 { pub fn commit(self) -> CaptureParts7 { let timer = self.timer; self.config.write_regs(&timer); - CCRn::::config_cap_mode(&timer, self.cap0.trigger.into(), self.cap0.select.into()); - CCRn::::config_cap_mode(&timer, self.cap1.trigger.into(), self.cap1.select.into()); - CCRn::::config_cap_mode(&timer, self.cap2.trigger.into(), self.cap2.select.into()); - CCRn::::config_cap_mode(&timer, self.cap3.trigger.into(), self.cap3.select.into()); - CCRn::::config_cap_mode(&timer, self.cap4.trigger.into(), self.cap4.select.into()); - CCRn::::config_cap_mode(&timer, self.cap5.trigger.into(), self.cap5.select.into()); - CCRn::::config_cap_mode(&timer, self.cap6.trigger.into(), self.cap6.select.into()); + CCRn::::config_cap_mode(&timer, self.cap0.trigger.into(), self.cap0.select); + CCRn::::config_cap_mode(&timer, self.cap1.trigger.into(), self.cap1.select); + CCRn::::config_cap_mode(&timer, self.cap2.trigger.into(), self.cap2.select); + CCRn::::config_cap_mode(&timer, self.cap3.trigger.into(), self.cap3.select); + CCRn::::config_cap_mode(&timer, self.cap4.trigger.into(), self.cap4.select); + CCRn::::config_cap_mode(&timer, self.cap5.trigger.into(), self.cap5.select); + CCRn::::config_cap_mode(&timer, self.cap6.trigger.into(), self.cap6.select); timer.continuous(); CaptureParts7 { diff --git a/src/delay.rs b/src/delay.rs index d84b3b0..f924768 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -1,6 +1,6 @@ //! Embedded hal delay implementation use msp430::asm; -use crate::hal::blocking::delay::{DelayMs}; +use crate::hal::blocking::delay::DelayMs; /// Delay provider struct pub struct Delay{ diff --git a/src/i2c.rs b/src/i2c.rs index 5a559cf..82ff0f0 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -370,8 +370,8 @@ impl SDL{ return Err::<(), I2CErr>(I2CErr::GotNACK); } - for i in 0 .. bytes.len() { - usci.uctxbuf_wr(bytes[i]); + for &byte in bytes { + usci.uctxbuf_wr(byte); ifg = usci.ifg_rd(); while !ifg.uctxifg0() { ifg = usci.ifg_rd(); @@ -404,10 +404,7 @@ impl SDL{ buffer: &mut [u8], ) -> Result<(), I2CErr>{ self.set_transmission_mode(TransmissionMode::Transmit); - let result = self.read(address, buffer); - if result.is_err() { - return result; - } + self.read(address, buffer)?; self.set_transmission_mode(TransmissionMode::Receive); self.write(address, bytes) } diff --git a/src/spi.rs b/src/spi.rs index e64c9ee..85e286e 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -7,7 +7,6 @@ use crate::{ hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel}, gpio::{Alternate1, Pin, P1, P4, Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7}, clock::{Smclk, Aclk}, - hal }; use nb::Error::WouldBlock; From 3d273e953c130ab3817620ff4a0f38e6cc4dd4c8 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 21 Dec 2024 11:55:17 +1300 Subject: [PATCH 39/82] Replace endlessly recursive `from_u8` implementation The `from_u8` macro implements `From`, but it uses `.into()` internally, which is automatically derived from `From` ! Could use mem::transmute for most of them, but there are a couple of edge cases like Ucssel, Ucastp. I think a manual implementation is a bit cleaner/safer. Not sure why this worked in the first place, perhaps those registers are never read in the examples. --- src/hw_traits/eusci.rs | 75 +++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 4d1320e..92c1a2f 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -1,17 +1,6 @@ use super::Steal; use msp430fr2355 as pac; -macro_rules! from_u8 { - ($typ: ty) => { - impl From for $typ { - #[inline(always)] - fn from(variant: u8) -> Self { - variant.into() - } - } - }; -} - /// Defines macros for a register associated struct to make reading/writing to this struct's /// register a lot less tedious. /// @@ -68,7 +57,17 @@ pub enum Ucssel { Aclk = 1, Smclk = 2, } -from_u8!(Ucssel); +impl From for Ucssel { + #[inline(always)] + fn from(u: u8) -> Self { + match u { + 0 => Ucssel::Uclk, + 1 => Ucssel::Aclk, + 2 | 3 => Ucssel::Smclk, + _ => unreachable!(), + } + } +} #[derive(Copy, Clone)] pub enum Ucmode { @@ -77,7 +76,18 @@ pub enum Ucmode { FourPinSPI0 = 2, I2CMode = 3, } -from_u8!(Ucmode); +impl From for Ucmode { + #[inline(always)] + fn from(u: u8) -> Self { + match u { + 0 => Ucmode::ThreePinSPI, + 1 => Ucmode::FourPinSPI1, + 2 => Ucmode::FourPinSPI0, + 3 => Ucmode::I2CMode, + _ => unreachable!(), + } + } +} #[derive(Copy, Clone)] pub enum Ucglit { @@ -86,7 +96,18 @@ pub enum Ucglit { Max12_5ns = 2, Max6_25ns = 3, } -from_u8!(Ucglit); +impl From for Ucglit { + #[inline(always)] + fn from(u: u8) -> Self { + match u { + 0 => Ucglit::Max50ns, + 1 => Ucglit::Max25ns, + 2 => Ucglit::Max12_5ns, + 3 => Ucglit::Max6_25ns, + _ => unreachable!(), + } + } +} /// Clock low timeout select #[derive(Copy, Clone)] @@ -100,7 +121,18 @@ pub enum Ucclto { /// = 165000 MODCLK cycles (approximately 34 ms) Ucclto11b = 3, } -from_u8!(Ucclto); +impl From for Ucclto { + #[inline(always)] + fn from(u: u8) -> Self { + match u { + 0 => Ucclto::Ucclto00b, + 1 => Ucclto::Ucclto01b, + 2 => Ucclto::Ucclto10b, + 3 => Ucclto::Ucclto11b, + _ => unreachable!(), + } + } +} /// Automatic STOP condition generation. In slave mode, only settings 00b and 01b /// are available. @@ -117,7 +149,18 @@ pub enum Ucastp { /// threshold. Ucastp10b = 2, } -from_u8!(Ucastp); +impl From for Ucastp { + #[inline(always)] + fn from(u: u8) -> Self { + match u { + 0 => Ucastp::Ucastp00b, + 1 => Ucastp::Ucastp01b, + 2 => Ucastp::Ucastp10b, + 3 => panic!(), // 0b11 is reserved, but the register could still feasibly have this value. What to do? + _ => unreachable!(), + } + } +} pub struct UcaCtlw0{ From 3a5e7ec1ce8d7bd9f6e386c439e61e0dfc02e0f2 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 21 Dec 2024 12:01:05 +1300 Subject: [PATCH 40/82] Add compile time errors for unimplemented functions I would have liked to put these in the actually implemented functions, namely SDL::{write_iter, exec, exec_iter}, but the trait implementations trigger the compile time errors regardless of whether they're used or not. --- src/i2c.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/i2c.rs b/src/i2c.rs index 82ff0f0..601feb4 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -463,6 +463,7 @@ impl WriteIter for SDL{ fn write(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> where B: IntoIterator{ + const {unimplemented!()} // See SDL::write_iter self.set_addressing_mode(AddressingMode::SevenBit); self.set_transmission_mode(TransmissionMode::Transmit); SDL::write_iter(self, address as u16, bytes) @@ -474,6 +475,7 @@ impl WriteIter for SDL{ fn write(&mut self, address: u16, bytes: B) -> Result<(), Self::Error> where B: IntoIterator{ + const {unimplemented!()} // See SDL::write_iter self.set_addressing_mode(AddressingMode::TenBit); self.set_transmission_mode(TransmissionMode::Transmit); SDL::write_iter(self, address, bytes) @@ -510,6 +512,7 @@ impl Transactional for SDL{ type Error = I2CErr; fn exec<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -> Result<(), Self::Error>{ + const {unimplemented!()} // See SDL::exec self.set_addressing_mode(AddressingMode::SevenBit); SDL::exec(self, address as u16, operations) } @@ -519,6 +522,7 @@ impl Transactional for SDL{ type Error = I2CErr; fn exec<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) -> Result<(), Self::Error>{ + const {unimplemented!()} // See SDL::exec self.set_addressing_mode(AddressingMode::TenBit); SDL::exec(self, address, operations) } @@ -529,6 +533,7 @@ impl TransactionalIter for SDL { fn exec_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> where O: IntoIterator>{ + const {unimplemented!()} // See SDL::exec_iter self.set_addressing_mode(AddressingMode::SevenBit); SDL::exec_iter(self, address as u16, operations) } @@ -539,6 +544,7 @@ impl TransactionalIter for SDL { fn exec_iter<'a, O>(&mut self, address: u16, operations: O) -> Result<(), Self::Error> where O: IntoIterator>{ + const {unimplemented!()} // See SDL::exec_iter self.set_addressing_mode(AddressingMode::TenBit); SDL::exec_iter(self, address, operations) } From 27ae07aaaa13d7dfd6959ccad3ed3b29acf14603 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 21 Dec 2024 12:06:09 +1300 Subject: [PATCH 41/82] Ensure examples compile with changes to ClockConfig Actually modifying the examples to use the delay object is a task for another day. Fix timer example --- examples/capture.rs | 2 +- examples/capture_intr.rs | 2 +- examples/clocks.rs | 2 +- examples/echo.rs | 2 +- examples/gpio_interrupts.rs | 2 +- examples/loopback.rs | 2 +- examples/pwm.rs | 2 +- examples/rtc.rs | 2 +- examples/timer.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/capture.rs b/examples/capture.rs index cac31a9..86303cf 100644 --- a/examples/capture.rs +++ b/examples/capture.rs @@ -33,7 +33,7 @@ fn main() -> ! { .config_pin0(|p| p.to_output()) .split(&pmm); - let (smclk, aclk) = ClockConfig::new(periph.CS) + let (smclk, aclk, _delay) = ClockConfig::new(periph.CS) .mclk_dcoclk(DcoclkFreqSel::_1MHz, MclkDiv::_1) .smclk_on(SmclkDiv::_1) .aclk_vloclk() diff --git a/examples/capture_intr.rs b/examples/capture_intr.rs index 9a3afb8..d6ce162 100644 --- a/examples/capture_intr.rs +++ b/examples/capture_intr.rs @@ -50,7 +50,7 @@ fn main() -> ! { with(|cs| unsafe { *RED_LED.borrow(cs).get() = Some(red_led) }); - let (_smclk, aclk) = ClockConfig::new(periph.CS) + let (_smclk, aclk, _delay) = ClockConfig::new(periph.CS) .mclk_dcoclk(DcoclkFreqSel::_1MHz, MclkDiv::_1) .smclk_on(SmclkDiv::_1) .aclk_vloclk() diff --git a/examples/clocks.rs b/examples/clocks.rs index 78c60d1..723d129 100644 --- a/examples/clocks.rs +++ b/examples/clocks.rs @@ -29,7 +29,7 @@ fn main() -> ! { .split(&pmm); let mut p1_0 = p1.pin0; - let (smclk, _aclk) = ClockConfig::new(periph.CS) + let (smclk, _aclk, _delay) = ClockConfig::new(periph.CS) .mclk_dcoclk(DcoclkFreqSel::_8MHz, MclkDiv::_1) .smclk_on(SmclkDiv::_1) .aclk_vloclk() diff --git a/examples/echo.rs b/examples/echo.rs index 7f19ee9..24b9708 100644 --- a/examples/echo.rs +++ b/examples/echo.rs @@ -28,7 +28,7 @@ fn main() -> ! { let mut fram = Fram::new(periph.FRCTL); let _wdt = Wdt::constrain(periph.WDT_A); - let (_smclk, aclk) = ClockConfig::new(periph.CS) + let (_smclk, aclk, _delay) = ClockConfig::new(periph.CS) .mclk_dcoclk(DcoclkFreqSel::_1MHz, MclkDiv::_1) .smclk_on(SmclkDiv::_2) .aclk_refoclk() diff --git a/examples/gpio_interrupts.rs b/examples/gpio_interrupts.rs index 64168f5..464fdf7 100644 --- a/examples/gpio_interrupts.rs +++ b/examples/gpio_interrupts.rs @@ -28,7 +28,7 @@ static P2IV: Mutex>>> = Mutex::new(RefCell::new(None)); #[entry] fn main() -> ! { let periph = msp430fr2355::Peripherals::take().unwrap(); - let (_smclk, aclk) = ClockConfig::new(periph.CS) + let (_smclk, aclk, _delay) = ClockConfig::new(periph.CS) .mclk_refoclk(MclkDiv::_1) // 32 KHz SMCLK .smclk_on(SmclkDiv::_2) diff --git a/examples/loopback.rs b/examples/loopback.rs index f3230d8..d294f2c 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -54,7 +54,7 @@ fn main() -> ! { let mut fram = Fram::new(periph.FRCTL); let _wdt = Wdt::constrain(periph.WDT_A); - let (smclk, _aclk) = ClockConfig::new(periph.CS) + let (smclk, _aclk, _delay) = ClockConfig::new(periph.CS) .mclk_dcoclk(DcoclkFreqSel::_4MHz, MclkDiv::_1) .smclk_on(SmclkDiv::_2) .aclk_refoclk() diff --git a/examples/pwm.rs b/examples/pwm.rs index b799724..f39102a 100644 --- a/examples/pwm.rs +++ b/examples/pwm.rs @@ -24,7 +24,7 @@ fn main() -> ! { let pmm = Pmm::new(periph.PMM); let p6 = Batch::new(periph.P6).split(&pmm); - let (smclk, _aclk) = ClockConfig::new(periph.CS) + let (smclk, _aclk, _delay) = ClockConfig::new(periph.CS) .mclk_dcoclk(DcoclkFreqSel::_1MHz, MclkDiv::_1) .smclk_on(SmclkDiv::_1) .aclk_vloclk() diff --git a/examples/rtc.rs b/examples/rtc.rs index f2f500f..5939133 100644 --- a/examples/rtc.rs +++ b/examples/rtc.rs @@ -33,7 +33,7 @@ fn main() -> ! { let mut led = p1.pin0; let mut button = p2.pin3; - let (_smclk, _aclk) = ClockConfig::new(periph.CS) + let (_smclk, _aclk, _delay) = ClockConfig::new(periph.CS) .mclk_refoclk(MclkDiv::_1) .smclk_on(SmclkDiv::_1) .aclk_vloclk() diff --git a/examples/timer.rs b/examples/timer.rs index 7726778..61511e7 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -30,7 +30,7 @@ fn main() -> ! { .split(&pmm); let mut p1_0 = p1.pin0; - let (_smclk, aclk) = ClockConfig::new(periph.CS) + let (_smclk, aclk, _delay) = ClockConfig::new(periph.CS) .mclk_dcoclk(DcoclkFreqSel::_1MHz, MclkDiv::_1) .smclk_on(SmclkDiv::_1) .aclk_vloclk() From 077bc7f06e44b3ce9cd793c39c4cf6c8ba9e76cd Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sun, 22 Dec 2024 19:55:50 +1300 Subject: [PATCH 42/82] Simplify ADC register conversion functions Add integer discriminants to enum definitions to simplify register conversion functions. Switch nb::Error type to `Infallible`, as the only error type returned is `WouldBlock`. Also remove unneeded return statements --- src/adc.rs | 118 ++++++++++++++++++++--------------------------------- 1 file changed, 45 insertions(+), 73 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index c7ab45c..bc95f39 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,115 +1,86 @@ use core::{u8}; +use core::convert::Infallible; use crate::gpio::*; use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; pub enum SampleTime { - _4, - _8, - _16, - _32, - _64, - _96, - _128, - _192, - _256, - _384, - _512, - _768, - _1024, + _4 = 0b0000, + _8 = 0b0001, + _16 = 0b0010, + _32 = 0b0011, + _64 = 0b0100, + _96 = 0b0101, + _128 = 0b0110, + _192 = 0b0111, + _256 = 0b1000, + _384 = 0b1001, + _512 = 0b1010, + _768 = 0b1011, + _1024 = 0b1100, } impl SampleTime { + #[inline(always)] fn adcsht(self) -> u8 { - match self { - SampleTime::_4 => 0b000, - SampleTime::_8 => 0b001, - SampleTime::_16 => 0b010, - SampleTime::_32 => 0b011, - SampleTime::_64 => 0b100, - SampleTime::_96 => 0b101, - SampleTime::_128 => 0b110, - SampleTime::_192 => 0b111, - SampleTime::_256 => 0b1000, - SampleTime::_384 => 0b1001, - SampleTime::_512 => 0b1010, - SampleTime::_768 => 0b1011, - SampleTime::_1024 => 0b1100, - } + self as u8 } } pub enum ClockDivider { - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, + _1 = 0b000, + _2 = 0b001, + _3 = 0b010, + _4 = 0b011, + _5 = 0b100, + _6 = 0b101, + _7 = 0b110, + _8 = 0b111, } impl ClockDivider { + #[inline(always)] fn adcdiv(self) -> u8 { - match self { - ClockDivider::_1 => 0b000, - ClockDivider::_2 => 0b001, - ClockDivider::_3 => 0b010, - ClockDivider::_4 => 0b011, - ClockDivider::_5 => 0b100, - ClockDivider::_6 => 0b101, - ClockDivider::_7 => 0b110, - ClockDivider::_8 => 0b111, - } + self as u8 } } pub enum ClockSource { - MODCLK, - ACLK, - SMCLK, + MODCLK = 0b00, + ACLK = 0b01, + SMCLK = 0b10, } impl ClockSource { + #[inline(always)] fn adcssel(self) -> u8 { - match self { - ClockSource::MODCLK => 0b00, - ClockSource::ACLK => 0b01, - ClockSource::SMCLK => 0b10, - } + self as u8 } } pub enum Predivider { - _1, - _4, - _64, + _1 = 0b00, + _4 = 0b01, + _64 = 0b10, } impl Predivider { + #[inline(always)] fn adcpdiv(self) -> u8 { - match self { - Predivider::_1 => 0b00, - Predivider::_4 => 0b01, - Predivider::_64 => 0b10, - } + self as u8 } } pub enum Resolution { - _8BIT, - _10BIT, - _12BIT, + _8BIT = 0b00, + _10BIT = 0b01, + _12BIT = 0b10, } impl Resolution { + #[inline(always)] fn adcres(self) -> u8 { - match self { - Resolution::_8BIT => 0b00, - Resolution::_10BIT => 0b01, - Resolution::_12BIT => 0b10, - } + self as u8 } } @@ -119,6 +90,7 @@ pub enum SamplingRate { } impl SamplingRate { + #[inline(always)] fn adcsr(self) -> bool { match self { SamplingRate::_200KSPS => false, @@ -246,11 +218,11 @@ impl Adc { } pub fn adc_is_busy(&self) -> bool { - return self.adc_reg.adcctl1.read().adcbusy().bit_is_set(); + self.adc_reg.adcctl1.read().adcbusy().bit_is_set() } pub fn adc_get_result(&self) -> u16 { - return self.adc_reg.adcmem0.read().bits(); + self.adc_reg.adcmem0.read().bits() } pub fn adc_set_pin(&mut self, _pin: &PIN) @@ -264,7 +236,7 @@ where WORD: From, PIN: Channel, ID = u8>, { - type Error = (); + type Error = Infallible; // Only returns WouldBlock fn read(&mut self, pin: &mut PIN ) -> nb::Result { if self.is_waiting { From 19f403a97af590b679a75e903d72bf6113d97a4d Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sun, 22 Dec 2024 20:01:04 +1300 Subject: [PATCH 43/82] ADC documentation Also remove legacy import --- src/adc.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index bc95f39..37dfbf0 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,22 +1,42 @@ -use core::{u8}; +//! Analog to Digital Converter (ADC) +//! +//! The ADC may read from any of the following pins: +//! +//! P1.0 - P1.7 (channels 0 to 7), P5.0 - P5.3 (channels 8 to 11) +//! + use core::convert::Infallible; use crate::gpio::*; use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; +/// How many ADCCLK cycles the ADC's sample-and-hold stage will last for. pub enum SampleTime { + /// Sample for 4 ADCCLK cycles _4 = 0b0000, + /// Sample for 8 ADCCLK cycles _8 = 0b0001, + /// Sample for 16 ADCCLK cycles _16 = 0b0010, + /// Sample for 32 ADCCLK cycles _32 = 0b0011, + /// Sample for 64 ADCCLK cycles _64 = 0b0100, + /// Sample for 96 ADCCLK cycles _96 = 0b0101, + /// Sample for 128 ADCCLK cycles _128 = 0b0110, + /// Sample for 192 ADCCLK cycles _192 = 0b0111, + /// Sample for 256 ADCCLK cycles _256 = 0b1000, + /// Sample for 384 ADCCLK cycles _384 = 0b1001, + /// Sample for 512 ADCCLK cycles _512 = 0b1010, + /// Sample for 768 ADCCLK cycles _768 = 0b1011, + /// Sample for 1024 ADCCLK cycles _1024 = 0b1100, } @@ -27,14 +47,23 @@ impl SampleTime { } } +/// How much the ADC input clock will be divided by after being divided by the predivider pub enum ClockDivider { + /// Divide the input clock by 1 _1 = 0b000, + /// Divide the input clock by 2 _2 = 0b001, + /// Divide the input clock by 3 _3 = 0b010, + /// Divide the input clock by 4 _4 = 0b011, + /// Divide the input clock by 5 _5 = 0b100, + /// Divide the input clock by 6 _6 = 0b101, + /// Divide the input clock by 7 _7 = 0b110, + /// Divide the input clock by 8 _8 = 0b111, } @@ -45,9 +74,13 @@ impl ClockDivider { } } +/// Which clock source the ADC uses as input. pub enum ClockSource { + /// Use MODCLK as the ADC input clock MODCLK = 0b00, + /// Use ACLK as the ADC input clock ACLK = 0b01, + /// Use SMCLK as the ADC input clock SMCLK = 0b10, } @@ -58,9 +91,13 @@ impl ClockSource { } } +/// How much the ADC input clock will be divided by prior to being divided by the ADC clock divider pub enum Predivider { + /// Divide the input clock by 1 _1 = 0b00, + /// Divide the input clock by 4 _4 = 0b01, + /// Divide the input clock by 64 _64 = 0b10, } @@ -71,9 +108,13 @@ impl Predivider { } } +/// The output resolution of the ADC conversion. Also determines how many ADCCLK cycles the conversion step takes. pub enum Resolution { + /// 8-bit ADC conversion result. The conversion step takes 10 ADCCLK cycles. _8BIT = 0b00, + /// 10-bit ADC conversion result. The conversion step takes 12 ADCCLK cycles. _10BIT = 0b01, + /// 12-bit ADC conversion result. The conversion step takes 14 ADCCLK cycles. _12BIT = 0b10, } @@ -84,8 +125,11 @@ impl Resolution { } } +/// Selects the drive capability of the ADC reference buffer, which can increase the maximum sampling speed at the cost of increased power draw. pub enum SamplingRate { + /// Maximum of 50 ksps. Lower power usage. _50KSPS, + /// Maximum of 200 ksps. Higher power usage. _200KSPS, } @@ -123,22 +167,32 @@ impl_adc_channel!(P5, Pin1, 9); impl_adc_channel!(P5, Pin2, 10); impl_adc_channel!(P5, Pin3, 11); +/// Controls the onboard ADC pub struct Adc { adc_reg: ADC, is_waiting: bool, } +/// Configuration object for an ADC. pub struct AdcConfig { + /// ADC register pub adc: ADC, + /// Which clock source the ADC takes as an input. This clock will first be divided by the predivider, then the clock divider, to generate ADCCLK. pub clock_source: ClockSource, + /// How much the input clock is divided by, after the predivider. pub clock_divider: ClockDivider, + /// How much the input clock is initially divided by, before the clock divider. pub predivider: Predivider, + /// How many bits the conversion result is. Also defines the number of ADCCLK cycles required to do the conversion step. pub resolution: Resolution, + /// Sets the maximum sampling rate of the ADC. Lower values use less power. pub sampling_rate: SamplingRate, + /// Determines the number of ADCCLK cycles the sampling time takes. pub sample_time: SampleTime, } impl AdcConfig { + /// Creates an ADC configuration pub fn new( adc: ADC, clock_source: ClockSource, @@ -159,6 +213,7 @@ impl AdcConfig { } } + /// Applies this ADC configuration to hardware registers, and returns an ADC. pub fn config_hw(self) -> Adc { let adc_reg = self.adc; unsafe { @@ -184,21 +239,25 @@ impl AdcConfig { let adcsr = self.sampling_rate.adcsr(); adc_reg.adcctl2.modify(|_, w| w.adcsr().bit(adcsr)); - Adc { adc_reg, is_waiting: false } } } impl Adc { + /// Create an ADC instance with a default configuration. + /// + /// If you need a custom configuration you should construct an ADC using AdcConfig instead. pub fn new(adc: ADC) -> Adc { Adc { adc_reg: adc, is_waiting: false } } + /// Enables this ADC, ready to start a conversion. pub fn adc_enable(&mut self) { unsafe {self.adc_reg.adcctl0.set_bits(|w| w.adcon().set_bit());} } + /// Disables this ADC to save power. pub fn adc_disable(&mut self) { unsafe { self.adc_reg.adcctl0.clear_bits(|w| w @@ -207,6 +266,7 @@ impl Adc { } } + /// Starts an ADC conversion. pub fn adc_start_conversion(&mut self) { unsafe { self.adc_reg.adcctl0 @@ -217,14 +277,17 @@ impl Adc { } + /// Whether the ADC is currently sampling or converting. pub fn adc_is_busy(&self) -> bool { self.adc_reg.adcctl1.read().adcbusy().bit_is_set() } + /// Gets the latest ADC conversion result. pub fn adc_get_result(&self) -> u16 { self.adc_reg.adcmem0.read().bits() } + /// Selects which pin to sample. Can only be modified when the ADC is not busy. pub fn adc_set_pin(&mut self, _pin: &PIN) where PIN: Channel, ID=u8> { self.adc_reg.adcmctl0.modify(|_, w| w.adcinch().bits(PIN::channel())); @@ -238,6 +301,9 @@ where { type Error = Infallible; // Only returns WouldBlock + /// Begins a single ADC conversion if one is not already underway. + /// + /// If the result is ready it is returned, otherwise returns `WouldBlock` fn read(&mut self, pin: &mut PIN ) -> nb::Result { if self.is_waiting { if self.adc_is_busy() { From 3d550b4c87315704b4f82715b52e38790ed420ae Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sun, 22 Dec 2024 20:28:22 +1300 Subject: [PATCH 44/82] Revert temporary project settings Remove GPIO delays. Issues with the MSP430 boostpack screen can be dealt with elsewhere --- Cargo.toml | 6 +----- src/gpio.rs | 9 --------- src/lib.rs | 2 +- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 138a1d8..3465b2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,7 @@ msp430 = "0.4.0" nb = "0.1.3" void = { version = "1.0.2", default-features = false } embedded-hal = { version = "0.2.7", features = ["unproven"] } - -[dependencies.msp430fr2355] -path = "../msp430fr2355" -version = "0.5.2" -features = ["rt", "critical-section"] +msp430fr2355 = { version = "0.5.2", features = ["rt", "critical-section"] } [dev-dependencies] panic-msp430 = "0.4.0" diff --git a/src/gpio.rs b/src/gpio.rs index a715ddb..f53a754 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -18,7 +18,6 @@ use crate::hw_traits::gpio::{GpioPeriph, IntrPeriph}; use crate::util::BitsExt; use core::marker::PhantomData; use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; -use msp430::asm; use msp430fr2355 as pac; pub use pac::{P1, P2, P3, P4, P5, P6}; @@ -360,10 +359,6 @@ impl OutputPin for Pin { #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - //TODO figure out why this is needed for screen - for _ in 0..200{ - asm::nop(); - } let p = unsafe { PORT::steal() }; p.pxout_clear(PIN::CLR_MASK); Ok(()) @@ -371,10 +366,6 @@ impl OutputPin for Pin { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - //TODO figure out why this is needed for screen - for _ in 0..200{ - asm::nop(); - } let p = unsafe { PORT::steal() }; p.pxout_set(PIN::SET_MASK); Ok(()) diff --git a/src/lib.rs b/src/lib.rs index ec4ceb0..689e5f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ #![allow(incomplete_features)] // Enable specialization without warnings #![feature(specialization)] #![feature(asm_experimental_arch)] -// #![deny(missing_docs)] +#![deny(missing_docs)] #![feature(asm_const)] pub mod adc; From 54aa73ad49aadc73c4bcbf85bfcd8d6c0c5aebe2 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 23 Dec 2024 13:21:25 +1300 Subject: [PATCH 45/82] Rename blinky example to gpio, add new blinky that uses Delay object --- examples/blinky.rs | 38 +++++++++++++++++--------------------- examples/gpio.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 examples/gpio.rs diff --git a/examples/blinky.rs b/examples/blinky.rs index 2cf9d3c..9cf7192 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -3,39 +3,35 @@ use embedded_hal::digital::v2::*; use msp430_rt::entry; -use msp430fr2x5x_hal::{gpio::Batch, pmm::Pmm, watchdog::Wdt}; +use msp430fr2x5x_hal::{ + clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, + fram::Fram, gpio::Batch, hal::blocking::delay::DelayMs, pmm::Pmm, watchdog::Wdt}; use panic_msp430 as _; // Red onboard LED should blink at a steady period. -// Green onboard LED should go on when P2.3 button is pressed #[entry] fn main() -> ! { + // Take peripherals and disable watchdog let periph = msp430fr2355::Peripherals::take().unwrap(); let _wdt = Wdt::constrain(periph.WDT_A); + // Configure GPIO let pmm = Pmm::new(periph.PMM); - let p1 = Batch::new(periph.P1).split(&pmm); - let p2 = Batch::new(periph.P2) - .config_pin3(|p| p.pullup()) - .split(&pmm); - let p6 = Batch::new(periph.P6) - .config_pin6(|p| p.to_output()) - .split(&pmm); + let port1 = Batch::new(periph.P1).split(&pmm); + let mut p1_0 = port1.pin0.to_output(); - let mut p1_0 = p1.pin0.to_output(); - let p2_3 = p2.pin3; - let mut p6_6 = p6.pin6; + // Configure clocks to get accurate delay timing + let mut fram = Fram::new(periph.FRCTL); + let (_smclk, _aclk, mut delay) = ClockConfig::new(periph.CS) + .mclk_dcoclk(DcoclkFreqSel::_8MHz, MclkDiv::_1) + .smclk_on(SmclkDiv::_1) + .freeze(&mut fram); loop { - p1_0.toggle().ok(); - - for _ in 0..5000 { - if p2_3.is_high().unwrap() { - p6_6.set_low().ok(); - } else { - p6_6.set_high().ok(); - } - } + // `toggle()` returns a `Result` because of embedded_hal, but the result is always `Ok` with MSP430 GPIO. + // Rust complains about unused Results, so we 'use' the Result by calling .ok() + p1_0.toggle().ok(); + delay.delay_ms(500_u16); } } diff --git a/examples/gpio.rs b/examples/gpio.rs new file mode 100644 index 0000000..498c8b5 --- /dev/null +++ b/examples/gpio.rs @@ -0,0 +1,41 @@ +#![no_main] +#![no_std] + +use embedded_hal::digital::v2::*; +use msp430_rt::entry; +use msp430fr2x5x_hal::{gpio::Batch, pmm::Pmm, watchdog::Wdt}; +use panic_msp430 as _; + +// Green onboard LED should go on when P2.3 button is pressed +#[entry] +fn main() -> ! { + let periph = msp430fr2355::Peripherals::take().unwrap(); + let _wdt = Wdt::constrain(periph.WDT_A); + + let pmm = Pmm::new(periph.PMM); + let p2 = Batch::new(periph.P2) + .config_pin3(|p| p.pullup()) + .split(&pmm); + let p6 = Batch::new(periph.P6) + .config_pin6(|p| p.to_output()) + .split(&pmm); + + let p2_3 = p2.pin3; + let mut p6_6 = p6.pin6; + + loop { + if p2_3.is_high().unwrap() { + p6_6.set_low().ok(); + } else { + p6_6.set_high().ok(); + } + } +} + +// The compiler will emit calls to the abort() compiler intrinsic if debug assertions are +// enabled (default for dev profile). MSP430 does not actually have meaningful abort() support +// so for now, we create our own in each application where debug assertions are present. +#[no_mangle] +extern "C" fn abort() -> ! { + panic!(); +} From f151cef985b75f2f13eaf4aea02b8f0e5e12e90f Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 23 Dec 2024 13:29:15 +1300 Subject: [PATCH 46/82] Reduce indentation in examples using let-else Add some comments about panic-never. Replace `.borrow(cs).borrow_mut()` with `.borrow_ref_mut(cs)` --- examples/capture_intr.rs | 87 ++++++++++++++++++------------------- examples/gpio_interrupts.rs | 24 ++++------ 2 files changed, 51 insertions(+), 60 deletions(-) diff --git a/examples/capture_intr.rs b/examples/capture_intr.rs index d6ce162..b1b9912 100644 --- a/examples/capture_intr.rs +++ b/examples/capture_intr.rs @@ -2,6 +2,8 @@ #![no_std] #![feature(abi_msp430_interrupt)] +// This example also demonstrates how to write panic-free code using panic_never. + use core::cell::UnsafeCell; use critical_section::with; use embedded_hal::digital::v2::ToggleableOutputPin; @@ -27,6 +29,7 @@ use panic_msp430 as _; #[cfg(not(debug_assertions))] use panic_never as _; +// We use UnsafeCell as a panic-free version of RefCell. If you aren't using `panic_never` then RefCell is more ergonomic. static CAPTURE: Mutex>>> = Mutex::new(UnsafeCell::new(None)); static VECTOR: Mutex>>> = @@ -38,38 +41,37 @@ static RED_LED: Mutex>>> = // so sometimes inputs are missed. #[entry] fn main() -> ! { - if let Some(periph) = msp430fr2355::Peripherals::take() { - let mut fram = Fram::new(periph.FRCTL); - Wdt::constrain(periph.WDT_A); - - let pmm = Pmm::new(periph.PMM); - let p1 = Batch::new(periph.P1) - .config_pin0(|p| p.to_output()) - .split(&pmm); - let red_led = p1.pin0; - - with(|cs| unsafe { *RED_LED.borrow(cs).get() = Some(red_led) }); - - let (_smclk, aclk, _delay) = ClockConfig::new(periph.CS) - .mclk_dcoclk(DcoclkFreqSel::_1MHz, MclkDiv::_1) - .smclk_on(SmclkDiv::_1) - .aclk_vloclk() - .freeze(&mut fram); - - let captures = CaptureParts3::config(periph.TB0, TimerConfig::aclk(&aclk)) - .config_cap1_input_A(p1.pin6.to_alternate2()) - .config_cap1_trigger(CapTrigger::FallingEdge) - .commit(); - let mut capture = captures.cap1; - let vectors = captures.tbxiv; - - setup_capture(&mut capture); - with(|cs| { - unsafe { *CAPTURE.borrow(cs).get() = Some(capture) } - unsafe { *VECTOR.borrow(cs).get() = Some(vectors) } - }); - unsafe { enable() }; - } + let Some(periph) = msp430fr2355::Peripherals::take() else { loop{} }; + let mut fram = Fram::new(periph.FRCTL); + Wdt::constrain(periph.WDT_A); + + let pmm = Pmm::new(periph.PMM); + let p1 = Batch::new(periph.P1) + .config_pin0(|p| p.to_output()) + .split(&pmm); + let red_led = p1.pin0; + + with(|cs| unsafe { *RED_LED.borrow(cs).get() = Some(red_led) }); + + let (_smclk, aclk, _delay) = ClockConfig::new(periph.CS) + .mclk_dcoclk(DcoclkFreqSel::_1MHz, MclkDiv::_1) + .smclk_on(SmclkDiv::_1) + .aclk_vloclk() + .freeze(&mut fram); + + let captures = CaptureParts3::config(periph.TB0, TimerConfig::aclk(&aclk)) + .config_cap1_input_A(p1.pin6.to_alternate2()) + .config_cap1_trigger(CapTrigger::FallingEdge) + .commit(); + let mut capture = captures.cap1; + let vectors = captures.tbxiv; + + setup_capture(&mut capture); + with(|cs| { + unsafe { *CAPTURE.borrow(cs).get() = Some(capture) } + unsafe { *VECTOR.borrow(cs).get() = Some(vectors) } + }); + unsafe { enable() }; loop {} } @@ -81,20 +83,15 @@ fn setup_capture, C>(capture: &mut Capture) { #[interrupt] fn TIMER0_B1() { with(|cs| { - if let Some(vector) = unsafe { &mut *VECTOR.borrow(cs).get() }.as_mut() { - if let Some(capture) = unsafe { &mut *CAPTURE.borrow(cs).get() }.as_mut() { - match vector.interrupt_vector() { - CaptureVector::Capture1(cap) => { - if cap.interrupt_capture(capture).is_ok() { - if let Some(led) = unsafe { &mut *RED_LED.borrow(cs).get() }.as_mut() { - led.toggle().void_unwrap(); - } - } - } - _ => {} - }; + let Some(vector) = unsafe { &mut *VECTOR.borrow(cs).get() }.as_mut() else { return }; + let Some(capture) = unsafe { &mut *CAPTURE.borrow(cs).get() }.as_mut() else { return }; + let Some(led) = unsafe { &mut *RED_LED.borrow(cs).get() }.as_mut() else { return }; + + if let CaptureVector::Capture1(cap) = vector.interrupt_vector() { + if cap.interrupt_capture(capture).is_ok() { + led.toggle().void_unwrap(); } - } + }; }); } diff --git a/examples/gpio_interrupts.rs b/examples/gpio_interrupts.rs index 464fdf7..fed5be6 100644 --- a/examples/gpio_interrupts.rs +++ b/examples/gpio_interrupts.rs @@ -53,8 +53,8 @@ fn main() -> ! { let mut green_led = p6.pin6; let p2iv = p2.pxiv; - with(|cs| *RED_LED.borrow(cs).borrow_mut() = Some(red_led)); - with(|cs| *P2IV.borrow(cs).borrow_mut() = Some(p2iv)); + with(|cs| RED_LED.borrow_ref_mut(cs).replace(red_led)); + with(|cs| P2IV.borrow_ref_mut(cs).replace(p2iv)); wdt.set_aclk(&aclk) .enable_interrupts() @@ -74,25 +74,19 @@ fn main() -> ! { #[interrupt] fn PORT2() { with(|cs| { - RED_LED.borrow(cs).borrow_mut().as_mut().map(|red_led| { - match P2IV - .borrow(cs) - .borrow_mut() - .as_mut() - .unwrap() - .get_interrupt_vector() - { - GpioVector::Pin7Isr => red_led.toggle().ok(), - _ => panic!(), - } - }) + let Some(ref mut red_led) = *RED_LED.borrow_ref_mut(cs) else { return }; + let Some(ref mut p2iv) = *P2IV.borrow_ref_mut(cs) else { return }; + + if let GpioVector::Pin7Isr = p2iv.get_interrupt_vector() { + red_led.toggle().ok(); + } }); } #[interrupt] fn WDT() { with(|cs| { - RED_LED.borrow(cs).borrow_mut().as_mut().map(|red_led| { + RED_LED.borrow_ref_mut(cs).as_mut().map(|red_led| { red_led.toggle().ok(); }) }); From 672014cb6cc655a20d067e1b9ec7e2c2ff6103f6 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 23 Dec 2024 17:02:11 +1300 Subject: [PATCH 47/82] Remove panics in serial to make panic_never examples compile again --- src/serial.rs | 57 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index e85dada..1ceddc6 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -178,7 +178,7 @@ impl_serial_pin!(UsciA1RxPin, P4, Pin2); /// Typestate for a serial interface with an unspecified clock source pub struct NoClockSet { - baudrate: u32, + baudrate: core::num::NonZeroU32, } /// Typestate for a serial interface with a specified clock source @@ -234,7 +234,8 @@ impl SerialConfig { parity, loopback, usci, - state: NoClockSet { baudrate }, + // Safety: .max(1) ensures baudrate is non-zero + state: NoClockSet { baudrate: unsafe {core::num::NonZeroU32::new_unchecked(baudrate.max(1))} }, } } @@ -288,20 +289,25 @@ struct BaudConfig { } #[inline] -fn calculate_baud_config(clk_freq: u32, bps: u32) -> BaudConfig { - // Prevent division by 0 - let bps = bps.max(1); +fn calculate_baud_config(clk_freq: u32, bps: core::num::NonZeroU32) -> BaudConfig { + let bps_u32: u32 = bps.into(); // Ensure n stays within the 16 bit boundary - let n = (clk_freq / bps).max(1).min(0xFFFF); + // Safety: NonZeroU32 prevents div/0 + // (clk_freq / bps).clamp(1, 0xFFFF) + let n = (unsafe{ clk_freq.checked_div(bps_u32).unwrap_unchecked()} ).clamp(1, 0xFFFF); let brs = lookup_brs(clk_freq, bps); - if n >= 16 { - let div = bps * 16; + if (n >= 16) && (bps_u32 < u32::MAX/16) { + let div = bps_u32 * 16; // n / 16, but more precise - let br = (clk_freq / div) as u16; + // Safety: 0 < bps < u32::MAX/16 implies 0 < div < u32::MAX + let br = (unsafe {clk_freq.checked_div(div).unwrap_unchecked()}) as u16; + // same as n % 16, but more precise - let brf = ((clk_freq % div) / bps) as u8; + // Safety: div and bps non-zero due to above checks + // (clk_freq % div) / bps + let brf = (unsafe{(clk_freq.checked_rem(div)).and_then(|a| a.checked_div(bps_u32)).unwrap_unchecked()}) as u8; BaudConfig { ucos16: true, br, @@ -317,9 +323,9 @@ fn calculate_baud_config(clk_freq: u32, bps: u32) -> BaudConfig { } } } - +const BRS_LOOKUP_LEN: usize = 36; // Data from table 22-4 of MSP430FR4xx and MSP430FR2xx family user's guide (Rev. I) -const BRS_LOOKUP_KEYS : [u16;36] = +const BRS_LOOKUP_KEYS : [u16;BRS_LOOKUP_LEN] = [ 0x0000, 0x00d9, 0x0125, 0x0156, 0x019a, 0x0201, 0x024a, 0x02ac, 0x036f, 0x038f, @@ -331,7 +337,7 @@ const BRS_LOOKUP_KEYS : [u16;36] = 0x0edc ]; -const BRS_LOOKUP_VALS : [u8;36] = +const BRS_LOOKUP_VALS : [u8;BRS_LOOKUP_LEN] = [ 0x00,0x01,0x02,0x04,0x08, 0x10,0x20,0x11,0x21,0x22, @@ -346,17 +352,18 @@ const BRS_LOOKUP_VALS : [u8;36] = #[inline(always)] fn binary_search_brs_table(res : u16) -> u8{ let mut low: usize = 0; - let mut high: usize = BRS_LOOKUP_KEYS.len() - 1; + let mut high: usize = BRS_LOOKUP_LEN - 1; + // Safety: 0 <= low <= mid <= high < BRS_LOOKUP_LEN while low != high { let mid = (low + high) >> 1; - let key = BRS_LOOKUP_KEYS[mid]; + let key = unsafe { *BRS_LOOKUP_KEYS.get_unchecked(mid) }; if res == key { - return BRS_LOOKUP_VALS[mid] + return unsafe {*BRS_LOOKUP_VALS.get_unchecked(mid)} }else if high - low == 1{ - return if res < BRS_LOOKUP_KEYS[high] { - BRS_LOOKUP_VALS[low] + return if res < unsafe{ *BRS_LOOKUP_KEYS.get_unchecked(high) } { + unsafe {*BRS_LOOKUP_VALS.get_unchecked(low)} } else { - BRS_LOOKUP_VALS[high] + unsafe {*BRS_LOOKUP_VALS.get_unchecked(high)} } }else if res > key{ low = mid; @@ -364,14 +371,18 @@ fn binary_search_brs_table(res : u16) -> u8{ high = mid - 1; } } - return BRS_LOOKUP_VALS[low]; + return unsafe{ *BRS_LOOKUP_VALS.get_unchecked(low)}; } #[inline(always)] -fn lookup_brs(clk_freq: u32, bps: u32) -> u8 { - let modulo = clk_freq % bps; +fn lookup_brs(clk_freq: u32, bps: core::num::NonZeroU32) -> u8 { + // Safety: NonZeroU32 prevents /div0 + // clk_freq % bps + let modulo = unsafe { clk_freq.checked_rem(bps.into()).unwrap_unchecked() }; // 12 fractional bit fixed point result - let fixed_point_result : u32 = (modulo << 12) / bps; + // (modulo << 12) / bps + let fixed_point_result: u32 = + unsafe { (modulo << 12).checked_div(bps.into()).unwrap_unchecked() }; // Throw away the upper bits since the fractional part is all we care about binary_search_brs_table(fixed_point_result as u16) From 6a5e4a40f3265312d771d425b249e8baa76a9de4 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 23 Dec 2024 19:35:11 +1300 Subject: [PATCH 48/82] Rustfmt --- examples/blinky.rs | 11 +- examples/capture_intr.rs | 8 +- examples/gpio_interrupts.rs | 4 +- src/adc.rs | 81 ++++++----- src/clock.rs | 10 +- src/delay.rs | 28 ++-- src/hw_traits/eusci.rs | 277 +++++++++++++++++------------------- src/hw_traits/gpio.rs | 4 +- src/i2c.rs | 225 ++++++++++++++++------------- src/lib.rs | 4 +- src/serial.rs | 78 +++++----- src/spi.rs | 85 +++++------ 12 files changed, 422 insertions(+), 393 deletions(-) diff --git a/examples/blinky.rs b/examples/blinky.rs index 9cf7192..30c1304 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -4,8 +4,13 @@ use embedded_hal::digital::v2::*; use msp430_rt::entry; use msp430fr2x5x_hal::{ - clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, - fram::Fram, gpio::Batch, hal::blocking::delay::DelayMs, pmm::Pmm, watchdog::Wdt}; + clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, + fram::Fram, + gpio::Batch, + hal::blocking::delay::DelayMs, + pmm::Pmm, + watchdog::Wdt, +}; use panic_msp430 as _; // Red onboard LED should blink at a steady period. @@ -30,7 +35,7 @@ fn main() -> ! { loop { // `toggle()` returns a `Result` because of embedded_hal, but the result is always `Ok` with MSP430 GPIO. // Rust complains about unused Results, so we 'use' the Result by calling .ok() - p1_0.toggle().ok(); + p1_0.toggle().ok(); delay.delay_ms(500_u16); } } diff --git a/examples/capture_intr.rs b/examples/capture_intr.rs index b1b9912..2857fb7 100644 --- a/examples/capture_intr.rs +++ b/examples/capture_intr.rs @@ -41,7 +41,7 @@ static RED_LED: Mutex>>> = // so sometimes inputs are missed. #[entry] fn main() -> ! { - let Some(periph) = msp430fr2355::Peripherals::take() else { loop{} }; + let Some(periph) = msp430fr2355::Peripherals::take() else { loop {} }; let mut fram = Fram::new(periph.FRCTL); Wdt::constrain(periph.WDT_A); @@ -83,9 +83,9 @@ fn setup_capture, C>(capture: &mut Capture) { #[interrupt] fn TIMER0_B1() { with(|cs| { - let Some(vector) = unsafe { &mut *VECTOR.borrow(cs).get() }.as_mut() else { return }; - let Some(capture) = unsafe { &mut *CAPTURE.borrow(cs).get() }.as_mut() else { return }; - let Some(led) = unsafe { &mut *RED_LED.borrow(cs).get() }.as_mut() else { return }; + let Some(vector) = unsafe { &mut *VECTOR.borrow(cs).get() }.as_mut() else { return; }; + let Some(capture) = unsafe { &mut *CAPTURE.borrow(cs).get() }.as_mut() else { return; }; + let Some(led) = unsafe { &mut *RED_LED.borrow(cs).get() }.as_mut() else { return; }; if let CaptureVector::Capture1(cap) = vector.interrupt_vector() { if cap.interrupt_capture(capture).is_ok() { diff --git a/examples/gpio_interrupts.rs b/examples/gpio_interrupts.rs index fed5be6..5c910b4 100644 --- a/examples/gpio_interrupts.rs +++ b/examples/gpio_interrupts.rs @@ -74,8 +74,8 @@ fn main() -> ! { #[interrupt] fn PORT2() { with(|cs| { - let Some(ref mut red_led) = *RED_LED.borrow_ref_mut(cs) else { return }; - let Some(ref mut p2iv) = *P2IV.borrow_ref_mut(cs) else { return }; + let Some(ref mut red_led) = *RED_LED.borrow_ref_mut(cs) else { return; }; + let Some(ref mut p2iv) = *P2IV.borrow_ref_mut(cs) else { return; }; if let GpioVector::Pin7Isr = p2iv.get_interrupt_vector() { red_led.toggle().ok(); diff --git a/src/adc.rs b/src/adc.rs index 37dfbf0..5cf8fd9 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,12 +1,12 @@ //! Analog to Digital Converter (ADC) //! //! The ADC may read from any of the following pins: -//! +//! //! P1.0 - P1.7 (channels 0 to 7), P5.0 - P5.3 (channels 8 to 11) -//! +//! -use core::convert::Infallible; use crate::gpio::*; +use core::convert::Infallible; use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; @@ -127,9 +127,9 @@ impl Resolution { /// Selects the drive capability of the ADC reference buffer, which can increase the maximum sampling speed at the cost of increased power draw. pub enum SamplingRate { - /// Maximum of 50 ksps. Lower power usage. + /// Maximum of 50 ksps. Lower power usage. _50KSPS, - /// Maximum of 200 ksps. Higher power usage. + /// Maximum of 200 ksps. Higher power usage. _200KSPS, } @@ -148,8 +148,10 @@ macro_rules! impl_adc_channel { ($port: ty, $pin: ty, $channel: literal ) => { impl Channel> for Pin<$port, $pin, Alternate3>> { type ID = u8; - - fn channel() -> Self::ID { $channel } + + fn channel() -> Self::ID { + $channel + } } }; } @@ -217,16 +219,22 @@ impl AdcConfig { pub fn config_hw(self) -> Adc { let adc_reg = self.adc; unsafe { - adc_reg.adcctl0.clear_bits(|w| w - .adcenc().clear_bit() - .adcon().clear_bit() - .adcsc().clear_bit()); + adc_reg.adcctl0.clear_bits(|w| { + w.adcenc() + .clear_bit() + .adcon() + .clear_bit() + .adcsc() + .clear_bit() + }); } let adcsht = self.sample_time.adcsht(); adc_reg.adcctl0.modify(|_, w| w.adcsht().bits(adcsht)); let adcssel = self.clock_source.adcssel(); - adc_reg.adcctl1.modify(|_, w| w.adcssel().bits(adcssel).adcshp().adcshp_1()); + adc_reg + .adcctl1 + .modify(|_, w| w.adcssel().bits(adcssel).adcshp().adcshp_1()); let adcdiv = self.clock_divider.adcdiv(); adc_reg.adcctl1.modify(|_, w| w.adcdiv().bits(adcdiv)); @@ -240,41 +248,47 @@ impl AdcConfig { let adcsr = self.sampling_rate.adcsr(); adc_reg.adcctl2.modify(|_, w| w.adcsr().bit(adcsr)); - Adc { adc_reg, is_waiting: false } + Adc { + adc_reg, + is_waiting: false, + } } } impl Adc { /// Create an ADC instance with a default configuration. - /// + /// /// If you need a custom configuration you should construct an ADC using AdcConfig instead. pub fn new(adc: ADC) -> Adc { - Adc { adc_reg: adc, is_waiting: false } + Adc { + adc_reg: adc, + is_waiting: false, + } } /// Enables this ADC, ready to start a conversion. pub fn adc_enable(&mut self) { - unsafe {self.adc_reg.adcctl0.set_bits(|w| w.adcon().set_bit());} + unsafe { + self.adc_reg.adcctl0.set_bits(|w| w.adcon().set_bit()); + } } /// Disables this ADC to save power. pub fn adc_disable(&mut self) { unsafe { - self.adc_reg.adcctl0.clear_bits(|w| w - .adcon().clear_bit() - .adcenc().clear_bit()); + self.adc_reg + .adcctl0 + .clear_bits(|w| w.adcon().clear_bit().adcenc().clear_bit()); } } /// Starts an ADC conversion. pub fn adc_start_conversion(&mut self) { unsafe { - self.adc_reg.adcctl0 - .set_bits(|w| w - .adcenc().set_bit() - .adcsc().set_bit()); + self.adc_reg + .adcctl0 + .set_bits(|w| w.adcenc().set_bit().adcsc().set_bit()); } - } /// Whether the ADC is currently sampling or converting. @@ -289,8 +303,12 @@ impl Adc { /// Selects which pin to sample. Can only be modified when the ADC is not busy. pub fn adc_set_pin(&mut self, _pin: &PIN) - where PIN: Channel, ID=u8> { - self.adc_reg.adcmctl0.modify(|_, w| w.adcinch().bits(PIN::channel())); + where + PIN: Channel, ID = u8>, + { + self.adc_reg + .adcmctl0 + .modify(|_, w| w.adcinch().bits(PIN::channel())); } } @@ -301,20 +319,19 @@ where { type Error = Infallible; // Only returns WouldBlock - /// Begins a single ADC conversion if one is not already underway. - /// + /// Begins a single ADC conversion if one is not already underway. + /// /// If the result is ready it is returned, otherwise returns `WouldBlock` - fn read(&mut self, pin: &mut PIN ) -> nb::Result { + fn read(&mut self, pin: &mut PIN) -> nb::Result { if self.is_waiting { if self.adc_is_busy() { return Err(nb::Error::WouldBlock); - } - else { + } else { self.is_waiting = false; return Ok(self.adc_get_result().into()); } } - + self.adc_disable(); self.adc_set_pin(pin); self.adc_enable(); diff --git a/src/clock.rs b/src/clock.rs index 0123173..b7289fa 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -10,12 +10,12 @@ use core::arch::asm; +use crate::delay::Delay; use crate::fram::{Fram, WaitStates}; use msp430fr2355 as pac; use pac::cs::csctl1::DCORSEL_A; use pac::cs::csctl4::{SELA_A, SELMS_A}; pub use pac::cs::csctl5::{DIVM_A as MclkDiv, DIVS_A as SmclkDiv}; -use crate::delay::Delay; /// REFOCLK frequency pub const REFOCLK: u16 = 32768; @@ -350,7 +350,7 @@ impl ClockConfig { ( Smclk(mclk_freq >> (self.smclk.0 as u32)), Aclk(self.aclk_sel.freq()), - Delay::new(mclk_freq) + Delay::new(mclk_freq), ) } } @@ -364,11 +364,7 @@ impl ClockConfig { self.configure_dco_fll(); unsafe { Self::configure_fram(fram, mclk_freq) }; self.configure_cs(); - ( - Aclk(self.aclk_sel.freq()), - Delay::new(mclk_freq) - ) - + (Aclk(self.aclk_sel.freq()), Delay::new(mclk_freq)) } } diff --git a/src/delay.rs b/src/delay.rs index f924768..645321c 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -1,32 +1,34 @@ //! Embedded hal delay implementation -use msp430::asm; use crate::hal::blocking::delay::DelayMs; +use msp430::asm; /// Delay provider struct -pub struct Delay{ - nops_per_ms: u16 +pub struct Delay { + nops_per_ms: u16, } -impl Delay{ +impl Delay { /// Create a new delay object - pub fn new(freq: u32) -> Self{ + pub fn new(freq: u32) -> Self { // ~21 nops needed per 2^20 Hz to delay 1 ms - let nops : u32 = 210 * (freq >> 20); - Delay{nops_per_ms: (nops as u16)} + let nops: u32 = 210 * (freq >> 20); + Delay { + nops_per_ms: (nops as u16), + } } } -impl DelayMs for Delay{ +impl DelayMs for Delay { #[inline] - fn delay_ms(&mut self, ms: u8){ + fn delay_ms(&mut self, ms: u8) { self.delay_ms(ms as u16); } } -impl DelayMs for Delay{ - fn delay_ms(&mut self, ms: u16){ - for _ in 0 .. ms{ - for _ in 0 .. self.nops_per_ms{ +impl DelayMs for Delay { + fn delay_ms(&mut self, ms: u16) { + for _ in 0..ms { + for _ in 0..self.nops_per_ms { asm::nop(); } } diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 92c1a2f..726766a 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -61,10 +61,10 @@ impl From for Ucssel { #[inline(always)] fn from(u: u8) -> Self { match u { - 0 => Ucssel::Uclk, - 1 => Ucssel::Aclk, - 2 | 3 => Ucssel::Smclk, - _ => unreachable!(), + 0 => Ucssel::Uclk, + 1 => Ucssel::Aclk, + 2 | 3 => Ucssel::Smclk, + _ => unreachable!(), } } } @@ -162,8 +162,7 @@ impl From for Ucastp { } } - -pub struct UcaCtlw0{ +pub struct UcaCtlw0 { pub ucpen: bool, pub ucpar: bool, pub ucmsb: bool, @@ -227,7 +226,7 @@ pub struct UcbStatw, UcbStatw_rd, UcbStatw_wr{ } // in order to avoid 4 separate structs, I manually implemented the macro for these registers -pub struct UcbI2coa{ +pub struct UcbI2coa { pub ucgcen: bool, pub ucoaen: bool, pub i2coa0: u16, @@ -277,7 +276,7 @@ pub struct UcbIFG, UcbIFG_rd, UcbIFG_wr{ } } -reg_struct!{ +reg_struct! { pub struct UcxSpiCtw0, UcxSpiCtw0_rd, UcxSpiCtw0_wr{ flags{ pub ucckph: bool, @@ -296,10 +295,7 @@ pub struct UcxSpiCtw0, UcxSpiCtw0_rd, UcxSpiCtw0_wr{ } } - -pub trait EUsci : Steal { - -} +pub trait EUsci: Steal {} pub trait EUsciUart: EUsci { type Statw: UartUcxStatw; @@ -334,7 +330,7 @@ pub trait EUsciUart: EUsci { } pub trait EUsciI2C: EUsci { - type IfgOut : I2CUcbIfgOut; + type IfgOut: I2CUcbIfgOut; fn transmit_ack(&self); fn transmit_nack(&self); @@ -344,8 +340,8 @@ pub trait EUsciI2C: EUsci { fn uctxstt_rd(&self) -> bool; fn uctxstp_rd(&self) -> bool; - fn set_ucsla10(&self, bit:bool); - fn set_uctr(&self, bit:bool); + fn set_ucsla10(&self, bit: bool); + fn set_uctr(&self, bit: bool); fn txifg0_rd(&self) -> bool; fn rxifg0_rd(&self) -> bool; @@ -359,56 +355,56 @@ pub trait EUsciI2C: EUsci { // Modify only when UCSWRST = 1 fn ctw0_rd(&self) -> UcbCtlw0; - fn ctw0_wr(&self, reg:&UcbCtlw0); + fn ctw0_wr(&self, reg: &UcbCtlw0); // Modify only when UCSWRST = 1 fn ctw1_rd(&self) -> UcbCtlw1; - fn ctw1_wr(&self, reg:&UcbCtlw1); + fn ctw1_wr(&self, reg: &UcbCtlw1); // Modify only when UCSWRST = 1 fn brw_rd(&self) -> u16; - fn brw_wr(&self, val:u16); + fn brw_wr(&self, val: u16); fn statw_rd(&self) -> UcbStatw; // Modify only when UCSWRST = 1 fn tbcnt_rd(&self) -> u16; - fn tbcnt_wr(&self, val:u16); + fn tbcnt_wr(&self, val: u16); fn ucrxbuf_rd(&self) -> u8; fn uctxbuf_wr(&self, val: u8); // Modify only when UCSWRST = 1 // the which parameter is used to select one of the 4 registers - fn i2coa_rd(&self, which:u8) -> UcbI2coa; - fn i2coa_wr(&self, which:u8, reg:&UcbI2coa); + fn i2coa_rd(&self, which: u8) -> UcbI2coa; + fn i2coa_wr(&self, which: u8, reg: &UcbI2coa); fn addrx_rd(&self) -> u16; // Modify only when UCSWRST = 1 fn addmask_rd(&self) -> u16; - fn addmask_wr(&self, val:u16); + fn addmask_wr(&self, val: u16); fn i2csa_rd(&self) -> u16; - fn i2csa_wr(&self, val:u16); + fn i2csa_wr(&self, val: u16); fn ie_rd(&self) -> UcbIe; - fn ie_wr(&self, reg:&UcbIe); + fn ie_wr(&self, reg: &UcbIe); fn ifg_rd(&self) -> Self::IfgOut; - fn ifg_wr(&self, reg:&UcbIFG); + fn ifg_wr(&self, reg: &UcbIFG); fn iv_rd(&self) -> u16; } -pub trait EusciSPI : EUsci{ - type Statw : SpiStatw; +pub trait EusciSPI: EUsci { + type Statw: SpiStatw; fn ctw0_set_rst(&self); fn ctw0_clear_rst(&self); - fn ctw0_wr(&self, reg:&UcxSpiCtw0); + fn ctw0_wr(&self, reg: &UcxSpiCtw0); - fn brw_wr(&self, val:u16); + fn brw_wr(&self, val: u16); fn statw_rd(&self) -> Self::Statw; @@ -417,18 +413,17 @@ pub trait EusciSPI : EUsci{ fn rxbuf_rd(&self) -> u8; - fn txbuf_wr(&self, val:u8); + fn txbuf_wr(&self, val: u8); - fn transmit_interrupt_set(&self, bit:bool); + fn transmit_interrupt_set(&self, bit: bool); - fn receive_interrupt_set(&self, bit:bool); + fn receive_interrupt_set(&self, bit: bool); fn transmit_flag(&self) -> bool; fn receive_flag(&self) -> bool; fn iv_rd(&self) -> u16; - } pub trait UartUcxStatw { @@ -439,7 +434,7 @@ pub trait UartUcxStatw { fn ucbusy(&self) -> bool; } -pub trait SpiStatw{ +pub trait SpiStatw { fn uclisten(&self) -> bool; fn ucfe(&self) -> bool; fn ucoe(&self) -> bool; @@ -467,7 +462,6 @@ macro_rules! eusci_impl { ($intr_vec:ident, $EUsci:ident, $eusci:ident, $ucxctlw0:ident, $ucxctlw1:ident, $ucxbrw:ident, $ucxstatw:ident, $ucxrxbuf:ident, $ucxtxbuf:ident, $ucxie:ident, $ucxifg:ident, $ucxiv:ident, $StatwSpi:ty) => { - impl Steal for pac::$EUsci { #[inline(always)] unsafe fn steal() -> Self { @@ -475,59 +469,57 @@ macro_rules! eusci_impl { } } - impl EUsci for pac::$EUsci { - - } + impl EUsci for pac::$EUsci {} impl EusciSPI for pac::$EUsci { type Statw = $StatwSpi; #[inline(always)] - fn ctw0_set_rst(&self){ + fn ctw0_set_rst(&self) { unsafe { self.$ucxctlw0().set_bits(|w| w.ucswrst().set_bit()) } } #[inline(always)] - fn ctw0_clear_rst(&self){ + fn ctw0_clear_rst(&self) { unsafe { self.$ucxctlw0().clear_bits(|w| w.ucswrst().clear_bit()) } } #[inline(always)] - fn ctw0_wr(&self, reg:&UcxSpiCtw0){ + fn ctw0_wr(&self, reg: &UcxSpiCtw0) { self.$ucxctlw0().write(UcxSpiCtw0_wr! {reg}); } #[inline(always)] - fn brw_wr(&self, val:u16){ - self.$ucxbrw().write(|w| unsafe{w.bits(val)}); + fn brw_wr(&self, val: u16) { + self.$ucxbrw().write(|w| unsafe { w.bits(val) }); } #[inline(always)] - fn statw_rd(&self) -> Self::Statw{ + fn statw_rd(&self) -> Self::Statw { self.$ucxstatw().read() } #[inline(always)] - fn uclisten_set(&self){ + fn uclisten_set(&self) { unsafe { self.$ucxstatw().set_bits(|w| w.uclisten().set_bit()) } } #[inline(always)] - fn uclisten_clear(&self){ + fn uclisten_clear(&self) { unsafe { self.$ucxstatw().clear_bits(|w| w.uclisten().clear_bit()) } } #[inline(always)] - fn rxbuf_rd(&self) -> u8{ + fn rxbuf_rd(&self) -> u8 { self.$ucxrxbuf().read().ucrxbuf().bits() } #[inline(always)] - fn txbuf_wr(&self, val:u8){ - self.$ucxtxbuf().write(|w| unsafe{w.uctxbuf().bits(val)}); + fn txbuf_wr(&self, val: u8) { + self.$ucxtxbuf().write(|w| unsafe { w.uctxbuf().bits(val) }); } #[inline(always)] - fn transmit_interrupt_set(&self, bit: bool){ + fn transmit_interrupt_set(&self, bit: bool) { match bit { true => unsafe { self.$ucxie().set_bits(|w| w.uctxie().set_bit()) }, false => unsafe { self.$ucxie().clear_bits(|w| w.uctxie().clear_bit()) }, @@ -535,7 +527,7 @@ macro_rules! eusci_impl { } #[inline(always)] - fn receive_interrupt_set(&self, bit: bool){ + fn receive_interrupt_set(&self, bit: bool) { match bit { true => unsafe { self.$ucxie().set_bits(|w| w.ucrxie().set_bit()) }, false => unsafe { self.$ucxie().clear_bits(|w| w.ucrxie().clear_bit()) }, @@ -543,34 +535,34 @@ macro_rules! eusci_impl { } #[inline(always)] - fn transmit_flag(&self) -> bool{ + fn transmit_flag(&self) -> bool { self.$ucxifg().read().uctxifg().bit() } #[inline(always)] - fn receive_flag(&self) -> bool{ + fn receive_flag(&self) -> bool { self.$ucxifg().read().ucrxifg().bit() } #[inline(always)] - fn iv_rd(&self) -> u16{ + fn iv_rd(&self) -> u16 { self.$ucxiv().read().uciv().bits() } } - impl SpiStatw for $StatwSpi{ + impl SpiStatw for $StatwSpi { #[inline(always)] - fn uclisten(&self) -> bool{ + fn uclisten(&self) -> bool { self.uclisten().bit() } #[inline(always)] - fn ucfe(&self) -> bool{ + fn ucfe(&self) -> bool { self.ucfe().bit() } #[inline(always)] - fn ucoe(&self) -> bool{ + fn ucoe(&self) -> bool { self.ucoe().bit() } @@ -579,8 +571,7 @@ macro_rules! eusci_impl { // self.ucbusy().bit() // } } - - } + }; } macro_rules! eusci_a_impl { @@ -589,7 +580,6 @@ macro_rules! eusci_a_impl { $ucaxifg:ident, $ucaxiv:ident, $Statw:ty, $StatwSpi:ty, $ucaxctlw0spi:ident, $ucaxstatwspi:ident, $ucaxiespi:ident, $ucaxifgspi:ident) => { - eusci_impl!( $intr_vec, $EUsci, @@ -734,7 +724,6 @@ macro_rules! eusci_a_impl { self.ucbusy().bit() } } - }; } @@ -762,58 +751,56 @@ macro_rules! eusci_b_impl { $StatwSpi ); - - impl EUsciI2C for pac::$EUsci { type IfgOut = $Ifg; #[inline(always)] - fn ctw0_rd_rst(&self) -> bool{ + fn ctw0_rd_rst(&self) -> bool { self.$ucbxctlw0().read().ucswrst().bit() } #[inline(always)] - fn ctw0_set_rst(&self){ - unsafe { self.$ucbxctlw0().set_bits(|w| w.ucswrst().set_bit()) } + fn ctw0_set_rst(&self) { + unsafe { self.$ucbxctlw0().set_bits(|w| w.ucswrst().set_bit()) } } #[inline(always)] - fn ctw0_clear_rst(&self){ - unsafe { self.$ucbxctlw0().clear_bits(|w| w.ucswrst().clear_bit()) } + fn ctw0_clear_rst(&self) { + unsafe { self.$ucbxctlw0().clear_bits(|w| w.ucswrst().clear_bit()) } } #[inline(always)] - fn transmit_ack(&self){ - unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxack().set_bit()) } + fn transmit_ack(&self) { + unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxack().set_bit()) } } #[inline(always)] - fn transmit_nack(&self){ - unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxnack().set_bit()) } + fn transmit_nack(&self) { + unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxnack().set_bit()) } } #[inline(always)] - fn transmit_start(&self){ - unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxstt().set_bit()) } + fn transmit_start(&self) { + unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxstt().set_bit()) } } #[inline(always)] - fn transmit_stop(&self){ - unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxstp().set_bit()) } + fn transmit_stop(&self) { + unsafe { self.$ucbxctlw0().set_bits(|w| w.uctxstp().set_bit()) } } #[inline(always)] - fn uctxstt_rd(&self) -> bool{ + fn uctxstt_rd(&self) -> bool { self.$ucbxctlw0().read().uctxstp().bit() } #[inline(always)] - fn uctxstp_rd(&self) -> bool{ + fn uctxstp_rd(&self) -> bool { self.$ucbxctlw0().read().uctxstp().bit() } #[inline(always)] - fn set_ucsla10(&self, bit:bool){ + fn set_ucsla10(&self, bit: bool) { match bit { true => unsafe { self.$ucbxctlw0().set_bits(|w| w.ucsla10().set_bit()) }, false => unsafe { self.$ucbxctlw0().clear_bits(|w| w.ucsla10().clear_bit()) }, @@ -821,7 +808,7 @@ macro_rules! eusci_b_impl { } #[inline(always)] - fn set_uctr(&self, bit:bool){ + fn set_uctr(&self, bit: bool) { match bit { true => unsafe { self.$ucbxctlw0().set_bits(|w| w.uctr().set_bit()) }, false => unsafe { self.$ucbxctlw0().clear_bits(|w| w.uctr().clear_bit()) }, @@ -829,223 +816,223 @@ macro_rules! eusci_b_impl { } #[inline(always)] - fn txifg0_rd(&self) -> bool{ + fn txifg0_rd(&self) -> bool { self.$ucbxifg().read().uctxifg0().bit() } #[inline(always)] - fn rxifg0_rd(&self) -> bool{ + fn rxifg0_rd(&self) -> bool { self.$ucbxifg().read().ucrxifg0().bit() } #[inline(always)] - fn ctw0_rd(&self) -> UcbCtlw0{ + fn ctw0_rd(&self) -> UcbCtlw0 { let content = self.$ucbxctlw0().read(); UcbCtlw0_rd! {content} } #[inline(always)] - fn ctw0_wr(&self, reg:&UcbCtlw0){ + fn ctw0_wr(&self, reg: &UcbCtlw0) { self.$ucbxctlw0().write(UcbCtlw0_wr! {reg}); } #[inline(always)] - fn ctw1_rd(&self) -> UcbCtlw1{ + fn ctw1_rd(&self) -> UcbCtlw1 { let content = self.$ucbxctlw1.read(); UcbCtlw1_rd! {content} } #[inline(always)] - fn ctw1_wr(&self, reg:&UcbCtlw1){ + fn ctw1_wr(&self, reg: &UcbCtlw1) { self.$ucbxctlw1.write(UcbCtlw1_wr! {reg}); } #[inline(always)] - fn brw_rd(&self) -> u16{ + fn brw_rd(&self) -> u16 { self.$ucbxbrw().read().bits() } #[inline(always)] - fn brw_wr(&self, val:u16){ + fn brw_wr(&self, val: u16) { self.$ucbxbrw().write(|w| unsafe { w.bits(val) }); } #[inline(always)] - fn statw_rd(&self) -> UcbStatw{ + fn statw_rd(&self) -> UcbStatw { let content = self.$ucbxstatw().read(); UcbStatw_rd! {content} } #[inline(always)] - fn tbcnt_rd(&self) -> u16{ + fn tbcnt_rd(&self) -> u16 { self.$ucbxtbcnt.read().bits() } #[inline(always)] - fn tbcnt_wr(&self, val:u16){ + fn tbcnt_wr(&self, val: u16) { self.$ucbxtbcnt.write(|w| unsafe { w.bits(val) }); } #[inline(always)] - fn ucrxbuf_rd(&self) -> u8{ + fn ucrxbuf_rd(&self) -> u8 { self.$ucbxrxbuf().read().bits() as u8 } #[inline(always)] - fn uctxbuf_wr(&self, val: u8){ + fn uctxbuf_wr(&self, val: u8) { self.$ucbxtxbuf().write(|w| unsafe { w.bits(val as u16) }); } - fn i2coa_rd(&self, which:u8) -> UcbI2coa{ + fn i2coa_rd(&self, which: u8) -> UcbI2coa { match which { 1 => { let content = self.$ucbxi2coa1.read(); - UcbI2coa{ - ucgcen : false, - ucoaen : content.ucoaen().bit(), - i2coa0 : ::from(content.i2coa1().bits()), + UcbI2coa { + ucgcen: false, + ucoaen: content.ucoaen().bit(), + i2coa0: ::from(content.i2coa1().bits()), } } 2 => { let content = self.$ucbxi2coa2.read(); - UcbI2coa{ - ucgcen : false, - ucoaen : content.ucoaen().bit(), - i2coa0 : ::from(content.i2coa2().bits()), + UcbI2coa { + ucgcen: false, + ucoaen: content.ucoaen().bit(), + i2coa0: ::from(content.i2coa2().bits()), } } - 3=>{ + 3 => { let content = self.$ucbxi2coa3.read(); - UcbI2coa{ - ucgcen : false, - ucoaen : content.ucoaen().bit(), - i2coa0 : ::from(content.i2coa3().bits()), + UcbI2coa { + ucgcen: false, + ucoaen: content.ucoaen().bit(), + i2coa0: ::from(content.i2coa3().bits()), } } - _ =>{ + _ => { let content = self.$ucbxi2coa0.read(); - UcbI2coa{ - ucgcen : content.ucgcen().bit(), - ucoaen : content.ucoaen().bit(), - i2coa0 : ::from(content.i2coa0().bits()), + UcbI2coa { + ucgcen: content.ucgcen().bit(), + ucoaen: content.ucoaen().bit(), + i2coa0: ::from(content.i2coa0().bits()), } } } } - fn i2coa_wr(&self, which:u8, reg:&UcbI2coa){ + fn i2coa_wr(&self, which: u8, reg: &UcbI2coa) { match which { 1 => { self.$ucbxi2coa1.write(|w| unsafe { - w.ucoaen().bit(reg.ucoaen) - .i2coa1().bits(reg.i2coa0 as u16) + w.ucoaen().bit(reg.ucoaen).i2coa1().bits(reg.i2coa0 as u16) }); } 2 => { self.$ucbxi2coa2.write(|w| unsafe { - w.ucoaen().bit(reg.ucoaen) - .i2coa2().bits(reg.i2coa0 as u16) + w.ucoaen().bit(reg.ucoaen).i2coa2().bits(reg.i2coa0 as u16) }); } - 3=>{ + 3 => { self.$ucbxi2coa3.write(|w| unsafe { - w.ucoaen().bit(reg.ucoaen) - .i2coa3().bits(reg.i2coa0 as u16) + w.ucoaen().bit(reg.ucoaen).i2coa3().bits(reg.i2coa0 as u16) }); } - _ =>{ + _ => { self.$ucbxi2coa0.write(|w| unsafe { - w.ucgcen().bit(reg.ucgcen) - .ucoaen().bit(reg.ucoaen) - .i2coa0().bits(reg.i2coa0 as u16) + w.ucgcen() + .bit(reg.ucgcen) + .ucoaen() + .bit(reg.ucoaen) + .i2coa0() + .bits(reg.i2coa0 as u16) }); } } } #[inline(always)] - fn addrx_rd(&self) -> u16{ + fn addrx_rd(&self) -> u16 { self.$ucbxaddrx.read().bits() } #[inline(always)] - fn addmask_rd(&self) -> u16{ + fn addmask_rd(&self) -> u16 { self.$ucbxaddmask.read().bits() } #[inline(always)] - fn addmask_wr(&self, val:u16){ + fn addmask_wr(&self, val: u16) { self.$ucbxaddmask.write(|w| unsafe { w.bits(val) }); } #[inline(always)] - fn i2csa_rd(&self) -> u16{ + fn i2csa_rd(&self) -> u16 { self.$ucbxi2csa.read().bits() } #[inline(always)] - fn i2csa_wr(&self, val:u16){ + fn i2csa_wr(&self, val: u16) { self.$ucbxi2csa.write(|w| unsafe { w.bits(val) }); } #[inline(always)] - fn ie_rd(&self) -> UcbIe{ + fn ie_rd(&self) -> UcbIe { let content = self.$ucbxie().read(); UcbIe_rd! {content} } #[inline(always)] - fn ie_wr(&self, reg:&UcbIe){ + fn ie_wr(&self, reg: &UcbIe) { self.$ucbxie().write(UcbIe_wr! {reg}); } #[inline(always)] - fn ifg_rd(&self) -> Self::IfgOut{ + fn ifg_rd(&self) -> Self::IfgOut { self.$ucbxifg().read() } #[inline(always)] - fn ifg_wr(&self, reg:&UcbIFG){ + fn ifg_wr(&self, reg: &UcbIFG) { self.$ucbxifg().write(UcbIFG_wr! {reg}); } #[inline(always)] - fn iv_rd(&self) -> u16{ + fn iv_rd(&self) -> u16 { self.$ucbxiv().read().bits() } } - impl I2CUcbIfgOut for $Ifg{ + impl I2CUcbIfgOut for $Ifg { #[inline(always)] - fn ucbcntifg(&self) -> bool{ + fn ucbcntifg(&self) -> bool { self.ucbcntifg().bit() } #[inline(always)] - fn ucnackifg(&self) -> bool{ + fn ucnackifg(&self) -> bool { self.ucnackifg().bit() } #[inline(always)] - fn ucalifg(&self) -> bool{ + fn ucalifg(&self) -> bool { self.ucalifg().bit() } #[inline(always)] - fn ucstpifg(&self) -> bool{ + fn ucstpifg(&self) -> bool { self.ucstpifg().bit() } #[inline(always)] - fn ucsttifg(&self) -> bool{ + fn ucsttifg(&self) -> bool { self.ucsttifg().bit() } #[inline(always)] - fn uctxifg0(&self) -> bool{ + fn uctxifg0(&self) -> bool { self.uctxifg0().bit() } #[inline(always)] - fn ucrxifg0(&self) -> bool{ + fn ucrxifg0(&self) -> bool { self.ucrxifg0().bit() } } - }; + }; } eusci_a_impl!( @@ -1151,5 +1138,3 @@ eusci_b_impl!( ucb1ie_spi, ucb1ifg_spi ); - - diff --git a/src/hw_traits/gpio.rs b/src/hw_traits/gpio.rs index f35b72f..c49faca 100644 --- a/src/hw_traits/gpio.rs +++ b/src/hw_traits/gpio.rs @@ -61,7 +61,7 @@ macro_rules! reg_methods { #[inline(always)] fn $wr(&self, bits: u8) { - self.$reg.write(|w| unsafe {w.bits(bits) }); + self.$reg.write(|w| unsafe { w.bits(bits) }); } #[inline(always)] @@ -73,7 +73,7 @@ macro_rules! reg_methods { fn $clear(&self, bits: u8) { unsafe { self.$reg.clear_bits(|w| w.bits(bits)) } } - } + }; } macro_rules! gpio_impl { diff --git a/src/i2c.rs b/src/i2c.rs index 601feb4..6677e5a 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -9,18 +9,22 @@ //! eUSCI_B1: {SCL:P4.7, SDA:P4.6} //! -use core::marker::PhantomData; -use msp430::asm; -use crate::{ - gpio::{Alternate1, Pin, P1, P4, Pin2, Pin3, Pin6, Pin7}, - hal::blocking::i2c::{Read, Write, WriteRead, WriteIter, Operation, TransactionalIter, - SevenBitAddress, TenBitAddress, Transactional}, - hw_traits::eusci::{EUsciI2C, UcbCtlw0, UcbCtlw1, UcbI2coa, UcbIe, UcbIFG, Ucssel, - Ucmode, Ucglit, Ucclto, Ucastp}, - pac -}; use crate::clock::{Aclk, Smclk}; use crate::hw_traits::eusci::I2CUcbIfgOut; +use crate::{ + gpio::{Alternate1, Pin, Pin2, Pin3, Pin6, Pin7, P1, P4}, + hal::blocking::i2c::{ + Operation, Read, SevenBitAddress, TenBitAddress, Transactional, TransactionalIter, Write, + WriteIter, WriteRead, + }, + hw_traits::eusci::{ + EUsciI2C, Ucastp, UcbCtlw0, UcbCtlw1, UcbI2coa, UcbIFG, UcbIe, Ucclto, Ucglit, Ucmode, + Ucssel, + }, + pac, +}; +use core::marker::PhantomData; +use msp430::asm; /// Configure bus to use 7bit or 10bit I2C slave addressing mode #[derive(Clone, Copy)] @@ -94,7 +98,7 @@ pub struct I2CBusConfig { } /// Marks a usci capable of I2C communication -pub trait EUsciI2CBus : EUsciI2C{ +pub trait EUsciI2CBus: EUsciI2C { /// I2C SCL type ClockPin; /// I2C SDA @@ -139,13 +143,10 @@ impl_i2c_pin!(UsciB1SCLPin, P4, Pin7); pub struct UsciB1SDAPin; impl_i2c_pin!(UsciB1SDAPin, P4, Pin6); -impl I2CBusConfig{ +impl I2CBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode - pub fn new( - usci: USCI, - )->Self{ - - let ctlw0 = UcbCtlw0{ + pub fn new(usci: USCI) -> Self { + let ctlw0 = UcbCtlw0 { uca10: false, ucsla10: false, ucmm: false, @@ -161,7 +162,7 @@ impl I2CBusConfig{ ucssel: Ucssel::Smclk, }; - let ctlw1 = UcbCtlw1{ + let ctlw1 = UcbCtlw1 { ucetxint: false, ucstpnack: false, ucswack: false, @@ -170,31 +171,31 @@ impl I2CBusConfig{ ucglit: Ucglit::Max6_25ns, }; - let i2coa0 = UcbI2coa{ + let i2coa0 = UcbI2coa { ucgcen: false, ucoaen: false, i2coa0: 0, }; - let i2coa1 = UcbI2coa{ + let i2coa1 = UcbI2coa { ucgcen: false, ucoaen: false, i2coa0: 0, }; - let i2coa2 = UcbI2coa{ + let i2coa2 = UcbI2coa { ucgcen: false, ucoaen: false, i2coa0: 0, }; - let i2coa3 = UcbI2coa{ + let i2coa3 = UcbI2coa { ucgcen: false, ucoaen: false, i2coa0: 0, }; - let ie = UcbIe{ + let ie = UcbIe { ucbit9ie: false, uctxie3: false, ucrxie3: false, @@ -212,7 +213,7 @@ impl I2CBusConfig{ ucrxie0: false, }; - let ifg = UcbIFG{ + let ifg = UcbIFG { ucbit9ifg: false, uctxifg3: false, ucrxifg3: false, @@ -230,7 +231,7 @@ impl I2CBusConfig{ ucrxifg0: false, }; - I2CBusConfig{ + I2CBusConfig { usci, divisor: 1, ctlw0, @@ -246,33 +247,37 @@ impl I2CBusConfig{ /// Configures this peripheral to use smclk #[inline] - pub fn use_smclk(&mut self, _smclk:&Smclk, clk_divisor:u16){ + pub fn use_smclk(&mut self, _smclk: &Smclk, clk_divisor: u16) { self.ctlw0.ucssel = Ucssel::Smclk; self.divisor = clk_divisor; } /// Configures this peripheral to use aclk #[inline] - pub fn use_aclk(&mut self, _aclk:&Aclk, clk_divisor:u16){ + pub fn use_aclk(&mut self, _aclk: &Aclk, clk_divisor: u16) { self.ctlw0.ucssel = Ucssel::Aclk; self.divisor = clk_divisor; } /// Configures the glitch filter length for the SDA and SCL lines #[inline(always)] - pub fn set_deglitch_time(&mut self, deglitch_time:GlitchFilter){ + pub fn set_deglitch_time(&mut self, deglitch_time: GlitchFilter) { self.ctlw1.ucglit = deglitch_time.into(); } /// Performs hardware configuration and creates the SDL pin - pub fn sdl, D: Into>(&self, _scl: C, _sdl: D) -> SDL{ + pub fn sdl, D: Into>( + &self, + _scl: C, + _sdl: D, + ) -> SDL { self.configure(); SDL(PhantomData) } /// Performs hardware configuration #[inline] - fn configure(&self){ + fn configure(&self) { self.usci.ctw0_set_rst(); self.usci.ctw0_wr(&self.ctlw0); @@ -289,7 +294,6 @@ impl I2CBusConfig{ self.usci.ctw0_clear_rst(); } - } /// I2C data pin @@ -297,7 +301,7 @@ pub struct SDL(PhantomData); /// I2C transmit/receive errors #[derive(Clone, Copy)] -pub enum I2CErr{ +pub enum I2CErr { /// Function not implemented Unimplemented = 0, /// Address was never acknolwedged by slave @@ -306,67 +310,80 @@ pub enum I2CErr{ ArbitrationLost, } -impl SDL{ - +impl SDL { #[inline(always)] - fn set_addressing_mode(&mut self, mode:AddressingMode){ + fn set_addressing_mode(&mut self, mode: AddressingMode) { let usci = unsafe { USCI::steal() }; usci.set_ucsla10(mode.into()) } #[inline(always)] - fn set_transmission_mode(&mut self, mode:TransmissionMode){ + fn set_transmission_mode(&mut self, mode: TransmissionMode) { let usci = unsafe { USCI::steal() }; usci.set_uctr(mode.into()) } /// Blocking read - fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), I2CErr>{ + fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), I2CErr> { let usci = unsafe { USCI::steal() }; usci.i2csa_wr(address); usci.transmit_start(); - while usci.uctxstt_rd() {asm::nop();} + while usci.uctxstt_rd() { + asm::nop(); + } let mut ifg = usci.ifg_rd(); if ifg.ucnackifg() { usci.transmit_stop(); - while usci.uctxstp_rd() {asm::nop();} + while usci.uctxstp_rd() { + asm::nop(); + } return Err::<(), I2CErr>(I2CErr::GotNACK); } - for i in 0 .. buffer.len()-1 { + for i in 0..buffer.len() - 1 { while !ifg.ucrxifg0() { - ifg = usci.ifg_rd(); + ifg = usci.ifg_rd(); } buffer[i] = usci.ucrxbuf_rd(); } usci.transmit_stop(); - while !ifg.ucrxifg0() {ifg = usci.ifg_rd();} - buffer[buffer.len()-1] = usci.ucrxbuf_rd(); + while !ifg.ucrxifg0() { + ifg = usci.ifg_rd(); + } + buffer[buffer.len() - 1] = usci.ucrxbuf_rd(); - while usci.uctxstp_rd() {asm::nop();} + while usci.uctxstp_rd() { + asm::nop(); + } Ok(()) } /// Blocking write - fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), I2CErr>{ + fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), I2CErr> { let usci = unsafe { USCI::steal() }; usci.i2csa_wr(address); usci.transmit_start(); let mut ifg = usci.ifg_rd(); - while !ifg.uctxifg0() {ifg = usci.ifg_rd();} + while !ifg.uctxifg0() { + ifg = usci.ifg_rd(); + } - while usci.uctxstt_rd() {asm::nop();} + while usci.uctxstt_rd() { + asm::nop(); + } ifg = usci.ifg_rd(); if ifg.ucnackifg() { usci.transmit_stop(); - while usci.uctxstp_rd() {asm::nop();} + while usci.uctxstp_rd() { + asm::nop(); + } return Err::<(), I2CErr>(I2CErr::GotNACK); } @@ -378,173 +395,181 @@ impl SDL{ } if ifg.ucnackifg() { usci.transmit_stop(); - while usci.uctxstp_rd() {asm::nop();} + while usci.uctxstp_rd() { + asm::nop(); + } return Err::<(), I2CErr>(I2CErr::GotNACK); } } // usci.uctxbuf_wr(bytes[bytes.len()-1]); usci.transmit_stop(); - while usci.uctxstp_rd() {asm::nop();} + while usci.uctxstp_rd() { + asm::nop(); + } Ok(()) } fn write_iter(&mut self, _address: u16, _bytes: B) -> Result<(), I2CErr> - where - B: IntoIterator{ - + where + B: IntoIterator, + { Err(I2CErr::Unimplemented) } /// blocking write then blocking read - fn write_read( - &mut self, - address: u16, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), I2CErr>{ + fn write_read(&mut self, address: u16, bytes: &[u8], buffer: &mut [u8]) -> Result<(), I2CErr> { self.set_transmission_mode(TransmissionMode::Transmit); self.read(address, buffer)?; self.set_transmission_mode(TransmissionMode::Receive); self.write(address, bytes) } - fn exec<'a>(&mut self, _address: u16, _operations: &mut [Operation<'a>]) - -> Result<(), I2CErr>{ + fn exec<'a>(&mut self, _address: u16, _operations: &mut [Operation<'a>]) -> Result<(), I2CErr> { Err(I2CErr::Unimplemented) } fn exec_iter<'a, O>(&mut self, _address: u16, _operations: O) -> Result<(), I2CErr> - where - O: IntoIterator>{ + where + O: IntoIterator>, + { Err(I2CErr::Unimplemented) } } - -impl Read for SDL{ +impl Read for SDL { type Error = I2CErr; - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error>{ + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::SevenBit); self.set_transmission_mode(TransmissionMode::Receive); SDL::read(self, address as u16, buffer) } } -impl Read for SDL{ +impl Read for SDL { type Error = I2CErr; - fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error>{ + fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::TenBit); self.set_transmission_mode(TransmissionMode::Receive); SDL::read(self, address, buffer) } } -impl Write for SDL{ +impl Write for SDL { type Error = I2CErr; - fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error>{ + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::SevenBit); self.set_transmission_mode(TransmissionMode::Transmit); SDL::write(self, address as u16, bytes) } } -impl Write for SDL{ +impl Write for SDL { type Error = I2CErr; - fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error>{ + fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::TenBit); self.set_transmission_mode(TransmissionMode::Transmit); SDL::write(self, address, bytes) } } -impl WriteIter for SDL{ +impl WriteIter for SDL { type Error = I2CErr; fn write(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator{ - const {unimplemented!()} // See SDL::write_iter + where + B: IntoIterator, + { + const { unimplemented!() } // See SDL::write_iter self.set_addressing_mode(AddressingMode::SevenBit); self.set_transmission_mode(TransmissionMode::Transmit); SDL::write_iter(self, address as u16, bytes) } } -impl WriteIter for SDL{ +impl WriteIter for SDL { type Error = I2CErr; fn write(&mut self, address: u16, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator{ - const {unimplemented!()} // See SDL::write_iter + where + B: IntoIterator, + { + const { unimplemented!() } // See SDL::write_iter self.set_addressing_mode(AddressingMode::TenBit); self.set_transmission_mode(TransmissionMode::Transmit); SDL::write_iter(self, address, bytes) } } -impl WriteRead for SDL{ +impl WriteRead for SDL { type Error = I2CErr; fn write_read( &mut self, address: u8, bytes: &[u8], buffer: &mut [u8], - ) -> Result<(), Self::Error>{ + ) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::SevenBit); SDL::write_read(self, address as u16, bytes, buffer) } } -impl WriteRead for SDL{ +impl WriteRead for SDL { type Error = I2CErr; fn write_read( &mut self, address: u16, bytes: &[u8], buffer: &mut [u8], - ) -> Result<(), Self::Error>{ + ) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::TenBit); SDL::write_read(self, address, bytes, buffer) } } -impl Transactional for SDL{ +impl Transactional for SDL { type Error = I2CErr; - fn exec<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) - -> Result<(), Self::Error>{ - const {unimplemented!()} // See SDL::exec + fn exec<'a>( + &mut self, + address: u8, + operations: &mut [Operation<'a>], + ) -> Result<(), Self::Error> { + const { unimplemented!() } // See SDL::exec self.set_addressing_mode(AddressingMode::SevenBit); SDL::exec(self, address as u16, operations) } } -impl Transactional for SDL{ +impl Transactional for SDL { type Error = I2CErr; - fn exec<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) - -> Result<(), Self::Error>{ - const {unimplemented!()} // See SDL::exec + fn exec<'a>( + &mut self, + address: u16, + operations: &mut [Operation<'a>], + ) -> Result<(), Self::Error> { + const { unimplemented!() } // See SDL::exec self.set_addressing_mode(AddressingMode::TenBit); SDL::exec(self, address, operations) } } -impl TransactionalIter for SDL { +impl TransactionalIter for SDL { type Error = I2CErr; fn exec_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>{ - const {unimplemented!()} // See SDL::exec_iter + where + O: IntoIterator>, + { + const { unimplemented!() } // See SDL::exec_iter self.set_addressing_mode(AddressingMode::SevenBit); SDL::exec_iter(self, address as u16, operations) } } -impl TransactionalIter for SDL { +impl TransactionalIter for SDL { type Error = I2CErr; fn exec_iter<'a, O>(&mut self, address: u16, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>{ - const {unimplemented!()} // See SDL::exec_iter + where + O: IntoIterator>, + { + const { unimplemented!() } // See SDL::exec_iter self.set_addressing_mode(AddressingMode::TenBit); SDL::exec_iter(self, address, operations) } diff --git a/src/lib.rs b/src/lib.rs index 689e5f4..dcb663d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,9 +45,9 @@ pub mod watchdog; mod hw_traits; mod util; +pub mod delay; pub mod i2c; pub mod spi; -pub mod delay; -pub use msp430fr2355 as pac; pub use embedded_hal as hal; +pub use msp430fr2355 as pac; diff --git a/src/serial.rs b/src/serial.rs index 1ceddc6..1b392fe 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -8,7 +8,7 @@ use crate::clock::{Aclk, Clock, Smclk}; use crate::gpio::{Alternate1, Pin, Pin1, Pin2, Pin3, Pin5, Pin6, Pin7, P1, P4}; -use crate::hw_traits::eusci::{EUsciUart, UartUcxStatw, Ucssel, UcaCtlw0}; +use crate::hw_traits::eusci::{EUsciUart, UartUcxStatw, UcaCtlw0, Ucssel}; use core::marker::PhantomData; use embedded_hal::serial::{Read, Write}; use msp430fr2355 as pac; @@ -235,7 +235,9 @@ impl SerialConfig { loopback, usci, // Safety: .max(1) ensures baudrate is non-zero - state: NoClockSet { baudrate: unsafe {core::num::NonZeroU32::new_unchecked(baudrate.max(1))} }, + state: NoClockSet { + baudrate: unsafe { core::num::NonZeroU32::new_unchecked(baudrate.max(1)) }, + }, } } @@ -294,20 +296,24 @@ fn calculate_baud_config(clk_freq: u32, bps: core::num::NonZeroU32) -> BaudConfi // Ensure n stays within the 16 bit boundary // Safety: NonZeroU32 prevents div/0 // (clk_freq / bps).clamp(1, 0xFFFF) - let n = (unsafe{ clk_freq.checked_div(bps_u32).unwrap_unchecked()} ).clamp(1, 0xFFFF); + let n = (unsafe { clk_freq.checked_div(bps_u32).unwrap_unchecked() }).clamp(1, 0xFFFF); let brs = lookup_brs(clk_freq, bps); - if (n >= 16) && (bps_u32 < u32::MAX/16) { + if (n >= 16) && (bps_u32 < u32::MAX / 16) { let div = bps_u32 * 16; // n / 16, but more precise - // Safety: 0 < bps < u32::MAX/16 implies 0 < div < u32::MAX - let br = (unsafe {clk_freq.checked_div(div).unwrap_unchecked()}) as u16; - + // Safety: 0 < bps < u32::MAX/16 implies 0 < div < u32::MAX + let br = (unsafe { clk_freq.checked_div(div).unwrap_unchecked() }) as u16; + // same as n % 16, but more precise // Safety: div and bps non-zero due to above checks // (clk_freq % div) / bps - let brf = (unsafe{(clk_freq.checked_rem(div)).and_then(|a| a.checked_div(bps_u32)).unwrap_unchecked()}) as u8; + let brf = (unsafe { + (clk_freq.checked_rem(div)) + .and_then(|a| a.checked_div(bps_u32)) + .unwrap_unchecked() + }) as u8; BaudConfig { ucos16: true, br, @@ -325,32 +331,20 @@ fn calculate_baud_config(clk_freq: u32, bps: core::num::NonZeroU32) -> BaudConfi } const BRS_LOOKUP_LEN: usize = 36; // Data from table 22-4 of MSP430FR4xx and MSP430FR2xx family user's guide (Rev. I) -const BRS_LOOKUP_KEYS : [u16;BRS_LOOKUP_LEN] = - [ - 0x0000, 0x00d9, 0x0125, 0x0156, 0x019a, - 0x0201, 0x024a, 0x02ac, 0x036f, 0x038f, - 0x0401, 0x04cd, 0x0556, 0x05b8, 0x0601, - 0x0668, 0x06dc, 0x0701, 0x0801, 0x0925, - 0x099b, 0x0a02, 0x0a4b, 0x0aab, 0x0b34, - 0x0b6f, 0x0c01, 0x0c94, 0x0cce, 0x0d55, - 0x0d8b, 0x0db7, 0x0e00, 0x0e68, 0x0eac, - 0x0edc - ]; - -const BRS_LOOKUP_VALS : [u8;BRS_LOOKUP_LEN] = - [ - 0x00,0x01,0x02,0x04,0x08, - 0x10,0x20,0x11,0x21,0x22, - 0x44,0x25,0x49,0x4A,0x52, - 0x92,0x53,0x55,0xAA,0x6B, - 0xAD,0xB5,0xB6,0xD6,0xB7, - 0xBB,0xDD,0xED,0xEE,0xBF, - 0xDF,0xEF,0xF7,0xFB,0xFD, - 0xFE - ]; +const BRS_LOOKUP_KEYS: [u16; BRS_LOOKUP_LEN] = [ + 0x0000, 0x00d9, 0x0125, 0x0156, 0x019a, 0x0201, 0x024a, 0x02ac, 0x036f, 0x038f, 0x0401, 0x04cd, + 0x0556, 0x05b8, 0x0601, 0x0668, 0x06dc, 0x0701, 0x0801, 0x0925, 0x099b, 0x0a02, 0x0a4b, 0x0aab, + 0x0b34, 0x0b6f, 0x0c01, 0x0c94, 0x0cce, 0x0d55, 0x0d8b, 0x0db7, 0x0e00, 0x0e68, 0x0eac, 0x0edc, +]; + +const BRS_LOOKUP_VALS: [u8; BRS_LOOKUP_LEN] = [ + 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x11, 0x21, 0x22, 0x44, 0x25, 0x49, 0x4A, 0x52, 0x92, + 0x53, 0x55, 0xAA, 0x6B, 0xAD, 0xB5, 0xB6, 0xD6, 0xB7, 0xBB, 0xDD, 0xED, 0xEE, 0xBF, 0xDF, 0xEF, + 0xF7, 0xFB, 0xFD, 0xFE, +]; #[inline(always)] -fn binary_search_brs_table(res : u16) -> u8{ +fn binary_search_brs_table(res: u16) -> u8 { let mut low: usize = 0; let mut high: usize = BRS_LOOKUP_LEN - 1; // Safety: 0 <= low <= mid <= high < BRS_LOOKUP_LEN @@ -358,20 +352,20 @@ fn binary_search_brs_table(res : u16) -> u8{ let mid = (low + high) >> 1; let key = unsafe { *BRS_LOOKUP_KEYS.get_unchecked(mid) }; if res == key { - return unsafe {*BRS_LOOKUP_VALS.get_unchecked(mid)} - }else if high - low == 1{ - return if res < unsafe{ *BRS_LOOKUP_KEYS.get_unchecked(high) } { - unsafe {*BRS_LOOKUP_VALS.get_unchecked(low)} + return unsafe { *BRS_LOOKUP_VALS.get_unchecked(mid) }; + } else if high - low == 1 { + return if res < unsafe { *BRS_LOOKUP_KEYS.get_unchecked(high) } { + unsafe { *BRS_LOOKUP_VALS.get_unchecked(low) } } else { - unsafe {*BRS_LOOKUP_VALS.get_unchecked(high)} - } - }else if res > key{ + unsafe { *BRS_LOOKUP_VALS.get_unchecked(high) } + }; + } else if res > key { low = mid; - }else{ + } else { high = mid - 1; } } - return unsafe{ *BRS_LOOKUP_VALS.get_unchecked(low)}; + return unsafe { *BRS_LOOKUP_VALS.get_unchecked(low) }; } #[inline(always)] @@ -510,7 +504,7 @@ impl Rx { /// Reads raw value from Rx buffer with no checks for validity #[inline(always)] - pub fn read_no_check(&mut self) -> u8{ + pub fn read_no_check(&mut self) -> u8 { let usci = unsafe { USCI::steal() }; usci.rx_rd() } diff --git a/src/spi.rs b/src/spi.rs index 85e286e..249d54d 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -1,17 +1,17 @@ //! embedded_hal SPI implmentation +use crate::hal::spi::{Mode, Phase, Polarity}; +use crate::{ + clock::{Aclk, Smclk}, + gpio::{Alternate1, Pin, Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7, P1, P4}, + hw_traits::eusci::{EusciSPI, Ucmode, Ucssel, UcxSpiCtw0}, +}; use core::marker::PhantomData; use embedded_hal::spi::FullDuplex; use msp430fr2355 as pac; -use crate::hal::spi::{Mode, Polarity, Phase}; -use crate::{ - hw_traits::eusci::{EusciSPI, UcxSpiCtw0, Ucmode, Ucssel}, - gpio::{Alternate1, Pin, P1, P4, Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7}, - clock::{Smclk, Aclk}, -}; use nb::Error::WouldBlock; /// Marks a eUSCI capable of SPI communication (in this case, all euscis do) -pub trait EUsciSPIBus : EusciSPI{ +pub trait EUsciSPIBus: EusciSPI { /// Master In Slave Out (refered to as SOMI in datasheet) type MISO; /// Master Out Slave In (refered to as SIMO in datasheet) @@ -26,32 +26,32 @@ impl EUsciSPIBus for pac::E_USCI_A0 { type MISO = UsciA0MISOPin; type MOSI = UsciA0MOSIPin; type SCLK = UsciA0SCLKPin; - type STE = UsciA0STEPin; + type STE = UsciA0STEPin; } impl EUsciSPIBus for pac::E_USCI_A1 { type MISO = UsciA1MISOPin; type MOSI = UsciA1MOSIPin; type SCLK = UsciA1SCLKPin; - type STE = UsciA1STEPin; + type STE = UsciA1STEPin; } impl EUsciSPIBus for pac::E_USCI_B0 { type MISO = UsciB0MISOPin; type MOSI = UsciB0MOSIPin; type SCLK = UsciB0SCLKPin; - type STE = UsciB0STEPin; + type STE = UsciB0STEPin; } impl EUsciSPIBus for pac::E_USCI_B1 { type MISO = UsciB1MISOPin; type MOSI = UsciB1MOSIPin; type SCLK = UsciB1SCLKPin; - type STE = UsciB1STEPin; + type STE = UsciB1STEPin; } // Allows a GPIO pin to be converted into an SPI object -macro_rules! impl_spi_pin{ +macro_rules! impl_spi_pin { ($struct_name: ident, $port: ty, $pin: ty) => { impl From>> for $struct_name { #[inline(always)] @@ -59,7 +59,7 @@ macro_rules! impl_spi_pin{ $struct_name } } - } + }; } /// SPI MISO pin for eUSCI A0 @@ -126,7 +126,7 @@ pub struct UsciB1STEPin; impl_spi_pin!(UsciB1STEPin, P4, Pin4); /// Struct used to configure a SPI bus -pub struct SPIBusConfig{ +pub struct SPIBusConfig { usci: USCI, prescaler: u16, @@ -134,10 +134,10 @@ pub struct SPIBusConfig{ ctlw0: UcxSpiCtw0, } -impl SPIBusConfig{ +impl SPIBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in SPI mode - pub fn new(usci: USCI, mode:Mode, msb_first:bool)->Self{ - let ctlw0 = UcxSpiCtw0{ + pub fn new(usci: USCI, mode: Mode, msb_first: bool) -> Self { + let ctlw0 = UcxSpiCtw0 { ucckph: match mode.phase { Phase::CaptureOnFirstTransition => true, Phase::CaptureOnSecondTransition => false, @@ -152,43 +152,50 @@ impl SPIBusConfig{ ucsync: true, ucstem: true, ucswrst: true, - ucmode: Ucmode::FourPinSPI0, + ucmode: Ucmode::FourPinSPI0, ucssel: Ucssel::Uclk, }; - - SPIBusConfig{ + SPIBusConfig { usci, prescaler: 0, - ctlw0 + ctlw0, } } /// Configures this peripheral to use smclk #[inline] - pub fn use_smclk(&mut self, _smclk:&Smclk, clk_divisor:u16){ + pub fn use_smclk(&mut self, _smclk: &Smclk, clk_divisor: u16) { self.ctlw0.ucssel = Ucssel::Smclk; self.prescaler = clk_divisor; } /// Configures this peripheral to use aclk #[inline] - pub fn use_aclk(&mut self, _aclk:&Aclk, clk_divisor:u16){ + pub fn use_aclk(&mut self, _aclk: &Aclk, clk_divisor: u16) { self.ctlw0.ucssel = Ucssel::Aclk; self.prescaler = clk_divisor; } /// Performs hardware configuration and creates an SPI bus - pub fn spi_pins - , SI: Into, CLK: Into, STE: Into> - (&mut self, _miso: SO, _mosi: SI, _sclk: CLK, _cs : STE) - -> SPIPins{ + pub fn spi_pins< + SO: Into, + SI: Into, + CLK: Into, + STE: Into, + >( + &mut self, + _miso: SO, + _mosi: SI, + _sclk: CLK, + _cs: STE, + ) -> SPIPins { self.configure_hw(); SPIPins(PhantomData) } #[inline] - fn configure_hw(&self){ + fn configure_hw(&self) { self.usci.ctw0_set_rst(); self.usci.ctw0_wr(&self.ctlw0); @@ -200,14 +207,12 @@ impl SPIBusConfig{ self.usci.transmit_interrupt_set(false); self.usci.receive_interrupt_set(false); } - } /// Represents a group of pins configured for SPI communication pub struct SPIPins(PhantomData); - -impl SPIPins{ +impl SPIPins { /// Enable or disable Rx interrupts, which fire when a byte is ready to be read #[inline(always)] pub fn rx_interrupt_set(&mut self, flag: bool) { @@ -224,7 +229,7 @@ impl SPIPins{ /// Writes raw value to Tx buffer with no checks for validity #[inline(always)] - pub fn write_no_check(&mut self, val: u8){ + pub fn write_no_check(&mut self, val: u8) { let usci = unsafe { USCI::steal() }; usci.txbuf_wr(val) } @@ -232,28 +237,28 @@ impl SPIPins{ /// SPI transmit/receive errors #[derive(Clone, Copy)] -pub enum SPIErr{ +pub enum SPIErr { /// Function not implemented Unimplemented = 0, } -impl FullDuplex for SPIPins{ +impl FullDuplex for SPIPins { type Error = SPIErr; - fn read(&mut self) -> nb::Result{ - let usci = unsafe{USCI::steal()}; + fn read(&mut self) -> nb::Result { + let usci = unsafe { USCI::steal() }; if usci.receive_flag() { Ok(usci.rxbuf_rd()) - }else{ + } else { Err(WouldBlock) } } - fn send(&mut self, word: u8) -> nb::Result<(), Self::Error>{ - let usci = unsafe{USCI::steal()}; + fn send(&mut self, word: u8) -> nb::Result<(), Self::Error> { + let usci = unsafe { USCI::steal() }; if usci.transmit_flag() { usci.txbuf_wr(word); Ok(()) - }else{ + } else { Err(WouldBlock) } } From 0580fa331ba9ef1ac0c276c04a04bae409c2a990 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 00:20:28 +1300 Subject: [PATCH 49/82] Make Delay::new() private and Delay clonable Also remove multiple implementations of DelayMs, as the compiler can't automatically infer the type for literals when there's multiple implementations. --- examples/blinky.rs | 2 +- src/delay.rs | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/blinky.rs b/examples/blinky.rs index 30c1304..4941639 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -36,7 +36,7 @@ fn main() -> ! { // `toggle()` returns a `Result` because of embedded_hal, but the result is always `Ok` with MSP430 GPIO. // Rust complains about unused Results, so we 'use' the Result by calling .ok() p1_0.toggle().ok(); - delay.delay_ms(500_u16); + delay.delay_ms(500); } } diff --git a/src/delay.rs b/src/delay.rs index 645321c..6e5f273 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -3,13 +3,14 @@ use crate::hal::blocking::delay::DelayMs; use msp430::asm; /// Delay provider struct +#[derive(Copy, Clone)] pub struct Delay { nops_per_ms: u16, } impl Delay { /// Create a new delay object - pub fn new(freq: u32) -> Self { + pub(crate) fn new(freq: u32) -> Self { // ~21 nops needed per 2^20 Hz to delay 1 ms let nops: u32 = 210 * (freq >> 20); Delay { @@ -18,14 +19,8 @@ impl Delay { } } -impl DelayMs for Delay { - #[inline] - fn delay_ms(&mut self, ms: u8) { - self.delay_ms(ms as u16); - } -} - impl DelayMs for Delay { + #[inline] fn delay_ms(&mut self, ms: u16) { for _ in 0..ms { for _ in 0..self.nops_per_ms { From 3370c1161c71361063925df11508ad86beec0438 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 00:30:06 +1300 Subject: [PATCH 50/82] Add typestate to Adc to prevent incorrect configurations Also tidy up AdcConfig by replacing .modify() with .write(). An enabled ADC can't be enabled again, and a Disabled ADC can't be disabled again. Only a disabled ADC can have it's pin configured. A helper function exists on Adc to reset and configure the pin in one function call. --- src/adc.rs | 166 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 100 insertions(+), 66 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 5cf8fd9..76e763f 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -6,7 +6,7 @@ //! use crate::gpio::*; -use core::convert::Infallible; +use core::{convert::Infallible, marker::PhantomData}; use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; @@ -146,7 +146,7 @@ impl SamplingRate { // Pins corresponding to an ADC channel. Pin types can have `::channel()` called on them to get their ADC channel index. macro_rules! impl_adc_channel { ($port: ty, $pin: ty, $channel: literal ) => { - impl Channel> for Pin<$port, $pin, Alternate3>> { + impl Channel> for Pin<$port, $pin, Alternate3>> { type ID = u8; fn channel() -> Self::ID { @@ -170,9 +170,10 @@ impl_adc_channel!(P5, Pin2, 10); impl_adc_channel!(P5, Pin3, 11); /// Controls the onboard ADC -pub struct Adc { +pub struct Adc { adc_reg: ADC, is_waiting: bool, + _phantom: PhantomData, } /// Configuration object for an ADC. @@ -216,106 +217,141 @@ impl AdcConfig { } /// Applies this ADC configuration to hardware registers, and returns an ADC. - pub fn config_hw(self) -> Adc { - let adc_reg = self.adc; - unsafe { - adc_reg.adcctl0.clear_bits(|w| { - w.adcenc() - .clear_bit() - .adcon() - .clear_bit() - .adcsc() - .clear_bit() - }); - } + pub fn config_hw(self) -> Adc { + let mut adc_reg = self.adc; + // Disable the ADC before we set the other bits. Some can only be set while the ADC is disabled. + disable_adc_reg(&mut adc_reg); + let adcsht = self.sample_time.adcsht(); - adc_reg.adcctl0.modify(|_, w| w.adcsht().bits(adcsht)); + adc_reg.adcctl0.write(|w| w.adcsht().bits(adcsht)); let adcssel = self.clock_source.adcssel(); - adc_reg - .adcctl1 - .modify(|_, w| w.adcssel().bits(adcssel).adcshp().adcshp_1()); - let adcdiv = self.clock_divider.adcdiv(); - adc_reg.adcctl1.modify(|_, w| w.adcdiv().bits(adcdiv)); + adc_reg.adcctl1.write(|w| {w + .adcssel().bits(adcssel) + .adcshp().adcshp_1() + .adcdiv().bits(adcdiv) + }); let adcpdiv = self.predivider.adcpdiv(); - adc_reg.adcctl2.modify(|_, w| w.adcpdiv().bits(adcpdiv)); - let adcres = self.resolution.adcres(); - adc_reg.adcctl2.modify(|_, w| w.adcres().bits(adcres)); - let adcsr = self.sampling_rate.adcsr(); - adc_reg.adcctl2.modify(|_, w| w.adcsr().bit(adcsr)); + adc_reg.adcctl2.write(|w| { w + .adcpdiv().bits(adcpdiv) + .adcres().bits(adcres) + .adcsr().bit(adcsr) + }); Adc { adc_reg, is_waiting: false, + _phantom: PhantomData, } } } +/// Typestate for an enabled ADC. It is ready to begin conversions. The ADC must be disabled before it can be reconfigured. +pub struct Enabled; +/// Typestate for a disabled ADC. It is ready to be configured. The ADC must be enabled before it can begin conversions. +pub struct Disabled; +/// Typestate trait for the current state of the ADC. The ADC may be either `Enabled` or `Disabled.` +pub trait AdcState: private::Sealed {} +impl AdcState for Enabled {} +impl AdcState for Disabled {} + +// Seal this supertrait so users can still refer to AdcState, but they can't add other implementations besides `Enabled` and `Disabled`. +mod private { + pub trait Sealed {} + impl Sealed for super::Enabled {} + impl Sealed for super::Disabled {} +} -impl Adc { - /// Create an ADC instance with a default configuration. - /// - /// If you need a custom configuration you should construct an ADC using AdcConfig instead. - pub fn new(adc: ADC) -> Adc { - Adc { - adc_reg: adc, - is_waiting: false, - } +impl Adc { + /// Whether the ADC is currently sampling or converting. + pub fn adc_is_busy(&self) -> bool { + self.adc_reg.adcctl1.read().adcbusy().bit_is_set() + } + + /// Gets the latest ADC conversion result. + pub fn adc_get_result(&self) -> u16 { + self.adc_reg.adcmem0.read().bits() } +} +impl Adc { /// Enables this ADC, ready to start a conversion. - pub fn adc_enable(&mut self) { - unsafe { - self.adc_reg.adcctl0.set_bits(|w| w.adcon().set_bit()); + pub fn into_enabled(mut self) -> Adc { + enable_adc_reg(&mut self.adc_reg); + Adc { + adc_reg: self.adc_reg, + is_waiting: self.is_waiting, + _phantom: PhantomData, } } + /// Selects which pin to sample. + pub fn set_pin(&mut self, _pin: &PIN) + where + PIN: Channel, + { + self.adc_reg + .adcmctl0 + .modify(|_, w| w.adcinch().bits(PIN::channel())); + } +} + +impl Adc { /// Disables this ADC to save power. - pub fn adc_disable(&mut self) { - unsafe { - self.adc_reg - .adcctl0 - .clear_bits(|w| w.adcon().clear_bit().adcenc().clear_bit()); + pub fn into_disabled(mut self) -> Adc { + disable_adc_reg(&mut self.adc_reg); + Adc { + adc_reg: self.adc_reg, + is_waiting: self.is_waiting, + _phantom: PhantomData, } } /// Starts an ADC conversion. - pub fn adc_start_conversion(&mut self) { + pub fn start_conversion(&mut self) { unsafe { - self.adc_reg - .adcctl0 - .set_bits(|w| w.adcenc().set_bit().adcsc().set_bit()); + self.adc_reg.adcctl0.set_bits(|w| w + .adcenc().set_bit() + .adcsc().set_bit()); } } - /// Whether the ADC is currently sampling or converting. - pub fn adc_is_busy(&self) -> bool { - self.adc_reg.adcctl1.read().adcbusy().bit_is_set() - } - - /// Gets the latest ADC conversion result. - pub fn adc_get_result(&self) -> u16 { - self.adc_reg.adcmem0.read().bits() - } - - /// Selects which pin to sample. Can only be modified when the ADC is not busy. - pub fn adc_set_pin(&mut self, _pin: &PIN) + // We use this fn to implement OneShot, as otherwise we'd need to consume the Adc to change state. + /// Disables the ADC, configures the input channel, then re-enables the ADC. + pub fn reset_and_set_pin(&mut self, _pin: &PIN) where - PIN: Channel, ID = u8>, + PIN: Channel, { + disable_adc_reg(&mut self.adc_reg); self.adc_reg .adcmctl0 .modify(|_, w| w.adcinch().bits(PIN::channel())); + + enable_adc_reg(&mut self.adc_reg); + } +} + +fn disable_adc_reg(adc: &mut ADC) { + unsafe { + adc.adcctl0.clear_bits(|w| w + .adcon().clear_bit() + .adcenc().clear_bit()); + } +} + +fn enable_adc_reg(adc: &mut ADC) { + unsafe { + adc.adcctl0.set_bits(|w| w.adcon().set_bit()); } } -impl OneShot, WORD, PIN> for Adc +impl OneShot, WORD, PIN> for Adc where WORD: From, - PIN: Channel, ID = u8>, + PIN: Channel, ID = u8>, { type Error = Infallible; // Only returns WouldBlock @@ -332,11 +368,9 @@ where } } - self.adc_disable(); - self.adc_set_pin(pin); - self.adc_enable(); + self.reset_and_set_pin(pin); - self.adc_start_conversion(); + self.start_conversion(); self.is_waiting = true; Err(nb::Error::WouldBlock) } From 9db37448155297af70cf80a5555878b7896187d4 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 01:11:55 +1300 Subject: [PATCH 51/82] Use Int / NonZero instead of unwrap_unchecked Also use const evaluation to unwrap known-safe NonZero's at compile time instead of using new_unchecked(). --- src/serial.rs | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index 1b392fe..1f3bebb 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -10,6 +10,7 @@ use crate::clock::{Aclk, Clock, Smclk}; use crate::gpio::{Alternate1, Pin, Pin1, Pin2, Pin3, Pin5, Pin6, Pin7, P1, P4}; use crate::hw_traits::eusci::{EUsciUart, UartUcxStatw, UcaCtlw0, Ucssel}; use core::marker::PhantomData; +use core::num::NonZeroU32; use embedded_hal::serial::{Read, Write}; use msp430fr2355 as pac; @@ -178,7 +179,7 @@ impl_serial_pin!(UsciA1RxPin, P4, Pin2); /// Typestate for a serial interface with an unspecified clock source pub struct NoClockSet { - baudrate: core::num::NonZeroU32, + baudrate: NonZeroU32, } /// Typestate for a serial interface with a specified clock source @@ -236,7 +237,7 @@ impl SerialConfig { usci, // Safety: .max(1) ensures baudrate is non-zero state: NoClockSet { - baudrate: unsafe { core::num::NonZeroU32::new_unchecked(baudrate.max(1)) }, + baudrate: NonZeroU32::new(baudrate).unwrap_or( const {NonZeroU32::new(1).unwrap()} ), }, } } @@ -291,29 +292,21 @@ struct BaudConfig { } #[inline] -fn calculate_baud_config(clk_freq: u32, bps: core::num::NonZeroU32) -> BaudConfig { - let bps_u32: u32 = bps.into(); +fn calculate_baud_config(clk_freq: u32, bps: NonZeroU32) -> BaudConfig { // Ensure n stays within the 16 bit boundary - // Safety: NonZeroU32 prevents div/0 - // (clk_freq / bps).clamp(1, 0xFFFF) - let n = (unsafe { clk_freq.checked_div(bps_u32).unwrap_unchecked() }).clamp(1, 0xFFFF); + let n = (clk_freq / bps).clamp(1, 0xFFFF); let brs = lookup_brs(clk_freq, bps); - if (n >= 16) && (bps_u32 < u32::MAX / 16) { - let div = bps_u32 * 16; + if (n >= 16) && (bps.get() < u32::MAX / 16) { + // div = bps * 16 + let div = bps.saturating_mul(const { NonZeroU32::new(16).unwrap() }); + // n / 16, but more precise - // Safety: 0 < bps < u32::MAX/16 implies 0 < div < u32::MAX - let br = (unsafe { clk_freq.checked_div(div).unwrap_unchecked() }) as u16; + let br = (clk_freq / div) as u16; // same as n % 16, but more precise - // Safety: div and bps non-zero due to above checks - // (clk_freq % div) / bps - let brf = (unsafe { - (clk_freq.checked_rem(div)) - .and_then(|a| a.checked_div(bps_u32)) - .unwrap_unchecked() - }) as u8; + let brf = ((clk_freq % div) / bps) as u8; BaudConfig { ucos16: true, br, @@ -369,14 +362,10 @@ fn binary_search_brs_table(res: u16) -> u8 { } #[inline(always)] -fn lookup_brs(clk_freq: u32, bps: core::num::NonZeroU32) -> u8 { - // Safety: NonZeroU32 prevents /div0 - // clk_freq % bps - let modulo = unsafe { clk_freq.checked_rem(bps.into()).unwrap_unchecked() }; +fn lookup_brs(clk_freq: u32, bps: NonZeroU32) -> u8 { + let modulo = clk_freq % bps; // 12 fractional bit fixed point result - // (modulo << 12) / bps - let fixed_point_result: u32 = - unsafe { (modulo << 12).checked_div(bps.into()).unwrap_unchecked() }; + let fixed_point_result: u32 = (modulo << 12) / bps; // Throw away the upper bits since the fractional part is all we care about binary_search_brs_table(fixed_point_result as u16) From f90f7b8f81efad1cc8febc42f5705f4431b72488 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 01:25:53 +1300 Subject: [PATCH 52/82] Add read_no_check() and write_no_check() --- src/serial.rs | 7 +++++++ src/spi.rs | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/src/serial.rs b/src/serial.rs index 1f3bebb..cf5c1a1 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -497,6 +497,13 @@ impl Rx { let usci = unsafe { USCI::steal() }; usci.rx_rd() } + + #[inline(always)] + /// Writes a byte into the Tx buffer with no checks for validity + pub fn write_no_check(&mut self, data: u8) { + let usci = unsafe { USCI::steal() }; + usci.tx_wr(data); + } } /// Serial receive errors diff --git a/src/spi.rs b/src/spi.rs index 249d54d..2a3a028 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -178,6 +178,7 @@ impl SPIBusConfig { } /// Performs hardware configuration and creates an SPI bus + #[inline(always)] pub fn spi_pins< SO: Into, SI: Into, @@ -233,6 +234,13 @@ impl SPIPins { let usci = unsafe { USCI::steal() }; usci.txbuf_wr(val) } + + #[inline(always)] + /// Reads a raw value from the Rx buffer with no checks for validity + pub fn read_no_check(&mut self) -> u8 { + let usci = unsafe { USCI::steal() }; + usci.rxbuf_rd() + } } /// SPI transmit/receive errors From 5dd4db08dfa15ea1faf10d78fdf0ca10db8b378e Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 01:53:05 +1300 Subject: [PATCH 53/82] Remove From impls and unused associated fns --- src/hw_traits/eusci.rs | 99 ++++++++---------------------------------- src/i2c.rs | 7 ++- 2 files changed, 25 insertions(+), 81 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 726766a..7c9e877 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -57,17 +57,6 @@ pub enum Ucssel { Aclk = 1, Smclk = 2, } -impl From for Ucssel { - #[inline(always)] - fn from(u: u8) -> Self { - match u { - 0 => Ucssel::Uclk, - 1 => Ucssel::Aclk, - 2 | 3 => Ucssel::Smclk, - _ => unreachable!(), - } - } -} #[derive(Copy, Clone)] pub enum Ucmode { @@ -76,18 +65,6 @@ pub enum Ucmode { FourPinSPI0 = 2, I2CMode = 3, } -impl From for Ucmode { - #[inline(always)] - fn from(u: u8) -> Self { - match u { - 0 => Ucmode::ThreePinSPI, - 1 => Ucmode::FourPinSPI1, - 2 => Ucmode::FourPinSPI0, - 3 => Ucmode::I2CMode, - _ => unreachable!(), - } - } -} #[derive(Copy, Clone)] pub enum Ucglit { @@ -96,18 +73,6 @@ pub enum Ucglit { Max12_5ns = 2, Max6_25ns = 3, } -impl From for Ucglit { - #[inline(always)] - fn from(u: u8) -> Self { - match u { - 0 => Ucglit::Max50ns, - 1 => Ucglit::Max25ns, - 2 => Ucglit::Max12_5ns, - 3 => Ucglit::Max6_25ns, - _ => unreachable!(), - } - } -} /// Clock low timeout select #[derive(Copy, Clone)] @@ -121,18 +86,6 @@ pub enum Ucclto { /// = 165000 MODCLK cycles (approximately 34 ms) Ucclto11b = 3, } -impl From for Ucclto { - #[inline(always)] - fn from(u: u8) -> Self { - match u { - 0 => Ucclto::Ucclto00b, - 1 => Ucclto::Ucclto01b, - 2 => Ucclto::Ucclto10b, - 3 => Ucclto::Ucclto11b, - _ => unreachable!(), - } - } -} /// Automatic STOP condition generation. In slave mode, only settings 00b and 01b /// are available. @@ -149,18 +102,6 @@ pub enum Ucastp { /// threshold. Ucastp10b = 2, } -impl From for Ucastp { - #[inline(always)] - fn from(u: u8) -> Self { - match u { - 0 => Ucastp::Ucastp00b, - 1 => Ucastp::Ucastp01b, - 2 => Ucastp::Ucastp10b, - 3 => panic!(), // 0b11 is reserved, but the register could still feasibly have this value. What to do? - _ => unreachable!(), - } - } -} pub struct UcaCtlw0 { pub ucpen: bool, @@ -194,9 +135,6 @@ pub struct UcbCtlw0, UcbCtlw0_rd, UcbCtlw0_wr { } } -// from_u8!(UCCLTO_A); -// from_u8!(UCASTP_A); - reg_struct! { pub struct UcbCtlw1, UcbCtlw1_rd, UcbCtlw1_wr { flags{ @@ -354,11 +292,11 @@ pub trait EUsciI2C: EUsci { fn ctw0_clear_rst(&self); // Modify only when UCSWRST = 1 - fn ctw0_rd(&self) -> UcbCtlw0; + // fn ctw0_rd(&self) -> UcbCtlw0; fn ctw0_wr(&self, reg: &UcbCtlw0); // Modify only when UCSWRST = 1 - fn ctw1_rd(&self) -> UcbCtlw1; + // fn ctw1_rd(&self) -> UcbCtlw1; fn ctw1_wr(&self, reg: &UcbCtlw1); // Modify only when UCSWRST = 1 @@ -388,7 +326,7 @@ pub trait EUsciI2C: EUsci { fn i2csa_rd(&self) -> u16; fn i2csa_wr(&self, val: u16); - fn ie_rd(&self) -> UcbIe; + // fn ie_rd(&self) -> UcbIe; fn ie_wr(&self, reg: &UcbIe); fn ifg_rd(&self) -> Self::IfgOut; @@ -825,22 +763,22 @@ macro_rules! eusci_b_impl { self.$ucbxifg().read().ucrxifg0().bit() } - #[inline(always)] - fn ctw0_rd(&self) -> UcbCtlw0 { - let content = self.$ucbxctlw0().read(); - UcbCtlw0_rd! {content} - } + // #[inline(always)] + // fn ctw0_rd(&self) -> UcbCtlw0 { + // let content = self.$ucbxctlw0().read(); + // UcbCtlw0_rd! {content} + // } #[inline(always)] fn ctw0_wr(&self, reg: &UcbCtlw0) { self.$ucbxctlw0().write(UcbCtlw0_wr! {reg}); } - #[inline(always)] - fn ctw1_rd(&self) -> UcbCtlw1 { - let content = self.$ucbxctlw1.read(); - UcbCtlw1_rd! {content} - } + // #[inline(always)] + // fn ctw1_rd(&self) -> UcbCtlw1 { + // let content = self.$ucbxctlw1.read(); + // UcbCtlw1_rd! {content} + // } #[inline(always)] fn ctw1_wr(&self, reg: &UcbCtlw1) { @@ -970,11 +908,12 @@ macro_rules! eusci_b_impl { self.$ucbxi2csa.write(|w| unsafe { w.bits(val) }); } - #[inline(always)] - fn ie_rd(&self) -> UcbIe { - let content = self.$ucbxie().read(); - UcbIe_rd! {content} - } + // #[inline(always)] + // fn ie_rd(&self) -> UcbIe { + // let content = self.$ucbxie().read(); + // UcbIe_rd! {content} + // } + #[inline(always)] fn ie_wr(&self, reg: &UcbIe) { self.$ucbxie().write(UcbIe_wr! {reg}); diff --git a/src/i2c.rs b/src/i2c.rs index 6677e5a..749b696 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -77,7 +77,12 @@ pub enum GlitchFilter { impl From for Ucglit { fn from(f: GlitchFilter) -> Ucglit { - Ucglit::from(f as u8) + match f { + GlitchFilter::Max50ns => Ucglit::Max50ns, + GlitchFilter::Max25ns => Ucglit::Max25ns, + GlitchFilter::Max12_5ns => Ucglit::Max12_5ns, + GlitchFilter::Max6_25ns => Ucglit::Max6_25ns, + } } } From f21c5ebddb5c2e1bd854517e9589be1f5bce664c Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 07:58:21 +1300 Subject: [PATCH 54/82] Add default docs to ADC configuration Move adc register from field of AdcConfig to parameter of `config_hw` function to derive Default for AdcConfig --- src/adc.rs | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 76e763f..9958870 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -11,10 +11,14 @@ use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; /// How many ADCCLK cycles the ADC's sample-and-hold stage will last for. +/// +/// Default: 8 cycles +#[derive(Default, Copy, Clone, PartialEq, Eq)] pub enum SampleTime { /// Sample for 4 ADCCLK cycles _4 = 0b0000, /// Sample for 8 ADCCLK cycles + #[default] _8 = 0b0001, /// Sample for 16 ADCCLK cycles _16 = 0b0010, @@ -48,8 +52,12 @@ impl SampleTime { } /// How much the ADC input clock will be divided by after being divided by the predivider +/// +/// Default: Divide by 1 +#[derive(Default, Copy, Clone, PartialEq, Eq)] pub enum ClockDivider { /// Divide the input clock by 1 + #[default] _1 = 0b000, /// Divide the input clock by 2 _2 = 0b001, @@ -75,8 +83,12 @@ impl ClockDivider { } /// Which clock source the ADC uses as input. +/// +/// Default: MODCLK +#[derive(Default, Copy, Clone, PartialEq, Eq)] pub enum ClockSource { /// Use MODCLK as the ADC input clock + #[default] MODCLK = 0b00, /// Use ACLK as the ADC input clock ACLK = 0b01, @@ -92,8 +104,12 @@ impl ClockSource { } /// How much the ADC input clock will be divided by prior to being divided by the ADC clock divider +/// +/// Default: Divide by 1 +#[derive(Default, Copy, Clone, PartialEq, Eq)] pub enum Predivider { /// Divide the input clock by 1 + #[default] _1 = 0b00, /// Divide the input clock by 4 _4 = 0b01, @@ -109,10 +125,14 @@ impl Predivider { } /// The output resolution of the ADC conversion. Also determines how many ADCCLK cycles the conversion step takes. +/// +/// Default: 10-bit resolution +#[derive(Default, Copy, Clone, PartialEq, Eq)] pub enum Resolution { /// 8-bit ADC conversion result. The conversion step takes 10 ADCCLK cycles. _8BIT = 0b00, /// 10-bit ADC conversion result. The conversion step takes 12 ADCCLK cycles. + #[default] _10BIT = 0b01, /// 12-bit ADC conversion result. The conversion step takes 14 ADCCLK cycles. _12BIT = 0b10, @@ -126,10 +146,14 @@ impl Resolution { } /// Selects the drive capability of the ADC reference buffer, which can increase the maximum sampling speed at the cost of increased power draw. +/// +/// Default: 200ksps +#[derive(Default, Copy, Clone, PartialEq, Eq)] pub enum SamplingRate { /// Maximum of 50 ksps. Lower power usage. _50KSPS, /// Maximum of 200 ksps. Higher power usage. + #[default] _200KSPS, } @@ -177,9 +201,15 @@ pub struct Adc { } /// Configuration object for an ADC. +/// +/// The default configuration is based on the default register values: +/// - MODCLK as input clock +/// - Predivider = 1 and clock divider = 1 +/// - 10-bit resolution +/// - 8 cycle sample time +/// - Max 200 ksps sample rate +#[derive(Default, Clone, PartialEq, Eq)] pub struct AdcConfig { - /// ADC register - pub adc: ADC, /// Which clock source the ADC takes as an input. This clock will first be divided by the predivider, then the clock divider, to generate ADCCLK. pub clock_source: ClockSource, /// How much the input clock is divided by, after the predivider. @@ -195,9 +225,8 @@ pub struct AdcConfig { } impl AdcConfig { - /// Creates an ADC configuration + /// Creates an ADC configuration. A default implementation is also available through `::default()` pub fn new( - adc: ADC, clock_source: ClockSource, clock_divider: ClockDivider, predivider: Predivider, @@ -206,7 +235,6 @@ impl AdcConfig { sample_time: SampleTime, ) -> AdcConfig { AdcConfig { - adc, clock_source, clock_divider, predivider, @@ -217,8 +245,7 @@ impl AdcConfig { } /// Applies this ADC configuration to hardware registers, and returns an ADC. - pub fn config_hw(self) -> Adc { - let mut adc_reg = self.adc; + pub fn config_hw(self, mut adc_reg: ADC) -> Adc { // Disable the ADC before we set the other bits. Some can only be set while the ADC is disabled. disable_adc_reg(&mut adc_reg); From 6404f79c6a5323e799897b1cab547342359eeb42 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 08:23:47 +1300 Subject: [PATCH 55/82] Remove obsolete Eusci trait --- src/hw_traits/eusci.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 7c9e877..13cd5ae 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -233,9 +233,7 @@ pub struct UcxSpiCtw0, UcxSpiCtw0_rd, UcxSpiCtw0_wr{ } } -pub trait EUsci: Steal {} - -pub trait EUsciUart: EUsci { +pub trait EUsciUart: Steal { type Statw: UartUcxStatw; fn ctl0_reset(&self); @@ -267,7 +265,7 @@ pub trait EUsciUart: EUsci { fn rxie_clear(&self); } -pub trait EUsciI2C: EUsci { +pub trait EUsciI2C: Steal { type IfgOut: I2CUcbIfgOut; fn transmit_ack(&self); @@ -334,7 +332,7 @@ pub trait EUsciI2C: EUsci { fn iv_rd(&self) -> u16; } -pub trait EusciSPI: EUsci { +pub trait EusciSPI: Steal { type Statw: SpiStatw; fn ctw0_set_rst(&self); @@ -407,8 +405,6 @@ macro_rules! eusci_impl { } } - impl EUsci for pac::$EUsci {} - impl EusciSPI for pac::$EUsci { type Statw = $StatwSpi; From ffb5b08f118fb80a2995a00804ad62d3d3e3ee91 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 14:30:34 +1300 Subject: [PATCH 56/82] Revert binary search UCBRSx lookup, use linear instead --- src/serial.rs | 92 ++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index cf5c1a1..be61ef9 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -322,53 +322,61 @@ fn calculate_baud_config(clk_freq: u32, bps: NonZeroU32) -> BaudConfig { } } } -const BRS_LOOKUP_LEN: usize = 36; -// Data from table 22-4 of MSP430FR4xx and MSP430FR2xx family user's guide (Rev. I) -const BRS_LOOKUP_KEYS: [u16; BRS_LOOKUP_LEN] = [ - 0x0000, 0x00d9, 0x0125, 0x0156, 0x019a, 0x0201, 0x024a, 0x02ac, 0x036f, 0x038f, 0x0401, 0x04cd, - 0x0556, 0x05b8, 0x0601, 0x0668, 0x06dc, 0x0701, 0x0801, 0x0925, 0x099b, 0x0a02, 0x0a4b, 0x0aab, - 0x0b34, 0x0b6f, 0x0c01, 0x0c94, 0x0cce, 0x0d55, 0x0d8b, 0x0db7, 0x0e00, 0x0e68, 0x0eac, 0x0edc, -]; - -const BRS_LOOKUP_VALS: [u8; BRS_LOOKUP_LEN] = [ - 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x11, 0x21, 0x22, 0x44, 0x25, 0x49, 0x4A, 0x52, 0x92, - 0x53, 0x55, 0xAA, 0x6B, 0xAD, 0xB5, 0xB6, 0xD6, 0xB7, 0xBB, 0xDD, 0xED, 0xEE, 0xBF, 0xDF, 0xEF, - 0xF7, 0xFB, 0xFD, 0xFE, -]; - -#[inline(always)] -fn binary_search_brs_table(res: u16) -> u8 { - let mut low: usize = 0; - let mut high: usize = BRS_LOOKUP_LEN - 1; - // Safety: 0 <= low <= mid <= high < BRS_LOOKUP_LEN - while low != high { - let mid = (low + high) >> 1; - let key = unsafe { *BRS_LOOKUP_KEYS.get_unchecked(mid) }; - if res == key { - return unsafe { *BRS_LOOKUP_VALS.get_unchecked(mid) }; - } else if high - low == 1 { - return if res < unsafe { *BRS_LOOKUP_KEYS.get_unchecked(high) } { - unsafe { *BRS_LOOKUP_VALS.get_unchecked(low) } - } else { - unsafe { *BRS_LOOKUP_VALS.get_unchecked(high) } - }; - } else if res > key { - low = mid; - } else { - high = mid - 1; - } - } - return unsafe { *BRS_LOOKUP_VALS.get_unchecked(low) }; -} #[inline(always)] fn lookup_brs(clk_freq: u32, bps: NonZeroU32) -> u8 { + // bps is between [1, u32::MAX] + // clk_freq is between [0, u32::MAX] + + // modulo = clk_freq % bps => modulo is between [0, bps-1] let modulo = clk_freq % bps; - // 12 fractional bit fixed point result - let fixed_point_result: u32 = (modulo << 12) / bps; - // Throw away the upper bits since the fractional part is all we care about - binary_search_brs_table(fixed_point_result as u16) + // fraction = modulo * 10_000 / bps, so within [0, ((bps-1) * 10_000) / bps]. + // To prove upper bound we note `(bps-1)/bps` is largest when bps == u32::MAX: + // (4_294_967_294 * 10_000) / 4_294_967_295 = 42_949_672_940_000 / 4_294_967_295 = 9999.99... truncated to 9_999 because integer division + // So fraction is within [0, 9999] + let fraction_as_ten_thousandths = + ((modulo as u64 * 10_000) / core::num::NonZeroU64::from(bps)) as u16; + + // See Table 22-4 from MSP430FR4xx and MSP430FR2xx family user's guide (Rev. I) + match fraction_as_ten_thousandths { + 0..529 => 0x00, + 529..715 => 0x01, + 715..835 => 0x02, + 835..1001 => 0x04, + 1001..1252 => 0x08, + 1252..1430 => 0x10, + 1430..1670 => 0x20, + 1670..2147 => 0x11, + 2147..2224 => 0x21, + 2224..2503 => 0x22, + 2503..3000 => 0x44, + 3000..3335 => 0x25, + 3335..3575 => 0x49, + 3575..3753 => 0x4A, + 3753..4003 => 0x52, + 4003..4286 => 0x92, + 4286..4378 => 0x53, + 4378..5002 => 0x55, + 5002..5715 => 0xAA, + 5715..6003 => 0x6B, + 6003..6254 => 0xAD, + 6254..6432 => 0xB5, + 6432..6667 => 0xB6, + 6667..7001 => 0xD6, + 7001..7147 => 0xB7, + 7147..7503 => 0xBB, + 7503..7861 => 0xDD, + 7861..8004 => 0xED, + 8004..8333 => 0xEE, + 8333..8464 => 0xBF, + 8464..8572 => 0xDF, + 8572..8751 => 0xEF, + 8751..9004 => 0xF7, + 9004..9170 => 0xFB, + 9170..9288 => 0xFD, + 9288.. => 0xFE, + } } impl SerialConfig { From 2f87605b0374d10c6629eb40268bd540055e262a Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 15:44:00 +1300 Subject: [PATCH 57/82] Revert ADC typestate, add typestate to AdcConfig --- src/adc.rs | 207 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 119 insertions(+), 88 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 9958870..4794804 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -5,7 +5,7 @@ //! P1.0 - P1.7 (channels 0 to 7), P5.0 - P5.3 (channels 8 to 11) //! -use crate::gpio::*; +use crate::{clock::{Aclk, Smclk}, gpio::*}; use core::{convert::Infallible, marker::PhantomData}; use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; @@ -82,18 +82,15 @@ impl ClockDivider { } } -/// Which clock source the ADC uses as input. -/// -/// Default: MODCLK #[derive(Default, Copy, Clone, PartialEq, Eq)] -pub enum ClockSource { +enum ClockSource { /// Use MODCLK as the ADC input clock #[default] - MODCLK = 0b00, + ModClk = 0b00, /// Use ACLK as the ADC input clock - ACLK = 0b01, + AClk = 0b01, /// Use SMCLK as the ADC input clock - SMCLK = 0b10, + SmClk = 0b10, } impl ClockSource { @@ -170,7 +167,7 @@ impl SamplingRate { // Pins corresponding to an ADC channel. Pin types can have `::channel()` called on them to get their ADC channel index. macro_rules! impl_adc_channel { ($port: ty, $pin: ty, $channel: literal ) => { - impl Channel> for Pin<$port, $pin, Alternate3>> { + impl Channel for Pin<$port, $pin, Alternate3>> { type ID = u8; fn channel() -> Self::ID { @@ -193,25 +190,54 @@ impl_adc_channel!(P5, Pin1, 9); impl_adc_channel!(P5, Pin2, 10); impl_adc_channel!(P5, Pin3, 11); -/// Controls the onboard ADC -pub struct Adc { - adc_reg: ADC, - is_waiting: bool, - _phantom: PhantomData, +/// Typestate trait for an ADC configuration. An ADC must have a clock selected before it can be configured +pub trait ClockConfigState : private::Sealed {} +/// Typestate for an ADC configuration with no clock source selected +pub struct NotConfigured; +/// Typestate for an ADC configuration with SMCLK selected as the clock source +pub struct UsingSmclk; +/// Typestate for an ADC configuration with ACLK selected as the clock source +pub struct UsingAclk; +/// Typestate for an ADC configuration with MODCLK selected as the clock source +pub struct UsingModclk; + +impl ClockConfigState for NotConfigured {} +impl ClockConfigState for UsingSmclk {} +impl ClockConfigState for UsingAclk {} +impl ClockConfigState for UsingModclk {} + +trait ClockConfigured : ClockConfigState { + fn clock_source() -> ClockSource; +} +impl ClockConfigured for UsingSmclk { + fn clock_source() -> ClockSource { ClockSource::SmClk } +} +impl ClockConfigured for UsingAclk { + fn clock_source() -> ClockSource { ClockSource::AClk } +} +impl ClockConfigured for UsingModclk { + fn clock_source() -> ClockSource { ClockSource::ModClk } +} + +// Seal the supertrait so users can still refer to the traits, but they can't add other implementations. +mod private { + pub trait Sealed {} + // AdcConfig states + impl Sealed for super::NotConfigured {} + impl Sealed for super::UsingSmclk {} + impl Sealed for super::UsingAclk {} + impl Sealed for super::UsingModclk {} } /// Configuration object for an ADC. /// /// The default configuration is based on the default register values: -/// - MODCLK as input clock /// - Predivider = 1 and clock divider = 1 /// - 10-bit resolution /// - 8 cycle sample time /// - Max 200 ksps sample rate -#[derive(Default, Clone, PartialEq, Eq)] -pub struct AdcConfig { - /// Which clock source the ADC takes as an input. This clock will first be divided by the predivider, then the clock divider, to generate ADCCLK. - pub clock_source: ClockSource, +#[derive(Clone, PartialEq, Eq)] +pub struct AdcConfig { /// How much the input clock is divided by, after the predivider. pub clock_divider: ClockDivider, /// How much the input clock is initially divided by, before the clock divider. @@ -222,37 +248,82 @@ pub struct AdcConfig { pub sampling_rate: SamplingRate, /// Determines the number of ADCCLK cycles the sampling time takes. pub sample_time: SampleTime, + _phantom: PhantomData, +} + +// Only implement Default for NotConfigured +impl Default for AdcConfig { + fn default() -> Self { + Self { + clock_divider: Default::default(), + predivider: Default::default(), + resolution: Default::default(), + sampling_rate: Default::default(), + sample_time: Default::default(), + _phantom: Default::default() } + } } -impl AdcConfig { +impl AdcConfig { /// Creates an ADC configuration. A default implementation is also available through `::default()` pub fn new( - clock_source: ClockSource, clock_divider: ClockDivider, predivider: Predivider, resolution: Resolution, sampling_rate: SamplingRate, sample_time: SampleTime, - ) -> AdcConfig { + ) -> AdcConfig { AdcConfig { - clock_source, clock_divider, predivider, resolution, sampling_rate, sample_time, + _phantom: PhantomData, } } - + /// Configure the ADC to use SMCLK + pub fn use_smclk(self, _smclk: &Smclk) -> AdcConfig{ + AdcConfig { + clock_divider: self.clock_divider, + predivider: self.predivider, + resolution: self.resolution, + sampling_rate: self.sampling_rate, + sample_time: self.sample_time, + _phantom: PhantomData } + } + /// Configure the ADC to use ACLK + pub fn use_aclk(self, _aclk: &Aclk) -> AdcConfig{ + AdcConfig { + clock_divider: self.clock_divider, + predivider: self.predivider, + resolution: self.resolution, + sampling_rate: self.sampling_rate, + sample_time: self.sample_time, + _phantom: PhantomData } + } + /// Configure the ADC to use MODCLK + pub fn use_modclk(self) -> AdcConfig{ + AdcConfig { + clock_divider: self.clock_divider, + predivider: self.predivider, + resolution: self.resolution, + sampling_rate: self.sampling_rate, + sample_time: self.sample_time, + _phantom: PhantomData } + } +} +#[allow(private_bounds)] +impl AdcConfig { /// Applies this ADC configuration to hardware registers, and returns an ADC. - pub fn config_hw(self, mut adc_reg: ADC) -> Adc { + pub fn configure(self, mut adc_reg: ADC) -> Adc { // Disable the ADC before we set the other bits. Some can only be set while the ADC is disabled. disable_adc_reg(&mut adc_reg); let adcsht = self.sample_time.adcsht(); adc_reg.adcctl0.write(|w| w.adcsht().bits(adcsht)); - let adcssel = self.clock_source.adcssel(); + let adcssel = CLOCK::clock_source().adcssel(); let adcdiv = self.clock_divider.adcdiv(); adc_reg.adcctl1.write(|w| {w .adcssel().bits(adcssel) @@ -272,27 +343,17 @@ impl AdcConfig { Adc { adc_reg, is_waiting: false, - _phantom: PhantomData, } } } -/// Typestate for an enabled ADC. It is ready to begin conversions. The ADC must be disabled before it can be reconfigured. -pub struct Enabled; -/// Typestate for a disabled ADC. It is ready to be configured. The ADC must be enabled before it can begin conversions. -pub struct Disabled; -/// Typestate trait for the current state of the ADC. The ADC may be either `Enabled` or `Disabled.` -pub trait AdcState: private::Sealed {} -impl AdcState for Enabled {} -impl AdcState for Disabled {} - -// Seal this supertrait so users can still refer to AdcState, but they can't add other implementations besides `Enabled` and `Disabled`. -mod private { - pub trait Sealed {} - impl Sealed for super::Enabled {} - impl Sealed for super::Disabled {} + +/// Controls the onboard ADC +pub struct Adc { + adc_reg: ADC, + is_waiting: bool, } -impl Adc { +impl Adc { /// Whether the ADC is currently sampling or converting. pub fn adc_is_busy(&self) -> bool { self.adc_reg.adcctl1.read().adcbusy().bit_is_set() @@ -302,21 +363,21 @@ impl Adc { pub fn adc_get_result(&self) -> u16 { self.adc_reg.adcmem0.read().bits() } -} -impl Adc { - /// Enables this ADC, ready to start a conversion. - pub fn into_enabled(mut self) -> Adc { - enable_adc_reg(&mut self.adc_reg); - Adc { - adc_reg: self.adc_reg, - is_waiting: self.is_waiting, - _phantom: PhantomData, + /// Enables this ADC, ready to start conversions. + pub fn enable(&mut self) { + unsafe { + self.adc_reg.adcctl0.set_bits(|w| w.adcon().set_bit()); } } + /// Disables this ADC to save power. + pub fn disable(&mut self) { + disable_adc_reg(&mut self.adc_reg); + } + /// Selects which pin to sample. - pub fn set_pin(&mut self, _pin: &PIN) + fn set_pin(&mut self, _pin: &PIN) where PIN: Channel, { @@ -324,21 +385,9 @@ impl Adc { .adcmctl0 .modify(|_, w| w.adcinch().bits(PIN::channel())); } -} - -impl Adc { - /// Disables this ADC to save power. - pub fn into_disabled(mut self) -> Adc { - disable_adc_reg(&mut self.adc_reg); - Adc { - adc_reg: self.adc_reg, - is_waiting: self.is_waiting, - _phantom: PhantomData, - } - } /// Starts an ADC conversion. - pub fn start_conversion(&mut self) { + fn start_conversion(&mut self) { unsafe { self.adc_reg.adcctl0.set_bits(|w| w .adcenc().set_bit() @@ -346,19 +395,6 @@ impl Adc { } } - // We use this fn to implement OneShot, as otherwise we'd need to consume the Adc to change state. - /// Disables the ADC, configures the input channel, then re-enables the ADC. - pub fn reset_and_set_pin(&mut self, _pin: &PIN) - where - PIN: Channel, - { - disable_adc_reg(&mut self.adc_reg); - self.adc_reg - .adcmctl0 - .modify(|_, w| w.adcinch().bits(PIN::channel())); - - enable_adc_reg(&mut self.adc_reg); - } } fn disable_adc_reg(adc: &mut ADC) { @@ -369,16 +405,10 @@ fn disable_adc_reg(adc: &mut ADC) { } } -fn enable_adc_reg(adc: &mut ADC) { - unsafe { - adc.adcctl0.set_bits(|w| w.adcon().set_bit()); - } -} - -impl OneShot, WORD, PIN> for Adc +impl OneShot for Adc where WORD: From, - PIN: Channel, ID = u8>, + PIN: Channel, { type Error = Infallible; // Only returns WouldBlock @@ -394,8 +424,9 @@ where return Ok(self.adc_get_result().into()); } } - - self.reset_and_set_pin(pin); + self.disable(); + self.set_pin(pin); + self.enable(); self.start_conversion(); self.is_waiting = true; From 88423495b66003b7d59a6febd1f418778719b3f3 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 16:29:46 +1300 Subject: [PATCH 58/82] Split interrupt set/clear fns Also mark read/write_no_check fns unsafe --- src/hw_traits/eusci.rs | 32 ++++++++++++++++++++------------ src/spi.rs | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 13cd5ae..df5ba5c 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -351,9 +351,13 @@ pub trait EusciSPI: Steal { fn txbuf_wr(&self, val: u8); - fn transmit_interrupt_set(&self, bit: bool); + fn set_transmit_interrupt(&self); - fn receive_interrupt_set(&self, bit: bool); + fn clear_transmit_interrupt(&self); + + fn set_receive_interrupt(&self); + + fn clear_receive_interrupt(&self); fn transmit_flag(&self) -> bool; @@ -453,19 +457,23 @@ macro_rules! eusci_impl { } #[inline(always)] - fn transmit_interrupt_set(&self, bit: bool) { - match bit { - true => unsafe { self.$ucxie().set_bits(|w| w.uctxie().set_bit()) }, - false => unsafe { self.$ucxie().clear_bits(|w| w.uctxie().clear_bit()) }, - } + fn set_transmit_interrupt(&self) { + unsafe { self.$ucxie().set_bits(|w| w.uctxie().set_bit()) } } #[inline(always)] - fn receive_interrupt_set(&self, bit: bool) { - match bit { - true => unsafe { self.$ucxie().set_bits(|w| w.ucrxie().set_bit()) }, - false => unsafe { self.$ucxie().clear_bits(|w| w.ucrxie().clear_bit()) }, - } + fn clear_transmit_interrupt(&self) { + unsafe { self.$ucxie().clear_bits(|w| w.uctxie().clear_bit()) } + } + + #[inline(always)] + fn set_receive_interrupt(&self) { + unsafe { self.$ucxie().set_bits(|w| w.ucrxie().set_bit()) } + } + + #[inline(always)] + fn clear_receive_interrupt(&self) { + unsafe { self.$ucxie().clear_bits(|w| w.ucrxie().clear_bit()) } } #[inline(always)] diff --git a/src/spi.rs b/src/spi.rs index 2a3a028..49edcfa 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -205,8 +205,8 @@ impl SPIBusConfig { self.usci.ctw0_clear_rst(); - self.usci.transmit_interrupt_set(false); - self.usci.receive_interrupt_set(false); + self.usci.clear_transmit_interrupt(); + self.usci.clear_receive_interrupt(); } } @@ -214,30 +214,48 @@ impl SPIBusConfig { pub struct SPIPins(PhantomData); impl SPIPins { - /// Enable or disable Rx interrupts, which fire when a byte is ready to be read + /// Enable Rx interrupts, which fire when a byte is ready to be read #[inline(always)] - pub fn rx_interrupt_set(&mut self, flag: bool) { + pub fn set_rx_interrupt(&mut self) { let usci = unsafe { USCI::steal() }; - usci.receive_interrupt_set(flag); + usci.set_receive_interrupt(); } - /// Enable or disable Tx interrupts, which fire when the transmit buffer is empty + /// Disable Rx interrupts, which fire when a byte is ready to be read #[inline(always)] - pub fn tx_interrupt_set(&mut self, flag: bool) { + pub fn clear_rx_interrupt(&mut self) { let usci = unsafe { USCI::steal() }; - usci.transmit_interrupt_set(flag); + usci.clear_receive_interrupt(); + } + + /// Enable Tx interrupts, which fire when the transmit buffer is empty + #[inline(always)] + pub fn set_tx_interrupt(&mut self) { + let usci = unsafe { USCI::steal() }; + usci.set_transmit_interrupt(); + } + + /// Disable Tx interrupts, which fire when the transmit buffer is empty + #[inline(always)] + pub fn clear_tx_interrupt(&mut self) { + let usci = unsafe { USCI::steal() }; + usci.clear_transmit_interrupt(); } /// Writes raw value to Tx buffer with no checks for validity + /// # Safety + /// May clobber unsent data still in the buffer #[inline(always)] - pub fn write_no_check(&mut self, val: u8) { + pub unsafe fn write_no_check(&mut self, val: u8) { let usci = unsafe { USCI::steal() }; usci.txbuf_wr(val) } #[inline(always)] /// Reads a raw value from the Rx buffer with no checks for validity - pub fn read_no_check(&mut self) -> u8 { + /// # Safety + /// May read duplicate data + pub unsafe fn read_no_check(&mut self) -> u8 { let usci = unsafe { USCI::steal() }; usci.rxbuf_rd() } From 14d8d73f8e442e2645caf30e09c90f876e1a406c Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 16:31:19 +1300 Subject: [PATCH 59/82] Remove stubbed implementations --- src/i2c.rs | 99 +----------------------------------------------------- 1 file changed, 1 insertion(+), 98 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index 749b696..020793f 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -14,8 +14,7 @@ use crate::hw_traits::eusci::I2CUcbIfgOut; use crate::{ gpio::{Alternate1, Pin, Pin2, Pin3, Pin6, Pin7, P1, P4}, hal::blocking::i2c::{ - Operation, Read, SevenBitAddress, TenBitAddress, Transactional, TransactionalIter, Write, - WriteIter, WriteRead, + Read, SevenBitAddress, TenBitAddress, Write, WriteRead, }, hw_traits::eusci::{ EUsciI2C, Ucastp, UcbCtlw0, UcbCtlw1, UcbI2coa, UcbIFG, UcbIe, Ucclto, Ucglit, Ucmode, @@ -307,8 +306,6 @@ pub struct SDL(PhantomData); /// I2C transmit/receive errors #[derive(Clone, Copy)] pub enum I2CErr { - /// Function not implemented - Unimplemented = 0, /// Address was never acknolwedged by slave GotNACK, /// Device lost arbitration @@ -415,13 +412,6 @@ impl SDL { Ok(()) } - fn write_iter(&mut self, _address: u16, _bytes: B) -> Result<(), I2CErr> - where - B: IntoIterator, - { - Err(I2CErr::Unimplemented) - } - /// blocking write then blocking read fn write_read(&mut self, address: u16, bytes: &[u8], buffer: &mut [u8]) -> Result<(), I2CErr> { self.set_transmission_mode(TransmissionMode::Transmit); @@ -429,17 +419,6 @@ impl SDL { self.set_transmission_mode(TransmissionMode::Receive); self.write(address, bytes) } - - fn exec<'a>(&mut self, _address: u16, _operations: &mut [Operation<'a>]) -> Result<(), I2CErr> { - Err(I2CErr::Unimplemented) - } - - fn exec_iter<'a, O>(&mut self, _address: u16, _operations: O) -> Result<(), I2CErr> - where - O: IntoIterator>, - { - Err(I2CErr::Unimplemented) - } } impl Read for SDL { @@ -478,32 +457,6 @@ impl Write for SDL { } } -impl WriteIter for SDL { - type Error = I2CErr; - fn write(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - const { unimplemented!() } // See SDL::write_iter - self.set_addressing_mode(AddressingMode::SevenBit); - self.set_transmission_mode(TransmissionMode::Transmit); - SDL::write_iter(self, address as u16, bytes) - } -} - -impl WriteIter for SDL { - type Error = I2CErr; - fn write(&mut self, address: u16, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - const { unimplemented!() } // See SDL::write_iter - self.set_addressing_mode(AddressingMode::TenBit); - self.set_transmission_mode(TransmissionMode::Transmit); - SDL::write_iter(self, address, bytes) - } -} - impl WriteRead for SDL { type Error = I2CErr; fn write_read( @@ -529,53 +482,3 @@ impl WriteRead for SDL { SDL::write_read(self, address, bytes, buffer) } } - -impl Transactional for SDL { - type Error = I2CErr; - fn exec<'a>( - &mut self, - address: u8, - operations: &mut [Operation<'a>], - ) -> Result<(), Self::Error> { - const { unimplemented!() } // See SDL::exec - self.set_addressing_mode(AddressingMode::SevenBit); - SDL::exec(self, address as u16, operations) - } -} - -impl Transactional for SDL { - type Error = I2CErr; - fn exec<'a>( - &mut self, - address: u16, - operations: &mut [Operation<'a>], - ) -> Result<(), Self::Error> { - const { unimplemented!() } // See SDL::exec - self.set_addressing_mode(AddressingMode::TenBit); - SDL::exec(self, address, operations) - } -} - -impl TransactionalIter for SDL { - type Error = I2CErr; - fn exec_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - const { unimplemented!() } // See SDL::exec_iter - self.set_addressing_mode(AddressingMode::SevenBit); - SDL::exec_iter(self, address as u16, operations) - } -} - -impl TransactionalIter for SDL { - type Error = I2CErr; - fn exec_iter<'a, O>(&mut self, address: u16, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - const { unimplemented!() } // See SDL::exec_iter - self.set_addressing_mode(AddressingMode::TenBit); - SDL::exec_iter(self, address, operations) - } -} From 11585dacb78cc75bc9a24a328ef29b0f198d52c9 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 18:37:35 +1300 Subject: [PATCH 60/82] Move GlitchFilter to constructor, check if buffers zero-sized --- src/i2c.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index 020793f..6f04a06 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -149,7 +149,7 @@ impl_i2c_pin!(UsciB1SDAPin, P4, Pin6); impl I2CBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode - pub fn new(usci: USCI) -> Self { + pub fn new(usci: USCI, deglitch_time: GlitchFilter) -> Self { let ctlw0 = UcbCtlw0 { uca10: false, ucsla10: false, @@ -172,7 +172,7 @@ impl I2CBusConfig { ucswack: false, ucclto: Ucclto::Ucclto00b, ucastp: Ucastp::Ucastp00b, - ucglit: Ucglit::Max6_25ns, + ucglit: deglitch_time.into(), }; let i2coa0 = UcbI2coa { @@ -263,12 +263,6 @@ impl I2CBusConfig { self.divisor = clk_divisor; } - /// Configures the glitch filter length for the SDA and SCL lines - #[inline(always)] - pub fn set_deglitch_time(&mut self, deglitch_time: GlitchFilter) { - self.ctlw1.ucglit = deglitch_time.into(); - } - /// Performs hardware configuration and creates the SDL pin pub fn sdl, D: Into>( &self, @@ -327,6 +321,8 @@ impl SDL { /// Blocking read fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), I2CErr> { + if buffer.is_empty() { return Ok(()) } + let usci = unsafe { USCI::steal() }; usci.i2csa_wr(address); @@ -366,6 +362,7 @@ impl SDL { /// Blocking write fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), I2CErr> { + if bytes.is_empty() { return Ok(()) } let usci = unsafe { USCI::steal() }; usci.i2csa_wr(address); From 7dab815b999c0ffc5a8a86654a31bcf0691e549a Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 18:45:53 +1300 Subject: [PATCH 61/82] Add typestate to I2cBusConfig Add UCLKI handling --- src/i2c.rs | 105 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index 6f04a06..f93cdd2 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -10,6 +10,7 @@ //! use crate::clock::{Aclk, Smclk}; +use crate::gpio::{Pin1, Pin5}; use crate::hw_traits::eusci::I2CUcbIfgOut; use crate::{ gpio::{Alternate1, Pin, Pin2, Pin3, Pin6, Pin7, P1, P4}, @@ -86,7 +87,7 @@ impl From for Ucglit { } ///Struct used to configure a I2C bus -pub struct I2CBusConfig { +pub struct I2CBusConfig { usci: USCI, divisor: u16, @@ -99,6 +100,7 @@ pub struct I2CBusConfig { i2coa3: UcbI2coa, ie: UcbIe, ifg: UcbIFG, + _phantom: PhantomData, } /// Marks a usci capable of I2C communication @@ -107,16 +109,20 @@ pub trait EUsciI2CBus: EUsciI2C { type ClockPin; /// I2C SDA type DataPin; + /// I2C UCLKI + type UClkIPin; } impl EUsciI2CBus for pac::E_USCI_B0 { type ClockPin = UsciB0SCLPin; type DataPin = UsciB0SDAPin; + type UClkIPin = UsciB0UCLKIPin; } impl EUsciI2CBus for pac::E_USCI_B1 { type ClockPin = UsciB1SCLPin; type DataPin = UsciB1SDAPin; + type UClkIPin = UsciB1UCLKIPin; } // Allows a GPIO pin to be converted into an I2C object @@ -139,6 +145,10 @@ impl_i2c_pin!(UsciB0SCLPin, P1, Pin3); pub struct UsciB0SDAPin; impl_i2c_pin!(UsciB0SDAPin, P1, Pin2); +/// UCLKI pin for eUSCI B0. Used as an external clock source. +pub struct UsciB0UCLKIPin; +impl_i2c_pin!(UsciB0UCLKIPin, P1, Pin1); + /// I2C SCL pin for eUSCI B1 pub struct UsciB1SCLPin; impl_i2c_pin!(UsciB1SCLPin, P4, Pin7); @@ -147,9 +157,44 @@ impl_i2c_pin!(UsciB1SCLPin, P4, Pin7); pub struct UsciB1SDAPin; impl_i2c_pin!(UsciB1SDAPin, P4, Pin6); -impl I2CBusConfig { +/// UCLKI pin for eUSCI B1. Used as an external clock source. +pub struct UsciB1UCLKIPin; +impl_i2c_pin!(UsciB1UCLKIPin, P4, Pin5); + +/// Typestate trait for an I2C bus configuration. An I2C bus must have a clock selected before it can be configured +pub trait ClockConfigState : private::Sealed {} +/// Typestate for an I2C bus configuration with no clock source selected +pub struct NotConfigured; +/// Typestate for an I2C bus configuration with SMCLK selected as the clock source +pub struct UsingSmclk; +/// Typestate for an I2C bus configuration with ACLK selected as the clock source +pub struct UsingAclk; +/// Typestate for an I2C bus configuration with UCLKI selected as the clock source +pub struct UsingUclkI; + +impl ClockConfigState for NotConfigured {} +impl ClockConfigState for UsingSmclk {} +impl ClockConfigState for UsingAclk {} +impl ClockConfigState for UsingUclkI {} + +trait ClockConfigured : ClockConfigState {} +impl ClockConfigured for UsingSmclk {} +impl ClockConfigured for UsingAclk {} +impl ClockConfigured for UsingUclkI {} + +// Seal the supertrait so users can still refer to the traits, but they can't add other implementations. +mod private { + pub trait Sealed {} + // I2cBusConfig states + impl Sealed for super::NotConfigured {} + impl Sealed for super::UsingSmclk {} + impl Sealed for super::UsingAclk {} + impl Sealed for super::UsingUclkI {} +} + +impl I2CBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode - pub fn new(usci: USCI, deglitch_time: GlitchFilter) -> Self { + pub fn new(usci: USCI, deglitch_time: GlitchFilter) -> I2CBusConfig { let ctlw0 = UcbCtlw0 { uca10: false, ucsla10: false, @@ -246,23 +291,69 @@ impl I2CBusConfig { i2coa3, ie, ifg, + _phantom: PhantomData, } } - /// Configures this peripheral to use smclk + /// Configures this peripheral to use SMCLK #[inline] - pub fn use_smclk(&mut self, _smclk: &Smclk, clk_divisor: u16) { + pub fn use_smclk(mut self, _smclk: &Smclk, clk_divisor: u16) -> I2CBusConfig { self.ctlw0.ucssel = Ucssel::Smclk; self.divisor = clk_divisor; + I2CBusConfig{ + usci: self.usci, + divisor: self.divisor, + ctlw0: self.ctlw0, + ctlw1: self.ctlw1, + i2coa0: self.i2coa0, + i2coa1: self.i2coa1, + i2coa2: self.i2coa2, + i2coa3: self.i2coa3, + ie: self.ie, + ifg: self.ifg, + _phantom: PhantomData } } - /// Configures this peripheral to use aclk + /// Configures this peripheral to use ACLK #[inline] - pub fn use_aclk(&mut self, _aclk: &Aclk, clk_divisor: u16) { + pub fn use_aclk(mut self, _aclk: &Aclk, clk_divisor: u16) -> I2CBusConfig { self.ctlw0.ucssel = Ucssel::Aclk; self.divisor = clk_divisor; + I2CBusConfig{ + usci: self.usci, + divisor: self.divisor, + ctlw0: self.ctlw0, + ctlw1: self.ctlw1, + i2coa0: self.i2coa0, + i2coa1: self.i2coa1, + i2coa2: self.i2coa2, + i2coa3: self.i2coa3, + ie: self.ie, + ifg: self.ifg, + _phantom: PhantomData } } + /// Configures this peripheral to use UCLK + #[inline] + pub fn use_uclk >(mut self, _uclk: Pin, clk_divisor: u16) -> I2CBusConfig { + self.ctlw0.ucssel = Ucssel::Uclk; + self.divisor = clk_divisor; + I2CBusConfig{ + usci: self.usci, + divisor: self.divisor, + ctlw0: self.ctlw0, + ctlw1: self.ctlw1, + i2coa0: self.i2coa0, + i2coa1: self.i2coa1, + i2coa2: self.i2coa2, + i2coa3: self.i2coa3, + ie: self.ie, + ifg: self.ifg, + _phantom: PhantomData } + } +} +#[allow(private_bounds)] +impl I2CBusConfig { /// Performs hardware configuration and creates the SDL pin pub fn sdl, D: Into>( &self, From ad636b016f74870ce363c3c9ab8718f3bb411b98 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 18:47:57 +1300 Subject: [PATCH 62/82] Rename SDL to I2cBus for clarity --- src/i2c.rs | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index f93cdd2..6b116dd 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -208,7 +208,7 @@ impl I2CBusConfig { uctxstt: false, ucswrst: true, ucmode: Ucmode::I2CMode, - ucssel: Ucssel::Smclk, + ucssel: Ucssel::Smclk, // overwritten by `use_smclk/uclk/aclk()` }; let ctlw1 = UcbCtlw1 { @@ -354,19 +354,19 @@ impl I2CBusConfig { #[allow(private_bounds)] impl I2CBusConfig { - /// Performs hardware configuration and creates the SDL pin - pub fn sdl, D: Into>( + /// Performs hardware configuration and creates the I2C bus + pub fn configure, D: Into>( &self, _scl: C, - _sdl: D, - ) -> SDL { - self.configure(); - SDL(PhantomData) + _sda: D, + ) -> I2cBus { + self.configure_regs(); + I2cBus(PhantomData) } /// Performs hardware configuration #[inline] - fn configure(&self) { + fn configure_regs(&self) { self.usci.ctw0_set_rst(); self.usci.ctw0_wr(&self.ctlw0); @@ -385,8 +385,8 @@ impl I2CBusConfig { } } -/// I2C data pin -pub struct SDL(PhantomData); +/// I2C data bus +pub struct I2cBus(PhantomData); /// I2C transmit/receive errors #[derive(Clone, Copy)] @@ -397,7 +397,7 @@ pub enum I2CErr { ArbitrationLost, } -impl SDL { +impl I2cBus { #[inline(always)] fn set_addressing_mode(&mut self, mode: AddressingMode) { let usci = unsafe { USCI::steal() }; @@ -509,43 +509,43 @@ impl SDL { } } -impl Read for SDL { +impl Read for I2cBus { type Error = I2CErr; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::SevenBit); self.set_transmission_mode(TransmissionMode::Receive); - SDL::read(self, address as u16, buffer) + I2cBus::read(self, address as u16, buffer) } } -impl Read for SDL { +impl Read for I2cBus { type Error = I2CErr; fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::TenBit); self.set_transmission_mode(TransmissionMode::Receive); - SDL::read(self, address, buffer) + I2cBus::read(self, address, buffer) } } -impl Write for SDL { +impl Write for I2cBus { type Error = I2CErr; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::SevenBit); self.set_transmission_mode(TransmissionMode::Transmit); - SDL::write(self, address as u16, bytes) + I2cBus::write(self, address as u16, bytes) } } -impl Write for SDL { +impl Write for I2cBus { type Error = I2CErr; fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::TenBit); self.set_transmission_mode(TransmissionMode::Transmit); - SDL::write(self, address, bytes) + I2cBus::write(self, address, bytes) } } -impl WriteRead for SDL { +impl WriteRead for I2cBus { type Error = I2CErr; fn write_read( &mut self, @@ -554,11 +554,11 @@ impl WriteRead for SDL { buffer: &mut [u8], ) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::SevenBit); - SDL::write_read(self, address as u16, bytes, buffer) + I2cBus::write_read(self, address as u16, bytes, buffer) } } -impl WriteRead for SDL { +impl WriteRead for I2cBus { type Error = I2CErr; fn write_read( &mut self, @@ -567,6 +567,6 @@ impl WriteRead for SDL { buffer: &mut [u8], ) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::TenBit); - SDL::write_read(self, address, bytes, buffer) + I2cBus::write_read(self, address, bytes, buffer) } } From 331db03d6743a6eeeb064d95014e64f68ad9f9f2 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 19:44:32 +1300 Subject: [PATCH 63/82] Add typestating to SPI pins, refactor SPIBus to SpiBus for consistency --- src/spi.rs | 62 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/src/spi.rs b/src/spi.rs index 49edcfa..e19e496 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -125,16 +125,43 @@ impl_spi_pin!(UsciB1SCLKPin, P4, Pin5); pub struct UsciB1STEPin; impl_spi_pin!(UsciB1STEPin, P4, Pin4); +/// Typestate trait for an SPI bus configuration. An SPI bus must have a clock selected before it can be configured +pub trait ClockConfigState : private::Sealed {} +/// Typestate for an SPI bus configuration with no clock source selected +pub struct NotConfigured; +/// Typestate for an SPI bus configuration with SMCLK selected as the clock source +pub struct UsingSmclk; +/// Typestate for an SPI bus configuration with ACLK selected as the clock source +pub struct UsingAclk; + +impl ClockConfigState for NotConfigured {} +impl ClockConfigState for UsingSmclk {} +impl ClockConfigState for UsingAclk {} + +trait ClockConfigured : ClockConfigState {} +impl ClockConfigured for UsingSmclk {} +impl ClockConfigured for UsingAclk {} + +// Seal the supertrait so users can still refer to the traits, but they can't add other implementations. +mod private { + pub trait Sealed {} + // SpiBusConfig states + impl Sealed for super::NotConfigured {} + impl Sealed for super::UsingSmclk {} + impl Sealed for super::UsingAclk {} +} + /// Struct used to configure a SPI bus -pub struct SPIBusConfig { +pub struct SpiBusConfig { usci: USCI, prescaler: u16, // Register configs ctlw0: UcxSpiCtw0, + _phantom: PhantomData, } -impl SPIBusConfig { +impl SpiBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in SPI mode pub fn new(usci: USCI, mode: Mode, msb_first: bool) -> Self { let ctlw0 = UcxSpiCtw0 { @@ -153,33 +180,38 @@ impl SPIBusConfig { ucstem: true, ucswrst: true, ucmode: Ucmode::FourPinSPI0, - ucssel: Ucssel::Uclk, + ucssel: Ucssel::Smclk, // overwritten by `use_smclk/aclk()` }; - SPIBusConfig { + SpiBusConfig { usci, prescaler: 0, ctlw0, + _phantom: PhantomData, } } /// Configures this peripheral to use smclk #[inline] - pub fn use_smclk(&mut self, _smclk: &Smclk, clk_divisor: u16) { + pub fn use_smclk(mut self, _smclk: &Smclk, clk_divisor: u16) -> SpiBusConfig{ self.ctlw0.ucssel = Ucssel::Smclk; self.prescaler = clk_divisor; + SpiBusConfig { usci: self.usci, prescaler: self.prescaler, ctlw0: self.ctlw0, _phantom: PhantomData } } /// Configures this peripheral to use aclk #[inline] - pub fn use_aclk(&mut self, _aclk: &Aclk, clk_divisor: u16) { + pub fn use_aclk(mut self, _aclk: &Aclk, clk_divisor: u16) -> SpiBusConfig { self.ctlw0.ucssel = Ucssel::Aclk; self.prescaler = clk_divisor; + SpiBusConfig { usci: self.usci, prescaler: self.prescaler, ctlw0: self.ctlw0, _phantom: PhantomData } } - +} +#[allow(private_bounds)] +impl SpiBusConfig { /// Performs hardware configuration and creates an SPI bus #[inline(always)] - pub fn spi_pins< + pub fn configure< SO: Into, SI: Into, CLK: Into, @@ -190,9 +222,9 @@ impl SPIBusConfig { _mosi: SI, _sclk: CLK, _cs: STE, - ) -> SPIPins { + ) -> SpiBus { self.configure_hw(); - SPIPins(PhantomData) + SpiBus(PhantomData) } #[inline] @@ -211,9 +243,9 @@ impl SPIBusConfig { } /// Represents a group of pins configured for SPI communication -pub struct SPIPins(PhantomData); +pub struct SpiBus(PhantomData); -impl SPIPins { +impl SpiBus { /// Enable Rx interrupts, which fire when a byte is ready to be read #[inline(always)] pub fn set_rx_interrupt(&mut self) { @@ -268,7 +300,7 @@ pub enum SPIErr { Unimplemented = 0, } -impl FullDuplex for SPIPins { +impl FullDuplex for SpiBus { type Error = SPIErr; fn read(&mut self) -> nb::Result { let usci = unsafe { USCI::steal() }; @@ -291,5 +323,5 @@ impl FullDuplex for SPIPins { } // Implementing FullDuplex above gets us a blocking write and transfer implementation for free -impl embedded_hal::blocking::spi::write::Default for SPIPins {} -impl embedded_hal::blocking::spi::transfer::Default for SPIPins {} +impl embedded_hal::blocking::spi::write::Default for SpiBus {} +impl embedded_hal::blocking::spi::transfer::Default for SpiBus {} From dde74d182c57f307c988d981aae9b1ce3946359d Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 20:20:26 +1300 Subject: [PATCH 64/82] Use constants instead of const blocks, work around u64 upcast in baud calcs Also mark `write/read_no_check()` as unsafe. --- src/serial.rs | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/serial.rs b/src/serial.rs index be61ef9..f695103 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -228,6 +228,7 @@ impl SerialConfig { loopback: Loopback, baudrate: u32, ) -> Self { + const ONE: NonZeroU32 = NonZeroU32::new(1).unwrap(); SerialConfig { order, cnt, @@ -235,9 +236,8 @@ impl SerialConfig { parity, loopback, usci, - // Safety: .max(1) ensures baudrate is non-zero state: NoClockSet { - baudrate: NonZeroU32::new(baudrate).unwrap_or( const {NonZeroU32::new(1).unwrap()} ), + baudrate: NonZeroU32::new(baudrate).unwrap_or(ONE), }, } } @@ -300,7 +300,8 @@ fn calculate_baud_config(clk_freq: u32, bps: NonZeroU32) -> BaudConfig { if (n >= 16) && (bps.get() < u32::MAX / 16) { // div = bps * 16 - let div = bps.saturating_mul(const { NonZeroU32::new(16).unwrap() }); + const SIXTEEN: NonZeroU32 = NonZeroU32::new(16).unwrap(); + let div = bps.saturating_mul(SIXTEEN); // n / 16, but more precise let br = (clk_freq / div) as u16; @@ -325,19 +326,25 @@ fn calculate_baud_config(clk_freq: u32, bps: NonZeroU32) -> BaudConfig { #[inline(always)] fn lookup_brs(clk_freq: u32, bps: NonZeroU32) -> u8 { - // bps is between [1, u32::MAX] - // clk_freq is between [0, u32::MAX] + // bps is between [1, 5_000_000] (datasheet max) + // clk_freq is between [0, 24_000_000] (datasheet max) - // modulo = clk_freq % bps => modulo is between [0, bps-1] + // modulo = clk_freq % bps => modulo is between [0, 4_999_999] let modulo = clk_freq % bps; - // fraction = modulo * 10_000 / bps, so within [0, ((bps-1) * 10_000) / bps]. - // To prove upper bound we note `(bps-1)/bps` is largest when bps == u32::MAX: - // (4_294_967_294 * 10_000) / 4_294_967_295 = 42_949_672_940_000 / 4_294_967_295 = 9999.99... truncated to 9_999 because integer division + // fraction = modulo * 10_000 / (bps), so within [0, ((bps-1) * 10_000) / bps]. + // To prove upper bound we note `(bps-1)/bps` is largest when bps == 5_000_000: + // (4_999_999 * 10_000) / 5_000_000 = 49_999_990_000 (watch out for overflow!) / 5_000_000 = 9999.99... truncated to 9_999 because integer division // So fraction is within [0, 9999] - let fraction_as_ten_thousandths = - ((modulo as u64 * 10_000) / core::num::NonZeroU64::from(bps)) as u16; - + let fraction_as_ten_thousandths = if modulo < u32::MAX/10_000 { + // Most accurate + ((modulo * 10_000) / bps) as u16 + } + else { + // Avoid overflow if modulo is large. Assume modulo < 5_000_000 from datasheet max + (((modulo * 500) / bps) * 20) as u16 + }; + // See Table 22-4 from MSP430FR4xx and MSP430FR2xx family user's guide (Rev. I) match fraction_as_ten_thousandths { 0..529 => 0x00, @@ -500,15 +507,19 @@ impl Rx { } /// Reads raw value from Rx buffer with no checks for validity + /// # Safety + /// May read duplicate data #[inline(always)] - pub fn read_no_check(&mut self) -> u8 { + pub unsafe fn read_no_check(&mut self) -> u8 { let usci = unsafe { USCI::steal() }; usci.rx_rd() } #[inline(always)] /// Writes a byte into the Tx buffer with no checks for validity - pub fn write_no_check(&mut self, data: u8) { + /// # Safety + /// May clobber unsent data still in the buffer + pub unsafe fn write_no_check(&mut self, data: u8) { let usci = unsafe { USCI::steal() }; usci.tx_wr(data); } From d6fb77f74f56e426985b394a70d0c9470aae552b Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sat, 4 Jan 2025 20:38:48 +1300 Subject: [PATCH 65/82] Remove macro_rd and associated fns --- src/hw_traits/eusci.rs | 60 ------------------------------------------ 1 file changed, 60 deletions(-) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index df5ba5c..2e6a5ce 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -29,17 +29,6 @@ macro_rules! reg_struct { $($(pub $int_name : $int_size , $(#[$i_attr])*)*)? } - #[allow(unused_macros)] - macro_rules! $macro_rd { - ($reader : expr) => { - $struct_name{ - $($($bool_name : $reader.$bool_name().bit(),)*)? - $($($val_name : <$val_type>::from(<$size>::from($reader.$val_name().variant())),)*)? - $($($int_name : <$int_size>::from($reader.$int_name().bits()),)*)? - } - }; - } - #[allow(unused_macros)] macro_rules! $macro_wr { ($reg : expr) => { |w| @@ -150,19 +139,6 @@ pub struct UcbCtlw1, UcbCtlw1_rd, UcbCtlw1_wr { } } -reg_struct! { -pub struct UcbStatw, UcbStatw_rd, UcbStatw_wr{ - flags{ - pub ucscllow: bool, - pub ucgc: bool, - pub ucbbusy: bool, - } - ints { - pub ucbcnt: u8, - } -} -} - // in order to avoid 4 separate structs, I manually implemented the macro for these registers pub struct UcbI2coa { pub ucgcen: bool, @@ -290,19 +266,15 @@ pub trait EUsciI2C: Steal { fn ctw0_clear_rst(&self); // Modify only when UCSWRST = 1 - // fn ctw0_rd(&self) -> UcbCtlw0; fn ctw0_wr(&self, reg: &UcbCtlw0); // Modify only when UCSWRST = 1 - // fn ctw1_rd(&self) -> UcbCtlw1; fn ctw1_wr(&self, reg: &UcbCtlw1); // Modify only when UCSWRST = 1 fn brw_rd(&self) -> u16; fn brw_wr(&self, val: u16); - fn statw_rd(&self) -> UcbStatw; - // Modify only when UCSWRST = 1 fn tbcnt_rd(&self) -> u16; fn tbcnt_wr(&self, val: u16); @@ -324,7 +296,6 @@ pub trait EUsciI2C: Steal { fn i2csa_rd(&self) -> u16; fn i2csa_wr(&self, val: u16); - // fn ie_rd(&self) -> UcbIe; fn ie_wr(&self, reg: &UcbIe); fn ifg_rd(&self) -> Self::IfgOut; @@ -342,8 +313,6 @@ pub trait EusciSPI: Steal { fn brw_wr(&self, val: u16); - fn statw_rd(&self) -> Self::Statw; - fn uclisten_set(&self); fn uclisten_clear(&self); @@ -432,11 +401,6 @@ macro_rules! eusci_impl { self.$ucxbrw().write(|w| unsafe { w.bits(val) }); } - #[inline(always)] - fn statw_rd(&self) -> Self::Statw { - self.$ucxstatw().read() - } - #[inline(always)] fn uclisten_set(&self) { unsafe { self.$ucxstatw().set_bits(|w| w.uclisten().set_bit()) } @@ -767,23 +731,11 @@ macro_rules! eusci_b_impl { self.$ucbxifg().read().ucrxifg0().bit() } - // #[inline(always)] - // fn ctw0_rd(&self) -> UcbCtlw0 { - // let content = self.$ucbxctlw0().read(); - // UcbCtlw0_rd! {content} - // } - #[inline(always)] fn ctw0_wr(&self, reg: &UcbCtlw0) { self.$ucbxctlw0().write(UcbCtlw0_wr! {reg}); } - // #[inline(always)] - // fn ctw1_rd(&self) -> UcbCtlw1 { - // let content = self.$ucbxctlw1.read(); - // UcbCtlw1_rd! {content} - // } - #[inline(always)] fn ctw1_wr(&self, reg: &UcbCtlw1) { self.$ucbxctlw1.write(UcbCtlw1_wr! {reg}); @@ -798,12 +750,6 @@ macro_rules! eusci_b_impl { self.$ucbxbrw().write(|w| unsafe { w.bits(val) }); } - #[inline(always)] - fn statw_rd(&self) -> UcbStatw { - let content = self.$ucbxstatw().read(); - UcbStatw_rd! {content} - } - #[inline(always)] fn tbcnt_rd(&self) -> u16 { self.$ucbxtbcnt.read().bits() @@ -912,12 +858,6 @@ macro_rules! eusci_b_impl { self.$ucbxi2csa.write(|w| unsafe { w.bits(val) }); } - // #[inline(always)] - // fn ie_rd(&self) -> UcbIe { - // let content = self.$ucbxie().read(); - // UcbIe_rd! {content} - // } - #[inline(always)] fn ie_wr(&self, reg: &UcbIe) { self.$ucbxie().write(UcbIe_wr! {reg}); From 75bf28373d2b1e39c2444bfe4aece627afdfab44 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sun, 5 Jan 2025 13:32:43 +1300 Subject: [PATCH 66/82] Simplify typestate implementations Trival for I2C and SPI, though ADC did depend on the generic argument. Reintroduce the clock_source field as private and give it a default value that will be overwritten when the user calls .using_smclk()/aclk/... --- src/adc.rs | 59 +++++++++++++++++++----------------------------------- src/i2c.rs | 41 +++++++++++++------------------------ src/spi.rs | 30 ++++++++++----------------- 3 files changed, 46 insertions(+), 84 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 4794804..26eedab 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -193,40 +193,18 @@ impl_adc_channel!(P5, Pin3, 11); /// Typestate trait for an ADC configuration. An ADC must have a clock selected before it can be configured pub trait ClockConfigState : private::Sealed {} /// Typestate for an ADC configuration with no clock source selected -pub struct NotConfigured; -/// Typestate for an ADC configuration with SMCLK selected as the clock source -pub struct UsingSmclk; -/// Typestate for an ADC configuration with ACLK selected as the clock source -pub struct UsingAclk; -/// Typestate for an ADC configuration with MODCLK selected as the clock source -pub struct UsingModclk; - -impl ClockConfigState for NotConfigured {} -impl ClockConfigState for UsingSmclk {} -impl ClockConfigState for UsingAclk {} -impl ClockConfigState for UsingModclk {} - -trait ClockConfigured : ClockConfigState { - fn clock_source() -> ClockSource; -} -impl ClockConfigured for UsingSmclk { - fn clock_source() -> ClockSource { ClockSource::SmClk } -} -impl ClockConfigured for UsingAclk { - fn clock_source() -> ClockSource { ClockSource::AClk } -} -impl ClockConfigured for UsingModclk { - fn clock_source() -> ClockSource { ClockSource::ModClk } -} +pub struct NoClockSet; +/// Typestate for an ADC configuration with a clock source selected +pub struct ClockSet; +impl ClockConfigState for NoClockSet {} +impl ClockConfigState for ClockSet {} // Seal the supertrait so users can still refer to the traits, but they can't add other implementations. mod private { pub trait Sealed {} // AdcConfig states - impl Sealed for super::NotConfigured {} - impl Sealed for super::UsingSmclk {} - impl Sealed for super::UsingAclk {} - impl Sealed for super::UsingModclk {} + impl Sealed for super::NoClockSet {} + impl Sealed for super::ClockSet {} } /// Configuration object for an ADC. @@ -238,6 +216,7 @@ mod private { /// - Max 200 ksps sample rate #[derive(Clone, PartialEq, Eq)] pub struct AdcConfig { + clock_source: ClockSource, /// How much the input clock is divided by, after the predivider. pub clock_divider: ClockDivider, /// How much the input clock is initially divided by, before the clock divider. @@ -252,9 +231,10 @@ pub struct AdcConfig { } // Only implement Default for NotConfigured -impl Default for AdcConfig { +impl Default for AdcConfig { fn default() -> Self { Self { + clock_source: Default::default(), clock_divider: Default::default(), predivider: Default::default(), resolution: Default::default(), @@ -264,7 +244,7 @@ impl Default for AdcConfig { } } -impl AdcConfig { +impl AdcConfig { /// Creates an ADC configuration. A default implementation is also available through `::default()` pub fn new( clock_divider: ClockDivider, @@ -272,8 +252,9 @@ impl AdcConfig { resolution: Resolution, sampling_rate: SamplingRate, sample_time: SampleTime, - ) -> AdcConfig { + ) -> AdcConfig { AdcConfig { + clock_source: ClockSource::ModClk, // This will be overwritten by `use_smclk/aclk/...` later clock_divider, predivider, resolution, @@ -283,8 +264,9 @@ impl AdcConfig { } } /// Configure the ADC to use SMCLK - pub fn use_smclk(self, _smclk: &Smclk) -> AdcConfig{ + pub fn use_smclk(self, _smclk: &Smclk) -> AdcConfig{ AdcConfig { + clock_source: ClockSource::SmClk, clock_divider: self.clock_divider, predivider: self.predivider, resolution: self.resolution, @@ -293,8 +275,9 @@ impl AdcConfig { _phantom: PhantomData } } /// Configure the ADC to use ACLK - pub fn use_aclk(self, _aclk: &Aclk) -> AdcConfig{ + pub fn use_aclk(self, _aclk: &Aclk) -> AdcConfig{ AdcConfig { + clock_source: ClockSource::AClk, clock_divider: self.clock_divider, predivider: self.predivider, resolution: self.resolution, @@ -303,8 +286,9 @@ impl AdcConfig { _phantom: PhantomData } } /// Configure the ADC to use MODCLK - pub fn use_modclk(self) -> AdcConfig{ + pub fn use_modclk(self) -> AdcConfig{ AdcConfig { + clock_source: ClockSource::ModClk, clock_divider: self.clock_divider, predivider: self.predivider, resolution: self.resolution, @@ -313,8 +297,7 @@ impl AdcConfig { _phantom: PhantomData } } } -#[allow(private_bounds)] -impl AdcConfig { +impl AdcConfig { /// Applies this ADC configuration to hardware registers, and returns an ADC. pub fn configure(self, mut adc_reg: ADC) -> Adc { // Disable the ADC before we set the other bits. Some can only be set while the ADC is disabled. @@ -323,7 +306,7 @@ impl AdcConfig { let adcsht = self.sample_time.adcsht(); adc_reg.adcctl0.write(|w| w.adcsht().bits(adcsht)); - let adcssel = CLOCK::clock_source().adcssel(); + let adcssel = self.clock_source.adcssel(); let adcdiv = self.clock_divider.adcdiv(); adc_reg.adcctl1.write(|w| {w .adcssel().bits(adcssel) diff --git a/src/i2c.rs b/src/i2c.rs index 6b116dd..5aeb67e 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -164,37 +164,24 @@ impl_i2c_pin!(UsciB1UCLKIPin, P4, Pin5); /// Typestate trait for an I2C bus configuration. An I2C bus must have a clock selected before it can be configured pub trait ClockConfigState : private::Sealed {} /// Typestate for an I2C bus configuration with no clock source selected -pub struct NotConfigured; -/// Typestate for an I2C bus configuration with SMCLK selected as the clock source -pub struct UsingSmclk; -/// Typestate for an I2C bus configuration with ACLK selected as the clock source -pub struct UsingAclk; -/// Typestate for an I2C bus configuration with UCLKI selected as the clock source -pub struct UsingUclkI; - -impl ClockConfigState for NotConfigured {} -impl ClockConfigState for UsingSmclk {} -impl ClockConfigState for UsingAclk {} -impl ClockConfigState for UsingUclkI {} - -trait ClockConfigured : ClockConfigState {} -impl ClockConfigured for UsingSmclk {} -impl ClockConfigured for UsingAclk {} -impl ClockConfigured for UsingUclkI {} +pub struct NoClockSet; +/// Typestate for an I2C bus configuration with a clock source selected +pub struct ClockSet; + +impl ClockConfigState for NoClockSet {} +impl ClockConfigState for ClockSet {} // Seal the supertrait so users can still refer to the traits, but they can't add other implementations. mod private { pub trait Sealed {} // I2cBusConfig states - impl Sealed for super::NotConfigured {} - impl Sealed for super::UsingSmclk {} - impl Sealed for super::UsingAclk {} - impl Sealed for super::UsingUclkI {} + impl Sealed for super::NoClockSet {} + impl Sealed for super::ClockSet {} } -impl I2CBusConfig { +impl I2CBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode - pub fn new(usci: USCI, deglitch_time: GlitchFilter) -> I2CBusConfig { + pub fn new(usci: USCI, deglitch_time: GlitchFilter) -> I2CBusConfig { let ctlw0 = UcbCtlw0 { uca10: false, ucsla10: false, @@ -297,7 +284,7 @@ impl I2CBusConfig { /// Configures this peripheral to use SMCLK #[inline] - pub fn use_smclk(mut self, _smclk: &Smclk, clk_divisor: u16) -> I2CBusConfig { + pub fn use_smclk(mut self, _smclk: &Smclk, clk_divisor: u16) -> I2CBusConfig { self.ctlw0.ucssel = Ucssel::Smclk; self.divisor = clk_divisor; I2CBusConfig{ @@ -316,7 +303,7 @@ impl I2CBusConfig { /// Configures this peripheral to use ACLK #[inline] - pub fn use_aclk(mut self, _aclk: &Aclk, clk_divisor: u16) -> I2CBusConfig { + pub fn use_aclk(mut self, _aclk: &Aclk, clk_divisor: u16) -> I2CBusConfig { self.ctlw0.ucssel = Ucssel::Aclk; self.divisor = clk_divisor; I2CBusConfig{ @@ -334,7 +321,7 @@ impl I2CBusConfig { } /// Configures this peripheral to use UCLK #[inline] - pub fn use_uclk >(mut self, _uclk: Pin, clk_divisor: u16) -> I2CBusConfig { + pub fn use_uclk >(mut self, _uclk: Pin, clk_divisor: u16) -> I2CBusConfig { self.ctlw0.ucssel = Ucssel::Uclk; self.divisor = clk_divisor; I2CBusConfig{ @@ -353,7 +340,7 @@ impl I2CBusConfig { } #[allow(private_bounds)] -impl I2CBusConfig { +impl I2CBusConfig { /// Performs hardware configuration and creates the I2C bus pub fn configure, D: Into>( &self, diff --git a/src/spi.rs b/src/spi.rs index e19e496..f970166 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -128,27 +128,19 @@ impl_spi_pin!(UsciB1STEPin, P4, Pin4); /// Typestate trait for an SPI bus configuration. An SPI bus must have a clock selected before it can be configured pub trait ClockConfigState : private::Sealed {} /// Typestate for an SPI bus configuration with no clock source selected -pub struct NotConfigured; -/// Typestate for an SPI bus configuration with SMCLK selected as the clock source -pub struct UsingSmclk; -/// Typestate for an SPI bus configuration with ACLK selected as the clock source -pub struct UsingAclk; +pub struct NoClockSet; +/// Typestate for an SPI bus configuration with a clock source selected +pub struct ClockSet; -impl ClockConfigState for NotConfigured {} -impl ClockConfigState for UsingSmclk {} -impl ClockConfigState for UsingAclk {} - -trait ClockConfigured : ClockConfigState {} -impl ClockConfigured for UsingSmclk {} -impl ClockConfigured for UsingAclk {} +impl ClockConfigState for NoClockSet {} +impl ClockConfigState for ClockSet {} // Seal the supertrait so users can still refer to the traits, but they can't add other implementations. mod private { pub trait Sealed {} // SpiBusConfig states - impl Sealed for super::NotConfigured {} - impl Sealed for super::UsingSmclk {} - impl Sealed for super::UsingAclk {} + impl Sealed for super::NoClockSet {} + impl Sealed for super::ClockSet {} } /// Struct used to configure a SPI bus @@ -161,7 +153,7 @@ pub struct SpiBusConfig { _phantom: PhantomData, } -impl SpiBusConfig { +impl SpiBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in SPI mode pub fn new(usci: USCI, mode: Mode, msb_first: bool) -> Self { let ctlw0 = UcxSpiCtw0 { @@ -193,7 +185,7 @@ impl SpiBusConfig { /// Configures this peripheral to use smclk #[inline] - pub fn use_smclk(mut self, _smclk: &Smclk, clk_divisor: u16) -> SpiBusConfig{ + pub fn use_smclk(mut self, _smclk: &Smclk, clk_divisor: u16) -> SpiBusConfig{ self.ctlw0.ucssel = Ucssel::Smclk; self.prescaler = clk_divisor; SpiBusConfig { usci: self.usci, prescaler: self.prescaler, ctlw0: self.ctlw0, _phantom: PhantomData } @@ -201,14 +193,14 @@ impl SpiBusConfig { /// Configures this peripheral to use aclk #[inline] - pub fn use_aclk(mut self, _aclk: &Aclk, clk_divisor: u16) -> SpiBusConfig { + pub fn use_aclk(mut self, _aclk: &Aclk, clk_divisor: u16) -> SpiBusConfig { self.ctlw0.ucssel = Ucssel::Aclk; self.prescaler = clk_divisor; SpiBusConfig { usci: self.usci, prescaler: self.prescaler, ctlw0: self.ctlw0, _phantom: PhantomData } } } #[allow(private_bounds)] -impl SpiBusConfig { +impl SpiBusConfig { /// Performs hardware configuration and creates an SPI bus #[inline(always)] pub fn configure< From 28622d8fcd70d1c1c372e1bed9e75e9a7ab45ee3 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sun, 5 Jan 2025 13:47:18 +1300 Subject: [PATCH 67/82] Refactor names EUsciI2CBus -> I2cUsci EUsciSPIBus -> SpiUsci UclkIPin -> ExternalClockPin --- src/i2c.rs | 42 +++++++++++++++++++++--------------------- src/spi.rs | 26 +++++++++++++------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index 5aeb67e..e29da05 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -87,7 +87,7 @@ impl From for Ucglit { } ///Struct used to configure a I2C bus -pub struct I2CBusConfig { +pub struct I2CBusConfig { usci: USCI, divisor: u16, @@ -104,25 +104,25 @@ pub struct I2CBusConfig { } /// Marks a usci capable of I2C communication -pub trait EUsciI2CBus: EUsciI2C { - /// I2C SCL +pub trait I2cUsci: EUsciI2C { + /// I2C SCL pin type ClockPin; - /// I2C SDA + /// I2C SDA pin type DataPin; - /// I2C UCLKI - type UClkIPin; + /// I2C external clock source pin. Only necessary if UCLKI is selected as a clock source. + type ExternalClockPin; } -impl EUsciI2CBus for pac::E_USCI_B0 { +impl I2cUsci for pac::E_USCI_B0 { type ClockPin = UsciB0SCLPin; type DataPin = UsciB0SDAPin; - type UClkIPin = UsciB0UCLKIPin; + type ExternalClockPin = UsciB0UCLKIPin; } -impl EUsciI2CBus for pac::E_USCI_B1 { +impl I2cUsci for pac::E_USCI_B1 { type ClockPin = UsciB1SCLPin; type DataPin = UsciB1SDAPin; - type UClkIPin = UsciB1UCLKIPin; + type ExternalClockPin = UsciB1UCLKIPin; } // Allows a GPIO pin to be converted into an I2C object @@ -179,7 +179,7 @@ mod private { impl Sealed for super::ClockSet {} } -impl I2CBusConfig { +impl I2CBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode pub fn new(usci: USCI, deglitch_time: GlitchFilter) -> I2CBusConfig { let ctlw0 = UcbCtlw0 { @@ -321,7 +321,7 @@ impl I2CBusConfig { } /// Configures this peripheral to use UCLK #[inline] - pub fn use_uclk >(mut self, _uclk: Pin, clk_divisor: u16) -> I2CBusConfig { + pub fn use_uclk >(mut self, _uclk: Pin, clk_divisor: u16) -> I2CBusConfig { self.ctlw0.ucssel = Ucssel::Uclk; self.divisor = clk_divisor; I2CBusConfig{ @@ -340,7 +340,7 @@ impl I2CBusConfig { } #[allow(private_bounds)] -impl I2CBusConfig { +impl I2CBusConfig { /// Performs hardware configuration and creates the I2C bus pub fn configure, D: Into>( &self, @@ -373,7 +373,7 @@ impl I2CBusConfig { } /// I2C data bus -pub struct I2cBus(PhantomData); +pub struct I2cBus(PhantomData); /// I2C transmit/receive errors #[derive(Clone, Copy)] @@ -384,7 +384,7 @@ pub enum I2CErr { ArbitrationLost, } -impl I2cBus { +impl I2cBus { #[inline(always)] fn set_addressing_mode(&mut self, mode: AddressingMode) { let usci = unsafe { USCI::steal() }; @@ -496,7 +496,7 @@ impl I2cBus { } } -impl Read for I2cBus { +impl Read for I2cBus { type Error = I2CErr; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::SevenBit); @@ -505,7 +505,7 @@ impl Read for I2cBus { } } -impl Read for I2cBus { +impl Read for I2cBus { type Error = I2CErr; fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::TenBit); @@ -514,7 +514,7 @@ impl Read for I2cBus { } } -impl Write for I2cBus { +impl Write for I2cBus { type Error = I2CErr; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::SevenBit); @@ -523,7 +523,7 @@ impl Write for I2cBus { } } -impl Write for I2cBus { +impl Write for I2cBus { type Error = I2CErr; fn write(&mut self, address: u16, bytes: &[u8]) -> Result<(), Self::Error> { self.set_addressing_mode(AddressingMode::TenBit); @@ -532,7 +532,7 @@ impl Write for I2cBus { } } -impl WriteRead for I2cBus { +impl WriteRead for I2cBus { type Error = I2CErr; fn write_read( &mut self, @@ -545,7 +545,7 @@ impl WriteRead for I2cBus { } } -impl WriteRead for I2cBus { +impl WriteRead for I2cBus { type Error = I2CErr; fn write_read( &mut self, diff --git a/src/spi.rs b/src/spi.rs index f970166..87f18dd 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -11,7 +11,7 @@ use msp430fr2355 as pac; use nb::Error::WouldBlock; /// Marks a eUSCI capable of SPI communication (in this case, all euscis do) -pub trait EUsciSPIBus: EusciSPI { +pub trait SpiUsci: EusciSPI { /// Master In Slave Out (refered to as SOMI in datasheet) type MISO; /// Master Out Slave In (refered to as SIMO in datasheet) @@ -22,28 +22,28 @@ pub trait EUsciSPIBus: EusciSPI { type STE; } -impl EUsciSPIBus for pac::E_USCI_A0 { +impl SpiUsci for pac::E_USCI_A0 { type MISO = UsciA0MISOPin; type MOSI = UsciA0MOSIPin; type SCLK = UsciA0SCLKPin; type STE = UsciA0STEPin; } -impl EUsciSPIBus for pac::E_USCI_A1 { +impl SpiUsci for pac::E_USCI_A1 { type MISO = UsciA1MISOPin; type MOSI = UsciA1MOSIPin; type SCLK = UsciA1SCLKPin; type STE = UsciA1STEPin; } -impl EUsciSPIBus for pac::E_USCI_B0 { +impl SpiUsci for pac::E_USCI_B0 { type MISO = UsciB0MISOPin; type MOSI = UsciB0MOSIPin; type SCLK = UsciB0SCLKPin; type STE = UsciB0STEPin; } -impl EUsciSPIBus for pac::E_USCI_B1 { +impl SpiUsci for pac::E_USCI_B1 { type MISO = UsciB1MISOPin; type MOSI = UsciB1MOSIPin; type SCLK = UsciB1SCLKPin; @@ -144,7 +144,7 @@ mod private { } /// Struct used to configure a SPI bus -pub struct SpiBusConfig { +pub struct SpiBusConfig { usci: USCI, prescaler: u16, @@ -153,7 +153,7 @@ pub struct SpiBusConfig { _phantom: PhantomData, } -impl SpiBusConfig { +impl SpiBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in SPI mode pub fn new(usci: USCI, mode: Mode, msb_first: bool) -> Self { let ctlw0 = UcxSpiCtw0 { @@ -200,7 +200,7 @@ impl SpiBusConfig { } } #[allow(private_bounds)] -impl SpiBusConfig { +impl SpiBusConfig { /// Performs hardware configuration and creates an SPI bus #[inline(always)] pub fn configure< @@ -235,9 +235,9 @@ impl SpiBusConfig { } /// Represents a group of pins configured for SPI communication -pub struct SpiBus(PhantomData); +pub struct SpiBus(PhantomData); -impl SpiBus { +impl SpiBus { /// Enable Rx interrupts, which fire when a byte is ready to be read #[inline(always)] pub fn set_rx_interrupt(&mut self) { @@ -292,7 +292,7 @@ pub enum SPIErr { Unimplemented = 0, } -impl FullDuplex for SpiBus { +impl FullDuplex for SpiBus { type Error = SPIErr; fn read(&mut self) -> nb::Result { let usci = unsafe { USCI::steal() }; @@ -315,5 +315,5 @@ impl FullDuplex for SpiBus { } // Implementing FullDuplex above gets us a blocking write and transfer implementation for free -impl embedded_hal::blocking::spi::write::Default for SpiBus {} -impl embedded_hal::blocking::spi::transfer::Default for SpiBus {} +impl embedded_hal::blocking::spi::write::Default for SpiBus {} +impl embedded_hal::blocking::spi::transfer::Default for SpiBus {} From f8a7e739e70374696a8ae6be5c9e9623d4e0e57b Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sun, 5 Jan 2025 19:18:38 +1300 Subject: [PATCH 68/82] Remove ClockConfigState, refactor ADC ClockSource into typestate --- src/adc.rs | 44 +++++++++++++++----------------------------- src/i2c.rs | 15 +-------------- src/spi.rs | 15 +-------------- 3 files changed, 17 insertions(+), 57 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 26eedab..03d2f6b 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -6,7 +6,7 @@ //! use crate::{clock::{Aclk, Smclk}, gpio::*}; -use core::{convert::Infallible, marker::PhantomData}; +use core::convert::Infallible; use embedded_hal::adc::{Channel, OneShot}; use msp430fr2355::ADC; @@ -190,22 +190,10 @@ impl_adc_channel!(P5, Pin1, 9); impl_adc_channel!(P5, Pin2, 10); impl_adc_channel!(P5, Pin3, 11); -/// Typestate trait for an ADC configuration. An ADC must have a clock selected before it can be configured -pub trait ClockConfigState : private::Sealed {} /// Typestate for an ADC configuration with no clock source selected pub struct NoClockSet; /// Typestate for an ADC configuration with a clock source selected -pub struct ClockSet; - -impl ClockConfigState for NoClockSet {} -impl ClockConfigState for ClockSet {} -// Seal the supertrait so users can still refer to the traits, but they can't add other implementations. -mod private { - pub trait Sealed {} - // AdcConfig states - impl Sealed for super::NoClockSet {} - impl Sealed for super::ClockSet {} -} +pub struct ClockSet(ClockSource); /// Configuration object for an ADC. /// @@ -215,8 +203,8 @@ mod private { /// - 8 cycle sample time /// - Max 200 ksps sample rate #[derive(Clone, PartialEq, Eq)] -pub struct AdcConfig { - clock_source: ClockSource, +pub struct AdcConfig { + state: STATE, /// How much the input clock is divided by, after the predivider. pub clock_divider: ClockDivider, /// How much the input clock is initially divided by, before the clock divider. @@ -227,20 +215,19 @@ pub struct AdcConfig { pub sampling_rate: SamplingRate, /// Determines the number of ADCCLK cycles the sampling time takes. pub sample_time: SampleTime, - _phantom: PhantomData, } -// Only implement Default for NotConfigured +// Only implement Default for NoClockSet impl Default for AdcConfig { fn default() -> Self { Self { - clock_source: Default::default(), + state: NoClockSet, clock_divider: Default::default(), predivider: Default::default(), resolution: Default::default(), sampling_rate: Default::default(), sample_time: Default::default(), - _phantom: Default::default() } + } } } @@ -254,47 +241,46 @@ impl AdcConfig { sample_time: SampleTime, ) -> AdcConfig { AdcConfig { - clock_source: ClockSource::ModClk, // This will be overwritten by `use_smclk/aclk/...` later + state: NoClockSet, clock_divider, predivider, resolution, sampling_rate, sample_time, - _phantom: PhantomData, } } /// Configure the ADC to use SMCLK pub fn use_smclk(self, _smclk: &Smclk) -> AdcConfig{ AdcConfig { - clock_source: ClockSource::SmClk, + state: ClockSet(ClockSource::SmClk), clock_divider: self.clock_divider, predivider: self.predivider, resolution: self.resolution, sampling_rate: self.sampling_rate, sample_time: self.sample_time, - _phantom: PhantomData } + } } /// Configure the ADC to use ACLK pub fn use_aclk(self, _aclk: &Aclk) -> AdcConfig{ AdcConfig { - clock_source: ClockSource::AClk, + state: ClockSet(ClockSource::AClk), clock_divider: self.clock_divider, predivider: self.predivider, resolution: self.resolution, sampling_rate: self.sampling_rate, sample_time: self.sample_time, - _phantom: PhantomData } + } } /// Configure the ADC to use MODCLK pub fn use_modclk(self) -> AdcConfig{ AdcConfig { - clock_source: ClockSource::ModClk, + state: ClockSet(ClockSource::ModClk), clock_divider: self.clock_divider, predivider: self.predivider, resolution: self.resolution, sampling_rate: self.sampling_rate, sample_time: self.sample_time, - _phantom: PhantomData } + } } } impl AdcConfig { @@ -306,7 +292,7 @@ impl AdcConfig { let adcsht = self.sample_time.adcsht(); adc_reg.adcctl0.write(|w| w.adcsht().bits(adcsht)); - let adcssel = self.clock_source.adcssel(); + let adcssel = self.state.0.adcssel(); let adcdiv = self.clock_divider.adcdiv(); adc_reg.adcctl1.write(|w| {w .adcssel().bits(adcssel) diff --git a/src/i2c.rs b/src/i2c.rs index e29da05..a73a96e 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -87,7 +87,7 @@ impl From for Ucglit { } ///Struct used to configure a I2C bus -pub struct I2CBusConfig { +pub struct I2CBusConfig { usci: USCI, divisor: u16, @@ -161,24 +161,11 @@ impl_i2c_pin!(UsciB1SDAPin, P4, Pin6); pub struct UsciB1UCLKIPin; impl_i2c_pin!(UsciB1UCLKIPin, P4, Pin5); -/// Typestate trait for an I2C bus configuration. An I2C bus must have a clock selected before it can be configured -pub trait ClockConfigState : private::Sealed {} /// Typestate for an I2C bus configuration with no clock source selected pub struct NoClockSet; /// Typestate for an I2C bus configuration with a clock source selected pub struct ClockSet; -impl ClockConfigState for NoClockSet {} -impl ClockConfigState for ClockSet {} - -// Seal the supertrait so users can still refer to the traits, but they can't add other implementations. -mod private { - pub trait Sealed {} - // I2cBusConfig states - impl Sealed for super::NoClockSet {} - impl Sealed for super::ClockSet {} -} - impl I2CBusConfig { /// Create a new configuration for setting up a EUSCI peripheral in I2C master mode pub fn new(usci: USCI, deglitch_time: GlitchFilter) -> I2CBusConfig { diff --git a/src/spi.rs b/src/spi.rs index 87f18dd..bc283c7 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -125,26 +125,13 @@ impl_spi_pin!(UsciB1SCLKPin, P4, Pin5); pub struct UsciB1STEPin; impl_spi_pin!(UsciB1STEPin, P4, Pin4); -/// Typestate trait for an SPI bus configuration. An SPI bus must have a clock selected before it can be configured -pub trait ClockConfigState : private::Sealed {} /// Typestate for an SPI bus configuration with no clock source selected pub struct NoClockSet; /// Typestate for an SPI bus configuration with a clock source selected pub struct ClockSet; -impl ClockConfigState for NoClockSet {} -impl ClockConfigState for ClockSet {} - -// Seal the supertrait so users can still refer to the traits, but they can't add other implementations. -mod private { - pub trait Sealed {} - // SpiBusConfig states - impl Sealed for super::NoClockSet {} - impl Sealed for super::ClockSet {} -} - /// Struct used to configure a SPI bus -pub struct SpiBusConfig { +pub struct SpiBusConfig { usci: USCI, prescaler: u16, From 81534e4cdc68f8157aa0e9c2b9cd0b2df319d009 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Sun, 5 Jan 2025 20:13:43 +1300 Subject: [PATCH 69/82] Use iterator in I2C read Roll `transmit_stop()` into the loop --- src/i2c.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index a73a96e..707883f 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -406,17 +406,16 @@ impl I2cBus { return Err::<(), I2CErr>(I2CErr::GotNACK); } - for i in 0..buffer.len() - 1 { + let len = buffer.len(); + for (idx, byte) in buffer.iter_mut().enumerate() { while !ifg.ucrxifg0() { ifg = usci.ifg_rd(); } - buffer[i] = usci.ucrxbuf_rd(); - } - usci.transmit_stop(); - while !ifg.ucrxifg0() { - ifg = usci.ifg_rd(); + *byte = usci.ucrxbuf_rd(); + if idx == len - 2 { + usci.transmit_stop(); + } } - buffer[buffer.len() - 1] = usci.ucrxbuf_rd(); while usci.uctxstp_rd() { asm::nop(); From 7aec60780cb771784e5a9ecedfd86d617cb899d0 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 6 Jan 2025 10:03:29 +1300 Subject: [PATCH 70/82] Fix underflow when array len is 1 --- src/i2c.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index 707883f..e99747a 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -408,13 +408,13 @@ impl I2cBus { let len = buffer.len(); for (idx, byte) in buffer.iter_mut().enumerate() { + if idx == len - 1 { + usci.transmit_stop(); + } while !ifg.ucrxifg0() { ifg = usci.ifg_rd(); } *byte = usci.ucrxbuf_rd(); - if idx == len - 2 { - usci.transmit_stop(); - } } while usci.uctxstp_rd() { From 06336005e65796545ab67b2c99a914c4c9ef5748 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Tue, 14 Jan 2025 20:34:10 +1300 Subject: [PATCH 71/82] Add SPI example, 3-pin SPI option, and overrun check --- examples/spi.rs | 70 ++++++++++++++++++++++++++++++++++++++++++ src/hw_traits/eusci.rs | 7 +++++ src/spi.rs | 38 ++++++++++++++++++----- 3 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 examples/spi.rs diff --git a/examples/spi.rs b/examples/spi.rs new file mode 100644 index 0000000..c745521 --- /dev/null +++ b/examples/spi.rs @@ -0,0 +1,70 @@ +#![no_main] +#![no_std] + +use embedded_hal::spi::FullDuplex; +use embedded_hal::blocking::spi::Transfer; +use embedded_hal::spi::MODE_0; +use embedded_hal::blocking::delay::DelayMs; +use msp430_rt::entry; +use msp430fr2x5x_hal::{ + clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, fram::Fram, gpio::Batch, pmm::Pmm, spi::{SpiBusConfig, SPIErr}, watchdog::Wdt +}; +use nb::block; +use panic_msp430 as _; + +#[entry] +fn main() -> ! { + let periph = msp430fr2355::Peripherals::take().unwrap(); + + let mut fram = Fram::new(periph.FRCTL); + let _wdt = Wdt::constrain(periph.WDT_A); + + let pmm = Pmm::new(periph.PMM); + let p1 = Batch::new(periph.P1) + .split(&pmm); + let miso = p1.pin7.to_alternate1(); + let mosi = p1.pin6.to_alternate1(); + let sck = p1.pin5.to_alternate1(); + let cs = p1.pin4.to_alternate1(); + + let (smclk, _aclk, mut delay) = ClockConfig::new(periph.CS) + .mclk_dcoclk(DcoclkFreqSel::_8MHz, MclkDiv::_1) + .smclk_on(SmclkDiv::_1) + .aclk_vloclk() + .freeze(&mut fram); + + let mut spi = SpiBusConfig::new(periph.E_USCI_A0, MODE_0, true) + .use_smclk(&smclk, 16) // 8MHz / 16 = 500kHz + .configure_with_hardware_cs(miso, mosi, sck, cs); + + loop { + // Non-blocking send. Sending is infallible, besides `nb::WouldBlock`. + spi.send(0b10101010).ok(); + + // Wait for the above send to complete. Wrap the non-blocking `.read()` with `block!()` to make it blocking. + // Note `.read()` only checks the recieve buffer, it does not send any SPI packets, + // so if you haven't previously used `.send()` there will be nothing to read. + match block!(spi.read()) { + Ok(_data) => (), + // We previously recieved some data but it was overwritten before it was read. + Err(SPIErr::OverrunError(_new_data)) => (), + } + + // Multi-byte blocking send + recieve example + let mut buf = [0b11001100; 10]; + match spi.transfer(&mut buf) { + Ok(_) => (), + Err(SPIErr::OverrunError(_)) => (), + }; + + delay.delay_ms(1000); + } +} + +// The compiler will emit calls to the abort() compiler intrinsic if debug assertions are +// enabled (default for dev profile). MSP430 does not actually have meaningful abort() support +// so for now, we create our own in each application where debug assertions are present. +#[no_mangle] +extern "C" fn abort() -> ! { + panic!(); +} diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 2e6a5ce..00a8c38 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -332,6 +332,8 @@ pub trait EusciSPI: Steal { fn receive_flag(&self) -> bool; + fn overrun_flag(&self) -> bool; + fn iv_rd(&self) -> u16; } @@ -450,6 +452,11 @@ macro_rules! eusci_impl { self.$ucxifg().read().ucrxifg().bit() } + #[inline(always)] + fn overrun_flag(&self) -> bool { + self.$ucxstatw().read().ucoe().bit() + } + #[inline(always)] fn iv_rd(&self) -> u16 { self.$ucxiv().read().uciv().bits() diff --git a/src/spi.rs b/src/spi.rs index bc283c7..ac4eae2 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -158,7 +158,7 @@ impl SpiBusConfig { ucsync: true, ucstem: true, ucswrst: true, - ucmode: Ucmode::FourPinSPI0, + ucmode: Ucmode::FourPinSPI0, // overwritten by `configure_with_software_cs()` ucssel: Ucssel::Smclk, // overwritten by `use_smclk/aclk()` }; @@ -188,9 +188,9 @@ impl SpiBusConfig { } #[allow(private_bounds)] impl SpiBusConfig { - /// Performs hardware configuration and creates an SPI bus + /// Performs hardware configuration and creates an SPI bus. The STE pin is used as an automatically controlled chip select pin. Suitable for systems with only one slave device. #[inline(always)] - pub fn configure< + pub fn configure_with_hardware_cs< SO: Into, SI: Into, CLK: Into, @@ -206,6 +206,23 @@ impl SpiBusConfig { SpiBus(PhantomData) } + /// Performs hardware configuration and creates an SPI bus. You must configure and control any chip select pins yourself. Suitable for systems with multiple slave devices. + #[inline(always)] + pub fn configure_with_software_cs< + SO: Into, + SI: Into, + CLK: Into, + >( + &mut self, + _miso: SO, + _mosi: SI, + _sclk: CLK + ) -> SpiBus { + self.ctlw0.ucmode = Ucmode::ThreePinSPI; + self.configure_hw(); + SpiBus(PhantomData) + } + #[inline] fn configure_hw(&self) { self.usci.ctw0_set_rst(); @@ -273,18 +290,25 @@ impl SpiBus { } /// SPI transmit/receive errors -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum SPIErr { - /// Function not implemented - Unimplemented = 0, + /// Data in the recieve buffer was overwritten before it was read. The contained data is the new contents of the recieve buffer. + OverrunError(u8), + // In future the framing error bit UCFE may appear here. Right now it's unimplemented. } impl FullDuplex for SpiBus { type Error = SPIErr; fn read(&mut self) -> nb::Result { let usci = unsafe { USCI::steal() }; + if usci.receive_flag() { - Ok(usci.rxbuf_rd()) + if usci.overrun_flag() { + Err(nb::Error::Other(SPIErr::OverrunError(usci.rxbuf_rd()))) + } + else { + Ok(usci.rxbuf_rd()) + } } else { Err(WouldBlock) } From 8ae77acc5bfc36d32f7db670cfdd4dbad085c4a0 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Tue, 14 Jan 2025 20:34:18 +1300 Subject: [PATCH 72/82] Add I2C example, make I2CErr Debug so it can be unwrapped --- examples/i2c.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ src/i2c.rs | 2 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 examples/i2c.rs diff --git a/examples/i2c.rs b/examples/i2c.rs new file mode 100644 index 0000000..be68290 --- /dev/null +++ b/examples/i2c.rs @@ -0,0 +1,74 @@ +#![no_main] +#![no_std] + +use embedded_hal::blocking::{i2c::{Read, Write, WriteRead}, delay::DelayMs}; +use msp430_rt::entry; +use msp430fr2x5x_hal::{ + clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, fram::Fram, gpio::Batch, i2c::{GlitchFilter, I2CBusConfig, I2CErr}, pmm::Pmm, watchdog::Wdt +}; +use panic_msp430 as _; + +#[entry] +fn main() -> ! { + let periph = msp430fr2355::Peripherals::take().unwrap(); + + let mut fram = Fram::new(periph.FRCTL); + let _wdt = Wdt::constrain(periph.WDT_A); + + let pmm = Pmm::new(periph.PMM); + let p4 = Batch::new(periph.P4) + .split(&pmm); + let scl = p4.pin7.to_alternate1(); + let sda = p4.pin6.to_alternate1(); + + let (smclk, _aclk, mut delay) = ClockConfig::new(periph.CS) + .mclk_dcoclk(DcoclkFreqSel::_8MHz, MclkDiv::_1) + .smclk_on(SmclkDiv::_1) + .aclk_vloclk() + .freeze(&mut fram); + + let mut i2c = I2CBusConfig::new(periph.E_USCI_B1, GlitchFilter::Max50ns) + .use_smclk(&smclk, 80) // 8MHz / 10 = 100kHz + .configure(scl, sda); + + loop { + // Blocking read. Read 10 bytes (length of buffer) from address 0x12. + // Pass a u8 address for 7-bit addressing mode, pass a u16 for 10-bit addressing mode. + let mut buf = [0u8; 10]; + match i2c.read(0x12_u8, &mut buf) { + Ok(_) => (), + Err(I2CErr::ArbitrationLost) => (), + Err(I2CErr::GotNACK) => (), + }; + + delay.delay_ms(10); + + // Blocking write. Write one byte to address 0x12. + match i2c.write(0x12_u8, &[0b10101010]) { + Ok(_) => (), + Err(I2CErr::ArbitrationLost) => (), + Err(I2CErr::GotNACK) => (), + }; + + delay.delay_ms(10); + + // Blocking send + recieve. Write 10 bytes to 0x12, read 20 bytes from 0x12 + let send_buf = [0b11001100; 10]; + let mut recv_buf = [0b11001100; 20]; + match i2c.write_read(0x12_u8, &send_buf, &mut recv_buf) { + Ok(_) => (), + Err(I2CErr::ArbitrationLost) => (), + Err(I2CErr::GotNACK) => (), + }; + + delay.delay_ms(1000); + } +} + +// The compiler will emit calls to the abort() compiler intrinsic if debug assertions are +// enabled (default for dev profile). MSP430 does not actually have meaningful abort() support +// so for now, we create our own in each application where debug assertions are present. +#[no_mangle] +extern "C" fn abort() -> ! { + panic!(); +} diff --git a/src/i2c.rs b/src/i2c.rs index e99747a..c22eb61 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -363,7 +363,7 @@ impl I2CBusConfig { pub struct I2cBus(PhantomData); /// I2C transmit/receive errors -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum I2CErr { /// Address was never acknolwedged by slave GotNACK, From c34890377c524d91c954fd1fdcf34c9e6b49d5d6 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Thu, 16 Jan 2025 10:06:20 +1300 Subject: [PATCH 73/82] Mark error enums as non-exhaustive --- src/i2c.rs | 2 ++ src/spi.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/i2c.rs b/src/i2c.rs index c22eb61..d6f6f56 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -364,11 +364,13 @@ pub struct I2cBus(PhantomData); /// I2C transmit/receive errors #[derive(Clone, Copy, Debug)] +#[non_exhaustive] pub enum I2CErr { /// Address was never acknolwedged by slave GotNACK, /// Device lost arbitration ArbitrationLost, + // Other errors such as the 'clock low timeout' UCCLTOIFG may appear here in future. } impl I2cBus { diff --git a/src/spi.rs b/src/spi.rs index ac4eae2..bd40d4f 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -291,6 +291,7 @@ impl SpiBus { /// SPI transmit/receive errors #[derive(Clone, Copy, Debug)] +#[non_exhaustive] pub enum SPIErr { /// Data in the recieve buffer was overwritten before it was read. The contained data is the new contents of the recieve buffer. OverrunError(u8), From a7edf4555b274eb719c48aecb14bd26a0f186c44 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Thu, 16 Jan 2025 10:30:56 +1300 Subject: [PATCH 74/82] Tidy examples --- examples/i2c.rs | 24 ++++++++++-------------- examples/spi.rs | 24 +++++++++++++----------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/examples/i2c.rs b/examples/i2c.rs index be68290..6bb78e2 100644 --- a/examples/i2c.rs +++ b/examples/i2c.rs @@ -4,7 +4,7 @@ use embedded_hal::blocking::{i2c::{Read, Write, WriteRead}, delay::DelayMs}; use msp430_rt::entry; use msp430fr2x5x_hal::{ - clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, fram::Fram, gpio::Batch, i2c::{GlitchFilter, I2CBusConfig, I2CErr}, pmm::Pmm, watchdog::Wdt + clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, fram::Fram, gpio::Batch, i2c::{GlitchFilter, I2CBusConfig}, pmm::Pmm, watchdog::Wdt }; use panic_msp430 as _; @@ -34,31 +34,27 @@ fn main() -> ! { loop { // Blocking read. Read 10 bytes (length of buffer) from address 0x12. // Pass a u8 address for 7-bit addressing mode, pass a u16 for 10-bit addressing mode. - let mut buf = [0u8; 10]; + let mut buf = [0; 10]; match i2c.read(0x12_u8, &mut buf) { Ok(_) => (), - Err(I2CErr::ArbitrationLost) => (), - Err(I2CErr::GotNACK) => (), + // You should handle any errors + Err(_e) => todo!(), }; - delay.delay_ms(10); - // Blocking write. Write one byte to address 0x12. match i2c.write(0x12_u8, &[0b10101010]) { Ok(_) => (), - Err(I2CErr::ArbitrationLost) => (), - Err(I2CErr::GotNACK) => (), + // You should handle any errors + Err(_e) => todo!(), }; - delay.delay_ms(10); - - // Blocking send + recieve. Write 10 bytes to 0x12, read 20 bytes from 0x12 + // Blocking send + recieve. Write 10 bytes to 0x12, then read 20 bytes let send_buf = [0b11001100; 10]; - let mut recv_buf = [0b11001100; 20]; + let mut recv_buf = [0; 20]; match i2c.write_read(0x12_u8, &send_buf, &mut recv_buf) { Ok(_) => (), - Err(I2CErr::ArbitrationLost) => (), - Err(I2CErr::GotNACK) => (), + // You should handle any errors + Err(_e) => todo!(), }; delay.delay_ms(1000); diff --git a/examples/spi.rs b/examples/spi.rs index c745521..a84d0ee 100644 --- a/examples/spi.rs +++ b/examples/spi.rs @@ -7,7 +7,7 @@ use embedded_hal::spi::MODE_0; use embedded_hal::blocking::delay::DelayMs; use msp430_rt::entry; use msp430fr2x5x_hal::{ - clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, fram::Fram, gpio::Batch, pmm::Pmm, spi::{SpiBusConfig, SPIErr}, watchdog::Wdt + clock::{ClockConfig, DcoclkFreqSel, MclkDiv, SmclkDiv}, fram::Fram, gpio::Batch, pmm::Pmm, spi::SpiBusConfig, watchdog::Wdt }; use nb::block; use panic_msp430 as _; @@ -38,23 +38,25 @@ fn main() -> ! { .configure_with_hardware_cs(miso, mosi, sck, cs); loop { - // Non-blocking send. Sending is infallible, besides `nb::WouldBlock`. - spi.send(0b10101010).ok(); + // Non-blocking send. Sending is infallible, besides `nb::WouldBlock` when the bus is busy. + // In this particular case we know the SPI bus isn't busy, so we unwrap here + spi.send(0b10101010).unwrap(); // Wait for the above send to complete. Wrap the non-blocking `.read()` with `block!()` to make it blocking. // Note `.read()` only checks the recieve buffer, it does not send any SPI packets, // so if you haven't previously used `.send()` there will be nothing to read. - match block!(spi.read()) { - Ok(_data) => (), - // We previously recieved some data but it was overwritten before it was read. - Err(SPIErr::OverrunError(_new_data)) => (), - } + let _data = match block!(spi.read()) { + Ok(d) => d, + // You should handle any errors + Err(_e) => todo!(), + }; // Multi-byte blocking send + recieve example - let mut buf = [0b11001100; 10]; - match spi.transfer(&mut buf) { + let mut send_recv_buf = [0b11001100; 10]; + match spi.transfer(&mut send_recv_buf) { Ok(_) => (), - Err(SPIErr::OverrunError(_)) => (), + // You should handle any errors + Err(_e) => todo!(), }; delay.delay_ms(1000); From 218832c1f4c2be233ffc95a06d9c3ea3338d4200 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Thu, 16 Jan 2025 10:33:40 +1300 Subject: [PATCH 75/82] Add method to change SPI mode after configuration In a multi-slave system some slaves may require a different SPI mode. --- src/hw_traits/eusci.rs | 19 +++++++++++++++++++ src/spi.rs | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/src/hw_traits/eusci.rs b/src/hw_traits/eusci.rs index 00a8c38..d9ce8b2 100644 --- a/src/hw_traits/eusci.rs +++ b/src/hw_traits/eusci.rs @@ -1,4 +1,5 @@ use super::Steal; +use embedded_hal::spi::{Mode, Phase, Polarity}; use msp430fr2355 as pac; /// Defines macros for a register associated struct to make reading/writing to this struct's @@ -311,6 +312,8 @@ pub trait EusciSPI: Steal { fn ctw0_wr(&self, reg: &UcxSpiCtw0); + fn set_spi_mode(&self, mode: Mode); + fn brw_wr(&self, val: u16); fn uclisten_set(&self); @@ -442,6 +445,22 @@ macro_rules! eusci_impl { unsafe { self.$ucxie().clear_bits(|w| w.ucrxie().clear_bit()) } } + #[inline(always)] + // Set the SPI mode without disturbing the rest of the register. + fn set_spi_mode(&self, mode: Mode) { + let ucckph = match mode.phase { + Phase::CaptureOnFirstTransition => true, + Phase::CaptureOnSecondTransition => false, + }; + let ucckpl = match mode.polarity { + Polarity::IdleLow => false, + Polarity::IdleHigh => true, + }; + self.$ucxctlw0().modify(|_, w| w + .ucckph().bit(ucckph) + .ucckpl().bit(ucckpl)); + } + #[inline(always)] fn transmit_flag(&self) -> bool { self.$ucxifg().read().uctxifg().bit() diff --git a/src/spi.rs b/src/spi.rs index bd40d4f..965f39d 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -287,6 +287,15 @@ impl SpiBus { let usci = unsafe { USCI::steal() }; usci.rxbuf_rd() } + + #[inline(always)] + /// Change the SPI mode + pub fn change_mode(&mut self, mode: Mode) { + let usci = unsafe { USCI::steal() }; + usci.ctw0_set_rst(); + usci.set_spi_mode(mode); + usci.ctw0_clear_rst(); + } } /// SPI transmit/receive errors From 670d7a39c2f0eb2c37e44cb55b22c6dfd6b52469 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Thu, 16 Jan 2025 17:42:55 +1300 Subject: [PATCH 76/82] Add ADC example, simplify OneShot implementation The previous generic implementation of OneShot would force you to always define the type of the output to disambiguate. Since `adc_get_result()` always returns a u16 just implement OneShot for u16. --- examples/adc.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ src/adc.rs | 7 +++-- 2 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 examples/adc.rs diff --git a/examples/adc.rs b/examples/adc.rs new file mode 100644 index 0000000..c08e0aa --- /dev/null +++ b/examples/adc.rs @@ -0,0 +1,68 @@ +#![no_main] +#![no_std] + +use embedded_hal::{adc::OneShot, digital::v2::*}; +use msp430_rt::entry; +use msp430fr2x5x_hal::{ + adc::{AdcConfig, ClockDivider, Predivider, Resolution, SampleTime, SamplingRate}, + gpio::Batch, + pmm::Pmm, + watchdog::Wdt, +}; +use nb::block; +use panic_msp430 as _; + +// If pin 1.1 is between 1V and 2V, the LED on pin 1.0 should light up. +#[entry] +fn main() -> ! { + // Take peripherals and disable watchdog + let periph = msp430fr2355::Peripherals::take().unwrap(); + let _wdt = Wdt::constrain(periph.WDT_A); + + // Configure GPIO + let pmm = Pmm::new(periph.PMM); + let port1 = Batch::new(periph.P1).split(&pmm); + let mut led = port1.pin0.to_output(); + let mut adc_pin = port1.pin1.to_alternate3(); + + // ADC setup + let mut adc = AdcConfig::new( + ClockDivider::_1, + Predivider::_1, + Resolution::_8BIT, + SamplingRate::_50KSPS, + SampleTime::_4, + ) + .use_modclk() + .configure(periph.ADC); + + adc.enable(); + + loop { + // Get ADC count + // .read() is infallible besides nb::WouldBlock, so it's safe to unwrap after block!() + let count = block!( adc.read(&mut adc_pin) ).unwrap(); + let reading_mv = count_to_mv_8bit(count); + + // Turn on LED if voltage between 1000 and 2000mV + if (1000..=2000).contains(&reading_mv) { + led.set_high().ok(); + } else { + led.set_low().ok(); + } + } +} + +fn count_to_mv_8bit(count: u16) -> u16 { + const REF_MV: u32 = 3300; + const RESOLUTION: u32 = 256; + ((count as u32 * REF_MV) / RESOLUTION) as u16 +} + +// The compiler will emit calls to the abort() compiler intrinsic if debug assertions are +// enabled (default for dev profile). MSP430 does not actually have meaningful abort() support +// so for now, we create our own in each application where debug assertions are present. +#[no_mangle] +extern "C" fn abort() -> ! { + panic!(); +} diff --git a/src/adc.rs b/src/adc.rs index 03d2f6b..4f6c928 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -374,9 +374,8 @@ fn disable_adc_reg(adc: &mut ADC) { } } -impl OneShot for Adc +impl OneShot for Adc where - WORD: From, PIN: Channel, { type Error = Infallible; // Only returns WouldBlock @@ -384,13 +383,13 @@ where /// Begins a single ADC conversion if one is not already underway. /// /// If the result is ready it is returned, otherwise returns `WouldBlock` - fn read(&mut self, pin: &mut PIN) -> nb::Result { + fn read(&mut self, pin: &mut PIN) -> nb::Result { if self.is_waiting { if self.adc_is_busy() { return Err(nb::Error::WouldBlock); } else { self.is_waiting = false; - return Ok(self.adc_get_result().into()); + return Ok(self.adc_get_result()); } } self.disable(); From 2d33bd1ba9cd361320aabaa31d663c1d2ff368e6 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Thu, 16 Jan 2025 21:05:48 +1300 Subject: [PATCH 77/82] Remove unecessary ADC enable from example --- examples/adc.rs | 2 -- src/adc.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index c08e0aa..319f8af 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -36,8 +36,6 @@ fn main() -> ! { .use_modclk() .configure(periph.ADC); - adc.enable(); - loop { // Get ADC count // .read() is infallible besides nb::WouldBlock, so it's safe to unwrap after block!() diff --git a/src/adc.rs b/src/adc.rs index 4f6c928..dfe45f6 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -380,7 +380,7 @@ where { type Error = Infallible; // Only returns WouldBlock - /// Begins a single ADC conversion if one is not already underway. + /// Begins a single ADC conversion if one isn't already underway, enabling the ADC in the process. /// /// If the result is ready it is returned, otherwise returns `WouldBlock` fn read(&mut self, pin: &mut PIN) -> nb::Result { From e9e94c6017d58b3ba6f203f815fc06702209c902 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Thu, 16 Jan 2025 21:40:23 +1300 Subject: [PATCH 78/82] Try out ADC voltage conversion in HAL --- examples/adc.rs | 16 +++++----------- src/adc.rs | 22 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/examples/adc.rs b/examples/adc.rs index 319f8af..573be97 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -1,7 +1,7 @@ #![no_main] #![no_std] -use embedded_hal::{adc::OneShot, digital::v2::*}; +use embedded_hal::digital::v2::*; use msp430_rt::entry; use msp430fr2x5x_hal::{ adc::{AdcConfig, ClockDivider, Predivider, Resolution, SampleTime, SamplingRate}, @@ -37,10 +37,10 @@ fn main() -> ! { .configure(periph.ADC); loop { - // Get ADC count - // .read() is infallible besides nb::WouldBlock, so it's safe to unwrap after block!() - let count = block!( adc.read(&mut adc_pin) ).unwrap(); - let reading_mv = count_to_mv_8bit(count); + // Get ADC voltage, assuming the ADC reference voltage is 3300mV + // It's infallible besides nb::WouldBlock, so it's safe to unwrap after block!() + // If you want a raw count use adc.read() instead. + let reading_mv = block!( adc.read_voltage_mv(&mut adc_pin, 3300) ).unwrap(); // Turn on LED if voltage between 1000 and 2000mV if (1000..=2000).contains(&reading_mv) { @@ -51,12 +51,6 @@ fn main() -> ! { } } -fn count_to_mv_8bit(count: u16) -> u16 { - const REF_MV: u32 = 3300; - const RESOLUTION: u32 = 256; - ((count as u32 * REF_MV) / RESOLUTION) as u16 -} - // The compiler will emit calls to the abort() compiler intrinsic if debug assertions are // enabled (default for dev profile). MSP430 does not actually have meaningful abort() support // so for now, we create our own in each application where debug assertions are present. diff --git a/src/adc.rs b/src/adc.rs index dfe45f6..40f03fb 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -364,6 +364,26 @@ impl Adc { } } + /// Convert an ADC count to a voltage value in millivolts. + pub fn count_to_mv(&self, count: u16, ref_voltage_mv: u16) -> u16 { + use crate::pac::adc::adcctl2::ADCRES_A; + let resolution = match self.adc_reg.adcctl2.read().adcres().variant() { + ADCRES_A::ADCRES_0 => 256, // 8-bit + ADCRES_A::ADCRES_1 => 1024, // 10-bit + ADCRES_A::ADCRES_2 => 4096, // 12-bit + ADCRES_A::ADCRES_3 => 4096, // Reserved, unreachable + }; + ((count as u32 * ref_voltage_mv as u32) / resolution) as u16 + } + + /// Begins a single ADC conversion if one isn't already underway, enabling the ADC in the process. + /// + /// If the result is ready it is returned as a voltage in millivolts, otherwise returns `WouldBlock`. + /// + /// If you instead want a raw count you should use the `.read()` method from the `OneShot` trait implementation. + pub fn read_voltage_mv>(&mut self, pin: &mut PIN, ref_voltage_mv: u16) -> nb::Result { + self.read(pin).map(|count| self.count_to_mv(count, ref_voltage_mv)) + } } fn disable_adc_reg(adc: &mut ADC) { @@ -382,7 +402,7 @@ where /// Begins a single ADC conversion if one isn't already underway, enabling the ADC in the process. /// - /// If the result is ready it is returned, otherwise returns `WouldBlock` + /// If the result is ready it is returned as an ADC count, otherwise returns `WouldBlock` fn read(&mut self, pin: &mut PIN) -> nb::Result { if self.is_waiting { if self.adc_is_busy() { From 745669f91c41b1ce64c581be3bff9b42f5a41913 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Thu, 16 Jan 2025 21:46:45 +1300 Subject: [PATCH 79/82] Replace error handling stubs in SPI, I2C examples with unwraps --- examples/i2c.rs | 21 ++++++--------------- examples/spi.rs | 16 +++++----------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/examples/i2c.rs b/examples/i2c.rs index 6bb78e2..204a982 100644 --- a/examples/i2c.rs +++ b/examples/i2c.rs @@ -35,27 +35,18 @@ fn main() -> ! { // Blocking read. Read 10 bytes (length of buffer) from address 0x12. // Pass a u8 address for 7-bit addressing mode, pass a u16 for 10-bit addressing mode. let mut buf = [0; 10]; - match i2c.read(0x12_u8, &mut buf) { - Ok(_) => (), - // You should handle any errors - Err(_e) => todo!(), - }; + // You should handle errors here rather than unwrapping + i2c.read(0x12_u8, &mut buf).unwrap(); // Blocking write. Write one byte to address 0x12. - match i2c.write(0x12_u8, &[0b10101010]) { - Ok(_) => (), - // You should handle any errors - Err(_e) => todo!(), - }; + // You should handle errors here rather than unwrapping + i2c.write(0x12_u8, &[0b10101010]).unwrap(); // Blocking send + recieve. Write 10 bytes to 0x12, then read 20 bytes let send_buf = [0b11001100; 10]; let mut recv_buf = [0; 20]; - match i2c.write_read(0x12_u8, &send_buf, &mut recv_buf) { - Ok(_) => (), - // You should handle any errors - Err(_e) => todo!(), - }; + // You should handle errors here rather than unwrapping + i2c.write_read(0x12_u8, &send_buf, &mut recv_buf).unwrap(); delay.delay_ms(1000); } diff --git a/examples/spi.rs b/examples/spi.rs index a84d0ee..1b5f695 100644 --- a/examples/spi.rs +++ b/examples/spi.rs @@ -39,25 +39,19 @@ fn main() -> ! { loop { // Non-blocking send. Sending is infallible, besides `nb::WouldBlock` when the bus is busy. - // In this particular case we know the SPI bus isn't busy, so we unwrap here + // In this particular case we know the SPI bus isn't busy, so unwrapping is safe here. spi.send(0b10101010).unwrap(); // Wait for the above send to complete. Wrap the non-blocking `.read()` with `block!()` to make it blocking. // Note `.read()` only checks the recieve buffer, it does not send any SPI packets, // so if you haven't previously used `.send()` there will be nothing to read. - let _data = match block!(spi.read()) { - Ok(d) => d, - // You should handle any errors - Err(_e) => todo!(), - }; + // You should handle errors here rather than unwrapping + let _data = block!(spi.read()).unwrap(); // Multi-byte blocking send + recieve example let mut send_recv_buf = [0b11001100; 10]; - match spi.transfer(&mut send_recv_buf) { - Ok(_) => (), - // You should handle any errors - Err(_e) => todo!(), - }; + // You should handle errors here rather than unwrapping + spi.transfer(&mut send_recv_buf).unwrap(); delay.delay_ms(1000); } From fc0c486882523c1546e90737e7794d20478a36a8 Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 20 Jan 2025 13:49:56 +1300 Subject: [PATCH 80/82] Add internal ADC channels --- src/adc.rs | 68 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 40f03fb..7894eca 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -2,7 +2,9 @@ //! //! The ADC may read from any of the following pins: //! -//! P1.0 - P1.7 (channels 0 to 7), P5.0 - P5.3 (channels 8 to 11) +//! P1.0 - P1.7 (channels 0 to 7), P5.0 - P5.3 (channels 8 to 11). +//! +//! ADC channels 12 to 15 are not associated with external pins, so in lieu of a pin use the `static`s below. //! use crate::{clock::{Aclk, Smclk}, gpio::*}; @@ -165,7 +167,7 @@ impl SamplingRate { } // Pins corresponding to an ADC channel. Pin types can have `::channel()` called on them to get their ADC channel index. -macro_rules! impl_adc_channel { +macro_rules! impl_adc_channel_pin { ($port: ty, $pin: ty, $channel: literal ) => { impl Channel for Pin<$port, $pin, Alternate3>> { type ID = u8; @@ -177,18 +179,56 @@ macro_rules! impl_adc_channel { }; } -impl_adc_channel!(P1, Pin0, 0); -impl_adc_channel!(P1, Pin1, 1); -impl_adc_channel!(P1, Pin2, 2); -impl_adc_channel!(P1, Pin3, 3); -impl_adc_channel!(P1, Pin4, 4); -impl_adc_channel!(P1, Pin5, 5); -impl_adc_channel!(P1, Pin6, 6); -impl_adc_channel!(P1, Pin7, 7); -impl_adc_channel!(P5, Pin0, 8); -impl_adc_channel!(P5, Pin1, 9); -impl_adc_channel!(P5, Pin2, 10); -impl_adc_channel!(P5, Pin3, 11); +impl_adc_channel_pin!(P1, Pin0, 0); +impl_adc_channel_pin!(P1, Pin1, 1); +impl_adc_channel_pin!(P1, Pin2, 2); +impl_adc_channel_pin!(P1, Pin3, 3); +impl_adc_channel_pin!(P1, Pin4, 4); +impl_adc_channel_pin!(P1, Pin5, 5); +impl_adc_channel_pin!(P1, Pin6, 6); +impl_adc_channel_pin!(P1, Pin7, 7); +impl_adc_channel_pin!(P5, Pin0, 8); +impl_adc_channel_pin!(P5, Pin1, 9); +impl_adc_channel_pin!(P5, Pin2, 10); +impl_adc_channel_pin!(P5, Pin3, 11); + +// A few ADC channels don't correspond to pins. +macro_rules! impl_adc_channel_extra { + ($type: ty, $channel: literal ) => { + impl Channel for $type { + type ID = u8; + + fn channel() -> Self::ID { + $channel + } + } + }; +} + +// We instead document the statics below. Users needn't deal with the structs themselves so it just adds noise to the docs. +#[doc(hidden)] +pub struct AdcTempSenseChannel; +impl_adc_channel_extra!(AdcTempSenseChannel, 12); +/// ADC channel 12, tied to the internal temperature sensor. Pass this to `adc.read()` to read this channel. +pub static ADC_CH12_TEMP_SENSE: AdcTempSenseChannel = AdcTempSenseChannel; + +#[doc(hidden)] +pub struct AdcVrefChannel; +impl_adc_channel_extra!(AdcVrefChannel, 13); +/// ADC channel 13, tied to the internal reference voltage. Pass this to `adc.read()` to read this channel. +pub static ADC_CH13_VREF: AdcVrefChannel = AdcVrefChannel; + +#[doc(hidden)] +pub struct AdcVssChannel; +impl_adc_channel_extra!(AdcVssChannel, 14); +/// ADC channel 14, tied to VSS. Pass this to `adc.read()` to read this channel. +pub static ADC_CH14_VSS: AdcVssChannel = AdcVssChannel; + +#[doc(hidden)] +pub struct AdcVccChannel; +impl_adc_channel_extra!(AdcVccChannel, 15); +/// ADC channel 15, tied to VCC. Pass this to `adc.read()` to read this channel. +pub static ADC_CH15_VCC: AdcVccChannel = AdcVccChannel; /// Typestate for an ADC configuration with no clock source selected pub struct NoClockSet; From 4a5caaf88a859a4acb73b1e13b6f8c2e1a44d58b Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 20 Jan 2025 14:00:30 +1300 Subject: [PATCH 81/82] Improve docs --- src/adc.rs | 12 ++++++++++-- src/i2c.rs | 9 +++++++-- src/spi.rs | 19 ++++++++++++++++++- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/adc.rs b/src/adc.rs index 7894eca..d380e59 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,5 +1,13 @@ //! Analog to Digital Converter (ADC) //! +//! Begin configuration by calling `AdcConfig::new()` or `::default()`. Once fully configured an `Adc` will be returned. +//! +//! `Adc` can read from a channel by calling `.read()` (an implementation of the embedded_hal `OneShot` trait) to return an ADC count. +//! +//! The `.count_to_mv()` method is available to convert an ADC count to a voltage in millivolts, given a reference voltage. +//! +//! As a convenience, `.read_voltage_mv()` combines `.read()` and `.count_to_mv()`. +//! //! The ADC may read from any of the following pins: //! //! P1.0 - P1.7 (channels 0 to 7), P5.0 - P5.3 (channels 8 to 11). @@ -356,7 +364,7 @@ impl AdcConfig { } } -/// Controls the onboard ADC +/// Controls the onboard ADC. The `read()` method is available through the embedded_hal `OneShot` trait. pub struct Adc { adc_reg: ADC, is_waiting: bool, @@ -418,7 +426,7 @@ impl Adc { /// Begins a single ADC conversion if one isn't already underway, enabling the ADC in the process. /// - /// If the result is ready it is returned as a voltage in millivolts, otherwise returns `WouldBlock`. + /// If the result is ready it is returned as a voltage in millivolts based on `ref_voltage_mv`, otherwise returns `WouldBlock`. /// /// If you instead want a raw count you should use the `.read()` method from the `OneShot` trait implementation. pub fn read_voltage_mv>(&mut self, pin: &mut PIN, ref_voltage_mv: u16) -> nb::Result { diff --git a/src/i2c.rs b/src/i2c.rs index d6f6f56..9a56f3d 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -2,11 +2,16 @@ //! //! Peripherals eUSCI_B0 and eUSCI_B1 can be used for I2C communication. //! +//! Begin by calling `I2cBusConfig::new()`. Once configured an `I2cBus` will be returned. +//! +//! `I2cBus` implements the blocking embedded_hal `Read`, `Write` and `WriteRead` traits. +//! Passing a `u8` address to these methods uses 7-bit addressing, passing a `u16` uses 10-bit addressing. +//! //! Pins used: //! -//! eUSCI_B0: {SCL:P1.3, SDA:P1.2} +//! eUSCI_B0: {SCL: `P1.3`, SDA: `P1.2`}. `P1.1` can optionally be used as an external clock source. //! -//! eUSCI_B1: {SCL:P4.7, SDA:P4.6} +//! eUSCI_B1: {SCL: `P4.7`, SDA: `P4.6`}. `P4.5` can optionally be used as an external clock source. //! use crate::clock::{Aclk, Smclk}; diff --git a/src/spi.rs b/src/spi.rs index 965f39d..f8773e9 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -1,4 +1,21 @@ -//! embedded_hal SPI implmentation +//! SPI +//! +//! Peripherals eUSCI_A0, eUSCI_A1, eUSCI_B0 and eUSCI_B1 can be used for SPI communication. +//! +//! Begin by calling `SpiBusConfig::new()`. Once configured an `SpiBus` will be returned. +//! +//! `SpiBus` implements the embedded_hal `FullDuplex` trait with non-blocking `.read()` and `.send()` methods, +//! and the blocking embedded_hal `Transfer` and `Write` traits, with `.transfer()` and `.write()` methods respectively. +//! +//! Pins used: +//! +//! eUSCI_A0: {MISO: `P1.7`, MOSI: `P1.6`, SCLK: `P1.5`}. `P1.4` can optionally used as a hardware-controlled chip select pin. +//! +//! eUSCI_A1: {MISO: `P4.3`, MOSI: `P4.2`, SCLK: `P4.1`}. `P4.0` can optionally used as a hardware-controlled chip select pin. +//! +//! eUSCI_B0: {MISO: `P1.3`, MOSI: `P1.2`, SCLK: `P1.1`}. `P1.0` can optionally used as a hardware-controlled chip select pin. +//! +//! eUSCI_B1: {MISO: `P4.7`, MOSI: `P4.6`, SCLK: `P4.5`}. `P4.4` can optionally used as a hardware-controlled chip select pin. use crate::hal::spi::{Mode, Phase, Polarity}; use crate::{ clock::{Aclk, Smclk}, From 1629c9ba2b197b5dc1938857513d0c05ed4d076f Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Mon, 20 Jan 2025 16:59:06 +1300 Subject: [PATCH 82/82] Add notes about reference voltage --- src/adc.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/adc.rs b/src/adc.rs index d380e59..39e6d5c 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -8,6 +8,8 @@ //! //! As a convenience, `.read_voltage_mv()` combines `.read()` and `.count_to_mv()`. //! +//! Currently the only supported ADC voltage reference is `AVCC`, the operating voltage of the MSP430. +//! //! The ADC may read from any of the following pins: //! //! P1.0 - P1.7 (channels 0 to 7), P5.0 - P5.3 (channels 8 to 11). @@ -245,6 +247,8 @@ pub struct ClockSet(ClockSource); /// Configuration object for an ADC. /// +/// Currently the only supported voltage reference is AVCC. +/// /// The default configuration is based on the default register values: /// - Predivider = 1 and clock divider = 1 /// - 10-bit resolution @@ -413,6 +417,8 @@ impl Adc { } /// Convert an ADC count to a voltage value in millivolts. + /// + /// `ref_voltage_mv` is the reference voltage of the ADC in millivolts. pub fn count_to_mv(&self, count: u16, ref_voltage_mv: u16) -> u16 { use crate::pac::adc::adcctl2::ADCRES_A; let resolution = match self.adc_reg.adcctl2.read().adcres().variant() {