void delay() { for (int i = 0; i < 1000000; ++i){ } } int main() { for(;;) { GPIOA->ODR ^= (1 << 5); GPIOC->ODR ^= (1 << 5); GPIOC->ODR ^= (1 << 8); GPIOC->ODR ^= (1 << 9); delay(); } return 0 ; }
delay()
function here is a purely formal, ordinary cycle, it cannot be optimized.255 bytes of readonly code memory
1 byte of readonly data memory
8 bytes of readwrite data memory
Gpio
class that should store a link to the port registers at their addresses. To do this, they use the overlay structure, most likely the idea was taken from here: Structure overlay : class Gpio { public: __forceinline inline void Toggle(const std::uint8_t bitNum) volatile { Odr ^= bitNum ; } private: volatile std::uint32_t Moder; volatile std::uint32_t Otyper; volatile std::uint32_t Ospeedr; volatile std::uint32_t Pupdr; volatile std::uint32_t Idr; volatile std::uint32_t Odr; // static_assert(sizeof(Gpio) == sizeof(std::uint32_t) * 6); } ;
Gpio
class with attributes that should be located at the addresses of the respective registers and the method for switching the state by the leg number:GpioPin
, which contains a pointer to Gpio
and the number of the leg: struct GpioPin { volatile Gpio* port ; std::uint32_t pinNum ; } ;
Toggle()
method of each LED: const GpioPin leds[] = {{reinterpret_cast<volatile Gpio*>(GpioaBaseAddr), 5}, {reinterpret_cast<volatile Gpio*>(GpiocBaseAddr), 5}, {reinterpret_cast<volatile Gpio*>(GpiocBaseAddr), 9}, {reinterpret_cast<volatile Gpio*>(GpiocBaseAddr), 9} } ; struct LedsDriver { __forceinline static inline void ToggelAll() { for (auto& it: leds) { it.port->Toggle(it.pinNum); } } } ;
constexpr std::uint32_t GpioaBaseAddr = 0x4002'0000 ; constexpr std::uint32_t GpiocBaseAddr = 0x4002'0800 ; class Gpio { public: __forceinline inline void Toggle(const std::uint8_t bitNum) volatile { Odr ^= bitNum ; } private: volatile std::uint32_t Moder; volatile std::uint32_t Otyper; volatile std::uint32_t Ospeedr; volatile std::uint32_t Pupdr; volatile std::uint32_t Idr; volatile std::uint32_t Odr; } ; // static_assert(sizeof(Gpio) == sizeof(std::uint32_t) * 6); struct GpioPin { volatile Gpio* port ; std::uint32_t pinNum ; } ; const GpioPin leds[] = {{reinterpret_cast<volatile Gpio*>(GpioaBaseAddr), 5}, {reinterpret_cast<volatile Gpio*>(GpiocBaseAddr), 5}, {reinterpret_cast<volatile Gpio*>(GpiocBaseAddr), 9}, {reinterpret_cast<volatile Gpio*>(GpiocBaseAddr), 9} } ; struct LedsDriver { __forceinline static inline void ToggelAll() { for (auto& it: leds) { it.port->Toggle(it.pinNum); } } } ; int main() { for(;;) { LedsContainer::ToggleAll() ; delay(); } return 0 ; }
275 bytes of readonly code memory
1 byte of readonly data memory
8 bytes of readwrite data memory
Port
class, but to get rid of pointers and variables that occupy RAM, you need to use static methods. The port class might look like this: template <std::uint32_t addr> struct Port { // - };
"#include "stm32f411xe.h"
, for example, for port A, it is defined as GPIOABASE. But we are forbidden to use headings, so we just need to make our constant. As a result, the class can be used like this: constexpr std::uint32_t GpioaBaseAddr = 0x4002'0000 ; constexpr std::uint32_t GpiocBaseAddr = 0x4002'0800 ; using PortA = Port<GpioaBaseAddr> ; using PortC = Port<GpiocBaseAddr> ;
template <std::uint32_t addr> struct Port { // __forceinline, __forceinline inline static void Toggle(const std::uint8_t bitNum) { *reinterpret_cast<std::uint32_t*>(addr+20) ^= (1 << bitNum) ; //addr + 20 ODR } };
Port<>
is, it can switch the status of the leg. The LED is sitting on a specific leg, so it is logical to make a class Pin
, which will have Port<>
and the leg number as template parameters. Since the Port<>
type is template, i.e. different for a different port, we can only transmit the universal type T. template <typename T, std::uint8_t pinNum> struct Pin { __forceinline inline static void Toggle() { T::Toggle(pinNum) ; } } ;
T
which has the Toggle()
method and it will work, although it is assumed that we should only transfer the type Port<>
. To protect against this, let us make sure that Port<>
inherited from the base class PortBase
, and in the template we will check that our transferred type is really based on PortBase
. We get the following: constexpr std::uint32_t OdrAddrShift = 20U; struct PortBase { }; template <std::uint32_t addr> struct Port: PortBase { __forceinline inline static void Toggle(const std::uint8_t bit) { *reinterpret_cast<std::uint32_t*>(addr ) ^= (1 << bit) ; } }; template <typename T, std::uint8_t pinNum, class = typename std::enable_if_t<std::is_base_of<PortBase, T>::value>> // struct Pin { __forceinline inline static void Toggle() { T::Toggle(pinNum) ; } } ;
PortBase
. using PortA = Port<GpioaBaseAddr> ; using PortC = Port<GpiocBaseAddr> ; using Led1 = Pin<PortA, 5> ; using Led2 = Pin<PortC, 5> ; using Led3 = Pin<PortC, 8> ; using Led4 = Pin<PortC, 9> ; int main() { for(;;) { Led1::Toggle(); Led2::Toggle(); Led3::Toggle(); Led4::Toggle(); delay(); } return 0 ; }
271 bytes of readonly code memory
1 byte of readonly data memory
24 bytes of readwrite data memory
Pin
class, and calculating the ODR register address inside the Toggle()
method: constexpr std::uint32_t OdrAddrShift = 20U; template <std::uint32_t addr, std::uint8_t pinNum, struct Pin { __forceinline inline static void Toggle() { *reinterpret_cast<std::uint32_t*>(addr + OdrAddrShift ) ^= (1 << bit) ; } } ; using Led1 = Pin<GpioaBaseAddr, 5> ;
251 bytes of readonly code memory
1 byte of readonly data memory
8 bytes of readwrite data memory
255 bytes of readonly code memory
1 byte of readonly data memory
8 bytes of readwrite data memory
int main() { for(;;) { LedsContainer::ToggleAll() ; delay(); } return 0 ; }
Pin<PortA, 5>
, Pin<PortC, 5>
, and I cannot store pointers to different types in an array. You can make a virtual base class for all Pin, but then a table of virtual functions will appear and I won’t be able to win students. class LedsContainer { private: constexpr static auto records = std::make_tuple ( Pin<PortA, 5>{}, Pin<PortC, 5>{}, Pin<PortC, 8>{}, Pin<PortC, 9>{} ) ; using tRecordsTuple = decltype(records) ; }
ToggleAll()
method to it: class LedsContainer { public: __forceinline static inline void ToggleAll() { // } private: constexpr static auto records = std::make_tuple ( Pin<PortA, 5>{}, Pin<PortC, 5>{}, Pin<PortC, 8>{}, Pin<PortC, 9>{} ) ; using tRecordsTuple = decltype(records) ; }
std::get<0>(records).Toggle()
, then the Toggle()
method will be called for the object of the class Pin<PortA, 5>
, if std::get<1>(records).Toggle()
, then the Toggle()
method will be called for an object of the class Pin<Port, 5>
and so on ... __forceinline static inline void ToggleAll() { std::get<0>(records).Toggle(); std::get<1>(records).Toggle(); std::get<2>(records).Toggle(); std::get<3>(records).Toggle(); }
class class LedsContainer { friend int main() ; public: __forceinline static inline void ToggleAll() { // 3,2,1,0 , visit(std::make_index_sequence<std::tuple_size<tRecordsTuple>::value>()); } private: __forceinline template<std::size_t... index> static inline void visit(std::index_sequence<index...>) { Pass((std::get<index>(records).Toggle(), true)...); // get<3>(records).Toggle(), get<2>(records).Toggle(), get<1>(records).Toggle(), get<0>(records).Toggle() } __forceinline template<typename... Args> static void inline Pass(Args... ) {// } constexpr static auto records = std::make_tuple ( Pin<PortA, 5>{}, Pin<PortC, 5>{}, Pin<PortC, 8>{}, Pin<PortC, 9>{} ) ; using tRecordsTuple = decltype(records) ; }
// LedsContainer::ToggleAll() ; // 4 : Pin<Port, 9>().Toggle() ; Pin<Port, 8>().Toggle() ; Pin<PortC, 5>().Toggle() ; Pin<PortA, 5>().Toggle() ; // Toggle() inline, : *reinterpret_cast<std::uint32_t*>(0x40020814 ) ^= (1 << 9) ; *reinterpret_cast<std::uint32_t*>(0x40020814 ) ^= (1 << 8) ; *reinterpret_cast<std::uint32_t*>(0x40020814 ) ^= (1 << 5) ; *reinterpret_cast<std::uint32_t*>(0x40020014 ) ^= (1 << 5) ;
#include <cstddef> #include <tuple> #include <utility> #include <cstdint> #include <type_traits> //#include "stm32f411xe.h" #define __forceinline _Pragma("inline=forced") constexpr std::uint32_t GpioaBaseAddr = 0x4002'0000 ; constexpr std::uint32_t GpiocBaseAddr = 0x4002'0800 ; constexpr std::uint32_t OdrAddrShift = 20U; struct PortBase { }; template <std::uint32_t addr> struct Port: PortBase { __forceinline inline static void Toggle(const std::uint8_t bit) { *reinterpret_cast<std::uint32_t*>(addr + OdrAddrShift) ^= (1 << bit) ; } }; template <typename T, std::uint8_t pinNum, class = typename std::enable_if_t<std::is_base_of<PortBase, T>::value>> struct Pin { __forceinline inline static void Toggle() { T::Toggle(pinNum) ; } } ; using PortA = Port<GpioaBaseAddr> ; using PortC = Port<GpiocBaseAddr> ; //using Led1 = Pin<PortA, 5> ; //using Led2 = Pin<PortC, 5> ; //using Led3 = Pin<PortC, 8> ; //using Led4 = Pin<PortC, 9> ; class LedsContainer { friend int main() ; public: __forceinline static inline void ToggleAll() { // 3,2,1,0 , visit(std::make_index_sequence<std::tuple_size<tRecordsTuple>::value>()); } private: __forceinline template<std::size_t... index> static inline void visit(std::index_sequence<index...>) { Pass((std::get<index>(records).Toggle(), true)...); } __forceinline template<typename... Args> static void inline Pass(Args... ) { } constexpr static auto records = std::make_tuple ( Pin<PortA, 5>{}, Pin<PortC, 5>{}, Pin<PortC, 8>{}, Pin<PortC, 9>{} ) ; using tRecordsTuple = decltype(records) ; } ; void delay() { for (int i = 0; i < 1000000; ++i){ } } int main() { for(;;) { LedsContainer::ToggleAll() ; //GPIOA->ODR ^= 1 << 5; //GPIOC->ODR ^= 1 << 5; //GPIOC->ODR ^= 1 << 8; //GPIOC->ODR ^= 1 << 9; delay(); } return 0 ; }
283 bytes of readonly code memory
1 byte of readonly data memory
24 bytes of readwrite data memory
251 bytes of readonly code memory
1 byte of readonly data memory
8 bytes of readwrite data memory
Param<float<>>
, Param<int<>>
and it is necessary, for example, reset all parameters to default values. Just here it is possible to put all of them in a tuple, since the type is different and call SetToDefault()
method for each parameter. True, if there are 100 such parameters, then the ROM will play off a lot, but the RAM will not suffer.std::apply()
. The code of the ToggleAll()
function is then simplified to this: __forceinline static inline void ToggleAll() { std::apply([](auto... args) { (args.Toggle(), ...); }, records); }
Source: https://habr.com/ru/post/457246/
All Articles