Baru-baru ini, saya sangat tertarik dengan masalah keandalan perangkat lunak untuk mikrokontroler, 0xd34df00d menasihati saya tentang obat kuat , tetapi sayangnya tangan saya belum mencapai studi tentang Haskell dan Gading untuk mikrokontroler, dan memang pada pendekatan yang sama sekali baru untuk pengembangan perangkat lunak selain OOP. Saya baru mulai dengan sangat lambat mencungkil pemrograman fungsional dan metode formal.
Semua upaya saya di bidang-bidang ini, seperti yang dikatakan dalam komentar untuk cinta teknologi , tetapi ada kecurigaan bahwa sekarang tidak ada yang akan membiarkan saya menggunakan pendekatan seperti itu (meskipun, seperti yang mereka katakan, kami akan menunggu dan melihat). Programmer, yang akan mendukung semua ini, benar-benar harus memiliki keterampilan khusus. Saya percaya bahwa setelah menulis program dalam bahasa seperti itu, kantor saya akan mencari orang yang dapat menerima kode seperti itu untuk waktu yang lama, oleh karena itu, dalam praktiknya, untuk siswa dan untuk pekerjaan, saya masih menggunakan C ++ dengan cara lama.
Saya akan terus mengembangkan topik perangkat lunak tertanam untuk mikrokontroler kecil di perangkat untuk sistem kritis keselamatan.
Kali ini saya akan mencoba menyarankan cara untuk bekerja dengan kaki mikrokontroler tertentu menggunakan pembungkus register, yang saya jelaskan dalam artikel terakhir, Mengamankan akses untuk mendaftarkan bidang dalam C ++ tanpa mengorbankan efisiensi (menggunakan contoh CortexM)
Untuk memiliki gagasan umum tentang apa yang ingin saya bicarakan, saya akan memberikan sedikit kode:
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
++, .