أطروحة على الدبوس. أفكار حول تكوين والعمل مع دبابيس في C ++ للميكروكونترولر (باستخدام CortexM كمثال)

في الآونة الأخيرة ، كنت مهتمًا جدًا بمسألة موثوقية البرامج الخاصة بالميكروكونترولر ، 0xd34df00d نصحتني بشأن الأدوية الفعالة ، لكن لسوء الحظ ، لم تصل يدي إلى دراسة هاسكل وإيفوري للميكروكونترولر ، وبالفعل عن مناهج جديدة تمامًا لتطوير البرمجيات بخلاف OOP. لقد بدأت ببطء شديد vkurivat البرمجة الوظيفية والأساليب الرسمية.


كل جهودي في هذه المجالات هي ، كما قيل في تعليق للحب للتكنولوجيا ، ولكن هناك شك في أن أحداً الآن لن يسمح لي باستخدام مثل هذه الأساليب (على الرغم من أنهم ، كما يقولون ، سوف ننتظر ونرى). يجب أن يكون للمبرمج ، الذي سيدعم هذا الأمر كله ، مهارات محددة. أعتقد أنه بمجرد كتابة برنامج بلغة كهذه ، سيبحث مكتبي عن شخص ما لفترة طويلة يمكنه قبول مثل هذا الرمز ، وبالتالي ، في الممارسة العملية ، للطلاب وللعمل ، ما زلت أستخدم C ++ بالطريقة القديمة.


سأستمر في تطوير موضوع البرنامج المضمن لوحدات التحكم الدقيقة الصغيرة في أجهزة أنظمة السلامة الهامة.


هذه المرة سأحاول اقتراح طريقة للعمل مع أرجل متحكم معينة باستخدام ملف التسجيل ، والذي وصفته في المقال الأخير ، الوصول الآمن لتسجيل الحقول في C ++ دون التضحية بالكفاءة (باستخدام CortexM كمثال)


من أجل الحصول على فكرة عامة عما أريد أن أتحدث عنه ، سأقدم لك شفرة صغيرة:


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 :


drawing

, , :


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 CLREX

: Cortex-M3


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 :


drawing

, . 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 , . .. , , .


( , , SPI e-paper):
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.5Output
GPIOC.3Output
GPIOC.13Input
GPIOC.12Input
GPIOC.11Input/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

++, .

Source: https://habr.com/ru/post/ar473612/


All Articles