diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-07-13 03:42:35 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-07-13 03:42:35 +0200 |
| commit | 549ddf7fc885946d8b28024363148735d7a88425 (patch) | |
| tree | c96a99138be33048dc73b1c04c6a2532bf11d3ad | |
| parent | b8e23bc74e59b53695d0ed0141b1e31a755ea032 (diff) | |
| parent | 1a4c13cf262869d88a38575eb14abd09900e8e01 (diff) | |
Merge pull request #285 from embassy-rs/rp-spi-clock
RP improvements
| -rw-r--r-- | embassy-macros/src/chip/rp.rs | 6 | ||||
| -rw-r--r-- | embassy-rp/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-rp/src/clocks.rs | 1 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 8 | ||||
| -rw-r--r-- | embassy-rp/src/spi.rs | 75 | ||||
| -rw-r--r-- | embassy-rp/src/timer.rs | 187 | ||||
| -rw-r--r-- | examples/rp/src/bin/blinky.rs | 5 |
7 files changed, 248 insertions, 38 deletions
diff --git a/embassy-macros/src/chip/rp.rs b/embassy-macros/src/chip/rp.rs index 04211f8f3..d40a44963 100644 --- a/embassy-macros/src/chip/rp.rs +++ b/embassy-macros/src/chip/rp.rs | |||
| @@ -3,10 +3,16 @@ use proc_macro2::TokenStream; | |||
| 3 | use quote::quote; | 3 | use quote::quote; |
| 4 | 4 | ||
| 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { | 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { |
| 6 | let embassy_path = embassy_prefix.append("embassy").path(); | ||
| 6 | let embassy_rp_path = embassy_prefix.append("embassy_rp").path(); | 7 | let embassy_rp_path = embassy_prefix.append("embassy_rp").path(); |
| 7 | quote!( | 8 | quote!( |
| 8 | use #embassy_rp_path::{interrupt, peripherals}; | 9 | use #embassy_rp_path::{interrupt, peripherals}; |
| 9 | 10 | ||
| 10 | let p = #embassy_rp_path::init(#config); | 11 | let p = #embassy_rp_path::init(#config); |
| 12 | |||
| 13 | let alarm = unsafe { <#embassy_rp_path::peripherals::TIMER_ALARM0 as #embassy_path::util::Steal>::steal() }; | ||
| 14 | let mut alarm = #embassy_rp_path::timer::Alarm::new(alarm); | ||
| 15 | let alarm = unsafe { make_static(&mut alarm) }; | ||
| 16 | executor.set_alarm(alarm); | ||
| 11 | ) | 17 | ) |
| 12 | } | 18 | } |
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 503d2c706..ba9984a80 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -22,6 +22,6 @@ cortex-m-rt = "0.6.13" | |||
| 22 | cortex-m = "0.7.1" | 22 | cortex-m = "0.7.1" |
| 23 | critical-section = "0.2.1" | 23 | critical-section = "0.2.1" |
| 24 | 24 | ||
| 25 | rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="91fa122b4923fdc02462a39ec109b161aedb29b4", features = ["rt"] } | 25 | rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="2ce29ba58ad904d3995ce65bb46807e853f1fbf9", features = ["rt"] } |
| 26 | #rp2040-pac2 = { path = "../../rp/rp2040-pac2" } | 26 | #rp2040-pac2 = { path = "../../rp/rp2040-pac2", features = ["rt"] } |
| 27 | embedded-hal = { version = "0.2.4", features = [ "unproven" ] } | 27 | embedded-hal = { version = "0.2.4", features = [ "unproven" ] } |
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 6835ecad3..3082cd0d7 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs | |||
| @@ -4,6 +4,7 @@ use crate::{pac, reset}; | |||
| 4 | 4 | ||
| 5 | const XOSC_MHZ: u32 = 12; | 5 | const XOSC_MHZ: u32 = 12; |
| 6 | 6 | ||
| 7 | /// safety: must be called exactly once at bootup | ||
| 7 | pub unsafe fn init() { | 8 | pub unsafe fn init() { |
| 8 | // Reset everything except: | 9 | // Reset everything except: |
| 9 | // - QSPI (we're using it to run this code!) | 10 | // - QSPI (we're using it to run this code!) |
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 1e6417c98..aefc86c00 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -11,10 +11,12 @@ pub use rp2040_pac2 as pac; | |||
| 11 | pub(crate) mod fmt; | 11 | pub(crate) mod fmt; |
| 12 | 12 | ||
| 13 | pub mod interrupt; | 13 | pub mod interrupt; |
| 14 | pub use embassy_macros::interrupt; | ||
| 14 | 15 | ||
| 15 | pub mod dma; | 16 | pub mod dma; |
| 16 | pub mod gpio; | 17 | pub mod gpio; |
| 17 | pub mod spi; | 18 | pub mod spi; |
| 19 | pub mod timer; | ||
| 18 | pub mod uart; | 20 | pub mod uart; |
| 19 | 21 | ||
| 20 | mod clocks; | 22 | mod clocks; |
| @@ -64,6 +66,11 @@ embassy_extras::peripherals! { | |||
| 64 | SPI0, | 66 | SPI0, |
| 65 | SPI1, | 67 | SPI1, |
| 66 | 68 | ||
| 69 | TIMER_ALARM0, | ||
| 70 | TIMER_ALARM1, | ||
| 71 | TIMER_ALARM2, | ||
| 72 | TIMER_ALARM3, | ||
| 73 | |||
| 67 | DMA_CH0, | 74 | DMA_CH0, |
| 68 | DMA_CH1, | 75 | DMA_CH1, |
| 69 | DMA_CH2, | 76 | DMA_CH2, |
| @@ -100,6 +107,7 @@ pub fn init(_config: config::Config) -> Peripherals { | |||
| 100 | 107 | ||
| 101 | unsafe { | 108 | unsafe { |
| 102 | clocks::init(); | 109 | clocks::init(); |
| 110 | timer::init(); | ||
| 103 | } | 111 | } |
| 104 | 112 | ||
| 105 | peripherals | 113 | peripherals |
diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 6c3aabfbe..ec2437e93 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs | |||
| @@ -33,6 +33,33 @@ pub struct Spi<'d, T: Instance> { | |||
| 33 | phantom: PhantomData<&'d mut T>, | 33 | phantom: PhantomData<&'d mut T>, |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | fn div_roundup(a: u32, b: u32) -> u32 { | ||
| 37 | (a + b - 1) / b | ||
| 38 | } | ||
| 39 | |||
| 40 | fn calc_prescs(freq: u32) -> (u8, u8) { | ||
| 41 | let clk_peri = crate::clocks::clk_peri_freq(); | ||
| 42 | |||
| 43 | // final SPI frequency: spi_freq = clk_peri / presc / postdiv | ||
| 44 | // presc must be in 2..=254, and must be even | ||
| 45 | // postdiv must be in 1..=256 | ||
| 46 | |||
| 47 | // divide extra by 2, so we get rid of the "presc must be even" requirement | ||
| 48 | let ratio = div_roundup(clk_peri, freq * 2); | ||
| 49 | if ratio > 127 * 256 { | ||
| 50 | panic!("Requested too low SPI frequency"); | ||
| 51 | } | ||
| 52 | |||
| 53 | let presc = div_roundup(ratio, 256); | ||
| 54 | let postdiv = if presc == 1 { | ||
| 55 | ratio | ||
| 56 | } else { | ||
| 57 | div_roundup(ratio, presc) | ||
| 58 | }; | ||
| 59 | |||
| 60 | ((presc * 2) as u8, (postdiv - 1) as u8) | ||
| 61 | } | ||
| 62 | |||
| 36 | impl<'d, T: Instance> Spi<'d, T> { | 63 | impl<'d, T: Instance> Spi<'d, T> { |
| 37 | pub fn new( | 64 | pub fn new( |
| 38 | inner: impl Unborrow<Target = T>, | 65 | inner: impl Unborrow<Target = T>, |
| @@ -46,45 +73,14 @@ impl<'d, T: Instance> Spi<'d, T> { | |||
| 46 | 73 | ||
| 47 | unsafe { | 74 | unsafe { |
| 48 | let p = inner.regs(); | 75 | let p = inner.regs(); |
| 76 | let (presc, postdiv) = calc_prescs(config.frequency); | ||
| 49 | 77 | ||
| 50 | let clk_peri = crate::clocks::clk_peri_freq(); | 78 | p.cpsr().write(|w| w.set_cpsdvsr(presc)); |
| 51 | assert!(config.frequency <= clk_peri); | ||
| 52 | |||
| 53 | // final SPI frequency: spi_freq = clk_peri / presc / postdiv | ||
| 54 | // presc must be in 2..=254, and must be even | ||
| 55 | // postdiv must be in 1..=256 | ||
| 56 | |||
| 57 | fn div_roundup(a: u32, b: u32) -> u32 { | ||
| 58 | (a + b - 1) / b | ||
| 59 | } | ||
| 60 | |||
| 61 | // divide extra by 2, so we get rid of the "presc must be even" requirement | ||
| 62 | let ratio = div_roundup(clk_peri, config.frequency * 2); | ||
| 63 | if ratio > 127 * 256 { | ||
| 64 | panic!("Requested too low SPI frequency"); | ||
| 65 | } | ||
| 66 | |||
| 67 | let presc = div_roundup(ratio, 256); | ||
| 68 | let postdiv = if presc == 1 { | ||
| 69 | ratio | ||
| 70 | } else { | ||
| 71 | div_roundup(ratio, presc) | ||
| 72 | }; | ||
| 73 | |||
| 74 | debug!( | ||
| 75 | "SPI: requested freq {=u32}, actual freq {=u32}, presc {=u32}, postdiv {=u32}", | ||
| 76 | config.frequency, | ||
| 77 | clk_peri / (presc * 2 * postdiv), | ||
| 78 | presc * 2, | ||
| 79 | postdiv | ||
| 80 | ); | ||
| 81 | |||
| 82 | p.cpsr().write(|w| w.set_cpsdvsr((presc * 2) as _)); | ||
| 83 | p.cr0().write(|w| { | 79 | p.cr0().write(|w| { |
| 84 | w.set_dss(0b0111); // 8bit | 80 | w.set_dss(0b0111); // 8bit |
| 85 | w.set_spo(config.polarity == ehnb::Polarity::IdleHigh); | 81 | w.set_spo(config.polarity == ehnb::Polarity::IdleHigh); |
| 86 | w.set_sph(config.phase == ehnb::Phase::CaptureOnSecondTransition); | 82 | w.set_sph(config.phase == ehnb::Phase::CaptureOnSecondTransition); |
| 87 | w.set_scr((postdiv - 1) as u8); | 83 | w.set_scr(postdiv); |
| 88 | }); | 84 | }); |
| 89 | p.cr1().write(|w| { | 85 | p.cr1().write(|w| { |
| 90 | w.set_sse(true); // enable | 86 | w.set_sse(true); // enable |
| @@ -139,6 +135,17 @@ impl<'d, T: Instance> Spi<'d, T> { | |||
| 139 | while p.sr().read().bsy() {} | 135 | while p.sr().read().bsy() {} |
| 140 | } | 136 | } |
| 141 | } | 137 | } |
| 138 | |||
| 139 | pub fn set_frequency(&mut self, freq: u32) { | ||
| 140 | let (presc, postdiv) = calc_prescs(freq); | ||
| 141 | let p = self.inner.regs(); | ||
| 142 | unsafe { | ||
| 143 | p.cpsr().write(|w| w.set_cpsdvsr(presc)); | ||
| 144 | p.cr0().modify(|w| { | ||
| 145 | w.set_scr(postdiv); | ||
| 146 | }); | ||
| 147 | } | ||
| 148 | } | ||
| 142 | } | 149 | } |
| 143 | 150 | ||
| 144 | impl<'d, T: Instance> eh::Write<u8> for Spi<'d, T> { | 151 | impl<'d, T: Instance> eh::Write<u8> for Spi<'d, T> { |
diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs new file mode 100644 index 000000000..5baa02607 --- /dev/null +++ b/embassy-rp/src/timer.rs | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use critical_section::CriticalSection; | ||
| 3 | use embassy::interrupt::{Interrupt, InterruptExt}; | ||
| 4 | use embassy::util::CriticalSectionMutex as Mutex; | ||
| 5 | |||
| 6 | use crate::{interrupt, pac}; | ||
| 7 | |||
| 8 | struct AlarmState { | ||
| 9 | timestamp: Cell<u64>, | ||
| 10 | callback: Cell<Option<(fn(*mut ()), *mut ())>>, | ||
| 11 | } | ||
| 12 | unsafe impl Send for AlarmState {} | ||
| 13 | |||
| 14 | const ALARM_COUNT: usize = 4; | ||
| 15 | const DUMMY_ALARM: AlarmState = AlarmState { | ||
| 16 | timestamp: Cell::new(0), | ||
| 17 | callback: Cell::new(None), | ||
| 18 | }; | ||
| 19 | |||
| 20 | static ALARMS: Mutex<[AlarmState; ALARM_COUNT]> = Mutex::new([DUMMY_ALARM; ALARM_COUNT]); | ||
| 21 | |||
| 22 | fn now() -> u64 { | ||
| 23 | loop { | ||
| 24 | unsafe { | ||
| 25 | let hi = pac::TIMER.timerawh().read(); | ||
| 26 | let lo = pac::TIMER.timerawl().read(); | ||
| 27 | let hi2 = pac::TIMER.timerawh().read(); | ||
| 28 | if hi == hi2 { | ||
| 29 | return (hi as u64) << 32 | (lo as u64); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | struct Timer; | ||
| 36 | impl embassy::time::Clock for Timer { | ||
| 37 | fn now(&self) -> u64 { | ||
| 38 | now() | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | pub trait AlarmInstance { | ||
| 43 | fn alarm_num(&self) -> usize; | ||
| 44 | } | ||
| 45 | |||
| 46 | impl AlarmInstance for crate::peripherals::TIMER_ALARM0 { | ||
| 47 | fn alarm_num(&self) -> usize { | ||
| 48 | 0 | ||
| 49 | } | ||
| 50 | } | ||
| 51 | impl AlarmInstance for crate::peripherals::TIMER_ALARM1 { | ||
| 52 | fn alarm_num(&self) -> usize { | ||
| 53 | 1 | ||
| 54 | } | ||
| 55 | } | ||
| 56 | impl AlarmInstance for crate::peripherals::TIMER_ALARM2 { | ||
| 57 | fn alarm_num(&self) -> usize { | ||
| 58 | 2 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | impl AlarmInstance for crate::peripherals::TIMER_ALARM3 { | ||
| 62 | fn alarm_num(&self) -> usize { | ||
| 63 | 3 | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | pub struct Alarm<T: AlarmInstance> { | ||
| 68 | inner: T, | ||
| 69 | } | ||
| 70 | |||
| 71 | impl<T: AlarmInstance> Alarm<T> { | ||
| 72 | pub fn new(inner: T) -> Self { | ||
| 73 | Self { inner } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | impl<T: AlarmInstance> embassy::time::Alarm for Alarm<T> { | ||
| 78 | fn set_callback(&self, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 79 | let n = self.inner.alarm_num(); | ||
| 80 | critical_section::with(|cs| { | ||
| 81 | let alarm = &ALARMS.borrow(cs)[n]; | ||
| 82 | alarm.callback.set(Some((callback, ctx))); | ||
| 83 | }) | ||
| 84 | } | ||
| 85 | |||
| 86 | fn set(&self, timestamp: u64) { | ||
| 87 | let n = self.inner.alarm_num(); | ||
| 88 | |||
| 89 | critical_section::with(|cs| { | ||
| 90 | let alarm = &ALARMS.borrow(cs)[n]; | ||
| 91 | alarm.timestamp.set(timestamp); | ||
| 92 | |||
| 93 | // Arm it. | ||
| 94 | // Note that we're not checking the high bits at all. This means the irq may fire early | ||
| 95 | // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire | ||
| 96 | // it is checked if the alarm time has passed. | ||
| 97 | unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; | ||
| 98 | |||
| 99 | let now = now(); | ||
| 100 | |||
| 101 | // If alarm timestamp has passed, trigger it instantly. | ||
| 102 | // This disarms it. | ||
| 103 | if timestamp <= now { | ||
| 104 | trigger_alarm(n, cs); | ||
| 105 | } | ||
| 106 | }) | ||
| 107 | } | ||
| 108 | |||
| 109 | fn clear(&self) { | ||
| 110 | self.set(u64::MAX); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | fn check_alarm(n: usize) { | ||
| 115 | critical_section::with(|cs| { | ||
| 116 | let alarm = &ALARMS.borrow(cs)[n]; | ||
| 117 | let timestamp = alarm.timestamp.get(); | ||
| 118 | if timestamp <= now() { | ||
| 119 | trigger_alarm(n, cs) | ||
| 120 | } else { | ||
| 121 | // Not elapsed, arm it again. | ||
| 122 | // This can happen if it was set more than 2^32 us in the future. | ||
| 123 | unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; | ||
| 124 | } | ||
| 125 | }); | ||
| 126 | |||
| 127 | // clear the irq | ||
| 128 | unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) } | ||
| 129 | } | ||
| 130 | |||
| 131 | fn trigger_alarm(n: usize, cs: CriticalSection) { | ||
| 132 | // disarm | ||
| 133 | unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } | ||
| 134 | |||
| 135 | let alarm = &ALARMS.borrow(cs)[n]; | ||
| 136 | alarm.timestamp.set(u64::MAX); | ||
| 137 | |||
| 138 | // Call after clearing alarm, so the callback can set another alarm. | ||
| 139 | if let Some((f, ctx)) = alarm.callback.get() { | ||
| 140 | f(ctx); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// safety: must be called exactly once at bootup | ||
| 145 | pub unsafe fn init() { | ||
| 146 | // init alarms | ||
| 147 | critical_section::with(|cs| { | ||
| 148 | let alarms = ALARMS.borrow(cs); | ||
| 149 | for a in alarms { | ||
| 150 | a.timestamp.set(u64::MAX); | ||
| 151 | } | ||
| 152 | }); | ||
| 153 | |||
| 154 | // enable all irqs | ||
| 155 | pac::TIMER.inte().write(|w| { | ||
| 156 | w.set_alarm(0, true); | ||
| 157 | w.set_alarm(1, true); | ||
| 158 | w.set_alarm(2, true); | ||
| 159 | w.set_alarm(3, true); | ||
| 160 | }); | ||
| 161 | interrupt::TIMER_IRQ_0::steal().enable(); | ||
| 162 | interrupt::TIMER_IRQ_1::steal().enable(); | ||
| 163 | interrupt::TIMER_IRQ_2::steal().enable(); | ||
| 164 | interrupt::TIMER_IRQ_3::steal().enable(); | ||
| 165 | |||
| 166 | embassy::time::set_clock(&Timer); | ||
| 167 | } | ||
| 168 | |||
| 169 | #[interrupt] | ||
| 170 | unsafe fn TIMER_IRQ_0() { | ||
| 171 | check_alarm(0) | ||
| 172 | } | ||
| 173 | |||
| 174 | #[interrupt] | ||
| 175 | unsafe fn TIMER_IRQ_1() { | ||
| 176 | check_alarm(1) | ||
| 177 | } | ||
| 178 | |||
| 179 | #[interrupt] | ||
| 180 | unsafe fn TIMER_IRQ_2() { | ||
| 181 | check_alarm(2) | ||
| 182 | } | ||
| 183 | |||
| 184 | #[interrupt] | ||
| 185 | unsafe fn TIMER_IRQ_3() { | ||
| 186 | check_alarm(3) | ||
| 187 | } | ||
diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index a162ed2f7..9c4767458 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs | |||
| @@ -11,6 +11,7 @@ mod example_common; | |||
| 11 | 11 | ||
| 12 | use defmt::*; | 12 | use defmt::*; |
| 13 | use embassy::executor::Spawner; | 13 | use embassy::executor::Spawner; |
| 14 | use embassy::time::{Duration, Timer}; | ||
| 14 | use embassy_rp::{gpio, Peripherals}; | 15 | use embassy_rp::{gpio, Peripherals}; |
| 15 | use gpio::{Level, Output}; | 16 | use gpio::{Level, Output}; |
| 16 | 17 | ||
| @@ -21,10 +22,10 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 21 | loop { | 22 | loop { |
| 22 | info!("led on!"); | 23 | info!("led on!"); |
| 23 | led.set_high(); | 24 | led.set_high(); |
| 24 | cortex_m::asm::delay(1_000_000); | 25 | Timer::after(Duration::from_secs(1)).await; |
| 25 | 26 | ||
| 26 | info!("led off!"); | 27 | info!("led off!"); |
| 27 | led.set_low(); | 28 | led.set_low(); |
| 28 | cortex_m::asm::delay(1_000_000); | 29 | Timer::after(Duration::from_secs(1)).await; |
| 29 | } | 30 | } |
| 30 | } | 31 | } |
