Again about C ++ templates in microcontrollers

Introduction

The idea of ​​using C ++ templates for programming controllers is nothing new; there is a lot of material available on the web. Let me briefly recall the main advantages: transferring a significant part of errors from runtime to compile-time due to strict type control, as well as an approximation to an object-oriented approach, which is close and convenient to many, without the need to store fields in a static class (all fields are template parameters). However, it is worth noting that almost all authors by and large limit themselves in their works to examples of working with registers and I / O ports. In my article, I want to continue these ideas.





You can't be a little pregnant

Object-oriented programming implies the presence of classes in a program that interact with each other. Interaction is expressed by the dependence of some classes on others. And this is where the essence of the header appears: to pass a template class B with static methods to class A as a parameter , then class B must also be made template, and so on along the entire chain.





For example, USART at least depends on its registers, then the declaration of the corresponding class will look something like the following (the code associated with the declaration of registers is taken from here to ensure consistency of articles, thanks @lamerok for the cool materials and examples. I haven't passed them wrappers, but I plan to.):





template <typename _Regs>
class Usart
{
public:
  static void Init()
  {
    _Regs::CR1Pack<_Regs::CR1::UE, _Regs::CR1::RE, _Regs::CR1::TE>::Set();
    //  - .
  }
}
      
      



We figured out the registers in order to declare a specific UART instance, you need to specialize the template





using Usart1 = Usart<USART1>;
      
      



Everything seems to be great, but in fact, a specific USART interface has different dependencies:





  1. Clock register (RCC_APB2).





  2. Interrupt number.





  3. A set of possible pins (Tx and Rx).





  4. Dma (Tx and 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. , , , , , , , , , , . .





  1. - ++





  2. Pin. ++ ( CortexM)





  3. ++ ( CortexM)





  4. AVR
















All Articles