Article suivant: STM32 pour les débutants

Je souhaite la bienvenue à tout le monde!



C'est mon premier article sur Habré, donc je vous demande de ne pas lancer d'objets lourds. Merci d'avance.



Commençons par l'arrière-plan. Il était une fois, je devais passer aux microcontrôleurs ST ARM. Cela était dû au fait que PIC et AVR étaient déjà rares et voulaient de nouvelles aventures. Parmi ceux disponibles en boulangerie et un grand nombre d'articles sur le "démarrage rapide", le choix s'est porté sur le STM32F100.



J'ai l'habitude de travailler à l'IAR. Oui, il existe d'autres IDE, mais la fonctionnalité IAR me suffit: un éditeur relativement pratique, pas un mauvais débogueur, et il est assez pratique de travailler avec des registres pendant le débogage.



Quand j'ai essayé de faire le premier projet, j'ai été déçu - CMSIS! N'importe qui d'autre, mais pour moi c'était (et reste) l'horreur: beaucoup de hêtres, des structures longues et incompréhensibles pour moi. Ce n'était pas intéressant de se plonger dans tout cela. J'ai essayé de compiler quelques exemples et j'ai réalisé que ce n'était pas notre méthode.



N'y a-t-il pas d'autres options? Il y a. Celui intégré à l'IAR: iostm32f10xx4.h et similaires incluent. Pas mal du tout:



RCC_APB2ENR_bit.ADC1EN = 1; //   ADC


Il ne restait plus qu'à le fourrer dans les classes et à l'utiliser. Et il l'a fait. Après un certain temps, il a fallu créer le code pour STM32f4xx. Et là encore une embuscade - il n'y a pas d'inclus. Que faire? - écrivez-vous. J'ai analysé les bibliothèques auto-écrites existantes et j'ai décidé de faire un peu différemment. Voilà de quoi parlera l'histoire.



Début



Je ne parlerai pas de l'installation de l'IAR et des pilotes du débogueur. Il n'y a rien de nouveau ici. J'ai IAR 8 avec une limite de code de 32 Ko. Le contrôleur STM32F103 installé sur le panneau de pilules de plue est sélectionné pour fonctionner.



Lancez IAR, créez un projet c ++, sélectionnez le contrôleur souhaité,

image

l'étape suivante consiste à étudier la documentation. Nous serons intéressés par le Manuel de référence RM0008. L'essentiel est de lire attentivement.



En général, lorsque j'ai appris à mes employés à programmer des contrôleurs, j'ai donné la tâche: allumer la LED (connectée à la jambe du contrôleur), utiliser un débogueur, modifier les registres et lire la documentation.



Module RCC. Tucking



Ce module est généralement oublié. Ils ne se souviennent que lorsqu'il est impossible de faire clignoter la LED.



Rappelles toi! Pour allumer n'importe quel périphérique, vous devez lui appliquer des impulsions d'horloge! Vous ne pouvez pas vous en passer.



Les ports d'E / S se trouvent sur le bus APB2. On trouve dans la documentation un registre pour contrôler le cadencement de ce bus, c'est RCC_APB2ENR:







Pour activer le cadencement du port C (la LED est juste soudée au PC13), il faut en écrire un sur le bit IOPCEN.



Nous allons maintenant trouver l'adresse du registre RCC_APB2ENR. Son offset est 0x18, l'adresse de base des registres RCC est 0x40021000.



Pour faciliter le travail avec des bits, créons une structure:



typedef struct
{
  uint32_t  AFIOEN         : 1;
  uint32_t                 : 1;
  uint32_t  IOPAEN         : 1;
  uint32_t  IOPBEN         : 1;
  uint32_t  IOPCEN         : 1;
  uint32_t  IOPDEN         : 1;
  uint32_t  IOPEEN         : 1;
  uint32_t                 : 2;
  uint32_t  ADC1EN         : 1;
  uint32_t  ADC2EN         : 1;
  uint32_t  TIM1EN         : 1;
  uint32_t  SPI1EN         : 1;
  uint32_t                 : 1;
  uint32_t  USART1EN       : 1;
  uint32_t                 :17;
} RCC_APB2ENR_b;


Afin de ne pas souffrir plus tard, nous listerons immédiatement toutes les adresses de registre:



