في الآونة الأخيرة ، كنت مهتمًا جدًا بمسألة موثوقية البرامج الخاصة بالميكروكونترولر ، 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 :
, , :
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 :
, . 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.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> ;
using Button1Pin = Pin<Port<GPIOC>, 13U, PinReadable> ;
using Button2Pin = Pin<Port<GPIOC>, 12U, PinReadableConfigurable> ;
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
++, .