summaryrefslogtreecommitdiff
path: root/ucoo
diff options
context:
space:
mode:
authorNicolas Schodet2015-09-04 13:46:03 +0200
committerNicolas Schodet2019-10-07 00:44:50 +0200
commit3f3e2aeac4128545cb921171296fbd4b3e21b510 (patch)
tree289f6c375440b9349dc1ab9950ee0a923beed57b /ucoo
parent35d889c3444f3d196e6e0415313282db4d381f02 (diff)
ucoo/hal/timer: add more timer usage options
Diffstat (limited to 'ucoo')
-rw-r--r--ucoo/hal/timer/test/test_timer.cc34
-rw-r--r--ucoo/hal/timer/timer.stm32.hh88
-rw-r--r--ucoo/hal/timer/timer.stm32.tcc422
3 files changed, 530 insertions, 14 deletions
diff --git a/ucoo/hal/timer/test/test_timer.cc b/ucoo/hal/timer/test/test_timer.cc
index 9002a28..e02e92f 100644
--- a/ucoo/hal/timer/test/test_timer.cc
+++ b/ucoo/hal/timer/test/test_timer.cc
@@ -25,17 +25,43 @@
#include "ucoo/arch/arch.hh"
#include "ucoo/hal/timer/timer.hh"
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+
#include <cstdio>
-#include <cinttypes>
int
main (int argc, const char **argv)
{
+ // PD13 (T4_CH2) is used as input capture.
+ // PWM signal is output on PA1 (T2_CH2), there is a button on PA0.
+ // Connect PD13 on PA0 or PA1. On each edge, there will be a pulse on
+ // PD15 (T4_CH4).
ucoo::arch_init (argc, argv);
ucoo::test_stream_setup ();
- ucoo::TimerHard<TIM2> timer;
- timer.enable (1000);
+ using Timer = ucoo::TimerHard<TIM4>;
+ using TimerRef = ucoo::TimerHard<TIM2>;
+ Timer timer;
+ TimerRef timerref;
+ // AF setup.
+ rcc_periph_clock_enable (RCC_GPIOA);
+ rcc_periph_clock_enable (RCC_GPIOD);
+ gpio_mode_setup (GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLDOWN, GPIO1);
+ gpio_set_af (GPIOA, GPIO_AF1, GPIO1);
+ gpio_mode_setup (GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLDOWN, GPIO13 | GPIO15);
+ gpio_set_af (GPIOD, GPIO_AF2, GPIO13 | GPIO15);
+ // Timers setup.
+ timerref.enable<TimerRef::OptionReloadValue<5000>,
+ TimerRef::OptionOutputCompare<2> > (2000);
+ timerref.set_output_compare (2, 2500);
+ timer.enable<Timer::OptionOnePulse,
+ Timer::OptionReloadValue<1000>,
+ Timer::OptionTrigger<2>,
+ Timer::OptionInputCapture<2, Timer::Filter::OFF, Timer::Map::SAME,
+ Timer::Polarity::BOTH>,
+ Timer::OptionOutputCompare<4, Timer::Polarity::INVERTED> > (2000);
+ timer.set_output_compare (4, 500);
while (1)
- printf ("%" PRIu32 "\n", timer.get_value ());
+ printf ("%u\n", timer.get_value ());
}
diff --git a/ucoo/hal/timer/timer.stm32.hh b/ucoo/hal/timer/timer.stm32.hh
index 414a687..a7d135d 100644
--- a/ucoo/hal/timer/timer.stm32.hh
+++ b/ucoo/hal/timer/timer.stm32.hh
@@ -32,14 +32,98 @@ template<uint32_t Base>
class TimerHard
{
public:
+ /// Maximum timer value.
+ static const unsigned int max;
+ public:
/// Destructor, disable.
~TimerHard ();
/// Enable timer.
- void enable (int freq);
+ template <typename... Options>
+ void enable (int freq, bool start = true);
/// Disable timer.
void disable ();
+ /// Manually start timer.
+ void start ();
+ /// Set reload value.
+ void set_reload (unsigned int value);
+ /// Set output compare value.
+ void set_output_compare (int ch, unsigned int value);
/// Read timer current value.
- static uint32_t get_value ();
+ static unsigned int get_value ();
+ /// Read input capture value.
+ unsigned int get_input_capture (int ch) const;
+ /// Wait until update event.
+ void wait_update () const;
+ /// Wait until a new input capture value is available.
+ void wait_input_capture (int ch) const;
+ /// Get timer frequency.
+ int get_freq () const { return freq_; }
+ private:
+ /// Enable updates (reload value, output compare values...).
+ void enable_updates ();
+ /// Disable updates.
+ void disable_updates ();
+ public:
+ /// Filter for timer input.
+ enum class Filter { OFF, CK_INT_N_2, CK_INT_N_4, CK_INT_N_8,
+ DTF_DIV_2_N_6, DTF_DIV_2_N_8, DTF_DIV_4_N_6, DTF_DIV_4_N_8,
+ DTF_DIV_8_N_6, DTF_DIV_8_N_8, DTF_DIV_16_N_5, DTF_DIV_16_N_6,
+ DTF_DIV_16_N_8, DTF_DIV_32_N_5, DTF_DIV_32_N_6, DTF_DIV_32_N_8 };
+ /// Mapping from timer input to input capture.
+ enum class Map {
+ SAME = 1, // Same timer input (for example TI1 for IC1).
+ OTHER = 2, // Other timer input (for example TI2 for IC1).
+ TRC = 3, // Internal trigger.
+ };
+ /// Polarity for input capture or output compare. Edge is only used for
+ /// input.
+ enum class Polarity {
+ NON_INVERTED = 1,
+ INVERTED = 3,
+ RISING = NON_INVERTED,
+ FALLING = INVERTED,
+ BOTH = 11,
+ };
+ /// Temporarily disable updates.
+ class UpdateDisabled
+ {
+ public:
+ /// Disable updates.
+ UpdateDisabled (TimerHard<Base> &timer);
+ /// Enable updates.
+ ~UpdateDisabled ();
+ private:
+ TimerHard<Base> &timer_;
+ };
+ friend class UpdateDisabled;
+ /// Base class for options.
+ struct Option;
+ /// Set the automatic reload value.
+ template<unsigned int value>
+ struct OptionReloadValue;
+ /// Set the timer in one pulse mode. You may want to unset start
+ /// parameter.
+ struct OptionOnePulse;
+ /// Set timer trigger input.
+ template<int timer_input>
+ struct OptionTrigger;
+ /// Set input capture options.
+ template<int channel, Filter filter = Filter::OFF, Map map = Map::SAME,
+ Polarity polarity = Polarity::NON_INVERTED>
+ struct OptionInputCapture;
+ /// Set output compare options. Setup PWM mode 1.
+ template<int channel, Polarity polarity = Polarity::NON_INVERTED>
+ struct OptionOutputCompare;
+ /// Set output compare value.
+ template<int channel, unsigned int value>
+ struct OptionOutputCompareValue;
+ private:
+ /// Helper to aggregate all options.
+ template<typename... Options>
+ struct CombinedOptions;
+ private:
+ /// When enabled, programmed frequency.
+ int freq_ = 0;
};
} // namespace ucoo
diff --git a/ucoo/hal/timer/timer.stm32.tcc b/ucoo/hal/timer/timer.stm32.tcc
index 6572424..ec76c4d 100644
--- a/ucoo/hal/timer/timer.stm32.tcc
+++ b/ucoo/hal/timer/timer.stm32.tcc
@@ -31,33 +31,104 @@ namespace ucoo {
template<uint32_t Base>
struct TimerHardware { };
+// TODO add more timers.
+// TODO timer uses a double frequency only if APB prescaler is not 1.
+
+template<>
+struct TimerHardware<TIM1>
+{
+ 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 int channels = 4;
+};
+
template<>
struct TimerHardware<TIM2>
{
static const enum rcc_periph_clken clken = RCC_TIM2;
- // TODO timer uses a double frequency only if APB prescaler is not 1.
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 int channels = 4;
+};
+
+template<>
+struct TimerHardware<TIM3>
+{
+ 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 int channels = 4;
+};
+
+template<>
+struct TimerHardware<TIM4>
+{
+ 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 int channels = 4;
};
template<uint32_t Base>
+const unsigned int TimerHard<Base>::max = TimerHardware<Base>::max;
+
+template<uint32_t Base>
TimerHard<Base>::~TimerHard ()
{
disable ();
}
template<uint32_t Base>
+template<typename... Options>
void
-TimerHard<Base>::enable (int freq)
+TimerHard<Base>::enable (int freq, bool start)
{
- rcc_periph_clock_enable (TimerHardware<Base>::clken);
- int in_freq = TimerHardware<Base>::freq ();
+ using Hard = TimerHardware<Base>;
+ rcc_periph_clock_enable (Hard::clken);
+ int in_freq = Hard::freq ();
int div = in_freq / freq;
assert (div <= 0x10000 && in_freq / div == freq);
- TIM_CR1 (Base) = 0;
+ freq_ = freq;
+ using OptionsCombined = CombinedOptions<Options...>;
+ 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) = 0xffffffff;
+ TIM_ARR (Base) = OptionsCombined::arr ? OptionsCombined::arr : Hard::max;
+ if (Hard::advanced)
+ TIM_BDTR (Base) = TIM_BDTR_MOE;
TIM_EGR (Base) = TIM_EGR_UG;
- TIM_CR1 (Base) = TIM_CR1_CEN;
+ if (start)
+ TIM_CR1 (Base) = TIM_CR1_ARPE | OptionsCombined::cr1 | TIM_CR1_CEN;
}
template<uint32_t Base>
@@ -66,15 +137,350 @@ TimerHard<Base>::disable ()
{
TIM_CR1 (Base) = 0;
rcc_periph_clock_disable (TimerHardware<Base>::clken);
+ freq_ = 0;
+}
+
+template<uint32_t Base>
+void
+TimerHard<Base>::start ()
+{
+ TIM_CR1 (Base) |= TIM_CR1_CEN;
+}
+
+template<uint32_t Base>
+void
+TimerHard<Base>::set_reload (unsigned int value)
+{
+ assert (value <= TimerHardware<Base>::max);
+ TIM_ARR (Base) = value;
}
template<uint32_t Base>
-uint32_t
+void
+TimerHard<Base>::set_output_compare (int ch, unsigned int value)
+{
+ assert (ch > 0 && ch <= TimerHardware<Base>::channels);
+ assert (value <= TimerHardware<Base>::max);
+ *(&TIM_CCR1 (Base) + ch - 1) = value;
+}
+
+template<uint32_t Base>
+unsigned int
TimerHard<Base>::get_value ()
{
return TIM_CNT (Base);
}
+template<uint32_t Base>
+unsigned int
+TimerHard<Base>::get_input_capture (int ch) const
+{
+ assert (ch > 0 && ch <= TimerHardware<Base>::channels);
+ return *(&TIM_CCR1 (Base) + ch - 1);
+}
+
+template<uint32_t Base>
+void
+TimerHard<Base>::wait_update () const
+{
+ unsigned int mask = TIM_SR_UIF;
+ while (!(TIM_SR (Base) & mask))
+ ;
+ TIM_SR (Base) = ~mask;
+}
+
+template<uint32_t Base>
+void
+TimerHard<Base>::wait_input_capture (int ch) const
+{
+ unsigned int mask = TIM_SR_CC1IF >> 1 << ch;
+ while (!(TIM_SR (Base) & mask))
+ ;
+ TIM_SR (Base) = ~mask;
+}
+
+template<uint32_t Base>
+void
+TimerHard<Base>::enable_updates ()
+{
+ TIM_CR1 (Base) &= ~TIM_CR1_UDIS;
+}
+
+template<uint32_t Base>
+void
+TimerHard<Base>::disable_updates ()
+{
+ TIM_CR1 (Base) |= TIM_CR1_UDIS;
+}
+
+template<uint32_t Base>
+TimerHard<Base>::UpdateDisabled::UpdateDisabled (TimerHard<Base> &timer)
+ : timer_ (timer)
+{
+ timer_.disable_updates ();
+}
+
+template<uint32_t Base>
+TimerHard<Base>::UpdateDisabled::~UpdateDisabled ()
+{
+ timer_.enable_updates ();
+}
+
+template<uint32_t Base>
+struct TimerHard<Base>::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<uint32_t Base>
+template<unsigned int value>
+struct TimerHard<Base>::OptionReloadValue : public TimerHard<Base>::Option
+{
+ static_assert (value <= TimerHardware<Base>::max, "value too large");
+ static const unsigned arr = value;
+};
+
+template<uint32_t Base>
+struct TimerHard<Base>::OptionOnePulse : public TimerHard<Base>::Option
+{
+ static const unsigned cr1 = TIM_CR1_OPM;
+};
+
+template<uint32_t Base>
+template<int timer_input>
+struct TimerHard<Base>::OptionTrigger : public TimerHard<Base>::Option
+{
+ static_assert (TimerHardware<Base>::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<uint32_t Base>
+template<int channel, typename TimerHard<Base>::Filter filter,
+ typename TimerHard<Base>::Map map,
+ typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::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<uint32_t Base>
+template<typename TimerHard<Base>::Filter filter,
+ typename TimerHard<Base>::Map map,
+ typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionInputCapture<1, filter, map, polarity>
+ : public TimerHard<Base>::Option
+{
+ static_assert (1 <= TimerHardware<Base>::channels, "no such channel");
+ static const unsigned ccmr1 =
+ (static_cast<int> (filter)
+ * (TIM_CCMR1_IC1F_MASK & ~(TIM_CCMR1_IC1F_MASK - 1)))
+ | (static_cast<int> (map)
+ * (TIM_CCMR1_CC1S_MASK & ~(TIM_CCMR1_CC1S_MASK - 1)));
+ static const unsigned ccer = static_cast<int> (polarity) * TIM_CCER_CC1E;
+};
+
+template<uint32_t Base>
+template<typename TimerHard<Base>::Filter filter,
+ typename TimerHard<Base>::Map map,
+ typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionInputCapture<2, filter, map, polarity>
+ : public TimerHard<Base>::Option
+{
+ static_assert (2 <= TimerHardware<Base>::channels, "no such channel");
+ static const unsigned ccmr1 =
+ (static_cast<int> (filter)
+ * (TIM_CCMR1_IC2F_MASK & ~(TIM_CCMR1_IC2F_MASK - 1)))
+ | (static_cast<int> (map)
+ * (TIM_CCMR1_CC2S_MASK & ~(TIM_CCMR1_CC2S_MASK - 1)));
+ static const unsigned ccer = static_cast<int> (polarity) * TIM_CCER_CC2E;
+};
+
+template<uint32_t Base>
+template<typename TimerHard<Base>::Filter filter,
+ typename TimerHard<Base>::Map map,
+ typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionInputCapture<3, filter, map, polarity>
+ : public TimerHard<Base>::Option
+{
+ static_assert (3 <= TimerHardware<Base>::channels, "no such channel");
+ static const unsigned ccmr2 =
+ (static_cast<int> (filter)
+ * (TIM_CCMR2_IC3F_MASK & ~(TIM_CCMR2_IC3F_MASK - 1)))
+ | (static_cast<int> (map)
+ * (TIM_CCMR2_CC3S_MASK & ~(TIM_CCMR2_CC3S_MASK - 1)));
+ static const unsigned ccer = static_cast<int> (polarity) * TIM_CCER_CC3E;
+};
+
+template<uint32_t Base>
+template<typename TimerHard<Base>::Filter filter,
+ typename TimerHard<Base>::Map map,
+ typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionInputCapture<4, filter, map, polarity>
+ : public TimerHard<Base>::Option
+{
+ static_assert (4 <= TimerHardware<Base>::channels, "no such channel");
+ static const unsigned ccmr2 =
+ (static_cast<int> (filter)
+ * (TIM_CCMR2_IC4F_MASK & ~(TIM_CCMR2_IC4F_MASK - 1)))
+ | (static_cast<int> (map)
+ * (TIM_CCMR2_CC4S_MASK & ~(TIM_CCMR2_CC4S_MASK - 1)));
+ static const unsigned ccer = static_cast<int> (polarity) * TIM_CCER_CC4E;
+};
+
+template<uint32_t Base>
+template<int channel, typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionOutputCompare
+{
+ static_assert (channel == 1, "no such channel");
+};
+
+template<uint32_t Base>
+template<typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionOutputCompare<1, polarity>
+ : public TimerHard<Base>::Option
+{
+ static_assert (1 <= TimerHardware<Base>::channels, "no such channel");
+ static const unsigned ccmr1 = TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC1PE;
+ static const unsigned ccer = static_cast<int> (polarity) * TIM_CCER_CC1E;
+};
+
+template<uint32_t Base>
+template<typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionOutputCompare<2, polarity>
+ : public TimerHard<Base>::Option
+{
+ static_assert (2 <= TimerHardware<Base>::channels, "no such channel");
+ static const unsigned ccmr1 = TIM_CCMR1_OC2M_PWM1 | TIM_CCMR1_OC2PE;
+ static const unsigned ccer = static_cast<int> (polarity) * TIM_CCER_CC2E;
+};
+
+template<uint32_t Base>
+template<typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionOutputCompare<3, polarity>
+ : public TimerHard<Base>::Option
+{
+ static_assert (3 <= TimerHardware<Base>::channels, "no such channel");
+ static const unsigned ccmr2 = TIM_CCMR2_OC3M_PWM1 | TIM_CCMR2_OC3PE;
+ static const unsigned ccer = static_cast<int> (polarity) * TIM_CCER_CC3E;
+};
+
+template<uint32_t Base>
+template<typename TimerHard<Base>::Polarity polarity>
+struct TimerHard<Base>::OptionOutputCompare<4, polarity>
+ : public TimerHard<Base>::Option
+{
+ static_assert (4 <= TimerHardware<Base>::channels, "no such channel");
+ static const unsigned ccmr2 = TIM_CCMR2_OC4M_PWM1 | TIM_CCMR2_OC4PE;
+ static const unsigned ccer = static_cast<int> (polarity) * TIM_CCER_CC4E;
+};
+
+template<uint32_t Base>
+template<int channel, unsigned int value>
+struct TimerHard<Base>::OptionOutputCompareValue
+ : public TimerHard<Base>::Option
+{
+ static_assert (channel == 1, "no such channel");
+};
+
+template<uint32_t Base>
+template<unsigned int value>
+struct TimerHard<Base>::OptionOutputCompareValue<1, value>
+ : public TimerHard<Base>::Option
+{
+ static_assert (1 <= TimerHardware<Base>::channels, "no such channel");
+ static_assert (value <= TimerHardware<Base>::max, "value too large");
+ static const unsigned ccr1 = value;
+};
+
+template<uint32_t Base>
+template<unsigned int value>
+struct TimerHard<Base>::OptionOutputCompareValue<2, value>
+ : public TimerHard<Base>::Option
+{
+ static_assert (2 <= TimerHardware<Base>::channels, "no such channel");
+ static_assert (value <= TimerHardware<Base>::max, "value too large");
+ static const unsigned ccr2 = value;
+};
+
+template<uint32_t Base>
+template<unsigned int value>
+struct TimerHard<Base>::OptionOutputCompareValue<3, value>
+ : public TimerHard<Base>::Option
+{
+ static_assert (3 <= TimerHardware<Base>::channels, "no such channel");
+ static_assert (value <= TimerHardware<Base>::max, "value too large");
+ static const unsigned ccr3 = value;
+};
+
+template<uint32_t Base>
+template<unsigned int value>
+struct TimerHard<Base>::OptionOutputCompareValue<4, value>
+ : public TimerHard<Base>::Option
+{
+ static_assert (4 <= TimerHardware<Base>::channels, "no such channel");
+ static_assert (value <= TimerHardware<Base>::max, "value too large");
+ static const unsigned ccr4 = value;
+};
+
+template<uint32_t Base>
+template<typename... Options>
+struct TimerHard<Base>::CombinedOptions : public TimerHard<Base>::Option
+{
+};
+
+template<uint32_t Base>
+template<typename Option, typename... Options>
+struct TimerHard<Base>::CombinedOptions<Option, Options...>
+{
+ static_assert (Base == Option::base, "option for another timer");
+ static const uint32_t base = Base;
+ static const unsigned cr1 = Option::cr1
+ | CombinedOptions<Options...>::cr1;
+ static const unsigned smcr = Option::smcr
+ | CombinedOptions<Options...>::smcr;
+ static const unsigned ccmr1 = Option::ccmr1
+ | CombinedOptions<Options...>::ccmr1;
+ static const unsigned ccmr2 = Option::ccmr2
+ | CombinedOptions<Options...>::ccmr2;
+ static const unsigned ccer = Option::ccer
+ | CombinedOptions<Options...>::ccer;
+ static const unsigned arr = Option::arr
+ | CombinedOptions<Options...>::arr;
+ static_assert (Option::arr == 0 || Option::arr == arr,
+ "conflicting reload values");
+ static const unsigned ccr1 = Option::ccr1
+ | CombinedOptions<Options...>::ccr1;
+ static_assert (Option::ccr1 == 0 || Option::ccr1 == ccr1,
+ "conflicting ccr1 values");
+ static const unsigned ccr2 = Option::ccr2
+ | CombinedOptions<Options...>::ccr2;
+ static_assert (Option::ccr2 == 0 || Option::ccr2 == ccr2,
+ "conflicting ccr2 values");
+ static const unsigned ccr3 = Option::ccr3
+ | CombinedOptions<Options...>::ccr3;
+ static_assert (Option::ccr3 == 0 || Option::ccr3 == ccr3,
+ "conflicting ccr3 values");
+ static const unsigned ccr4 = Option::ccr4
+ | CombinedOptions<Options...>::ccr4;
+ static_assert (Option::ccr4 == 0 || Option::ccr4 == ccr4,
+ "conflicting ccr4 values");
+};
+
} // namespace ucoo
#endif // ucoo_hal_timer_timer_stm32_tcc