enum AddrRCC
{
  RCC_CR          = 0x40021000,
  RCC_CFGR        = 0x40021004,
  RCC_CIR         = 0x40021008,
  RCC_APB2RSTR    = 0x4002100C,
  RCC_APB1RSTR    = 0x40021010,
  RCC_AHBENR      = 0x40021014,
  RCC_APB2ENR     = 0x40021018,
  RCC_APB1ENR     = 0x4002101C,
  RCC_BDCR        = 0x40021020,
  RCC_CSR         = 0x40021024
};


il reste maintenant à écrire le code pour activer les périphériques:



static void EnablePort(uint8_t port_name)
{
  volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
  switch (port_name)
  {
    case 'A': apb2enr->IOPAEN = 1; break;
    case 'a': apb2enr->IOPAEN = 1; break;
    case 'B': apb2enr->IOPBEN = 1; break;
    case 'b': apb2enr->IOPBEN = 1; break;
    case 'C': apb2enr->IOPCEN = 1; break;
    case 'c': apb2enr->IOPCEN = 1; break;
    case 'D': apb2enr->IOPDEN = 1; break;
    case 'd': apb2enr->IOPDEN = 1; break;
    case 'E': apb2enr->IOPEEN = 1; break;
    case 'e': apb2enr->IOPEEN = 1; break;
  }
}


Lorsque vous travaillez avec des registres, n'oubliez pas les volatils , sinon, après optimisation par le compilateur, nous rechercherons les erreurs pendant longtemps et gronderons les développeurs du compilateur.



Nous faisons de même pour activer la synchronisation d'autres périphériques.



