Introdução
A ideia de usar templates C ++ para controladores de programação não é nova, há muito material disponível na web. Deixe-me relembrar brevemente as principais vantagens: transferir uma parte significativa dos erros do tempo de execução para o tempo de compilação devido ao controle de tipo estrito, bem como uma aproximação a uma abordagem orientada a objetos, que é próxima e conveniente para muitos, sem a necessidade de armazene campos em uma classe estática (todos os campos são parâmetros de modelo). Porém, é importante notar que quase todos os autores em geral se limitam em seus trabalhos a exemplos de trabalho com registradores e portas de E / S. No meu artigo, quero dar continuidade a essas idéias.
Você não pode estar um pouco grávida
A programação orientada a objetos implica na presença de classes em um programa que interagem umas com as outras. A interação é expressa pela dependência de algumas classes de outras. E aqui a essência do cabeçalho aparece: para passar uma classe de modelo B com métodos estáticos para a classe A como um parâmetro , então a classe B também deve ser transformada em modelo, e assim por diante ao longo de toda a cadeia.
Por exemplo, USART pelo menos depende de seus registros, então a declaração da classe correspondente será parecida com isto (o código associado à declaração de registros é retirado daqui para garantir a consistência dos artigos, obrigado @lamerok pelos materiais legais e exemplos. Não passei os invólucros, mas pretendo.):
template <typename _Regs>
class Usart
{
public:
static void Init()
{
_Regs::CR1Pack<_Regs::CR1::UE, _Regs::CR1::RE, _Regs::CR1::TE>::Set();
// - .
}
}
Nós descobrimos os registradores para declarar uma instância UART específica, você precisa especializar o modelo
using Usart1 = Usart<USART1>;
Tudo parece ótimo, mas, na verdade, uma interface USART específica tem dependências diferentes:
Registro do relógio (RCC_APB2).
Número de interrupção.
Um conjunto de pinos possíveis (Tx e Rx).
Dma (Tx e Rx).
- - , . ,
template <typename _Regs, typename _ClockCtrl>
class Usart
{
public:
static void Init()
{
//
_ClockCtrl::Enable()
_Regs::CR1Pack<_Regs::CR1::UE, _Regs::CR1::RE, _Regs::CR1::TE>::Set();
...
}
}
. , Init / RCC_ARB2, ?
-, , . , USART
// TX UART SPL.
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure); //
: , , , , . :
// Tx Rx .
template<typename _Regs, IRQn_Type _IRQNumber, typename _ClockCtrl, typename _TxPins, typename _RxPins, typename _DmaTx, typename _DmaRx>
template<typename TxPin, typename RxPin>
void Usart<_Regs, _IRQNumber, _ClockCtrl, _TxPins, _RxPins, _DmaTx, _DmaRx>::SelectTxRxPins()
{
const int8_t txPinIndex = TypeIndex<TxPin, _TxPins>::value;
const int8_t rxPinIndex = !std::is_same_v<RxPin, IO::NullPin>
? TypeIndex<RxPin, typename _RxPins>::value
: -1; // , NullPin
static_assert(txPinIndex >= 0);
// Rx
static_assert(rxPinIndex >= -1);
SelectTxRxPins<txPinIndex, rxPinIndex>();
}
USART SelectTxRxPins , , - static_assert.
, , C++ GPIO, , .
// DS1307
template <typename _I2CBus>
class Ds1307
{
...
static Time GetDateTime()
{
Time time;
_I2CBus::Read(Ds1307Address, 0x00, &time, sizeof(time));
...
, , :
using Rtc = Ds1307<I2c1>;
, , ( neiver, - ++ easyelectronics.ru) "". github "Mcucpp", . , , , , , ( , 2019, ). , , , , , Doxy-, , . Zhele, Stm32. , , , .
" , " © ..
, , gpio, , i2c/spi/uart/one-wire, , .
, . custom- CubeIDE. , , , , , , , , , , . .