Registros C ++, metaprogramação e microcontrolador

Olá Habr!


Há vários anos, tenho realizado todos os meus projetos para a linha de microcontroladores stm32 em C ++.


Durante esse período, acumulei uma certa quantidade de material que pode ser do interesse de outros desenvolvedores.


Para evitar perguntas desnecessárias: Eu uso um monte de QtCreator + gcc + gdb + openocd. Como trabalhar com isso já foi descrito muitas vezes, por isso não vou parar por aí, mas vou falar mais sobre minhas abordagens para trabalhar com microcontroladores.


No nível mais baixo do projeto, como regra, existem drivers periféricos. Vamos começar com eles.


Tento, sempre que possível, não mexer com monstros como SPL, HAL e, Deus me perdoe, CubeMX, exceto para terminar os projetos de outras pessoas por dinheiro. A ideologia da estrutura "quanto às grandes máquinas", na minha opinião, é cruel lá. Concebidas por funções flexíveis e fáceis de usar (embora alguém discuta isso), elas se transformam em um código de máquina bastante grande e subótimo, onde muitas operações desnecessárias são executadas no microcontrolador em tempo de execução. Sim, os controladores modernos são poderosos, mas o computador no qual montamos o projeto é muito mais poderoso, então deixe-o fazer tudo.


Determinei por mim mesmo os requisitos básicos para a implementação do gerenciamento periférico:


  • todas as expressões constantes devem ser computadas no estágio de compilação e cair no código da 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