From e4e42337e0e5ffd3414ec5c47c052d888621d5de Mon Sep 17 00:00:00 2001 From: cs2dsb Date: Wed, 20 Nov 2019 10:00:45 +0000 Subject: [PATCH 01/10] Added all timers for all variants as described by CubeMX (#133) * Added all timers for all variants as described by CubeMX --- CHANGELOG.md | 3 +- README.md | 11 +- src/adc.rs | 17 ++- src/lib.rs | 13 ++- src/pwm_input.rs | 278 +++++++++++++++++++++++------------------------ src/rcc.rs | 83 +++++++++++--- src/timer.rs | 151 ++++++++++++++++++++++--- 7 files changed, 372 insertions(+), 184 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d80918..376a899b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `select_frequency` method to RTC - Unidirectional DMA support for SPI (TX only) - Added USB driver for `stm32f102` and `stm32f103` devices - +- Added all timers for all variants as described by CubeMX. Commented out {TIM9, TIM10} for XL and {TIM12, TIM13, TIM14} for XL and F100-HIGH due to missing fields for those devices in stm32-rs. - ADC measurement now can be run by timer ### Breaking changes @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Starting the timer does not generate interrupt requests anymore - Make MAPR::mapr() private - i2c mode now takes Hertz instead of a generic u32 +- Timers that were previously incorrectly available without medium/high/xl density features may now be missing ### Fixed diff --git a/README.md b/README.md index c7631e56..e038d86e 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,19 @@ This crate supports multiple microcontrollers in the stm32f1 family. Which specific microcontroller you want to build for has to be -specified with a feature, for example `stm32f103`. +specified with a feature, for example `stm32f103`. If no microcontroller is specified, the crate will not compile. +You may also need to specify the density of the device with `medium`, `high` or `xl` +to enable certain peripherals. Generally the density can be determined by the 2nd character +after the number in the device name (i.e. For STM32F103C6U, the 6 indicates a low-density +device) but check the datasheet or CubeMX to be sure. +* 4, 6 => low density, no feature required +* 8, B => `medium` feature +* C, D, E => `high` feature +* F, G => `xl` feature + ### Supported Microcontrollers * `stm32f100` diff --git a/src/adc.rs b/src/adc.rs index 88388ebd..07e5ce51 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -11,10 +11,13 @@ use core::sync::atomic::{self, Ordering}; use cortex_m::asm::delay; use crate::stm32::ADC1; -#[cfg(any( +#[cfg(feature = "stm32f103")] +use crate::stm32::ADC2; +#[cfg(all( feature = "stm32f103", + feature = "high", ))] -use crate::stm32::ADC2; +use crate::stm32::ADC3; /// Continuous mode pub struct Continuous; @@ -484,11 +487,17 @@ adc_hal! { ADC1: (adc1), } -#[cfg(any( +#[cfg(feature = "stm32f103")] +adc_hal! { + ADC2: (adc2), +} + +#[cfg(all( feature = "stm32f103", + feature = "high", ))] adc_hal! { - ADC2: (adc2), + ADC3: (adc3), } pub struct AdcPayload { diff --git a/src/lib.rs b/src/lib.rs index f57dd17b..fc119fc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,14 +94,21 @@ #![no_std] +// Some pac crates have fields specified in such a way that they are safe and other +// have them unsafe (likely an SVD error that needs patching). Unsafe blocks for +// one device cause warnings for the safe devices. This disables that warning. +#![allow(unused_unsafe)] + // If no target specified, print error message. #[cfg(not(any(feature = "stm32f100", feature = "stm32f101", feature = "stm32f103")))] compile_error!("Target not found. A `--feature ` is required."); // If any two or more targets are specified, print error message. -#[cfg(all(feature = "stm32f100", feature = "stm32f101"))] -#[cfg(all(feature = "stm32f100", feature = "stm32f103"))] -#[cfg(all(feature = "stm32f101", feature = "stm32f103"))] +#[cfg(any( + all(feature = "stm32f100", feature = "stm32f101"), + all(feature = "stm32f100", feature = "stm32f103"), + all(feature = "stm32f101", feature = "stm32f103"), +))] compile_error!("Multiple targets specified. Only a single `--feature ` can be specified."); #[cfg(feature = "device-selected")] diff --git a/src/pwm_input.rs b/src/pwm_input.rs index 46938fe5..bc490077 100644 --- a/src/pwm_input.rs +++ b/src/pwm_input.rs @@ -4,11 +4,15 @@ use core::marker::PhantomData; use core::mem; -use crate::stm32::{DBGMCU as DBG, TIM1, TIM2, TIM3, TIM4}; +use crate::pac::{DBGMCU as DBG, TIM2, TIM3}; +#[cfg(feature = "medium")] +use crate::pac::TIM4; use crate::afio::MAPR; -use crate::gpio::gpioa::{PA0, PA1, PA15, PA6, PA7, PA8, PA9}; -use crate::gpio::gpiob::{PB3, PB4, PB5, PB6, PB7}; +use crate::gpio::gpioa::{PA0, PA1, PA15, PA6, PA7}; +use crate::gpio::gpiob::{PB3, PB4, PB5}; +#[cfg(feature = "medium")] +use crate::gpio::gpiob::{PB6, PB7}; use crate::gpio::{Floating, Input}; use crate::rcc::Clocks; use crate::time::Hertz; @@ -18,6 +22,7 @@ pub trait Pins { const REMAP: u8; } +#[cfg(feature = "medium")] impl Pins for (PB6>, PB7>) { const REMAP: u8 = 0b0; } @@ -38,10 +43,6 @@ impl Pins for (PA15>, PB3>) { const REMAP: u8 = 0b11; } -impl Pins for (PA8>, PA9>) { - const REMAP: u8 = 0b00; -} - /// PWM Input pub struct PwmInput { _timer: PhantomData, @@ -132,6 +133,7 @@ impl Timer { } } +#[cfg(feature = "medium")] impl Timer { pub fn pwm_input( mut self, @@ -161,146 +163,140 @@ fn compute_arr_presc(freq: u32, clock: u32) -> (u16, u16) { (core::cmp::max(1, arr as u16), presc as u16) } macro_rules! hal { - ($($TIMX:ident: ($timX:ident, $pclkX:ident ),)+) => { - $( - fn $timX( - tim: $TIMX, - _pins: PINS, - clk: Hertz, - mode : Configuration, + ($($TIMX:ident: ($timX:ident, $pclkX:ident ),)+) => { + $( + fn $timX( + tim: $TIMX, + _pins: PINS, + clk: Hertz, + mode : Configuration, ) -> PwmInput<$TIMX,PINS> - where - PINS: Pins<$TIMX>, - T : Into - { - use crate::pwm_input::Configuration::*; - // Disable capture on both channels during setting - // (for Channel X bit is CCXE) - tim.ccer.modify(|_,w| w.cc1e().clear_bit().cc2e().clear_bit() - .cc1p().clear_bit().cc2p().set_bit()); - - - // Define the direction of the channel (input/output) - // and the used input - tim.ccmr1_input().modify( |_,w| w.cc1s().ti1().cc2s().ti1()); - - tim.dier.write(|w| w.cc1ie().set_bit()); - - // Configure slave mode control register - // Selects the trigger input to be used to synchronize the counter - // 101: Filtered Timer Input 1 (TI1FP1) - // --------------------------------------- - // Slave Mode Selection : - // 100: Reset Mode - Rising edge of the selected trigger input (TRGI) - // reinitializes the counter and generates an update of the registers. - tim.smcr.modify( |_,w| unsafe {w.ts().bits(0b101).sms().bits(0b100)}); - - match mode { - Frequency(f) => { - let freq = f.into().0; - let max_freq = if freq > 5 {freq/5} else {1}; - let (arr,presc) = compute_arr_presc(max_freq, clk.0); - tim.arr.write(|w| w.arr().bits(arr)); - tim.psc.write(|w| w.psc().bits(presc) ); - - }, - DutyCycle(f) => { - let freq = f.into().0; - let max_freq = if freq > 2 {freq/2 + freq/4 + freq/8} else {1}; - let (arr,presc) = compute_arr_presc(max_freq, clk.0); - tim.arr.write(|w| w.arr().bits(arr)); - tim.psc.write(|w| w.psc().bits(presc) ); - }, - RawFrequency(f) => { - let freq = f.into().0; - let (arr,presc) = compute_arr_presc(freq, clk.0); - tim.arr.write(|w| w.arr().bits(arr)); - tim.psc.write(|w| w.psc().bits(presc) ); - } - RawValues{arr, presc} => { - tim.arr.write(|w| w.arr().bits(arr)); - tim.psc.write(|w| w.psc().bits(presc) ); - - } + where + PINS: Pins<$TIMX>, + T : Into + { + use crate::pwm_input::Configuration::*; + // Disable capture on both channels during setting + // (for Channel X bit is CCXE) + tim.ccer.modify(|_,w| w.cc1e().clear_bit().cc2e().clear_bit() + .cc1p().clear_bit().cc2p().set_bit()); + + // Define the direction of the channel (input/output) + // and the used input + tim.ccmr1_input().modify( |_,w| w.cc1s().ti1().cc2s().ti1()); + + tim.dier.write(|w| w.cc1ie().set_bit()); + + // Configure slave mode control register + // Selects the trigger input to be used to synchronize the counter + // 101: Filtered Timer Input 1 (TI1FP1) + // --------------------------------------- + // Slave Mode Selection : + // 100: Reset Mode - Rising edge of the selected trigger input (TRGI) + // reinitializes the counter and generates an update of the registers. + tim.smcr.modify( |_,w| unsafe {w.ts().bits(0b101).sms().bits(0b100)}); + + match mode { + Frequency(f) => { + let freq = f.into().0; + let max_freq = if freq > 5 {freq/5} else {1}; + let (arr,presc) = compute_arr_presc(max_freq, clk.0); + tim.arr.write(|w| w.arr().bits(arr)); + tim.psc.write(|w| w.psc().bits(presc) ); + }, + DutyCycle(f) => { + let freq = f.into().0; + let max_freq = if freq > 2 {freq/2 + freq/4 + freq/8} else {1}; + let (arr,presc) = compute_arr_presc(max_freq, clk.0); + tim.arr.write(|w| w.arr().bits(arr)); + tim.psc.write(|w| w.psc().bits(presc) ); + }, + RawFrequency(f) => { + let freq = f.into().0; + let (arr,presc) = compute_arr_presc(freq, clk.0); + tim.arr.write(|w| w.arr().bits(arr)); + tim.psc.write(|w| w.psc().bits(presc) ); + } + RawValues{arr, presc} => { + tim.arr.write(|w| w.arr().bits(arr)); + tim.psc.write(|w| w.psc().bits(presc) ); + } + } + + // Enable Capture on both channels + tim.ccer.modify(|_,w| w.cc1e().set_bit().cc2e().set_bit()); + + tim.cr1.modify(|_,w| w.cen().set_bit()); + unsafe { mem::MaybeUninit::uninit().assume_init() } } - // Enable Capture on both channels - tim.ccer.modify(|_,w| w.cc1e().set_bit().cc2e().set_bit()); - - - tim.cr1.modify(|_,w| w.cen().set_bit()); - - - unsafe { mem::MaybeUninit::uninit().assume_init() } - } - - impl PwmInput<$TIMX,PINS> where PINS : Pins<$TIMX> { - /// Return the frequency sampled by the timer - pub fn read_frequency(&self, mode : ReadMode, clocks : &Clocks) -> Result { - - match mode { - ReadMode::WaitForNextCapture => self.wait_for_capture(), - _ => (), - } - let presc = unsafe { (*$TIMX::ptr()).psc.read().bits() as u16}; - let ccr1 = unsafe { (*$TIMX::ptr()).ccr1.read().bits() as u16}; - - // Formulas : - // - // F_timer = F_pclk / (PSC+1)*(ARR+1) - // Frac_arr = (CCR1+1)/(ARR+1) - // F_signal = F_timer/Frac_arr - // <=> F_signal = [(F_plck)/((PSC+1)*(ARR+1))] * [(ARR+1)/(CCR1+1)] - // <=> F_signal = F_pclk / ((PSC+1)*(CCR1+1)) - // - // Where : - // * PSC is the prescaler register - // * ARR is the auto-reload register - // * F_timer is the number of time per second where the timer overflow under normal - // condition - // - if ccr1 == 0 { - Err(Error::FrequencyTooLow) - } - else { - let clk : u32 = clocks.$pclkX().0; - Ok(Hertz(clk/((presc+1) as u32*(ccr1 + 1)as u32))) + impl PwmInput<$TIMX,PINS> where PINS : Pins<$TIMX> { + /// Return the frequency sampled by the timer + pub fn read_frequency(&self, mode : ReadMode, clocks : &Clocks) -> Result { + match mode { + ReadMode::WaitForNextCapture => self.wait_for_capture(), + _ => (), + } + + let presc = unsafe { (*$TIMX::ptr()).psc.read().bits() as u16}; + let ccr1 = unsafe { (*$TIMX::ptr()).ccr1.read().bits() as u16}; + + // Formulas : + // + // F_timer = F_pclk / (PSC+1)*(ARR+1) + // Frac_arr = (CCR1+1)/(ARR+1) + // F_signal = F_timer/Frac_arr + // <=> F_signal = [(F_plck)/((PSC+1)*(ARR+1))] * [(ARR+1)/(CCR1+1)] + // <=> F_signal = F_pclk / ((PSC+1)*(CCR1+1)) + // + // Where : + // * PSC is the prescaler register + // * ARR is the auto-reload register + // * F_timer is the number of time per second where the timer overflow under normal + // condition + // + if ccr1 == 0 { + Err(Error::FrequencyTooLow) + } else { + let clk : u32 = clocks.$pclkX().0; + Ok(Hertz(clk/((presc+1) as u32*(ccr1 + 1)as u32))) + } + } + + /// Return the duty in the form of a fraction : (duty_cycle/period) + pub fn read_duty(&self, mode : ReadMode) -> Result<(u16,u16),Error> { + match mode { + ReadMode::WaitForNextCapture => self.wait_for_capture(), + _ => (), + } + + // Formulas : + // Duty_cycle = (CCR2+1)/(CCR1+1) + let ccr1 = unsafe { (*$TIMX::ptr()).ccr1.read().bits() as u16}; + let ccr2 = unsafe { (*$TIMX::ptr()).ccr2.read().bits() as u16}; + if ccr1 == 0 { + Err(Error::FrequencyTooLow) + } else { + Ok((ccr2,ccr1)) + } + } + + /// Wait until the timer has captured a period + fn wait_for_capture(&self) { + unsafe { (*$TIMX::ptr()).sr.write(|w| w.uif().clear_bit().cc1if().clear_bit().cc1of().clear_bit())}; + while unsafe { (*$TIMX::ptr()).sr.read().cc1if().bit_is_clear()} {} + } } - } - - - /// Return the duty in the form of a fraction : (duty_cycle/period) - pub fn read_duty(&self, mode : ReadMode) -> Result<(u16,u16),Error> { - - match mode { - ReadMode::WaitForNextCapture => self.wait_for_capture(), - _ => (), - } - - // Formulas : - // Duty_cycle = (CCR2+1)/(CCR1+1) - let ccr1 = unsafe { (*$TIMX::ptr()).ccr1.read().bits() as u16}; - let ccr2 = unsafe { (*$TIMX::ptr()).ccr2.read().bits() as u16}; - if ccr1 == 0 { - Err(Error::FrequencyTooLow) - } else { - Ok((ccr2,ccr1)) - } - } - - /// Wait until the timer has captured a period - fn wait_for_capture(&self) { - unsafe { (*$TIMX::ptr()).sr.write(|w| w.uif().clear_bit().cc1if().clear_bit().cc1of().clear_bit())}; - while unsafe { (*$TIMX::ptr()).sr.read().cc1if().bit_is_clear()} {} - } - } + )+ + } +} - )+ - } +hal! { + TIM2: (tim2, pclk1_tim), + TIM3: (tim3, pclk1_tim), } +#[cfg(feature = "medium")] hal! { - TIM2: (tim2, pclk1_tim), - TIM3: (tim3, pclk1_tim), - TIM4: (tim4, pclk1_tim), + TIM4: (tim4, pclk1_tim), } diff --git a/src/rcc.rs b/src/rcc.rs index 0173d981..4991ec33 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -269,7 +269,7 @@ impl CFGR { _ => (true, false), }; - let apre_bits = self + let apre_bits: u8 = self .adcclk .map(|adcclk| match pclk2 / adcclk { 0..=2 => 0b00, @@ -530,18 +530,15 @@ macro_rules! ahb_bus { } } -#[cfg(any( - feature = "stm32f100", - feature = "stm32f103", -))] -bus! { - TIM1 => (APB2, tim1en, tim1rst), -} #[cfg(feature = "stm32f103")] bus! { ADC2 => (APB2, adc2en, adc2rst), CAN1 => (APB1, canen, canrst), } +#[cfg(all(feature = "stm32f103", feature = "high",))] +bus! { + ADC3 => (APB2, adc3en, adc3rst), +} bus! { ADC1 => (APB2, adc1en, adc1rst), AFIO => (APB2, afioen, afiorst), @@ -554,19 +551,11 @@ bus! { I2C2 => (APB1, i2c2en, i2c2rst), SPI1 => (APB2, spi1en, spi1rst), SPI2 => (APB1, spi2en, spi2rst), - TIM2 => (APB1, tim2en, tim2rst), - TIM3 => (APB1, tim3en, tim3rst), - TIM4 => (APB1, tim4en, tim4rst), USART1 => (APB2, usart1en, usart1rst), USART2 => (APB1, usart2en, usart2rst), USART3 => (APB1, usart3en, usart3rst), WWDG => (APB1, wwdgen, wwdgrst), } -#[cfg(feature = "high")] -bus! { - TIM6 => (APB1, tim6en, tim6rst), - TIM7 => (APB1, tim7en, tim7rst), -} ahb_bus! { CRC => (crcen), @@ -578,3 +567,65 @@ ahb_bus! { ahb_bus! { FSMC => (fsmcen), } + +bus! { + TIM2 => (APB1, tim2en, tim2rst), + TIM3 => (APB1, tim3en, tim3rst), +} + +#[cfg(any(feature = "stm32f100", feature = "stm32f103", feature = "stm32f105",))] +bus! { + TIM1 => (APB2, tim1en, tim1rst), +} + +#[cfg(any(feature = "stm32f100", feature = "stm32f105", feature = "high",))] +bus! { + TIM6 => (APB1, tim6en, tim6rst), +} + +#[cfg(any( + all( + feature = "high", + any(feature = "stm32f101", feature = "stm32f103", feature = "stm32f107",) + ), + any(feature = "stm32f100", feature = "stm32f105",) +))] +bus! { + TIM7 => (APB1, tim7en, tim7rst), +} + +#[cfg(feature = "stm32f100")] +bus! { + TIM15 => (APB2, tim15en, tim15rst), + TIM16 => (APB2, tim16en, tim16rst), + TIM17 => (APB2, tim17en, tim17rst), +} + +#[cfg(feature = "medium")] +bus! { + TIM4 => (APB1, tim4en, tim4rst), +} + +#[cfg(feature = "high")] +bus! { + TIM5 => (APB1, tim5en, tim5rst), +} + +#[cfg(any(feature = "xl", all(feature = "stm32f100", feature = "high",)))] +bus! { + TIM12 => (APB1, tim12en, tim12rst), + TIM13 => (APB1, tim13en, tim13rst), + TIM14 => (APB1, tim14en, tim14rst), +} + +#[cfg(all(feature = "stm32f103", feature = "high",))] +bus! { + TIM8 => (APB2, tim8en, tim8rst), +} + +#[cfg(feature = "xl")] +bus! { + TIM9 => (APB2, tim9en, tim9rst), + TIM10 => (APB2, tim10en, tim10rst), + TIM11 => (APB2, tim11en, tim11rst), +} diff --git a/src/timer.rs b/src/timer.rs index 9c94eb70..abe6ab06 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,7 +1,45 @@ //! # Timer use crate::hal::timer::{CountDown, Periodic}; -use crate::pac::{DBGMCU as DBG, TIM1, TIM2, TIM3, TIM4}; +use crate::pac::{DBGMCU as DBG, TIM2, TIM3}; +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +use crate::pac::TIM1; +#[cfg(feature = "medium")] +use crate::pac::TIM4; +#[cfg(feature = "high")] +use crate::pac::TIM5; +#[cfg(any( + feature = "stm32f100", + feature = "stm32f105", + feature = "high", +))] +use crate::pac::TIM6; +#[cfg(any( + all( + feature = "high", + any( + feature = "stm32f101", + feature = "stm32f103", + feature = "stm32f107", + ), + ), + any( + feature = "stm32f100", + feature = "stm32f105", +)))] +use crate::pac::TIM7; +#[cfg(all( + feature = "stm32f103", + feature = "high", +))] +use crate::pac::TIM8; +#[cfg(feature = "stm32f100")] +use crate::pac::{TIM15, TIM16, TIM17}; + use cast::{u16, u32, u64}; use cortex_m::peripheral::syst::SystClkSource; use cortex_m::peripheral::SYST; @@ -124,7 +162,7 @@ impl CountDown for CountDownTimer { impl Periodic for CountDownTimer {} macro_rules! hal { - ($($TIMX:ident: ($timX:ident, $timbase:ident, $pclkX:ident, $dbg_timX_stop:ident),)+) => { + ($($TIMX:ident: ($timX:ident, $pclkX:ident, $dbg_timX_stop:ident$(,$master_timbase:ident)*),)+) => { $( impl Timer<$TIMX> { /// Initialize timer @@ -147,16 +185,19 @@ macro_rules! hal { timer } - pub fn start_master(self, timeout: T, mode: crate::pac::$timbase::cr2::MMS_A) -> CountDownTimer<$TIMX> - where - T: Into, - { - let Self { tim, clk } = self; - let mut timer = CountDownTimer { tim, clk }; - timer.tim.cr2.modify(|_,w| w.mms().variant(mode)); - timer.start(timeout); - timer - } + $( + /// Starts timer in count down mode at a given frequency and additionally configures the timers master mode + pub fn start_master(self, timeout: T, mode: crate::pac::$master_timbase::cr2::MMS_A) -> CountDownTimer<$TIMX> + where + T: Into, + { + let Self { tim, clk } = self; + let mut timer = CountDownTimer { tim, clk }; + timer.tim.cr2.modify(|_,w| w.mms().variant(mode)); + timer.start(timeout); + timer + } + )? /// Resets timer peripheral #[inline(always)] @@ -248,7 +289,7 @@ macro_rules! hal { let (psc, arr) = compute_arr_presc(timeout.into().0, self.clk.0); self.tim.psc.write(|w| w.psc().bits(psc) ); - self.tim.arr.write(|w| w.arr().bits(arr) ); + self.tim.arr.write(|w| unsafe { w.arr().bits(arr) }); // Trigger an update event to load the prescaler value to the clock self.reset(); @@ -280,16 +321,90 @@ fn compute_arr_presc(freq: u32, clock: u32) -> (u16, u16) { (psc, arr) } +hal! { + TIM2: (tim2, pclk1_tim, dbg_tim2_stop, tim2), + TIM3: (tim3, pclk1_tim, dbg_tim3_stop, tim2), +} + +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +hal! { + TIM1: (tim1, pclk2_tim, dbg_tim1_stop, tim1), +} + #[cfg(any( feature = "stm32f100", - feature = "stm32f103", + feature = "stm32f105", + feature = "high", +))] +hal! { + TIM6: (tim6, pclk2_tim, dbg_tim6_stop, tim6), +} + +#[cfg(any( + all( + feature = "high", + any( + feature = "stm32f101", + feature = "stm32f103", + feature = "stm32f107", + ), + ), + any( + feature = "stm32f100", + feature = "stm32f105", +)))] +hal! { + TIM7: (tim7, pclk2_tim, dbg_tim7_stop, tim6), +} + +#[cfg(feature = "stm32f100")] +hal! { + TIM15: (tim15, pclk2_tim, dbg_tim15_stop), + TIM16: (tim16, pclk2_tim, dbg_tim16_stop), + TIM17: (tim17, pclk2_tim, dbg_tim17_stop), +} + +#[cfg(feature = "medium")] +hal! { + TIM4: (tim4, pclk1_tim, dbg_tim4_stop, tim2), +} + +#[cfg(feature = "high")] +hal! { + TIM5: (tim5, pclk2_tim, dbg_tim5_stop, tim2), +} + +#[cfg(all( + feature = "stm32f103", + feature = "high", ))] hal! { - TIM1: (tim1, tim1, pclk2_tim, dbg_tim1_stop), + TIM8: (tim8, pclk2_tim, dbg_tim8_stop, tim1), } +//TODO: restore these timers once stm32-rs has been updated +/* + * dbg_tim(12-13)_stop fields missing from 103 xl in stm32-rs + * dbg_tim(9-10)_stop fields missing from 101 xl in stm32-rs +#[cfg(any( + feature = "xl", + all( + feature = "stm32f100", + feature = "high", +)))] +hal! { + TIM12: (tim12, pclk2_tim, dbg_tim12_stop), + TIM13: (tim13, pclk2_tim, dbg_tim13_stop), + TIM14: (tim14, pclk2_tim, dbg_tim14_stop), +} +#[cfg(feature = "xl")] hal! { - TIM2: (tim2, tim2, pclk1_tim, dbg_tim2_stop), - TIM3: (tim3, tim2, pclk1_tim, dbg_tim3_stop), - TIM4: (tim4, tim2, pclk1_tim, dbg_tim4_stop), + TIM9: (tim9, pclk2_tim, dbg_tim9_stop), + TIM10: (tim10, pclk2_tim, dbg_tim10_stop), + TIM11: (tim11, pclk2_tim, dbg_tim11_stop), } +*/ \ No newline at end of file From 7eace3e379362743f8b7e57b8670ff1fa0354e02 Mon Sep 17 00:00:00 2001 From: cs2dsb Date: Thu, 21 Nov 2019 20:50:44 +0000 Subject: [PATCH 02/10] Removed crate level allow(unused_unsafe) in favour of attributes on the specific instances (#140) --- src/lib.rs | 5 ----- src/rtc.rs | 3 +++ src/timer.rs | 3 +++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fc119fc2..20931f83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,11 +94,6 @@ #![no_std] -// Some pac crates have fields specified in such a way that they are safe and other -// have them unsafe (likely an SVD error that needs patching). Unsafe blocks for -// one device cause warnings for the safe devices. This disables that warning. -#![allow(unused_unsafe)] - // If no target specified, print error message. #[cfg(not(any(feature = "stm32f100", feature = "stm32f101", feature = "stm32f103")))] compile_error!("Target not found. A `--feature ` is required."); diff --git a/src/rtc.rs b/src/rtc.rs index 0f7ef528..ba4a7091 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -103,6 +103,9 @@ impl Rtc { // Set alarm time // See section 18.3.5 for explanation let alarm_value = counter_value - 1; + + // TODO: Remove this `allow` once these fields are made safe for stm32f100 + #[allow(unused_unsafe)] self.perform_write(|s| { s.regs.alrh.write(|w| unsafe{w.alrh().bits((alarm_value >> 16) as u16)}); s.regs.alrl.write(|w| unsafe{w.alrl().bits(alarm_value as u16)}); diff --git a/src/timer.rs b/src/timer.rs index abe6ab06..9a33e485 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -289,6 +289,9 @@ macro_rules! hal { let (psc, arr) = compute_arr_presc(timeout.into().0, self.clk.0); self.tim.psc.write(|w| w.psc().bits(psc) ); + + // TODO: Remove this `allow` once this field is made safe for stm32f100 + #[allow(unused_unsafe)] self.tim.arr.write(|w| unsafe { w.arr().bits(arr) }); // Trigger an update event to load the prescaler value to the clock From 306412d9f0bb2b8d062084ece6b3c60401c79d8d Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Sun, 24 Nov 2019 19:15:48 +0100 Subject: [PATCH 03/10] Unbump as_slice --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index da9a5a80..b6c101cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ cortex-m = "0.6.0" nb = "0.1.2" cortex-m-rt = "0.6.8" stm32f1 = "0.9.0" -as-slice = "0.2" +as-slice = "0.1" [dependencies.void] default-features = false From d0067500e48dec5ddaf031afbcf13542bf235864 Mon Sep 17 00:00:00 2001 From: Zgarbul Andrey Date: Sun, 1 Dec 2019 14:57:55 +0300 Subject: [PATCH 04/10] Pwm remap: another try (#147) * implement remap for Timers when pwm is used move Remap to timer.rs, add gpio::Mode --- CHANGELOG.md | 2 + examples/pwm_custom.rs | 33 +---- src/gpio.rs | 11 +- src/pwm.rs | 268 ++++++++++++++++------------------------- src/pwm_input.rs | 121 ++++++++++++------- src/qei.rs | 92 +++++++++----- src/timer.rs | 129 +++++++++++++++++++- 7 files changed, 380 insertions(+), 276 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 376a899b..0b59062e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Added `Mode` marker trait for `gpio` pins that correspondent to pin mode. - RCC `Bus` trait + private `Enable` and `Reset` traits - Added `micros_since` and `reset` methods to timer - Added `select_frequency` method to RTC @@ -19,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Breaking changes +- Implement more pin combinations for PWM configuration, added PWM for TIM1 (API for custom PWM pins was removed as it's no more needed) - Bump `stm32f1` dependency (`0.9.0`) - `void::Void` replaced with `Infallible` where it is possible - Change timer/pwm init API diff --git a/examples/pwm_custom.rs b/examples/pwm_custom.rs index aaa07c1f..e11abf1a 100644 --- a/examples/pwm_custom.rs +++ b/examples/pwm_custom.rs @@ -8,38 +8,13 @@ use panic_halt as _; use cortex_m::asm; use stm32f1xx_hal::{ - gpio::gpiob::{PB4, PB5}, - gpio::{Alternate, PushPull}, - pac::{self,TIM3}, + pac, prelude::*, - pwm::{Pins, Pwm, C1, C2}, timer::Timer, }; use cortex_m_rt::entry; -// Using PB5 channel for TIM3 PWM output -// struct MyChannels(PB5>); -// impl Pins for MyChannels { -// const REMAP: u8 = 0b10; -// const C1: bool = false; -// const C2: bool = true; -// const C3: bool = false; -// const C4: bool = false; -// type Channels = Pwm; -// } - -// Using PB4 and PB5 channels for TIM3 PWM output -struct MyChannels(PB4>, PB5>); -impl Pins for MyChannels { - const REMAP: u8 = 0b10; - const C1: bool = true; - const C2: bool = true; - const C3: bool = false; - const C4: bool = false; - type Channels = (Pwm, Pwm); -} - #[entry] fn main() -> ! { let p = pac::Peripherals::take().unwrap(); @@ -59,11 +34,7 @@ fn main() -> ! { let p1 = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl); let mut pwm = Timer::tim3(p.TIM3, &clocks, &mut rcc.apb1) - .pwm( - MyChannels(p0, p1), - &mut afio.mapr, - 1.khz(), - ); + .pwm((p0, p1), &mut afio.mapr, 1.khz()); let max = pwm.0.get_max_duty(); diff --git a/src/gpio.rs b/src/gpio.rs index 8be1c732..7207f8a3 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -27,6 +27,10 @@ pub trait GpioExt { fn split(self, apb2: &mut APB2) -> Self::Parts; } + +/// Marker trait for pin mode detection. +pub trait Mode {} + /// Marker trait for active states. pub trait Active {} @@ -95,7 +99,8 @@ macro_rules! gpio { State, Active, Debugger, - Pxx + Pxx, + Mode, }; /// GPIO parts @@ -216,6 +221,8 @@ macro_rules! gpio { pub type $PXx = Pxx; + impl Mode for Generic {} + $( /// Pin @@ -223,6 +230,8 @@ macro_rules! gpio { _mode: PhantomData, } + impl Mode for $PXi {} + impl $PXi { /// Put the pin in an active state. The caller /// must enforce that the pin is really in this diff --git a/src/pwm.rs b/src/pwm.rs index 5dbfe60a..cb84f0d0 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -14,14 +14,17 @@ ```rust let gpioa = ..; // Set up and split GPIOA let pins = ( - gpioa.pa0.into_alternate_push_pull(), - gpioa.pa1.into_alternate_push_pull(), - gpioa.pa2.into_alternate_push_pull(), - gpioa.pa3.into_alternate_push_pull(), + gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl), + gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl), + gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl), + gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl), ); ``` - Then call the `pwm` function on the corresponding timer: + Then call the `pwm` function on the corresponding timer. + + NOTE: In some cases you need to specify remap you need, especially for TIM2 + (see [Alternate function remapping](super::timer)): ``` let device: pac::Peripherals = ..; @@ -29,104 +32,13 @@ // Put the timer in PWM mode using the specified pins // with a frequency of 100 hz. let (c0, c1, c2, c3) = Timer::tim2(device.TIM2, &clocks, &mut rcc.apb1) - .pwm(pins, &mut afio.mapr, 100.hz()); + .pwm::(pins, &mut afio.mapr, 100.hz()); // Set the duty cycle of channel 0 to 50% c0.set_duty(c0.get_max_duty() / 2); // PWM outputs are disabled by default c0.enable() ``` - - ## Usage for custom channel combinations - - Note that crate itself defines only basic channel combinations for default AFIO remappings, - where all the channels are enabled. Meanwhile it is possible to configure PWM for any custom - selection of channels. The `Pins` trait shows the mapping between timers, output pins and - channels. So this trait needs to be implemented for the custom combination of channels and - AFIO remappings. However minor additional efforts are needed since it is not possible to - implement a foreign trait for a foreign type. The trick is to use the newtype pattern. - - The first example selects PB5 channel for TIM3 PWM output: - - ``` - struct MyChannels(PB5>); - - impl Pins for MyChannels { - const REMAP: u8 = 0b10; // use TIM3 AFIO remapping for PB4, PB5, PB0, PB1 pins - const C1: bool = false; - const C2: bool = true; // use channel C2 - const C3: bool = false; - const C4: bool = false; - type Channels = Pwm; - } - ``` - - The second example selects PC8 and PC9 channels for TIM3 PWM output: - - ``` - struct MyChannels(PC8>, PC9>); - - impl Pins for MyChannels { - const REMAP: u8 = 0b11; // use TIM3 AFIO remapping for PC6, PC7, PC8, PC9 pins - const C1: bool = false; - const C2: bool = false; - const C3: bool = true; // use channel C3 - const C4: bool = true; // use channel C4 - type Channels = (Pwm, Pwm); - } - ``` - - REMAP value and channel pins should be specified according to the stm32f1xx specification, - e.g. the section 9.3.7 "Timer alternate function remapping" in RM0008 Rev 20. - - Finally, here is a complete example for two channels: - - ``` - use stm32f1xx_hal::stm32::TIM3; - use stm32f1xx_hal::gpio::gpiob::{PB4, PB5}; - use stm32f1xx_hal::gpio::{Alternate, PushPull}; - use stm32f1xx_hal::pwm::{Pins, Pwm, C1, C2, C3, C4}; - - struct MyChannels(PB4>, PB5>); - - impl Pins for MyChannels - { - const REMAP: u8 = 0b10; - const C1: bool = true; - const C2: bool = true; - const C3: bool = false; - const C4: bool = false; - type Channels = (Pwm, Pwm) - } - - ... - - let gpiob = ..; // Set up and split GPIOB - - let p1 = gpiob.pb4.into_alternate_push_pull(&mut gpiob.crl); - let p2 = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl); - - ... - - let device: pac::Peripherals = ..; - - let (mut c1, mut c2) = device.TIM3.pwm( - MyChannels(p1, p2), - &mut afio.mapr, - 100.hz(), - clocks, - &mut rcc.apb1 - ); - - // Set the duty cycle of channels C1 and C2 to 50% and 25% respectively - c1.set_duty(c1.get_max_duty() / 2); - c2.set_duty(c2.get_max_duty() / 4); - - // PWM outputs are disabled by default - c1.enable() - c2.enable() - - ``` */ use core::marker::PhantomData; @@ -134,85 +46,102 @@ use core::mem; use cast::{u16, u32}; use crate::hal; -use crate::pac::{TIM2, TIM3, TIM4}; +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +use crate::pac::TIM1; +use crate::pac::{TIM2, TIM3}; +#[cfg(feature = "medium")] +use crate::pac::TIM4; use crate::afio::MAPR; use crate::bb; -use crate::gpio::gpioa::{PA0, PA1, PA2, PA3, PA6, PA7}; -use crate::gpio::gpiob::{PB0, PB1, PB6, PB7, PB8, PB9}; -use crate::gpio::{Alternate, PushPull}; +use crate::gpio::{self, Alternate, PushPull}; use crate::time::Hertz; use crate::timer::Timer; -pub trait Pins { - const REMAP: u8; - const C1: bool; - const C2: bool; - const C3: bool; - const C4: bool; +pub trait Pins { + const C1: bool = false; + const C2: bool = false; + const C3: bool = false; + const C4: bool = false; type Channels; } -impl Pins - for ( - PA0>, - PA1>, - PA2>, - PA3>, - ) -{ - const REMAP: u8 = 0b00; - const C1: bool = true; - const C2: bool = true; - const C3: bool = true; - const C4: bool = true; - type Channels = (Pwm, Pwm, Pwm, Pwm); +use crate::timer::sealed::{Remap, Ch1, Ch2, Ch3, Ch4}; +macro_rules! pins_impl { + ( $( ( $($PINX:ident),+ ), ( $($TRAIT:ident),+ ), ( $($ENCHX:ident),* ); )+ ) => { + $( + #[allow(unused_parens)] + impl Pins for ($($PINX),+) + where + REMAP: Remap, + $($PINX: $TRAIT + gpio::Mode>,)+ + { + $(const $ENCHX: bool = true;)+ + type Channels = ($(Pwm),+); + } + )+ + }; } -impl Pins - for ( - PA6>, - PA7>, - PB0>, - PB1>, - ) -{ - const REMAP: u8 = 0b00; - const C1: bool = true; - const C2: bool = true; - const C3: bool = true; - const C4: bool = true; - type Channels = (Pwm, Pwm, Pwm, Pwm); -} +pins_impl!( + (P1, P2, P3, P4), (Ch1, Ch2, Ch3, Ch4), (C1, C2, C3, C4); + (P2, P3, P4), (Ch2, Ch3, Ch4), (C2, C3, C4); + (P1, P3, P4), (Ch1, Ch3, Ch4), (C1, C3, C4); + (P1, P2, P4), (Ch1, Ch2, Ch4), (C1, C2, C4); + (P1, P2, P3), (Ch1, Ch2, Ch3), (C1, C2, C3); + (P3, P4), (Ch3, Ch4), (C3, C4); + (P2, P4), (Ch2, Ch4), (C2, C4); + (P2, P3), (Ch2, Ch3), (C2, C3); + (P1, P4), (Ch1, Ch4), (C1, C4); + (P1, P3), (Ch1, Ch3), (C1, C3); + (P1, P2), (Ch1, Ch2), (C1, C2); + (P1), (Ch1), (C1); + (P2), (Ch2), (C2); + (P3), (Ch3), (C3); + (P4), (Ch4), (C4); +); + +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +impl Timer { + pub fn pwm( + self, + _pins: PINS, + mapr: &mut MAPR, + freq: T, + ) -> PINS::Channels + where + REMAP: Remap, + PINS: Pins, + T: Into, + { + mapr.modify_mapr(|_, w| unsafe { w.tim1_remap().bits(REMAP::REMAP) }); -impl Pins - for ( - PB6>, - PB7>, - PB8>, - PB9>, - ) -{ - const REMAP: u8 = 0b0; - const C1: bool = true; - const C2: bool = true; - const C3: bool = true; - const C4: bool = true; - type Channels = (Pwm, Pwm, Pwm, Pwm); + let Self { tim, clk } = self; + tim1(tim, _pins, freq.into(), clk) + } } impl Timer { - pub fn pwm( + pub fn pwm( self, _pins: PINS, mapr: &mut MAPR, freq: T, ) -> PINS::Channels where - PINS: Pins, + REMAP: Remap, + PINS: Pins, T: Into, { - mapr.modify_mapr(|_, w| unsafe { w.tim2_remap().bits(PINS::REMAP) }); + mapr.modify_mapr(|_, w| unsafe { w.tim2_remap().bits(REMAP::REMAP) }); let Self { tim, clk } = self; tim2(tim, _pins, freq.into(), clk) @@ -220,35 +149,38 @@ impl Timer { } impl Timer { - pub fn pwm( + pub fn pwm( self, _pins: PINS, mapr: &mut MAPR, freq: T, ) -> PINS::Channels where - PINS: Pins, + REMAP: Remap, + PINS: Pins, T: Into, { - mapr.modify_mapr(|_, w| unsafe { w.tim3_remap().bits(PINS::REMAP) }); + mapr.modify_mapr(|_, w| unsafe { w.tim3_remap().bits(REMAP::REMAP) }); let Self { tim, clk } = self; tim3(tim, _pins, freq.into(), clk) } } +#[cfg(feature = "medium")] impl Timer { - pub fn pwm( + pub fn pwm( self, _pins: PINS, mapr: &mut MAPR, freq: T, ) -> PINS::Channels where - PINS: Pins, + REMAP: Remap, + PINS: Pins, T: Into, { - mapr.modify_mapr(|_, w| w.tim4_remap().bit(PINS::REMAP == 1)); + mapr.modify_mapr(|_, w| w.tim4_remap().bit(REMAP::REMAP == 1)); let Self { tim, clk } = self; tim4(tim, _pins, freq.into(), clk) @@ -268,14 +200,15 @@ pub struct C4; macro_rules! hal { ($($TIMX:ident: ($timX:ident),)+) => { $( - fn $timX( + fn $timX( tim: $TIMX, _pins: PINS, freq: Hertz, clk: Hertz, ) -> PINS::Channels where - PINS: Pins<$TIMX>, + REMAP: Remap, + PINS: Pins, { if PINS::C1 { tim.ccmr1_output() @@ -415,8 +348,21 @@ macro_rules! hal { } } +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +hal! { + TIM1: (tim1), +} + hal! { TIM2: (tim2), TIM3: (tim3), +} + +#[cfg(feature = "medium")] +hal! { TIM4: (tim4), } diff --git a/src/pwm_input.rs b/src/pwm_input.rs index bc490077..79d65670 100644 --- a/src/pwm_input.rs +++ b/src/pwm_input.rs @@ -4,48 +4,37 @@ use core::marker::PhantomData; use core::mem; -use crate::pac::{DBGMCU as DBG, TIM2, TIM3}; +use crate::pac::DBGMCU as DBG; +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +use crate::pac::TIM1; +use crate::pac::{TIM2, TIM3}; #[cfg(feature = "medium")] use crate::pac::TIM4; use crate::afio::MAPR; -use crate::gpio::gpioa::{PA0, PA1, PA15, PA6, PA7}; -use crate::gpio::gpiob::{PB3, PB4, PB5}; -#[cfg(feature = "medium")] -use crate::gpio::gpiob::{PB6, PB7}; -use crate::gpio::{Floating, Input}; +use crate::gpio::{self, Floating, Input}; use crate::rcc::Clocks; use crate::time::Hertz; use crate::timer::Timer; -pub trait Pins { - const REMAP: u8; -} - -#[cfg(feature = "medium")] -impl Pins for (PB6>, PB7>) { - const REMAP: u8 = 0b0; -} - -impl Pins for (PA6>, PA7>) { - const REMAP: u8 = 0b00; -} +pub trait Pins {} -impl Pins for (PB4>, PB5>) { - const REMAP: u8 = 0b10; -} +use crate::timer::sealed::{Remap, Ch1, Ch2}; -impl Pins for (PA0>, PA1>) { - const REMAP: u8 = 0b00; -} - -impl Pins for (PA15>, PB3>) { - const REMAP: u8 = 0b11; -} +impl Pins for (P1, P2) +where + REMAP: Remap, + P1: Ch1 + gpio::Mode>, + P2: Ch2 + gpio::Mode> {} /// PWM Input -pub struct PwmInput { +pub struct PwmInput { _timer: PhantomData, + _remap: PhantomData, _pins: PhantomData, } @@ -95,19 +84,45 @@ where RawValues { arr: u16, presc: u16 }, } +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +impl Timer { + pub fn pwm_input( + mut self, + pins: PINS, + mapr: &mut MAPR, + dbg: &mut DBG, + mode: Configuration, + ) -> PwmInput + where + REMAP: Remap, + PINS: Pins, + T: Into, + { + mapr.modify_mapr(|_, w| unsafe { w.tim1_remap().bits(REMAP::REMAP) }); + self.stop_in_debug(dbg, false); + let Self { tim, clk } = self; + tim1(tim, pins, clk, mode) + } +} + impl Timer { - pub fn pwm_input( + pub fn pwm_input( mut self, pins: PINS, mapr: &mut MAPR, dbg: &mut DBG, mode: Configuration, - ) -> PwmInput + ) -> PwmInput where - PINS: Pins, + REMAP: Remap, + PINS: Pins, T: Into, { - mapr.modify_mapr(|_, w| unsafe { w.tim2_remap().bits(PINS::REMAP) }); + mapr.modify_mapr(|_, w| unsafe { w.tim2_remap().bits(REMAP::REMAP) }); self.stop_in_debug(dbg, false); let Self { tim, clk } = self; tim2(tim, pins, clk, mode) @@ -115,18 +130,19 @@ impl Timer { } impl Timer { - pub fn pwm_input( + pub fn pwm_input( mut self, pins: PINS, mapr: &mut MAPR, dbg: &mut DBG, mode: Configuration, - ) -> PwmInput + ) -> PwmInput where - PINS: Pins, + REMAP: Remap, + PINS: Pins, T: Into, { - mapr.modify_mapr(|_, w| unsafe { w.tim3_remap().bits(PINS::REMAP) }); + mapr.modify_mapr(|_, w| unsafe { w.tim3_remap().bits(REMAP::REMAP) }); self.stop_in_debug(dbg, false); let Self { tim, clk } = self; tim3(tim, pins, clk, mode) @@ -135,18 +151,19 @@ impl Timer { #[cfg(feature = "medium")] impl Timer { - pub fn pwm_input( + pub fn pwm_input( mut self, pins: PINS, mapr: &mut MAPR, dbg: &mut DBG, mode: Configuration, - ) -> PwmInput + ) -> PwmInput where - PINS: Pins, + REMAP: Remap, + PINS: Pins, T: Into, { - mapr.modify_mapr(|_, w| w.tim4_remap().bit(PINS::REMAP == 1)); + mapr.modify_mapr(|_, w| w.tim4_remap().bit(REMAP::REMAP == 1)); self.stop_in_debug(dbg, false); let Self { tim, clk } = self; tim4(tim, pins, clk, mode) @@ -165,14 +182,15 @@ fn compute_arr_presc(freq: u32, clock: u32) -> (u16, u16) { macro_rules! hal { ($($TIMX:ident: ($timX:ident, $pclkX:ident ),)+) => { $( - fn $timX( + fn $timX( tim: $TIMX, _pins: PINS, clk: Hertz, mode : Configuration, - ) -> PwmInput<$TIMX,PINS> + ) -> PwmInput<$TIMX, REMAP, PINS> where - PINS: Pins<$TIMX>, + REMAP: Remap, + PINS: Pins, T : Into { use crate::pwm_input::Configuration::*; @@ -230,7 +248,11 @@ macro_rules! hal { unsafe { mem::MaybeUninit::uninit().assume_init() } } - impl PwmInput<$TIMX,PINS> where PINS : Pins<$TIMX> { + impl PwmInput<$TIMX, REMAP, PINS> + where + REMAP: Remap, + PINS: Pins, + { /// Return the frequency sampled by the timer pub fn read_frequency(&self, mode : ReadMode, clocks : &Clocks) -> Result { match mode { @@ -291,6 +313,15 @@ macro_rules! hal { } } +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +hal! { + TIM1: (tim1, pclk2_tim), +} + hal! { TIM2: (tim2, pclk1_tim), TIM3: (tim3, pclk1_tim), diff --git a/src/qei.rs b/src/qei.rs index 938193b6..d72c464b 100644 --- a/src/qei.rs +++ b/src/qei.rs @@ -1,43 +1,55 @@ //! # Quadrature Encoder Interface use core::u16; +use core::marker::PhantomData; + use crate::hal::{self, Direction}; -use crate::pac::{TIM2, TIM3, TIM4}; +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +use crate::pac::TIM1; +use crate::pac::{TIM2, TIM3}; +#[cfg(feature = "medium")] +use crate::pac::TIM4; use crate::afio::MAPR; -use crate::gpio::gpioa::{PA0, PA1, PA6, PA7}; -use crate::gpio::gpiob::{PB6, PB7}; -use crate::gpio::{Floating, Input}; - -use crate::timer::Timer; -pub trait Pins { - const REMAP: u8; -} - -impl Pins for (PA0>, PA1>) { - const REMAP: u8 = 0b00; -} +use crate::timer::{Timer, sealed::Remap}; +use crate::pwm_input::Pins; -impl Pins for (PA6>, PA7>) { - const REMAP: u8 = 0b00; +pub struct Qei { + tim: TIM, + pins: PINS, + _remap: PhantomData, } -impl Pins for (PB6>, PB7>) { - const REMAP: u8 = 0b00; -} +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +impl Timer { + pub fn qei(self, pins: PINS, mapr: &mut MAPR) -> Qei + where + REMAP: Remap, + PINS: Pins, + { + mapr.modify_mapr(|_, w| unsafe { w.tim1_remap().bits(REMAP::REMAP) }); -pub struct Qei { - tim: TIM, - pins: PINS, + let Self { tim, clk: _ } = self; + Qei::_tim1(tim, pins) + } } impl Timer { - pub fn qei(self, pins: PINS, mapr: &mut MAPR) -> Qei + pub fn qei(self, pins: PINS, mapr: &mut MAPR) -> Qei where - PINS: Pins, + REMAP: Remap, + PINS: Pins, { - mapr.modify_mapr(|_, w| unsafe { w.tim2_remap().bits(PINS::REMAP) }); + mapr.modify_mapr(|_, w| unsafe { w.tim2_remap().bits(REMAP::REMAP) }); let Self { tim, clk: _ } = self; Qei::_tim2(tim, pins) @@ -45,23 +57,26 @@ impl Timer { } impl Timer { - pub fn qei(self, pins: PINS, mapr: &mut MAPR) -> Qei + pub fn qei(self, pins: PINS, mapr: &mut MAPR) -> Qei where - PINS: Pins, + REMAP: Remap, + PINS: Pins, { - mapr.modify_mapr(|_, w| unsafe { w.tim3_remap().bits(PINS::REMAP) }); + mapr.modify_mapr(|_, w| unsafe { w.tim3_remap().bits(REMAP::REMAP) }); let Self { tim, clk: _ } = self; Qei::_tim3(tim, pins) } } +#[cfg(feature = "medium")] impl Timer { - pub fn qei(self, pins: PINS, mapr: &mut MAPR) -> Qei + pub fn qei(self, pins: PINS, mapr: &mut MAPR) -> Qei where - PINS: Pins, + REMAP: Remap, + PINS: Pins, { - mapr.modify_mapr(|_, w| w.tim4_remap().bit(PINS::REMAP == 1)); + mapr.modify_mapr(|_, w| w.tim4_remap().bit(REMAP::REMAP == 1)); let Self { tim, clk: _ } = self; Qei::_tim4(tim, pins) @@ -71,7 +86,7 @@ impl Timer { macro_rules! hal { ($($TIMX:ident: ($timX:ident, $timXen:ident, $timXrst:ident),)+) => { $( - impl Qei<$TIMX, PINS> { + impl Qei<$TIMX, REMAP, PINS> { fn $timX(tim: $TIMX, pins: PINS) -> Self { // Configure TxC1 and TxC2 as captures tim.ccmr1_input().write(|w| w.cc1s().ti1().cc2s().ti2()); @@ -94,7 +109,7 @@ macro_rules! hal { tim.arr.write(|w| w.arr().bits(u16::MAX)); tim.cr1.write(|w| w.cen().set_bit()); - Qei { tim, pins } + Qei { tim, pins, _remap: PhantomData } } pub fn release(self) -> ($TIMX, PINS) { @@ -102,7 +117,7 @@ macro_rules! hal { } } - impl hal::Qei for Qei<$TIMX, PINS> { + impl hal::Qei for Qei<$TIMX, REMAP, PINS> { type Count = u16; fn count(&self) -> u16 { @@ -122,8 +137,19 @@ macro_rules! hal { } } +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +hal! { + TIM1: (_tim1, tim1en, tim1rst), +} hal! { TIM2: (_tim2, tim2en, tim2rst), TIM3: (_tim3, tim3en, tim3rst), +} +#[cfg(feature = "medium")] +hal! { TIM4: (_tim4, tim4en, tim4rst), } diff --git a/src/timer.rs b/src/timer.rs index 9a33e485..ee7065ce 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,4 +1,48 @@ -//! # Timer +/*! + # Timer + + ## Alternate function remapping + + ### TIM1 + + Not available on STM32F101. + + | Channel | Tim1NoRemap | + |:---:|:-----------:| + | CH1 | PA8 | + | CH2 | PA9 | + | CH3 | PA10 | + | CH4 | PA11 | + + ### TIM2 + + | Channel | Tim2NoRemap | Tim2PartialRemap1 | Tim2PartialRemap2 | Tim2FullRemap | + |:---:|:-----------:|:-----------------:|:-----------------:|:-------------:| + | CH1 | PA0 | PA15 | PA0 | PA15 | + | CH2 | PA1 | PB3 | PA1 | PB3 | + | CH3 | PA2 | PA2 | PB10 | PB10 | + | CH4 | PA3 | PA3 | PB10 | PB11 | + + ### TIM3 + + | Channel | Tim3NoRemap | Tim3PartialRemap | Tim3FullRemap | + |:---:|:-----------:|:----------------:|:-------------:| + | CH1 | PA6 | PB4 | PC6 | + | CH2 | PA7 | PB5 | PC7 | + | CH3 | PB0 | PB0 | PC8 | + | CH4 | PB1 | PB1 | PC9 | + + ### TIM4 + + Not available on low density devices. + + | Channel | Tim4NoRemap | Tim4Remap | + |:---:|:-----------:|:---------:| + | CH1 | PB6 | PD12 | + | CH2 | PB7 | PD13 | + | CH3 | PB8 | PD14 | + | CH4 | PB9 | PD15 | +*/ use crate::hal::timer::{CountDown, Periodic}; use crate::pac::{DBGMCU as DBG, TIM2, TIM3}; @@ -65,6 +109,81 @@ pub struct CountDownTimer { clk: Hertz, } + +pub(crate) mod sealed { + pub trait Remap { + type Periph; + const REMAP: u8; + } + pub trait Ch1 {} + pub trait Ch2 {} + pub trait Ch3 {} + pub trait Ch4 {} +} + +macro_rules! remap { + ($($name:ident: ($TIMX:ident, $state:literal, $P1:ident, $P2:ident, $P3:ident, $P4:ident),)+) => { + $( + pub struct $name; + impl sealed::Remap for $name { + type Periph = $TIMX; + const REMAP: u8 = $state; + } + impl sealed::Ch1<$name> for $P1 {} + impl sealed::Ch2<$name> for $P2 {} + impl sealed::Ch3<$name> for $P3 {} + impl sealed::Ch4<$name> for $P4 {} + )+ + } +} + +use crate::gpio::gpioa::{PA0, PA1, PA2, PA3, PA6, PA7, PA15}; +use crate::gpio::gpiob::{PB0, PB1, PB3, PB4, PB5, PB10, PB11}; +use crate::gpio::gpioc::{PC6, PC7, PC8, PC9}; + + +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +use crate::gpio::{ + gpioa::{PA8, PA9, PA10, PA11}, + gpioe::{PE9, PE11, PE13, PE14}, +}; +#[cfg(any( + feature = "stm32f100", + feature = "stm32f103", + feature = "stm32f105", +))] +remap!( + Tim1NoRemap: (TIM1, 0b00, PA8, PA9, PA10, PA11), + //Tim1PartialRemap: (TIM1, 0b01, PA8, PA9, PA10, PA11), + Tim1FullRemap: (TIM1, 0b11, PE9, PE11, PE13, PE14), +); + +remap!( + Tim2NoRemap: (TIM2, 0b00, PA0, PA1, PA2, PA3), + Tim2PartialRemap1: (TIM2, 0b01, PA15, PB3, PA2, PA3), + Tim2PartialRemap2: (TIM2, 0b10, PA0, PA1, PB10, PB11), + Tim2FullRemap: (TIM2, 0b11, PA15, PB3, PB10, PB11), + + Tim3NoRemap: (TIM3, 0b00, PA6, PA7, PB0, PB1), + Tim3PartialRemap: (TIM3, 0b10, PB4, PB5, PB0, PB1), + Tim3FullRemap: (TIM3, 0b11, PC6, PC7, PC8, PC9), +); + +#[cfg(feature = "medium")] +use crate::gpio::{ + gpiob::{PB6, PB7, PB8, PB9}, + gpiod::{PD12, PD13, PD14, PD15} +}; +#[cfg(feature = "medium")] +remap!( + Tim4NoRemap: (TIM4, 0b00, PB6, PB7, PB8, PB9), + Tim4Remap: (TIM4, 0b01, PD12, PD13, PD14, PD15), +); + impl Timer { pub fn syst(mut syst: SYST, clocks: &Clocks) -> Self { syst.set_clock_source(SystClkSource::Core); @@ -330,8 +449,8 @@ hal! { } #[cfg(any( - feature = "stm32f100", - feature = "stm32f103", + feature = "stm32f100", + feature = "stm32f103", feature = "stm32f105", ))] hal! { @@ -340,7 +459,7 @@ hal! { #[cfg(any( feature = "stm32f100", - feature = "stm32f105", + feature = "stm32f105", feature = "high", ))] hal! { @@ -410,4 +529,4 @@ hal! { TIM10: (tim10, pclk2_tim, dbg_tim10_stop), TIM11: (tim11, pclk2_tim, dbg_tim11_stop), } -*/ \ No newline at end of file +*/ From 6aea05b291b50864d20ab0db66ca33262a0f8ea4 Mon Sep 17 00:00:00 2001 From: Zgarbul Andrey Date: Mon, 2 Dec 2019 11:20:30 +0300 Subject: [PATCH 05/10] alternative implementation for SPI pins (#145) Allows usage of the SPI peripheral without using all the pins. --- CHANGELOG.md | 2 + src/rcc.rs | 5 ++ src/spi.rs | 198 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 155 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b59062e..a4a4c35d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- Pins can be passed in any order to SPI constructor, + `NoSck`, `NoMiso` and `NoMosi` can be also passed instead of real pin - DMA traits now require AsSlice instead of AsRef - GPIO `downgrade` function now returns a `Pxx` instead of a type specific to a GPIO port diff --git a/src/rcc.rs b/src/rcc.rs index 4991ec33..43bc4ace 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -557,6 +557,11 @@ bus! { WWDG => (APB1, wwdgen, wwdgrst), } +#[cfg(feature = "high")] +bus! { + SPI3 => (APB1, spi3en, spi3rst), +} + ahb_bus! { CRC => (crcen), DMA1 => (dma1en), diff --git a/src/spi.rs b/src/spi.rs index 9ba093e3..1bbae5fb 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -1,4 +1,34 @@ -//! # Serial Peripheral Interface +/*! + # Serial Peripheral Interface + + ## Alternate function remapping + + ### SPI1 + + | Function | Spi1NoRemap | Spi1Remap | + |:----:|:-----------:|:---------:| + | SCK | PA5 | PB3 | + | MISO | PA6 | PB4 | + | MOSI | PA7 | PB5 | + + ### SPI2 + + | Function | Spi2NoRemap | + |:----:|:-----------:| + | SCK | PB13 | + | MISO | PB14 | + | MOSI | PB15 | + + ### SPI3 + + Available only on high density devices. + + | Function | Spi3NoRemap | Spi3Remap | + |:----:|:-----------:|:---------:| + | SCK | PB3 | PC10 | + | MISO | PB4 | PC11 | + | MOSI | PB5 | PC12 | +*/ use core::ptr; @@ -6,6 +36,8 @@ use nb; pub use crate::hal::spi::{Mode, Phase, Polarity}; use crate::pac::{SPI1, SPI2}; +#[cfg(feature="high")] +use crate::pac::SPI3; use crate::afio::MAPR; use crate::gpio::gpioa::{PA5, PA6, PA7}; @@ -33,47 +65,88 @@ pub enum Error { _Extensible, } -pub trait Pins { - const REMAP: bool; -} +use core::marker::PhantomData; -impl Pins - for ( - PA5>, - PA6>, - PA7>, - ) -{ - const REMAP: bool = false; +mod sealed { + pub trait Remap { + type Periph; + const REMAP: bool; + } + pub trait Sck {} + pub trait Miso {} + pub trait Mosi {} + pub struct _Sck; + pub struct _Miso; + pub struct _Mosi; } +use sealed::{Remap, Sck, Miso, Mosi}; -impl Pins - for ( - PB3>, - PB4>, - PB5>, - ) -{ - const REMAP: bool = true; +pub trait Pins { + type _Pos; } - -impl Pins - for ( - PB13>, - PB14>, - PB15>, - ) -{ - const REMAP: bool = false; +macro_rules! pins_impl { + ( $( ( $($PINX:ident),+ ), ( $($TRAIT:ident),+ ), ( $($POS:ident),* ); )+ ) => { + $( + #[allow(unused_parens)] + impl Pins for ($($PINX),+) + where + $($PINX: $TRAIT,)+ + { + type _Pos = ($(sealed::$POS),+); + } + )+ + }; } -pub struct Spi { +pins_impl!( + (SCK, MISO, MOSI), (Sck, Miso, Mosi), (_Sck, _Miso, _Mosi); + (SCK, MOSI, MISO), (Sck, Mosi, Miso), (_Sck, _Mosi, _Miso); + (MOSI, SCK, MISO), (Mosi, Sck, Miso), (_Mosi, _Sck, _Miso); + (MOSI, MISO, SCK), (Mosi, Miso, Sck), (_Mosi, _Miso, _Sck); + (MISO, MOSI, SCK), (Miso, Mosi, Sck), (_Miso, _Mosi, _Sck); + (MISO, SCK, MOSI), (Miso, Sck, Mosi), (_Miso, _Sck, _Mosi); +); + +pub struct Spi { spi: SPI, pins: PINS, + _remap: PhantomData, } -impl Spi { - pub fn spi1( +/// A filler type for when the SCK pin is unnecessary +pub struct NoSck; +/// A filler type for when the Miso pin is unnecessary +pub struct NoMiso; +/// A filler type for when the Mosi pin is unnecessary +pub struct NoMosi; + +impl Sck for NoSck {} +impl Miso for NoMiso {} +impl Mosi for NoMosi {} + +macro_rules! remap { + ($name:ident, $SPIX:ident, $state:literal, $SCK:ident, $MISO:ident, $MOSI:ident) => { + pub struct $name; + impl Remap for $name { + type Periph = $SPIX; + const REMAP: bool = $state; + } + impl Sck<$name> for $SCK> {} + impl Miso<$name> for $MISO> {} + impl Mosi<$name> for $MOSI> {} + } +} + +remap!(Spi1NoRemap, SPI1, false, PA5, PA6, PA7); +remap!(Spi1Remap, SPI1, true, PB3, PB4, PB5); +remap!(Spi2NoRemap, SPI2, false, PB13, PB14, PB15); +#[cfg(feature="high")] +remap!(Spi3NoRemap, SPI3, false, PB3, PB4, PB5); +#[cfg(feature = "stm32f105")] +remap!(Spi3Remap, SPI3, true, PC10, PC11, PC12); + +impl Spi { + pub fn spi1( spi: SPI1, pins: PINS, mapr: &mut MAPR, @@ -84,15 +157,16 @@ impl Spi { ) -> Self where F: Into, - PINS: Pins, + REMAP: Remap, + PINS: Pins, { - mapr.modify_mapr(|_, w| w.spi1_remap().bit(PINS::REMAP)); + mapr.modify_mapr(|_, w| w.spi1_remap().bit(REMAP::REMAP)); Spi::_spi1(spi, pins, mode, freq.into(), clocks, apb) } } -impl Spi { - pub fn spi2( +impl Spi { + pub fn spi2( spi: SPI2, pins: PINS, mode: Mode, @@ -102,16 +176,36 @@ impl Spi { ) -> Self where F: Into, - PINS: Pins, + REMAP: Remap, + PINS: Pins, { Spi::_spi2(spi, pins, mode, freq.into(), clocks, apb) } } +#[cfg(feature="high")] +impl Spi { + pub fn spi3( + spi: SPI3, + pins: PINS, + mode: Mode, + freq: F, + clocks: Clocks, + apb: &mut ::Bus, + ) -> Self + where + F: Into, + REMAP: Remap, + PINS: Pins, + { + Spi::_spi3(spi, pins, mode, freq.into(), clocks, apb) + } +} + macro_rules! hal { ($($SPIX:ident: ($spiX:ident),)+) => { $( - impl Spi<$SPIX, PINS> { + impl Spi<$SPIX, REMAP, PINS> { fn $spiX( spi: $SPIX, pins: PINS, @@ -171,7 +265,7 @@ macro_rules! hal { .set_bit() ); - Spi { spi, pins } + Spi { spi, pins, _remap: PhantomData } } pub fn free(self) -> ($SPIX, PINS) { @@ -179,7 +273,7 @@ macro_rules! hal { } } - impl crate::hal::spi::FullDuplex for Spi<$SPIX, PINS> { + impl crate::hal::spi::FullDuplex for Spi<$SPIX, REMAP, PINS> { type Error = Error; fn read(&mut self) -> nb::Result { @@ -222,9 +316,9 @@ macro_rules! hal { } - impl crate::hal::blocking::spi::transfer::Default for Spi<$SPIX, PINS> {} + impl crate::hal::blocking::spi::transfer::Default for Spi<$SPIX, REMAP, PINS> {} - impl crate::hal::blocking::spi::write::Default for Spi<$SPIX, PINS> {} + impl crate::hal::blocking::spi::write::Default for Spi<$SPIX, REMAP, PINS> {} )+ } } @@ -233,24 +327,28 @@ hal! { SPI1: (_spi1), SPI2: (_spi2), } +#[cfg(feature="high")] +hal! { + SPI3: (_spi3), +} // DMA -pub struct SpiPayload { - spi: Spi +pub struct SpiPayload { + spi: Spi } -pub type SpiTxDma = TxDma, CHANNEL>; +pub type SpiTxDma = TxDma, CHANNEL>; macro_rules! spi_dma { ($SPIi:ident, $TCi:ident) => { - impl Transmit for SpiTxDma<$SPIi, PINS, $TCi> { + impl Transmit for SpiTxDma<$SPIi, REMAP, PINS, $TCi> { type TxChannel = $TCi; type ReceivedWord = u8; } - impl Spi<$SPIi, PINS> { - pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, PINS, $TCi> { + impl Spi<$SPIi, REMAP, PINS> { + pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, REMAP, PINS, $TCi> { let payload = SpiPayload{ spi: self }; @@ -258,7 +356,7 @@ macro_rules! spi_dma { } } - impl TransferPayload for SpiTxDma<$SPIi, PINS, $TCi> { + impl TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, $TCi> { fn start(&mut self) { self.payload.spi.spi.cr2.modify(|_, w| w.txdmaen().set_bit()); self.channel.start(); @@ -269,7 +367,7 @@ macro_rules! spi_dma { } } - impl crate::dma::WriteDma for SpiTxDma<$SPIi, PIN, $TCi> + impl crate::dma::WriteDma for SpiTxDma<$SPIi, REMAP, PIN, $TCi> where A: AsSlice, B: Static From bbc3d79a861187f76c9546830ec7d475be246281 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Mon, 2 Dec 2019 19:36:24 +0100 Subject: [PATCH 06/10] Make PWM example compile without feature --- examples/pwm.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/pwm.rs b/examples/pwm.rs index 862b57a7..003d8d71 100644 --- a/examples/pwm.rs +++ b/examples/pwm.rs @@ -29,10 +29,10 @@ fn main() -> ! { let mut gpiob = p.GPIOB.split(&mut rcc.apb2); // TIM2 - // let c1 = gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl); - // let c2 = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl); - // let c3 = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); - // let c4 = gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl); + let c1 = gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl); + let c2 = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl); + let c3 = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); + let c4 = gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl); // TIM3 // let c1 = gpioa.pa6.into_alternate_push_pull(&mut gpioa.crl); @@ -40,11 +40,11 @@ fn main() -> ! { // let c3 = gpiob.pb0.into_alternate_push_pull(&mut gpiob.crl); // let c4 = gpiob.pb1.into_alternate_push_pull(&mut gpiob.crl); - // TIM4 - let c1 = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); - let c2 = gpiob.pb7.into_alternate_push_pull(&mut gpiob.crl); - let c3 = gpiob.pb8.into_alternate_push_pull(&mut gpiob.crh); - let c4 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh); + // TIM4 (Only available with the "medium" density feature) + // let c1 = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); + // let c2 = gpiob.pb7.into_alternate_push_pull(&mut gpiob.crl); + // let c3 = gpiob.pb8.into_alternate_push_pull(&mut gpiob.crh); + // let c4 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh); let mut pwm = Timer::tim4(p.TIM4, &clocks, &mut rcc.apb1) .pwm((c1, c2, c3, c4), &mut afio.mapr, 1.khz()) From 5dd8f0da00a8111548e6bff069794aa4d0bcccc3 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Mon, 2 Dec 2019 19:37:39 +0100 Subject: [PATCH 07/10] Discuss features in top level docs --- src/lib.rs | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 20931f83..0315a76c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,11 +17,30 @@ //! - stm32f103 //! - stm32f100 //! -//! ```toml -//! [dependencies.stm32f1xx-hal] -//! version = "0.2.1" -//! features = ["stm32f103", "rt"] -//! ``` +//! ## Usage +//! +//! This crate supports multiple microcontrollers in the +//! stm32f1 family. Which specific microcontroller you want to build for has to be +//! specified with a feature, for example `stm32f103`. +//! +//! If no microcontroller is specified, the crate will not compile. +//! +//! The currently supported variants are +//! +//! - `stm32f100` +//! - `stm32f101` +//! - `stm32f103` +//! +//! You may also need to specify the density of the device with `medium`, `high` or `xl` +//! to enable certain peripherals. Generally the density can be determined by the 2nd character +//! after the number in the device name (i.e. For STM32F103C6U, the 6 indicates a low-density +//! device) but check the datasheet or CubeMX to be sure. +//! * 4, 6 => low density, no feature required +//! * 8, B => `medium` feature +//! * C, D, E => `high` feature +//! * F, G => `xl` feature +//! +//! //! //! [cortex-m-quickstart]: https://docs.rs/cortex-m-quickstart/0.3.1 //! @@ -36,7 +55,7 @@ //! #![no_std] //! #![no_main] //! -//! extern crate panic_halt; +//! use panic_halt as _; //! //! use nb::block; //! @@ -46,6 +65,7 @@ //! timer::Timer, //! }; //! use cortex_m_rt::entry; +//! use embedded_hal::digital::v2::OutputPin; //! //! #[entry] //! fn main() -> ! { @@ -54,25 +74,23 @@ //! // Get access to the device specific peripherals from the peripheral access crate //! let dp = pac::Peripherals::take().unwrap(); //! -//! // Take ownership over the raw flash and rcc devices and convert them -//! // into the corresponding HAL structs +//! // Take ownership over the raw flash and rcc devices and convert them into the corresponding +//! // HAL structs //! let mut flash = dp.FLASH.constrain(); //! let mut rcc = dp.RCC.constrain(); //! -//! // Freeze the configuration of all the clocks in the system and store -//! // the frozen frequencies in `clocks` +//! // Freeze the configuration of all the clocks in the system and store the frozen frequencies in +//! // `clocks` //! let clocks = rcc.cfgr.freeze(&mut flash.acr); //! //! // Acquire the GPIOC peripheral //! let mut gpioc = dp.GPIOC.split(&mut rcc.apb2); //! -//! // Configure gpio C pin 13 as a push-pull output. The `crh` register is -//! // passed to the function in order to configure the port. For pins 0-7, -//! // crl should be passed instead. +//! // Configure gpio C pin 13 as a push-pull output. The `crh` register is passed to the function +//! // in order to configure the port. For pins 0-7, crl should be passed instead. //! let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); //! // Configure the syst timer to trigger an update every second -//! let mut timer = Timer::syst(cp.SYST, clocks) -//! .start_count_down(1.hz()); +//! let mut timer = Timer::syst(cp.SYST, &clocks).start_count_down(1.hz()); //! //! // Wait for the timer to trigger an update and change the state of the LED //! loop { From 5630d99aa64298713302ef55fa75b23aed56b7a7 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Mon, 2 Dec 2019 19:59:00 +0100 Subject: [PATCH 08/10] Update PWM example with latest pwm changes --- examples/pwm.rs | 16 +++++++++------- src/pwm.rs | 12 ++++++++++++ src/qei.rs | 8 +++++++- src/timer.rs | 3 +++ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/examples/pwm.rs b/examples/pwm.rs index 003d8d71..6638964c 100644 --- a/examples/pwm.rs +++ b/examples/pwm.rs @@ -10,7 +10,7 @@ use cortex_m::asm; use stm32f1xx_hal::{ prelude::*, pac, - timer::Timer, + timer::{Tim2NoRemap, Timer}, }; use cortex_m_rt::entry; @@ -25,14 +25,16 @@ fn main() -> ! { let mut afio = p.AFIO.constrain(&mut rcc.apb2); - // let mut gpioa = p.GPIOA.split(&mut rcc.apb2); - let mut gpiob = p.GPIOB.split(&mut rcc.apb2); + let mut gpioa = p.GPIOA.split(&mut rcc.apb2); + // let mut gpiob = p.GPIOB.split(&mut rcc.apb2); // TIM2 let c1 = gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl); let c2 = gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl); let c3 = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl); - let c4 = gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl); + // If you don't want to use all channels, just leave some out + // let c4 = gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl); + let pins = (c1, c2, c3); // TIM3 // let c1 = gpioa.pa6.into_alternate_push_pull(&mut gpioa.crl); @@ -46,9 +48,9 @@ fn main() -> ! { // let c3 = gpiob.pb8.into_alternate_push_pull(&mut gpiob.crh); // let c4 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh); - let mut pwm = Timer::tim4(p.TIM4, &clocks, &mut rcc.apb1) - .pwm((c1, c2, c3, c4), &mut afio.mapr, 1.khz()) - .3; + let mut pwm = Timer::tim2(p.TIM2, &clocks, &mut rcc.apb1) + .pwm::(pins, &mut afio.mapr, 1.khz()) + .2; let max = pwm.get_max_duty(); diff --git a/src/pwm.rs b/src/pwm.rs index cb84f0d0..8f158421 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -13,12 +13,24 @@ ```rust let gpioa = ..; // Set up and split GPIOA + // Select the pins you want to use let pins = ( gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl), gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl), gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl), gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl), ); + + // Set up the timer as a PWM output. Since there are multiple remap + // options for tim2 that use the same pins, you need to specify + // the remap generic parameter. + let (c1, c2, c3, c4) = Timer::tim2(p.TIM2, &clocks, &mut rcc.apb1) + .pwm::(pins, &mut afio.mapr, 1.khz()) + .3; + + // Start using the channels + c1.set_duty(c1.get_max_duty()); + // ... ``` Then call the `pwm` function on the corresponding timer. diff --git a/src/qei.rs b/src/qei.rs index d72c464b..5e682187 100644 --- a/src/qei.rs +++ b/src/qei.rs @@ -1,4 +1,10 @@ -//! # Quadrature Encoder Interface +/** + # Quadrature Encoder Interface + + NOTE: In some cases you need to specify remap you need, especially for TIM2 + (see [Alternate function remapping](super::timer)): +*/ + use core::u16; use core::marker::PhantomData; diff --git a/src/timer.rs b/src/timer.rs index ee7065ce..a2fb889f 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -3,6 +3,9 @@ ## Alternate function remapping + This is a list of the remap settings you can use to assign pins to PWM channels + and the QEI peripherals + ### TIM1 Not available on STM32F101. From f3f82b345cfb12551440959191fe07f5958425fb Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Mon, 2 Dec 2019 20:02:44 +0100 Subject: [PATCH 09/10] Clean up cargo.toml --- Cargo.toml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b6c101cd..e54cc20f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,6 @@ panic-semihosting = "0.5.2" panic-itm = "0.4.1" cortex-m-rtfm = "0.4.3" cortex-m-semihosting = "0.3.3" -enc28j60 = "0.2.1" heapless = "0.4.3" m = "0.1.1" mfrc522 = "0.2.0" @@ -72,26 +71,10 @@ version = "0.4.0" default-features = false version = "1.5.2" -#[dev-dependencies.jnet] -#git = "https://github.com/japaric/jnet" -#rev = "df96b408049ca952ad7844d6552e87cf8fc18d2a" - -#[dev-dependencies.motor-driver] -#git = "https://github.com/japaric/motor-driver" -#rev = "00c8b15223643090d69f1acfb8b7a7a43a440417" - -#[dev-dependencies.mpu9250] -#git = "https://github.com/japaric/mpu9250" -#rev = "8f9ecad690093cb71c41301ca5e5705706150610" - [dev-dependencies.serde] default-features = false version = "1.0.90" -#[dev-dependencies.serde-json-core] -#git = "https://github.com/japaric/serde-json-core" -#rev = "6f12b77c1ffeae167989fe06e0d8b15978bd6d18" - [features] device-selected = [] doc = [] From b0b696ce3dd8b04c21bb2826bfc0c5ca3a1aaa3f Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Tue, 3 Dec 2019 09:23:18 +0100 Subject: [PATCH 10/10] Reword section about timer remaping --- src/pwm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pwm.rs b/src/pwm.rs index 8f158421..eba5a189 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -21,9 +21,9 @@ gpioa.pa3.into_alternate_push_pull(&mut gpioa.crl), ); - // Set up the timer as a PWM output. Since there are multiple remap - // options for tim2 that use the same pins, you need to specify - // the remap generic parameter. + // Set up the timer as a PWM output. If selected pins may correspond to different remap options, + // then you must specify the remap generic parameter. Otherwise, if there is no such ambiguity, + // the remap generic parameter can be omitted without complains from the compiler. let (c1, c2, c3, c4) = Timer::tim2(p.TIM2, &clocks, &mut rcc.apb1) .pwm::(pins, &mut afio.mapr, 1.khz()) .3;