Сложность здесь заключается в том, что эти структуры регистров периферии часто объявляются очень разными способами. Это может быть дефайн, в котором целочисленный литерал приводится к указателю на соответствующую структуру. А может быть хитрым образом переменная, размещенная по нужному адресу с помощью каких-то компиляторных расширений. Просто взять адрес такой структуры и передать его как параметр шаблона в большинстве случаев не получится, так как компилятор может не посчитать его пригоднам для параметра шаблона. Самый надёжный, на мой взгляд, способ - обращаться к этой структуре по имени. Для этого делаем макрос, который объявляет хитрую структуру-обёртку:
Код
#define IO_STRUCT_WRAPPER(STRUCT_PTR, CLASS_NAME, STRUCT_TYPE) \
struct CLASS_NAME\
{\
typedef STRUCT_TYPE DataT;\
STRUCT_TYPE* operator->(){return ((STRUCT_TYPE *)(STRUCT_PTR));}\
}
В ней определён оператор ->, который позволяет прозрачно обращаться к отдельным регистрам в структуре. С помощью этого макроса объявляем обёртки:
Код
IO_STRUCT_WRAPPER(DMA1, Dma1, DMA_TypeDef);
IO_STRUCT_WRAPPER(DMA1_Channel1, Dma1Channel1, DMA_Channel_TypeDef);
...
IO_STRUCT_WRAPPER(DMA1_Channel7, Dma1Channel7, DMA_Channel_TypeDef);
Код
template<class DmaRegs, class Clock, int Channels>
class DmaModule :public DmaBase
{
...
};
В шаблон структура-обёртка передаётся как обычный типовой параметр:
template<class Module, class ChannelRegs, int Channel>
class DmaChannel :public DmaBase
{
public:
static void Init(Mode mode, void *buffer, void *periph, uint32_t bufferSize)
{
Module::Enable();
ChannelRegs()->CCR = 0;
ChannelRegs()->CNDTR = bufferSize;
ChannelRegs()->CPAR = reinterpret_cast<uint32_t>(periph);
ChannelRegs()->CMAR = reinterpret_cast<uint32_t>(buffer);
ChannelRegs()->CCR = mode | DMA_CCR1_EN;
}
...
};
typedef DmaModule<Private::Dma1, Clock::Dma1Clock, 7> Dma1;
typedef DmaChannel<Dma1, Private::Dma1Channel1, 1> Dma1Channel1;
Для доступа к отдельным регистрам в структуре используестя стрелка:
Код
ChannelRegs()->CCR = 0;