Einführung
Die Idee, C ++ - Vorlagen für die Programmierung von Controllern zu verwenden, ist nichts Neues, da im Web viel Material verfügbar ist. Lassen Sie mich kurz auf die Hauptvorteile eingehen: Übertragen eines erheblichen Teils der Fehler von der Laufzeit zur Kompilierungszeit aufgrund strenger Typkontrolle sowie Annäherung an einen objektorientierten Ansatz, der für viele nah und bequem ist, ohne dass dies erforderlich ist Speichern Sie Felder in einer statischen Klasse (alle Felder sind Vorlagenparameter). Es ist jedoch anzumerken, dass sich fast alle Autoren im Großen und Ganzen in ihren Arbeiten auf Beispiele für die Arbeit mit Registern und E / A-Ports beschränken. In meinem Artikel möchte ich diese Ideen fortsetzen.
Du kannst nicht ein bisschen schwanger sein
Objektorientierte Programmierung impliziert das Vorhandensein von Klassen in einem Programm, die miteinander interagieren. Die Interaktion wird durch die Abhängigkeit einiger Klassen von anderen ausgedrückt. Und hier erscheint die Essenz des Headers: Um eine Vorlagenklasse B mit statischen Methoden als Parameter an Klasse A zu übergeben , muss Klasse B auch als Vorlage erstellt werden, und so weiter entlang der gesamten Kette.
Zum Beispiel hängt USART zumindest von seinen Registern ab, dann sieht die Deklaration der entsprechenden Klasse ungefähr so aus (der mit der Deklaration der Register verknüpfte Code wird von hier übernommen , um die Konsistenz der Artikel zu gewährleisten, danke @lamerok für die coolen Materialien und Beispiele. Ich habe sie nicht verpackt, aber ich habe vor.):
template <typename _Regs>
class Usart
{
public:
static void Init()
{
_Regs::CR1Pack<_Regs::CR1::UE, _Regs::CR1::RE, _Regs::CR1::TE>::Set();
// - .
}
}
Wir haben die Register herausgefunden, um eine bestimmte UART-Instanz zu deklarieren. Sie müssen die Vorlage spezialisieren
using Usart1 = Usart<USART1>;
Alles scheint großartig zu sein, aber tatsächlich hat eine bestimmte USART-Schnittstelle unterschiedliche Abhängigkeiten:
Taktregister (RCC_APB2).
Nummer unterbrechen.
Eine Reihe möglicher Pins (Tx und Rx).
Dma (Tx und 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. , , , , , , , , , , . .