Recently, I was very interested in the issue of reliability of software for microcontrollers, 0xd34df00d advised me on potent drugs , but unfortunately my hands did not reach the study of Haskell and Ivory for microcontrollers, and indeed on completely new approaches to software development other than OOP. I just started very slowly gouge functional programming and formal methods.
All my efforts in these areas are, as was said in a comment for the love of technology , but there is a suspicion that now no one will let me use such approaches (although, as they say, we will wait and see). The programmer, who will support this whole thing, really must have specific skills. I believe that once having written a program in such a language, my office will look for someone for a long time who can accept such a code, so in practice for students and for work I still use C ++ in the old fashioned way.
I will continue to develop the topic of embedded software for small microcontrollers in devices for safety critical systems.
This time Iβll try to suggest a way to work with specific microcontroller legs using the register wrapper, which I described in the last article, Safe access to register fields in C ++ without sacrificing efficiency (using CortexM as an example)
In order to have a general idea of ββwhat I want to talk about, I will give a small piece of code:
using Led1Pin = Pin<Port<GPIOA>, 5U, PinWriteableConfigurable> ; using Led2Pin = Pin<Port<GPIOC>, 5U, PinWriteableConfigurable> ; using Led3Pin = Pin<Port<GPIOC>, 8U, PinWriteable> ; using Led4Pin = Pin<Port<GPIOC>, 9U, PinWriteable> ; using ButtonPin = Pin<Port<GPIOC>, 10U, PinReadable> ; // 2 // GPIOA::BSRR::Set(32) ; // reinterpret_cast<volataile uint32_t *>(0x40020018) = 32U // GPIO::BSRR::Set(800) ; // reinterpret_cast<volataile uint32_t *>(0x40020818) = 800U PinsPack<Led1Pin, Led2Pin, Led3Pin, Led4Pin>::Set() ; // , ButtonPin::Set() auto res = ButtonPin::Get() ;
. - Pina, , Pin, , - , , ++ .
, . β , , , - .
CMSIS ( Cube), :
- -, , , ;
- , , ;
- , , 30 , , , .
, - NDA, - , , ( ), , .
, . , . .
β .
, , .. , , ( - , UART, SPI USB). . :
- 0 1,
0 β ();
1 β (). - .
0 β ;
1 β .
, 0 1, "" (c ), .
Set()
β 1, Reset()
β 0, Get()
β , SetInput()
β ,SetOutput()
β . , Reset()
.
Port :
, , :
template <typename T> struct Port { __forceinline static void Set(std::uint32_t value) { T::BSRR::Write(static_cast<typename T::BSRR::Type>(value)) ; } __forceinline static auto Get() { return T::IDR::Get() ; } __forceinline static void SetInput(std::uint32_t pinNum) { assert(pinNum <= 15U); using ModerType = typename T::MODER::Type ; static constexpr auto mask = T::MODER::FieldValues::Input::Mask ; const ModerType offset = static_cast<ModerType>(pinNum * 2U) ; auto value = T::MODER::Get() ; // MODER value &= ~(mask << offset); // value |= (value << offset) ; // ( ) *reinterpret_cast<volatile Type *>(T::MODER::Address) = value ; // } // ... __forceinline static void SetOutput(std::uint32_t pinNum) { assert(pinNum <= 15U); using ModerType = typename T::MODER::Type ; AtomicUtils<ModerType>::Set( T::MODER::Address, T::MODER::FieldValues::Output::Mask, T::MODER::FieldValues::Output::Value, static_cast<ModerType>(pinNum * uint8_t{2U}) ) ; } } ;
, :
LDREX , .
STREX β - , , . . LDREX STREX - ( , ), STREX 1. , ( ) . , LDREX STREX, , .
template <typename T> struct AtomicUtils { static void Set(T address, T mask, T value, T offset) { T oldRegValue ; T newRegValue ; do { oldRegValue = *reinterpret_cast<volatile T*>(address); newRegValue = oldRegValue; newRegValue &= ~(mask << (offset)); newRegValue |= (value << (offset)); } while ( !AtomicUtils<T>::TryToWrite(reinterpret_cast<volatile T *>(address), oldRegValue, newRegValue) ) ; } private: static bool TryToWrite(volatile T* ptr, T oldValue, T newValue) { using namespace std ; // if(__LDREX(ptr) == static_cast<uint32_t>(oldValue)) { // return (__STREX(static_cast<uint32_t>(newValue), static_cast<volatile uint32_t*>(ptr)) == 0) ; } __CLREX(); return false ; } };
. , , , . ( ), . , , . Pin
Pin
Pin -. , .
, Pin , Get()
Set()
. . , , Pin . , , , , . Pin :
, . Pin
, . , , .
template<typename Port, uint8_t pinNum> struct Pin { using PortType = Port ; static constexpr uint32_t pin = pinNum ; static void Set() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::Set(1U << pinNum) ; } static void Reset() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::Reset(1U << (pinNum)) << 16) ; } static auto Get() { return (Port::Get() & ( 1 << pinNum)) >> pinNum; } static void SetInput() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::SetInput(pinNum); } static void SetOutput() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::SetOutput(pinNum); } } ;
: 16, 15. , 15 . , , . , , . Pin, Pina 15.
:
using Led1 = Pin<Port<GPIOA>, 5U> ; using Led4 = Pin<Port<GPIOC>, 9U> ; Led1::Set() ; Led4::Set() ;
.
Vadimatorikda C++ ++ . . :
Β« Β» . , , HAL-;
, . , , β¦ , . , .
__low_level_int() IAR , . .. , , .
extern "C" { int __low_level_init(void) { //Switch on external 16 MHz oscillator RCC::CR::HSEON::Enable::Set() ; while (!RCC::CR::HSERDY::Enable::IsSet()) { } //Switch system clock on external oscillator RCC::CFGR::SW::Hse::Set() ; while (!RCC::CFGR::SWS::Hse::IsSet()) { } //Switch on clock on PortA and PortC, PortB RCC::AHB1ENRPack< RCC::AHB1ENR::GPIOCEN::Enable, RCC::AHB1ENR::GPIOAEN::Enable, RCC::AHB1ENR::GPIOBEN::Enable >::Set() ; RCC::APB1ENRPack< RCC::APB1ENR::TIM5EN::Enable, RCC::APB1ENR::SPI2EN::Enable >::Set() ; // LED1 on PortA.5, set PortA.5 as output GPIOA::MODER::MODER5::Output::Set() ; // PortB.13 - SPI3_CLK, PortB.15 - SPI2_MOSI, PB1 -CS, PB2- DC, PB8 -Reset GPIOB::MODERPack< GPIOB::MODER::MODER1::Output, //CS GPIOB::MODER::MODER2::Output, //DC GPIOB::MODER::MODER8::Output, //Reset GPIOB::MODER::MODER9::Intput, //Busy GPIOB::MODER::MODER13::Alternate, //CLK GPIOB::MODER::MODER15::Alternate, //MOSI >::Set() ; GPIOB::AFRHPack< GPIOB::AFRH::AFRH13::Af5, GPIOB::AFRH::AFRH15::Af5 >::Set() ; // LED2 on PortC.9, LED3 on PortC.8, LED4 on PortC.5 so set PortC.5,8,9 as output GPIOC::MODERPack< GPIOC::MODER::MODER5::Output, GPIOC::MODER::MODER8::Output, GPIOC::MODER::MODER9::Output >::Set() ; SPI2::CR1Pack< SPI2::CR1::MSTR::Master, //SPI2 master SPI2::CR1::BIDIMODE::Unidirectional2Line, SPI2::CR1::DFF::Data8bit, SPI2::CR1::CPOL::Low, SPI2::CR1::CPHA::Phase1edge, SPI2::CR1::SSM::NssSoftwareEnable, SPI2::CR1::BR::PclockDiv64, SPI2::CR1::LSBFIRST::MsbFisrt, SPI2::CR1::CRCEN::CrcCalcDisable >::Set() ; SPI2::CRCPR::CRCPOLY::Set(10U) ; return 1; } }
, __low_level_init() , - , , .
, , - , Pina ( , - ), ( ).
Pina . , ( ) Pina , SetInput()
SetOutput()
. Pin
Pin
, , , , . , .
, Pin, Set()
, Pin, , Get()
.
, , . :
struct PinConfigurable{}; //Pin struct PinReadable{}; //Pin struct PinWriteable{}; // Pin struct PinReadableConfigurable: PinReadable, PinConfigurable{}; //Pin struct PinWriteableConfigurable: PinWriteable, PinConfigurable{}; // Pin struct PinAlmighty: PinReadableConfigurable, PinWriteableConfigurable{}; // Pin
Pin , .
C SFINAE Pin, , 3 :
template<typename Port, uint8_t pinNum, typename Interface> struct Pin { using PortType = Port ; static constexpr uint32_t pin = pinNum ; // Set() __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinWriteable, T>::value>> static void Set() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::Set(uint8_t(1U) << pinNum) ; } // Get() __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinReadable, T>::value>> static auto Get() { return (Port::Get() & ( 1 << pinNum)) >> pinNum; } // __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinWriteableConfigurable, T>::value>> static void SetOutput() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::SetOutput(pinNum); } } ;
#ifndef REGISTERS_PIN_HPP #define REGISTERS_PIN_HPP #include "susudefs.hpp" //for __forceinline #include "port.hpp" //for Port struct PinConfigurable { }; struct PinReadable { }; struct PinWriteable { }; struct PinReadableConfigurable: PinReadable, PinConfigurable { }; struct PinWriteableConfigurable: PinWriteable, PinConfigurable { }; struct PinAlmighty: PinReadableConfigurable, PinWriteableConfigurable { }; template<typename Port, uint8_t pinNum, typename Interface> struct Pin { using PortType = Port ; static constexpr uint32_t pin = pinNum ; constexpr Pin() = default; __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinWriteable, T>::value>> static void Set() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::Set(uint8_t(1U) << pinNum) ; } __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinWriteable, T>::value>> static void Reset() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::Reset((uint8_t(1U) << (pinNum)) << 16) ; } __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinWriteable, T>::value>> static void Toggle() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::Toggle(uint8_t(1U) << pinNum) ; } __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinReadable, T>::value>> static auto Get() { return (Port::Get() & ( 1 << pinNum)) >> pinNum; } __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinReadableConfigurable, T>::value>> static void SetInput() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::SetInput(pinNum); } __forceinline template<typename T = Interface, class = typename std::enable_if_t<std::is_base_of<PinWriteableConfigurable, T>::value>> static void SetOutput() { static_assert(pinNum <= 15U, "There are only 16 pins on port") ; Port::SetOutput(pinNum); } } ; #endif //REGISTERS_PIN_HPP
, , , :
GPIOA.5 | Output | |
GPIOC.3 | Output | |
GPIOC.13 | Input | |
GPIOC.12 | Input | |
GPIOC.11 | Input/Output |
-
GPIOA.5
, .
, , GPIOA.5 Pin :Set()
SetOutput()
:
using Led1Pin = Pin<Port<GPIOA>, 5U, PinWriteableConfigurable> ;
-
GPIOC.3
, , .
,__low_level_init
, ..Set()
. Pina :
using Led3Pin = Pin<Port<GPIOC>, 8U, PinWriteable> ;
-
GPIOC.13
.
using Button1Pin = Pin<Port<GPIOC>, 13U, PinReadable> ;
-
GPIOC.12
, , :
using Button2Pin = Pin<Port<GPIOC>, 12U, PinReadableConfigurable> ;
-
GPIOC.11
, :
using SuperPin = Pin<Port<GPIOC>, 11U, PinAlmighty> ;
, () , :
Led1Pin::SetOutput() ; Led1Pin::Set() ; Led1::SetInput() ; //, SetInput() e. PinReadableConfigurable auto res = Led1Pin()::Get(); //, Get() . PinWriteable Led3::SetOuptut(); //, SetOutput() . PinWriteableConfigurable auto res = Button1Pin::Get() ; Button1Pin::Set(); //, Set() . PinWriteable Button1Pin::SetInput(); //, SetInput() . PinReadableConfigurable Button2Pin::SetInput() ; Button2Pin::Get() ; SuperPin::SetInput() ; res = SuperPin::Get() ; SuperPin::SetOutput() ; SuperPin::Set() ;
.. , , Pin
, , Pin
, . Pin
, PinReadable
, , .
, inline, , Set()
, :
Led1Pin::Set() ;
:
*reinterpret_cast<volataile uint32_t*>(0x40020018) = 32 ;
CMSIS
GPIOA->BSRR = GPIO_BSRR_BS5 ;
, , , ( ) Pin, ?
Pin
, , 4 , : GPIOA.5
, GPIOC.5
, GPIOC.8
, GPIOC.9
;
, :
// Pin using Led1Pin = Pin<Port<GPIOA>, 5U, PinWriteable> ; using Led2Pin = Pin<Port<GPIOC>, 5U, PinWriteable> ; using Led3Pin = Pin<Port<GPIOC>, 8U, PinWriteable> ; using Led4Pin = Pin<Port<GPIOC>, 9U, PinWriteable> ; void main() { Led1Pin::Set(); Led2Pin::Set(); Led3Pin::Set(); Led4Pin::Set(); }
, , - , Pin 10, 10 β . PinsPack:
template<typename ...T> struct PinsPack{ __forceinline inline static void Set() { Pass((T::Set(), true)...) ; } ... private: // __forceinline template<typename... Args> static void inline Pass(Args... ) { } } ;
, 4 :
void main() { PinsPack<Led1Pin, Led2Pin, Led3Pin, Led4Pin>::Set() ; // 4 // Led1Pin::Set(); -> GPIOA::BSRR::Set(32) ; // Led2Pin::Set(); -> GPIOC::BSRR::Set(32) ; // Led3Pin::Set(); -> GPIOC::BSRR::Set(256) ; // Led4Pin::Set(); -> GPIOC::BSRR::Set(512) ; }
, , 2 :
GPIOA::BSRR::Set(32) ; // GPIOA.5 1 GPIO::BSRR::Set(800) ; // GPIOC.5, GPIOC.8, GPIOC.9
++, .