En conséquence, nous avons obtenu la classe suivante (tout n'est pas répertorié):



STM32F1xx_RCC.h

#pragma once
#include "stdint.h"
namespace STM32F1xx
{
  class RCC
  {
  protected:
    enum AddrRCC
    {
      RCC_CR          = 0x40021000,
      RCC_CFGR        = 0x40021004,
      RCC_CIR         = 0x40021008,
      RCC_APB2RSTR    = 0x4002100C,
      RCC_APB1RSTR    = 0x40021010,
      RCC_AHBENR      = 0x40021014,
      RCC_APB2ENR     = 0x40021018,
      RCC_APB1ENR     = 0x4002101C,
      RCC_BDCR        = 0x40021020,
      RCC_CSR         = 0x40021024
    };
    
    typedef struct {
      uint32_t  HSION          : 1;
      uint32_t  HSIRDY         : 1;
      uint32_t                 : 1;
      uint32_t  HSI_TRIM       : 5;
      uint32_t  HSI_CAL        : 8;
      uint32_t  HSEON          : 1;
      uint32_t  HSERDY         : 1;
      uint32_t  HSEBYP         : 1;
      uint32_t  CSSON          : 1;
      uint32_t                 : 4;
      uint32_t  PLLON          : 1;
      uint32_t  PLLRDY         : 1;
      uint32_t                 : 6;
    } RCC_CR_b;
		
    typedef struct {
      uint32_t  SW             : 2;
      uint32_t  SWS            : 2;
      uint32_t  HPRE           : 4;
      uint32_t  PPRE1          : 3;
      uint32_t  PPRE2          : 3;
      uint32_t  ADC_PRE        : 2;
      uint32_t  PLLSRC         : 1;
      uint32_t  PLLXTPRE       : 1;
      uint32_t  PLLMUL         : 4;
      uint32_t  USBPRE         : 1;
      uint32_t                 : 1;
      uint32_t  MCO            : 3;
      uint32_t                 : 5;
    } RCC_CFGR_b;

    typedef struct
    {
      uint32_t  TIM2EN         : 1;
      uint32_t  TIM3EN         : 1;
      uint32_t  TIM4EN         : 1;
      uint32_t                 : 8;
      uint32_t  WWDGEN         : 1;
      uint32_t                 : 2;
      uint32_t  SPI2EN         : 1;
      uint32_t                 : 2;
      uint32_t  USART2EN       : 1;
      uint32_t  USART3EN       : 1;
      uint32_t                 : 2;
      uint32_t  I2C1EN         : 1;
      uint32_t  I2C2EN         : 1;
      uint32_t  USBEN          : 1;
      uint32_t                 : 1;
      uint32_t  CANEN          : 1;
      uint32_t                 : 1;
      uint32_t  BKPEN          : 1;
      uint32_t  PWREN          : 1;
      uint32_t                 : 3;
    } RCC_APB1ENR_b;
		
    typedef struct
    {
      uint32_t  AFIOEN         : 1;
      uint32_t                 : 1;
      uint32_t  IOPAEN         : 1;
      uint32_t  IOPBEN         : 1;
      uint32_t  IOPCEN         : 1;
      uint32_t  IOPDEN         : 1;
      uint32_t  IOPEEN         : 1;
      uint32_t                 : 2;
      uint32_t  ADC1EN         : 1;
      uint32_t  ADC2EN         : 1;
      uint32_t  TIM1EN         : 1;
      uint32_t  SPI1EN         : 1;
      uint32_t                 : 1;
      uint32_t  USART1EN       : 1;
      uint32_t                 :17;
    } RCC_APB2ENR_b;

    typedef struct {
      uint32_t  DMAEN          : 1;
      uint32_t                 : 1;
      uint32_t  SRAMEN         : 1;
      uint32_t                 : 1;
      uint32_t  FLITFEN        : 1;
      uint32_t                 : 1;
      uint32_t  CRCEN          : 1;
      uint32_t                 :25;
    } RCC_AHBENR_r;
    
  public:
    static void EnablePort(uint8_t port_name)
    {
      volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
      switch (port_name)
      {
        case 'A': apb2enr->IOPAEN = 1; break;
        case 'a': apb2enr->IOPAEN = 1; break;
        case 'B': apb2enr->IOPBEN = 1; break;
        case 'b': apb2enr->IOPBEN = 1; break;
        case 'C': apb2enr->IOPCEN = 1; break;
        case 'c': apb2enr->IOPCEN = 1; break;
        case 'D': apb2enr->IOPDEN = 1; break;
        case 'd': apb2enr->IOPDEN = 1; break;
        case 'E': apb2enr->IOPEEN = 1; break;
        case 'e': apb2enr->IOPEEN = 1; break;
      }
    }

    static void DisablePort(char port_name)
    {
      volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
      switch (port_name)
      {
        case 'A': apb2enr->IOPAEN = 0; break;
        case 'a': apb2enr->IOPAEN = 0; break;
        case 'B': apb2enr->IOPBEN = 0; break;
        case 'b': apb2enr->IOPBEN = 0; break;
        case 'C': apb2enr->IOPCEN = 0; break;
        case 'c': apb2enr->IOPCEN = 0; break;
        case 'D': apb2enr->IOPDEN = 0; break;
        case 'd': apb2enr->IOPDEN = 0; break;
        case 'E': apb2enr->IOPEEN = 0; break;
        case 'e': apb2enr->IOPEEN = 0; break;
      }
    }

    static void EnableAFIO()
    {
      volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
      apb2enr->AFIOEN = 1;
    }

    static void DisableAFIO()
    {
      volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
      apb2enr->AFIOEN = 0;
    }
    
    static void EnableI2C(int PortNumber)
    {
      switch (PortNumber)
      {
        case 1:
        {
          volatile RCC_APB1ENR_b* apb1enr = reinterpret_cast<RCC_APB1ENR_b*>(RCC_APB1ENR);
          apb1enr->I2C1EN = 1;
          break;
        }
        case 2:
        {
          volatile RCC_APB1ENR_b* apb1enr = reinterpret_cast<RCC_APB1ENR_b*>(RCC_APB1ENR);
          apb1enr->I2C2EN = 1;
          break;
        }

      }
    }

    static void EnableUART(int PortNumber)
    {
      switch (PortNumber)
      {
        case 1:
        {
          volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
          apb2enr->USART1EN = 1;
          break;
        }
        case 2:
        {
          volatile RCC_APB1ENR_b* apb1enr = reinterpret_cast<RCC_APB1ENR_b*>(RCC_APB1ENR);
          apb1enr->USART2EN = 1;
          break;
        }
        case 3:
        {
          volatile RCC_APB1ENR_b* apb1enr = reinterpret_cast<RCC_APB1ENR_b*>(RCC_APB1ENR);
          apb1enr->USART3EN = 1;
          break;
        }

      }
    }
    
    static void DisableUART(int PortNumber)
    {
      switch (PortNumber)
      {
        case 1:
        {
          volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
          apb2enr->USART1EN = 0;
          break;
        }
        case 2:
        {
          volatile RCC_APB1ENR_b* apb1enr = reinterpret_cast<RCC_APB1ENR_b*>(RCC_APB1ENR);
          apb1enr->USART2EN = 0;
          break;
        }
        case 3:
        {
          volatile RCC_APB1ENR_b* apb1enr = reinterpret_cast<RCC_APB1ENR_b*>(RCC_APB1ENR);
          apb1enr->USART3EN = 0;
          break;
        }
      }
    }
    
    static void EnableSPI(int PortNumber)
    {
      switch (PortNumber)
      {
        case 1:
        {
          volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
          apb2enr->SPI1EN = 1;
          break;
        }
        case 2:
        {
          volatile RCC_APB1ENR_b* apb1enr = reinterpret_cast<RCC_APB1ENR_b*>(RCC_APB1ENR);
          apb1enr->SPI2EN = 1;
          break;
        }
      }
    }

    static void DisableSPI(int PortNumber)
    {
      switch (PortNumber)
      {
        case 1:
        {
          volatile RCC_APB2ENR_b* apb2enr = reinterpret_cast<RCC_APB2ENR_b*>(RCC_APB2ENR);
          apb2enr->SPI1EN = 0;
          break;
        }
        case 2:
        {
          volatile RCC_APB1ENR_b* apb1enr = reinterpret_cast<RCC_APB1ENR_b*>(RCC_APB1ENR);
          apb1enr->SPI2EN = 0;
          break;
        }
      }
    }
    
    static void EnableDMA()
    {
      volatile RCC_AHBENR_r* ahbenr = reinterpret_cast<RCC_AHBENR_r*>(RCC_AHBENR);
      ahbenr->DMAEN = 1;
    }
    
    static void DisableDMA()
    {
      volatile RCC_AHBENR_r* ahbenr = reinterpret_cast<RCC_AHBENR_r*>(RCC_AHBENR);
      ahbenr->DMAEN = 0;
    }
  };
}




Vous pouvez maintenant joindre un fichier dans main.cpp et utiliser:



#include "STM32F1xx_RCC.h"

using namespace STM32F1xx;

int main()
{
  RCC::EnablePort('c');
  return 0;
}


Vous pouvez maintenant travailler avec les ports. GPIO



Ouvrez la section E / S à usage général et à fonction alternative de la documentation. Trouvez le tableau de configuration des bits du port:







Les bits CNF [1: 0] définissent le mode de fonctionnement du port (entrée analogique, entrée numérique, sortie), les bits MODE [1: 0] correspondent à la vitesse de fonctionnement du port en mode sortie.



Jetons un œil aux registres GPIOx_CRL et GPIOx_CRH (x = A, B, C, ...), vous







pouvez voir que les bits vont séquentiellement:



CNF [1: 0], MODE [1: 0]



puis créer des constantes avec les modes de port



enum mode_e
{
  ANALOGINPUT             = 0,
  INPUT                   = 4,
  INPUTPULLED             = 8,

  OUTPUT_10MHZ            = 1,
  OUTPUT_OD_10MHZ         = 5,
  ALT_OUTPUT_10MHZ        = 9,
  ALT_OUTPUT_OD_10MHZ     = 13,

  OUTPUT_50MHZ            = 3,
  OUTPUT_OD_50MHZ         = 7,
  ALT_OUTPUT_50MHZ        = 11,
  ALT_OUTPUT_OD_50MHZ     = 15,

  OUTPUT_2MHZ             = 2,
  OUTPUT_OD_2MHZ          = 6,
  ALT_OUTPUT_2MHZ         = 10,
  ALT_OUTPUT_OD_2MHZ      = 14,

  OUTPUT                  = 3,
  OUTPUT_OD               = 7,
  ALT_OUTPUT              = 11,
  ALT_OUTPUT_OD           = 15
};


alors la méthode de configuration ressemblera à ceci:



// pin_number -  
void Mode(mode_e mode)
{
  uint32_t* addr;
  if(pin_number > 7)
    addr = reinterpret_cast<uint32_t*>(GPIOA_CRH);
  else
    addr = reinterpret_cast<uint32_t*>(GPIOA_CRL);
  
  int bit_offset;
  if(pin_number > 7)
    bit_offset = (pin_number - 8) * 4;
  else
    bit_offset = pin_number * 4;

  uint32_t mask = ~(15 << bit_offset);
  *addr &= mask;
  *addr |= ((int)mode) << bit_offset;
}


vous pouvez maintenant créer des méthodes plus pratiques pour sélectionner un mode:



    void ModeInput()              { Mode(INPUT);         }
    void ModeAnalogInput()        { Mode(ANALOGINPUT);   }
    void ModeInputPulled()        { Mode(INPUTPULLED);   }
    void ModeOutput()             { Mode(OUTPUT);        }
    void ModeOutputOpenDrain()    { Mode(OUTPUT_OD);     }
    void ModeAlternate()          { Mode(ALT_OUTPUT);    }
    void ModeAlternateOpenDrain() { Mode(ALT_OUTPUT_OD); }


Dans la documentation, nous trouvons les adresses des registres de contrôle des ports et les listons:



enum AddrGPIO
{
  PortA           = 0x40010800,
  GPIOA_CRL       = 0x40010800,
  GPIOA_CRH       = 0x40010804,
  GPIOA_IDR       = 0x40010808,
  GPIOA_ODR       = 0x4001080C,
  GPIOA_BSRR      = 0x40010810,
  GPIOA_BRR       = 0x40010814,
  GPIOA_LCKR      = 0x40010818,
  PortB           = 0x40010C00,
  PortC           = 0x40011000,
  PortD           = 0x40011400,
  PortE           = 0x40011800,
  PortF           = 0x40011C00,
  PortG           = 0x40012000
};


On a longtemps pensé utiliser une adresse de base et des décalages ou des adresses absolues. Au final, je me suis arrêté à ce dernier. Cela ajoute une surcharge, mais c'est plus facile à trouver en mémoire pendant le débogage.



Modernisons la méthode:



if(pin_number > 7)
  addr = reinterpret_cast<uint32_t*>(GPIOA_CRH - PortA + PortAddr);
else
  addr = reinterpret_cast<uint32_t*>(GPIOA_CRL - PortA + PortAddr);


Peut-être que quelqu'un aura une contraction oculaire, mais je n'en ai pas encore trouvé de plus belle.



Pour transférer le tronçon à l'état logique souhaité, il suffit d'écrire le bit correspondant dans le registre ODRx. Par exemple, comme ceci:



void Set(bool st)
{
  uint32_t* addr;
  addr = reinterpret_cast<uint32_t*>(GPIOA_ODR - PortA + PortAddr);

  if(st)
    *addr |= 1 << pin_number;
  else
  {
    int mask = ~(1 << pin_number);
    *addr &= mask;
  } 
}


Vous pouvez également utiliser les registres GPIOx_BSRR pour contrôler l'état.

Par analogie, nous faisons des méthodes de lecture de l'état du port, des méthodes de configuration et d'initialisation (n'oubliez pas d'activer la synchronisation). En conséquence, nous avons obtenu la classe suivante pour travailler avec les ports:



STM32F1xx_Pin.h
#pragma once
#include <stdint.h>
#include "STM32F1xx_RCC.h"

namespace STM32F1xx
{
  class Pin
  {
  public:
    enum mode_e
    {
      ANALOGINPUT             = 0,
      INPUT                   = 4,
      INPUTPULLED             = 8,

      OUTPUT_10MHZ            = 1,
      OUTPUT_OD_10MHZ         = 5,
      ALT_OUTPUT_10MHZ        = 9,
      ALT_OUTPUT_OD_10MHZ     = 13,

      OUTPUT_50MHZ            = 3,
      OUTPUT_OD_50MHZ         = 7,
      ALT_OUTPUT_50MHZ        = 11,
      ALT_OUTPUT_OD_50MHZ     = 15,

      OUTPUT_2MHZ             = 2,
      OUTPUT_OD_2MHZ          = 6,
      ALT_OUTPUT_2MHZ         = 10,
      ALT_OUTPUT_OD_2MHZ      = 14,

      OUTPUT                  = 3,
      OUTPUT_OD               = 7,
      ALT_OUTPUT              = 11,
      ALT_OUTPUT_OD           = 15
    };
    
  private:
    enum AddrGPIO
    {
      PortA           = 0x40010800,
      GPIOA_CRL       = 0x40010800,
      GPIOA_CRH       = 0x40010804,
      GPIOA_IDR       = 0x40010808,
      GPIOA_ODR       = 0x4001080C,
      GPIOA_BSRR      = 0x40010810,
      GPIOA_BRR       = 0x40010814,
      GPIOA_LCKR      = 0x40010818,
      PortB           = 0x40010C00,
      PortC           = 0x40011000,
      PortD           = 0x40011400,
      PortE           = 0x40011800,
      PortF           = 0x40011C00,
      PortG           = 0x40012000
    };
    
  private:
    int   pin_number;
    int   PortAddr;
    
  public:
    Pin()                               { }
    Pin(char port_name, int pin_number) { Init(port_name, pin_number); }
    ~Pin()
    {
      Off();
      ModeAnalogInput();
    }
  public:
    void Init(char port_name, int pin_number)
    {
      this->pin_number = pin_number;
      RCC::EnablePort(port_name);
      switch (port_name)
      {
        case 'A': PortAddr = PortA; break;
        case 'a': PortAddr = PortA; break;
        case 'B': PortAddr = PortB; break;
        case 'b': PortAddr = PortB; break;
        case 'C': PortAddr = PortC; break;
        case 'c': PortAddr = PortC; break;
        case 'D': PortAddr = PortD; break;
        case 'd': PortAddr = PortD; break;
        case 'E': PortAddr = PortE; break;
        case 'e': PortAddr = PortE; break;
      }
    }

    void ModeInput()              { Mode(INPUT);         }
    void ModeAnalogInput()        { Mode(ANALOGINPUT);   }
    void ModeInputPulled()        { Mode(INPUTPULLED);   }
    void ModeOutput()             { Mode(OUTPUT);        }
    void ModeOutputOpenDrain()    { Mode(OUTPUT_OD);     }
    void ModeAlternate()          { Mode(ALT_OUTPUT);    }
    void ModeAlternateOpenDrain() { Mode(ALT_OUTPUT_OD); }

    void NoPullUpDown()
    {
      uint32_t* addr;
      if(pin_number > 7)
        addr = reinterpret_cast<uint32_t*>(GPIOA_CRH - PortA + PortAddr);
      else
        addr = reinterpret_cast<uint32_t*>(GPIOA_CRL - PortA + PortAddr);

      int bit_offset;
      if(pin_number > 7)
        bit_offset = (pin_number - 8) * 4;
      else
         bit_offset = pin_number * 4;

      int mask = ~((1 << 3) << bit_offset);
      *addr &= mask;
    }
    
    void Mode(mode_e mode)
    {
      uint32_t* addr;
      if(pin_number > 7)
        addr = reinterpret_cast<uint32_t*>(GPIOA_CRH - PortA + PortAddr);
      else
        addr = reinterpret_cast<uint32_t*>(GPIOA_CRL - PortA + PortAddr);
      
      int bit_offset;
      if(pin_number > 7)
        bit_offset = (pin_number - 8) * 4;
      else
        bit_offset = pin_number * 4;

      uint32_t mask = ~(15 << bit_offset);
      *addr &= mask;
      *addr |= ((int)mode) << bit_offset;
    }

    void Set(bool st)
    {
      uint32_t* addr;
      addr = reinterpret_cast<uint32_t*>(GPIOA_ODR - PortA + PortAddr);

      if(st)
        *addr |= 1 << pin_number;
      else
      {
        int mask = ~(1 << pin_number);
        *addr &= mask;
      } 
    }

    void On()
    {
      uint32_t* addr;
      addr = reinterpret_cast<uint32_t*>(GPIOA_ODR - PortA + PortAddr);
      int bit_offset = pin_number;
      *addr |= 1 << bit_offset;
    }

    void Off()
    {
      uint32_t* addr;
      addr = reinterpret_cast<uint32_t*>(GPIOA_ODR - PortA + PortAddr);
      int bit_offset = pin_number;
      int mask = ~(1 << bit_offset);
      *addr &= mask;
    }

    bool Get()
    {
      uint32_t* addr = reinterpret_cast<uint32_t*>(GPIOA_IDR - PortA + PortAddr);
      int bit_offset = pin_number;
      int mask = (1 << bit_offset);
      bool ret_val = (*addr & mask);
      return ret_val;
    }
  };
};




Eh bien, essayons:

#include "STM32F1xx_Pin.h"

using namespace STM32F1xx;

Pin led('c', 13);

int main()
{
  led.ModeOutput();
  led.On();
  led.Off();
  return 0;
}


Nous passons par le débogueur et nous nous assurons que la LED s'allume d'abord (après led.ModeOutput ();), puis s'éteint (led.On ();) et se rallume (led.Off ();). C'est parce que la LED est connectée à la jambe via la ligne électrique. Par conséquent, lorsque la broche est basse, la LED s'allume.



Pas super totaux



Dans cet article, j'ai essayé (j'espère que cela a réussi) de montrer comment vous pouvez vous simplifier un peu la vie, rendre le code plus lisible. Ou vice versa - comment ne pas le faire. Chacun décidera par lui-même.

Il était possible d'écrire simplement des wrappers pour CMSIS, mais ce n'est pas intéressant.



Merci pour votre temps. Si vous êtes intéressé par la suite, faites-le moi savoir.



All Articles