C ++, registros de metaprogramación y microcontroladores

Hola Habr!


Desde hace varios años, he estado haciendo todos mis proyectos para la línea de microcontroladores stm32 en C ++.


Durante este tiempo, he acumulado una cierta cantidad de material que puede ser de interés para otros desarrolladores.


Para evitar preguntas innecesarias: uso un montón de QtCreator + gcc + gdb + openocd. Se ha descrito muchas veces cómo trabajar con él, así que no me detendré allí, pero les contaré más sobre mis enfoques para trabajar con microcontroladores.


En el nivel inferior del proyecto, por regla general, hay controladores periféricos. Comencemos con ellos.


Intento, siempre que sea posible, no meterme con monstruos como SPL, HAL y, Dios me perdone, CubeMX, excepto para terminar los proyectos de otras personas por dinero. La ideología del marco "en cuanto a máquinas grandes", en mi opinión, es viciosa allí. Concebido por funciones flexibles y fáciles de usar (aunque alguien discutirá con esto) se convierten en un código de máquina bastante grande y subóptimo, donde se realizan muchas operaciones innecesarias en el microcontrolador en tiempo de ejecución. Sí, los controladores modernos son potentes, pero la computadora en la que ensamblamos el proyecto es mucho más potente, así que déjelo hacer todo.


Determiné por mí mismo los requisitos básicos para la implementación de la gestión periférica:


  • todas las expresiones constantes deben calcularse en la etapa de compilación y caer en código máquina como números;
  • , ;
  • , ;
  • .

, . - CMSIS, , ST. , , , , .


STM32f103 Flash access control register, , .



flash CMSIS 72:


FLASH->ACR = (FLASH->ACR &
              (~(FLASH_ACR_LATENCY_Msk
                 | FLASH_ACR_PRFTBE_Msk ))) //    
             | FLASH_ACR_LATENCY_1          //   ,    
             | FLASH_ACR_PRFTBE;

, ? , - , , . . , , :


