Недавно вернулся к этой теме, но немного по другому поводу.
Повод такой: бывает так, что, в зависимости от номера периферийного модуля, он может иметь разные свойства.
Например, каждый канал DMA на STM32L0xx может быть подключен только к определённому перечню периферийных устройств.
(Канал 1 может быть подключен к ADC, TIM2_CH3 и AES_IN, канал 2 - к SPI2_RX, USART2_RX, LPUART1_RX и I2C1_TX. И так далее).
Хотелось иметь у каждого канала функцию SelectChannel(), которая бы принимала в качестве аргумента только допустимые для данного канала значения.
И я решил посмотреть, не поможет ли тут C++11. Вот что получилось.
Объявляем шаблонный класс свойств канала:
Код
template<DmaChannelNum chNum> struct DmaChannelTraits;
Определяем свойства для каждого канала:
Код
template<> struct DmaChannelTraits<DMA1_CH1>
{
enum { CHANNEL_NO = 1 };
enum { CSELR_SHIFT = (CHANNEL_NO-1)*4 };
enum class ChannelSelection : uint32_t // possible CSELR values
{
CH_SEL_ADC = (0x00 << CSELR_SHIFT),
CH_SEL_TIM2_CH3 = (0x08 << CSELR_SHIFT),
CH_SEL_AES_IN = (0x0B << CSELR_SHIFT),
CH_SEL_MASK = (0x0F << CSELR_SHIFT),
};
};
И так для всех каналов:
Код
template<> struct DmaChannelTraits<DMA1_CH6>
{
enum { CHANNEL_NO = 1 };
enum { CSELR_SHIFT = (CHANNEL_NO-1)*4 };
enum class ChannelSelection : uint32_t // possible CSELR values
{
CH_SEL_SPI2_RX = (0x02 << CSELR_SHIFT),
CH_SEL_USART2_RX = (0x04 << CSELR_SHIFT),
CH_SEL_LPUART1_RX = (0x05 << CSELR_SHIFT),
CH_SEL_I2C1_TX = (0x06 << CSELR_SHIFT),
CH_SEL_MASK = (0x0F << CSELR_SHIFT),
};
};
И затем, уже в шаблоне канала:
Код
template<DmaChannelNum chNum>
class DmaChannel
{
private:
typedef DmaChannelTraits<chNum> ChannelTraits;
public:
using ChannelSelection = typename ChannelTraits::ChannelSelection;
static void SelectChannel(ChannelSelection cs)
{
DMA->CSELR =
(DMA->CSELR & ~static_cast<uint32_t>(ChannelSelection::CH_SEL_MASK))
| static_cast<uint32_t>(cs);
}
}
Теперь, если мы попробуем выбрать для канала неверный источник:
Код
typedef DmaChannel<DMA1_CH1> Dma1Channel1;
Dma1Channel1::SelectChannel(Dma1Channel1::ChannelSelection::CH_SEL_SPI2_RX);
,то получим ошибку времени компиляции.
Кроме того, парсер эклипсы настолько умён, что в автоподстановке показывает правильные возможные значения для параметра.
ЗЫ. Результат целиком можно посмотреть
здесь.
Если бы я знал, что такое электричество...