#ifndef ucoo_hal_timer_timer_stm32_tcc #define ucoo_hal_timer_timer_stm32_tcc // ucoolib - Microcontroller object oriented library. {{{ // // Copyright (C) 2015 Nicolas Schodet // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // // }}} #include #include namespace ucoo { template struct TimerHardware { }; // TODO add more timers. // TODO timer uses a double frequency only if APB prescaler is not 1. template<> struct TimerHardware { static const enum rcc_periph_clken clken = RCC_TIM1; static int freq () { return 2 * rcc_apb2_frequency; } static const unsigned int max = 0xffff; static const bool advanced = true; static const bool slave = true; static const bool one_pulse = true; static const int channels = 4; }; template<> struct TimerHardware { static const enum rcc_periph_clken clken = RCC_TIM2; static int freq () { return 2 * rcc_apb1_frequency; } #if defined TARGET_stm32f4 static const unsigned int max = 0xffffffff; #else static const unsigned int max = 0xffff; #endif static const bool advanced = false; static const bool slave = true; static const bool one_pulse = true; static const int channels = 4; }; template<> struct TimerHardware { static const enum rcc_periph_clken clken = RCC_TIM3; static int freq () { return 2 * rcc_apb1_frequency; } static const unsigned int max = 0xffff; static const bool advanced = false; static const bool slave = true; static const bool one_pulse = true; static const int channels = 4; }; template<> struct TimerHardware { static const enum rcc_periph_clken clken = RCC_TIM4; static int freq () { return 2 * rcc_apb1_frequency; } static const unsigned int max = 0xffff; static const bool advanced = false; static const bool slave = true; static const bool one_pulse = true; static const int channels = 4; }; template<> struct TimerHardware { static const enum rcc_periph_clken clken = RCC_TIM10; static int freq () { return 2 * rcc_apb2_frequency; } static const unsigned int max = 0xffff; static const bool advanced = false; static const bool slave = false; static const bool one_pulse = false; static const int channels = 1; }; template<> struct TimerHardware { static const enum rcc_periph_clken clken = RCC_TIM10; static int freq () { return 2 * rcc_apb2_frequency; } static const unsigned int max = 0xffff; static const bool advanced = false; static const bool slave = false; static const bool one_pulse = false; static const int channels = 1; }; template const unsigned int TimerHard::max = TimerHardware::max; template TimerHard::~TimerHard () { disable (); } template template void TimerHard::enable (int freq, bool start) { using Hard = TimerHardware; rcc_periph_clock_enable (Hard::clken); int in_freq = Hard::freq (); int div = in_freq / freq; assert (div <= 0x10000 && in_freq / div == freq); freq_ = freq; using OptionsCombined = CombinedOptions; TIM_CR1 (Base) = TIM_CR1_ARPE | OptionsCombined::cr1; TIM_CR2 (Base) = 0; if (Hard::slave) TIM_SMCR (Base) = OptionsCombined::smcr; if (Hard::channels > 0) { TIM_CCER (Base) = 0; TIM_CCMR1 (Base) = OptionsCombined::ccmr1; if (Hard::channels > 2) TIM_CCMR2 (Base) = OptionsCombined::ccmr2; TIM_CCER (Base) = OptionsCombined::ccer; TIM_CCR1 (Base) = OptionsCombined::ccr1; if (Hard::channels >= 2) TIM_CCR2 (Base) = OptionsCombined::ccr2; if (Hard::channels >= 3) TIM_CCR3 (Base) = OptionsCombined::ccr3; if (Hard::channels >= 4) TIM_CCR4 (Base) = OptionsCombined::ccr4; } TIM_PSC (Base) = div - 1; TIM_ARR (Base) = OptionsCombined::arr ? OptionsCombined::arr : Hard::max; if (Hard::advanced) TIM_BDTR (Base) = TIM_BDTR_MOE; TIM_EGR (Base) = TIM_EGR_UG; if (start) TIM_CR1 (Base) = TIM_CR1_ARPE | OptionsCombined::cr1 | TIM_CR1_CEN; } template void TimerHard::disable () { TIM_CR1 (Base) = 0; rcc_periph_clock_disable (TimerHardware::clken); freq_ = 0; } template void TimerHard::start () { TIM_CR1 (Base) |= TIM_CR1_CEN; } template void TimerHard::set_reload (unsigned int value) { assert (value <= TimerHardware::max); TIM_ARR (Base) = value; } template void TimerHard::set_output_compare (int ch, unsigned int value) { assert (ch > 0 && ch <= TimerHardware::channels); assert (value <= TimerHardware::max); *(&TIM_CCR1 (Base) + ch - 1) = value; } template unsigned int TimerHard::get_value () { return TIM_CNT (Base); } template unsigned int TimerHard::get_input_capture (int ch) const { assert (ch > 0 && ch <= TimerHardware::channels); return *(&TIM_CCR1 (Base) + ch - 1); } template void TimerHard::wait_update () const { unsigned int mask = TIM_SR_UIF; while (!(TIM_SR (Base) & mask)) ; TIM_SR (Base) = ~mask; } template void TimerHard::wait_input_capture (int ch) const { unsigned int mask = TIM_SR_CC1IF >> 1 << ch; while (!(TIM_SR (Base) & mask)) ; TIM_SR (Base) = ~mask; } template void TimerHard::enable_updates () { TIM_CR1 (Base) &= ~TIM_CR1_UDIS; } template void TimerHard::disable_updates () { TIM_CR1 (Base) |= TIM_CR1_UDIS; } template TimerHard::UpdateDisabled::UpdateDisabled (TimerHard &timer) : timer_ (timer) { timer_.disable_updates (); } template TimerHard::UpdateDisabled::~UpdateDisabled () { timer_.enable_updates (); } template struct TimerHard::Option { static const uint32_t base = Base; static const unsigned cr1 = 0; static const unsigned smcr = 0; static const unsigned ccmr1 = 0; static const unsigned ccmr2 = 0; static const unsigned ccer = 0; static const unsigned arr = 0; static const unsigned ccr1 = 0; static const unsigned ccr2 = 0; static const unsigned ccr3 = 0; static const unsigned ccr4 = 0; }; template template struct TimerHard::OptionReloadValue : public TimerHard::Option { static_assert (value <= TimerHardware::max, "value too large"); static const unsigned arr = value; }; template struct TimerHard::OptionOnePulse : public TimerHard::Option { static_assert (TimerHardware::one_pulse, "no one pulse mode"); static const unsigned cr1 = TIM_CR1_OPM; }; template template struct TimerHard::OptionTrigger : public TimerHard::Option { static_assert (TimerHardware::slave, "no external trigger"); static_assert (timer_input >= 1 && timer_input <= 2, "no such input"); static const unsigned smcr = (timer_input == 1 ? TIM_SMCR_TS_IT1FP1 : TIM_SMCR_TS_IT1FP2) | TIM_SMCR_SMS_TM; }; template template::Filter filter, typename TimerHard::Map map, typename TimerHard::Polarity polarity> struct TimerHard::OptionInputCapture { static_assert (channel == 1, "no such channel"); // Always activate input capture (through Polarity values), even when only // used as a trigger, no harm is done. }; template template::Filter filter, typename TimerHard::Map map, typename TimerHard::Polarity polarity> struct TimerHard::OptionInputCapture<1, filter, map, polarity> : public TimerHard::Option { static_assert (1 <= TimerHardware::channels, "no such channel"); static const unsigned ccmr1 = (static_cast (filter) * (TIM_CCMR1_IC1F_MASK & ~(TIM_CCMR1_IC1F_MASK - 1))) | (static_cast (map) * (TIM_CCMR1_CC1S_MASK & ~(TIM_CCMR1_CC1S_MASK - 1))); static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC1E; }; template template::Filter filter, typename TimerHard::Map map, typename TimerHard::Polarity polarity> struct TimerHard::OptionInputCapture<2, filter, map, polarity> : public TimerHard::Option { static_assert (2 <= TimerHardware::channels, "no such channel"); static const unsigned ccmr1 = (static_cast (filter) * (TIM_CCMR1_IC2F_MASK & ~(TIM_CCMR1_IC2F_MASK - 1))) | (static_cast (map) * (TIM_CCMR1_CC2S_MASK & ~(TIM_CCMR1_CC2S_MASK - 1))); static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC2E; }; template template::Filter filter, typename TimerHard::Map map, typename TimerHard::Polarity polarity> struct TimerHard::OptionInputCapture<3, filter, map, polarity> : public TimerHard::Option { static_assert (3 <= TimerHardware::channels, "no such channel"); static const unsigned ccmr2 = (static_cast (filter) * (TIM_CCMR2_IC3F_MASK & ~(TIM_CCMR2_IC3F_MASK - 1))) | (static_cast (map) * (TIM_CCMR2_CC3S_MASK & ~(TIM_CCMR2_CC3S_MASK - 1))); static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC3E; }; template template::Filter filter, typename TimerHard::Map map, typename TimerHard::Polarity polarity> struct TimerHard::OptionInputCapture<4, filter, map, polarity> : public TimerHard::Option { static_assert (4 <= TimerHardware::channels, "no such channel"); static const unsigned ccmr2 = (static_cast (filter) * (TIM_CCMR2_IC4F_MASK & ~(TIM_CCMR2_IC4F_MASK - 1))) | (static_cast (map) * (TIM_CCMR2_CC4S_MASK & ~(TIM_CCMR2_CC4S_MASK - 1))); static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC4E; }; template template::Polarity polarity> struct TimerHard::OptionOutputCompare { static_assert (channel == 1, "no such channel"); }; template template::Polarity polarity> struct TimerHard::OptionOutputCompare<1, polarity> : public TimerHard::Option { static_assert (1 <= TimerHardware::channels, "no such channel"); static const unsigned ccmr1 = TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC1PE; static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC1E; }; template template::Polarity polarity> struct TimerHard::OptionOutputCompare<2, polarity> : public TimerHard::Option { static_assert (2 <= TimerHardware::channels, "no such channel"); static const unsigned ccmr1 = TIM_CCMR1_OC2M_PWM1 | TIM_CCMR1_OC2PE; static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC2E; }; template template::Polarity polarity> struct TimerHard::OptionOutputCompare<3, polarity> : public TimerHard::Option { static_assert (3 <= TimerHardware::channels, "no such channel"); static const unsigned ccmr2 = TIM_CCMR2_OC3M_PWM1 | TIM_CCMR2_OC3PE; static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC3E; }; template template::Polarity polarity> struct TimerHard::OptionOutputCompare<4, polarity> : public TimerHard::Option { static_assert (4 <= TimerHardware::channels, "no such channel"); static const unsigned ccmr2 = TIM_CCMR2_OC4M_PWM1 | TIM_CCMR2_OC4PE; static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC4E; }; template template struct TimerHard::OptionOutputCompareValue : public TimerHard::Option { static_assert (channel == 1, "no such channel"); }; template template struct TimerHard::OptionOutputCompareValue<1, value> : public TimerHard::Option { static_assert (1 <= TimerHardware::channels, "no such channel"); static_assert (value <= TimerHardware::max, "value too large"); static const unsigned ccr1 = value; }; template template struct TimerHard::OptionOutputCompareValue<2, value> : public TimerHard::Option { static_assert (2 <= TimerHardware::channels, "no such channel"); static_assert (value <= TimerHardware::max, "value too large"); static const unsigned ccr2 = value; }; template template struct TimerHard::OptionOutputCompareValue<3, value> : public TimerHard::Option { static_assert (3 <= TimerHardware::channels, "no such channel"); static_assert (value <= TimerHardware::max, "value too large"); static const unsigned ccr3 = value; }; template template struct TimerHard::OptionOutputCompareValue<4, value> : public TimerHard::Option { static_assert (4 <= TimerHardware::channels, "no such channel"); static_assert (value <= TimerHardware::max, "value too large"); static const unsigned ccr4 = value; }; template template struct TimerHard::CombinedOptions : public TimerHard::Option { }; template template struct TimerHard::CombinedOptions { static_assert (Base == Option::base, "option for another timer"); static const uint32_t base = Base; static const unsigned cr1 = Option::cr1 | CombinedOptions::cr1; static const unsigned smcr = Option::smcr | CombinedOptions::smcr; static const unsigned ccmr1 = Option::ccmr1 | CombinedOptions::ccmr1; static const unsigned ccmr2 = Option::ccmr2 | CombinedOptions::ccmr2; static const unsigned ccer = Option::ccer | CombinedOptions::ccer; static const unsigned arr = Option::arr | CombinedOptions::arr; static_assert (Option::arr == 0 || Option::arr == arr, "conflicting reload values"); static const unsigned ccr1 = Option::ccr1 | CombinedOptions::ccr1; static_assert (Option::ccr1 == 0 || Option::ccr1 == ccr1, "conflicting ccr1 values"); static const unsigned ccr2 = Option::ccr2 | CombinedOptions::ccr2; static_assert (Option::ccr2 == 0 || Option::ccr2 == ccr2, "conflicting ccr2 values"); static const unsigned ccr3 = Option::ccr3 | CombinedOptions::ccr3; static_assert (Option::ccr3 == 0 || Option::ccr3 == ccr3, "conflicting ccr3 values"); static const unsigned ccr4 = Option::ccr4 | CombinedOptions::ccr4; static_assert (Option::ccr4 == 0 || Option::ccr4 == ccr4, "conflicting ccr4 values"); }; } // namespace ucoo #endif // ucoo_hal_timer_timer_stm32_tcc