Tratado sobre o Pin. Pensamentos sobre como configurar e trabalhar com pinos em C ++ para microcontroladores (usando o CortexM como exemplo)

Recentemente, eu estava muito interessado na questão da confiabilidade do software para microcontroladores, 0xd34df00d me aconselhou sobre medicamentos potentes , mas infelizmente minhas mãos não chegaram ao estudo de Haskell e Ivory para microcontroladores e, de fato, sobre abordagens completamente novas para o desenvolvimento de software que não sejam OOP. Eu comecei muito lentamente arrancar programação funcional e métodos formais.


Todos os meus esforços nessas áreas são, como foi dito em um comentário pelo amor à tecnologia , mas há uma suspeita de que agora ninguém me permita usar essas abordagens (embora, como dizem, esperemos e vejamos). O programador, que apoiará tudo isso, realmente deve ter habilidades específicas. Acredito que, depois de escrever um programa em um idioma assim, meu escritório procure alguém por um longo tempo que possa aceitar esse código; portanto, na prática, para estudantes e trabalho, ainda uso C ++ da maneira antiga.


Continuarei a desenvolver o tópico de software incorporado para pequenos microcontroladores em dispositivos para sistemas críticos de segurança.


Desta vez, tentarei sugerir uma maneira de trabalhar com pernas específicas de microcontroladores usando o wrapper de registro, que descrevi no último artigo, Acesso seguro a registrar campos em C ++ sem sacrificar a eficiência (usando o CortexM como exemplo)


Para ter uma idéia geral do que quero falar, darei um pequeno pedaço de código:


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/pt473612/


All Articles