0x80001ec                  04 4a        ldr r2, [pc, #16]   ; (0x8000200 <main()+20>)
0x80001ee  <+    2>        13 68        ldr r3, [r2, #0]
0x80001f0  <+    4>        23 f0 17 03  bic.w   r3, r3, #23
0x80001f4  <+    8>        43 f0 12 03  orr.w   r3, r3, #18
0x80001f8  <+   12>        13 60        str r3, [r2, #0]

, : --. ?


. .


, . . flash :


struct Regs {
    uint32_t ACR;   
    //      
};

:


struct ACR {
    constexpr static uint8_t LATENCY[]{ 0, 3 };
    constexpr static uint8_t HLFCYA[]{ 3, 1 };
    constexpr static uint8_t PRFTBE[]{ 4, 1 };
    constexpr static uint8_t PRFTBS[]{ 5, 1 };
};  

— , — . Python SVD .


, . constexpr :


constexpr static uint32_t base = 0x40022000; //    
INLINE constexpr static volatile Regs* rg()
{
    return reinterpret_cast<volatile Regs*>(base);
}

base , (, , , , ) . .


INLINE


#ifndef INLINE
#define INLINE __attribute__((__always_inline__)) inline 
#endif

, . , , gcc .


flash :


INLINE static void setLatency(Flash::Latency latency, bool prefetchBufferEnable = false) 
{ 
    setRegister(rg()->ACR,
        ACR::LATENCY, static_cast<uint8_t>(latency),
        ACR::PRFTBE, prefetchBufferEnable
    ); 
}

, : , . latency , . static_cast<uint8_t>(latency) , , , Latency :


enum class Latency : uint8_t {
    zeroWaitState = 0b000,
    oneWaitState = 0b001,
    twoWaitStates = 0b010
};


Flash::setLatency(Flash::Latency::twoWaitStates, true);

:


0x80001ec                  04 4a        ldr r2, [pc, #16]   ; (0x8000200 <main()+20>)
0x80001ee  <+    2>        13 68        ldr r3, [r2, #0]
0x80001f0  <+    4>        23 f0 17 03  bic.w   r3, r3, #23
0x80001f4  <+    8>        43 f0 12 03  orr.w   r3, r3, #18
0x80001f8  <+    12>       13 60        str r3, [r2, #0]

CMSIS: --, .


? setRegister. :


template<typename T, typename V, typename... Args>
INLINE constexpr static void setRegister(volatile uint32_t& reg,
                                         const T field,
                                         const V value,
                                         const Args... args)
{
    uint32_t mask = setMaskR(field, value, args...);
    uint32_t val = setBitsR(field, value, args...);

    reg = (reg & (~mask)) | val;
}

( uint32_t) — . .
:


template<typename V, typename T>
INLINE constexpr static uint32_t setBitsR(T field, V val)
{
    return (val << (field[0]));
}

template<typename V, typename T, typename... Args>
INLINE constexpr static uint32_t  setBitsR(T field, V val, Args... args)
{
    return (val << (field[0])) | setBitsR(args...);
}  

template<typename V, typename T>
INLINE constexpr static uint32_t setMaskR(T field, V val)
{
    return ((((1 << field[1]) - 1) << field[0]));
}

template<typename V, typename T, typename... Args>
INLINE constexpr static uint32_t  setMaskR(T field, V val, Args... args)
{
    return ((((1 << field[1]) - 1) << field[0])) | setMaskR(args...);
}

"" . " " , , , , , , .


setRegister? . , - - — . , , , , volatile, , . , , .


. :


Flash::setLatency(Flash::Latency::twoWaitStates, true);
Flash::setLatency(Flash::Latency::oneWaitState, true);

--:


0x80001ec                  07 4a        ldr r2, [pc, #28]   ; (0x800020c <main()+32>)
0x80001ee  <+    2>        13 68        ldr r3, [r2, #0]
0x80001f0  <+    4>        23 f0 17 03  bic.w   r3, r3, #23
0x80001f4  <+    8>        43 f0 12 03  orr.w   r3, r3, #18
0x80001f8  <+   12>        13 60        str r3, [r2, #0]        ;  
0x80001fa  <+   14>        13 68        ldr r3, [r2, #0]
0x80001fc  <+   16>        23 f0 17 03  bic.w   r3, r3, #23
0x8000200  <+   20>        43 f0 11 03  orr.w   r3, r3, #17
0x8000204  <+   24>        13 60        str r3, [r2, #0]        ;  

:


template<typename T>
INLINE constexpr static uint32_t getRegField(volatile uint32_t& reg,
                                             const T field)
{
    uint32_t mask = (((1 << field[1]) - 1) << field[0]); 
    return ((reg & mask) >> field[0]);
}

.


— :


INLINE static bool getLatencyPrefetch()
{
    return getRegField(rg()->ACR,
                       ACR::LATENCY,
                       ACR::PRFTBE);
}

flash :


struct Flash {
    constexpr static uint32_t base = 0x40022000; //<   

struct ACR {
    constexpr static uint8_t LATENCY[]{ 0, 3 };
    constexpr static uint8_t HLFCYA[]{ 3, 1 };
    constexpr static uint8_t PRFTBE[]{ 4, 1 };
    constexpr static uint8_t PRFTBS[]{ 5, 1 };
};    

enum class Latency : uint8_t {
    zeroWaitState = 0b000,
    oneWaitState = 0b001,
    twoWaitStates = 0b010
};

INLINE constexpr static volatile Regs* rg()
{
    return reinterpret_cast<volatile Regs*>(base);
}

INLINE static void setLatency(Flash::Latency latency, bool prefetchBufferEnable = false) 
{ 
    Utility::setRegister(rg()->ACR,
        ACR::LATENCY, static_cast<uint8_t>(latency),
        ACR::PRFTBE, prefetchBufferEnable
    ); 
}
INLINE static bool getLatencyPrefetch()
{
    return getRegField(rg()->ACR,
                       ACR::LATENCY,
                       ACR::PRFTBE);
}
};    

. , , . .


- , .


, . , DSP-. — .


All Articles