STM32 Teil 3: Erstes Projekt

Wir beobachten eine Gesellschaft, die zunehmend von Maschinen abhängig ist, diese aber gleichzeitig alle ineffizienter nutzt. - Douglas Rushkoff


Dieser Satz sollte als Motivation für jeden Programmierer dienen. Schließlich entscheiden Sie selbst, wie die Maschine ihre Ressourcen nutzt. Aber von Anfang an vertraut der Mensch sein Recht an, im Gegenzug für den einfachen Weg an Dritte zu entscheiden. Bevor Sie mich nach den Vorteilen meiner Artikel fragen, wenn es einen „Würfel“ gibt, fragen Sie sich, warum der „Würfel“ für mich entscheidet.

STM32 Teil 1:
STM32- Grundlagen Teil 2: Initialisierung

Lassen Sie uns also unser Abenteuer fortsetzen. Wir haben bereits das Initialisierungsskript geschrieben, den Linker und den Compiler herausgefunden. Es ist Zeit, die LED zu blinken. In diesem Artikel werden wir die Grundlagen des RCC- und GPIO-Blocks erläutern und einige Header hinzufügen, die wir in zukünftigen Projekten verwenden werden. Gehen.

RCC - Reset and Clock Control, ein Registerblock, der den Takt des Prozessors und der Peripheriegeräte steuert. Dieses Gerät steuert mehrere Taktquellen: HSI (High Speed ​​Internal), HSE (High Speed ​​Extern), PLL / PLLSAI / PLLI2S (Phased Loop Lock). Alle drei dieser Taktquellen können natürlich vom Prozessor verwendet werden, wenn die RCC-Blockregister korrekt konfiguriert sind. Wir werden im nächsten Artikel ausführlich darauf eingehen.

GPIO - Allzweck-Ein- und Ausgang. Der Registerblock, der zum Blinken von LEDs erstellt wird. Das heißt, er ist ein Block zur Steuerung der Beine unserer Mikrometer, ein Portal zur realen Welt. Im Gegensatz zum bekannten Atmega328p (Arduino) bietet stm32 eine breitere Anpassung der Beine. Zum Beispiel ist die Open Drain-Fußfunktion beim Mega nicht verfügbar. Außerdem kann die Steuerung der Geschwindigkeit konfiguriert werden, mit der Großbritannien die Spannung am Bein erhöht.

Also lasst uns schon blinken, ja. Zuerst brauchen wir zwei Header, in denen wir die Struktur dieser Register beschreiben. Daher erhalten wir das Referenzhandbuch und studieren es auf der Suche nach den Informationen, die wir benötigen. Ich arbeite an STM32F746NG - Discovery also den ganzen Code für dieses Board.

Das erste, was wir brauchen, sind die Adressen dieser Blöcke, grundlegend. Beginnen wir mit dem RCC-Block und erstellen den rcc.h-Header. Darin erstellen wir das Makro RCC_BASE, beschreiben die Struktur des Registerblocks und erstellen einen Zeiger auf die Struktur.

rcc.h.

#define RCC_BASE   0x40023800

typedef struct
{
  volatile unsigned int CR;           
  volatile unsigned int PLLCFGR;      
  volatile unsigned int CFGR;         
  volatile unsigned int CIR;          
  volatile unsigned int AHB1RSTR;     
  volatile unsigned int AHB2RSTR;     
  volatile unsigned int AHB3RSTR;     
  unsigned int          RESERVED0;    
  volatile unsigned int APB1RSTR;     
  volatile unsigned int APB2RSTR;     
  unsigned int          RESERVED1[2]; 
  volatile unsigned int AHB1ENR;      
  volatile unsigned int AHB2ENR;      
  volatile unsigned int AHB3ENR;      
  unsigned int          RESERVED2;    
  volatile unsigned int APB1ENR;      
  volatile unsigned int APB2ENR;      
  unsigned int          RESERVED3[2]; 
  volatile unsigned int AHB1LPENR;    
  volatile unsigned int AHB2LPENR;    
  volatile unsigned int AHB3LPENR;    
  unsigned int          RESERVED4;    
  volatile unsigned int APB1LPENR;    
  volatile unsigned int APB2LPENR;    
  unsigned int          RESERVED5[2]; 
  volatile unsigned int BDCR;         
  volatile unsigned int CSR;          
  unsigned int          RESERVED6[2]; 
  volatile unsigned int SSCGR;        
  volatile unsigned int PLLI2SCFGR;   
  volatile unsigned int PLLSAICFGR;   
  volatile unsigned int DCKCFGR1;     
  volatile unsigned int DCKCFGR2;     

} RCC_Struct;

#define RCC ((RCC_Struct *) RCC_BASE)


Lassen Sie uns dieselbe Operation mit dem GPIO-Registerblock ausführen, nur ohne Zeiger, und ein weiteres Makro hinzufügen, GPIO_OFFSET. Sprechen Sie unten darüber.

gpio.h

#define GPIO_BASE   0x40020000
#define GPIO_OFFSET 0x400

typedef struct
{
   volatile unsigned int MODER;   
   volatile unsigned int OTYPER;  
   volatile unsigned int OSPEEDR; 
   volatile unsigned int PUPDR;   
   volatile unsigned int IDR;     
   volatile unsigned int ODR;     
   volatile unsigned int BSRR;    
   volatile unsigned int LCKR;    
   volatile unsigned int AFR[2];  

} GPIO_Struct;


Mal sehen, was hier los ist und warum wir Strukturen brauchen. Tatsache ist, dass wir über einen Zeiger auf eine Struktur auf das Register zugreifen können. Wir müssen es nicht initialisieren, da es physisch bereits vorhanden ist. Z.B:

RCC->AHB1ENR = 0; 


Dies spart Speicherplatz und erfordert ihn manchmal überhaupt nicht. Aber heute geht es nicht ums Sparen.

Wir haben also zwei Header bereit, es bleibt zu lernen, wie man mit Hilfe dieser Register springt und erstellt int main();. Es ist alles einfach und nicht ganz. In STM32 müssen wir, um auf den Registerblock zuzugreifen, zuerst eine Uhr darauf anwenden, sonst erreichen die Daten ihn. Ich werde nicht tief in die Struktur des Reifens eintauchen, sondern nur sagen, wie es ist. Blöcke werden auf verschiedenen Reifen platziert. Unsere Einheit befindet sich im AHB1-Bus. Das heißt, wir müssen einen bestimmten Port auf dem AHB1-Bus aktivieren, in meinem Fall ist es Port I. Für den Anfang benötigen wir natürlich die Hauptfunktion.

Lassen Sie uns unsere Startdatei ein wenig aktualisieren und int main (); hinzufügen. und erstellen Sie dann die Datei main.c selbst

extern void *_estack;

void Reset_Handler();
void Default_Handler();

//   
int  main();

void NMI_Handler()                    __attribute__ ((weak, alias ("Default_Handler")));
void HardFault_Handler()              __attribute__ ((weak, alias ("Default_Handler")));

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack,
	&Reset_Handler,
	&NMI_Handler,
	&HardFault_Handler
};

extern void *_sidata, *_sdata, *_edata, *_sbss, *_ebss;



void __attribute__((naked, noreturn)) Reset_Handler()
{


	void **pSource, **pDest;
	for (pSource = &_sidata, pDest = &_sdata; pDest != &_edata; pSource++, pDest++)
		*pDest = *pSource;

	for (pDest = &_sbss; pDest != &_ebss; pDest++)
		*pDest = 0;

    //   
    main();

	while(1);
}

void __attribute__((naked, noreturn)) Default_Handler(){
    while(1);
}


Und jetzt erstellen wir die Datei main.c. selbst In der Datei habe ich versucht, alles in den Kommentaren zum Code zu malen, also haben wir ihn gelesen und vertieft. Wenn Sie Fragen haben, schreiben Sie in die Kommentare unten, die ich beantworten werde.

#include "rcc.h"
#include "gpio.h"


int main()
{
    //  GPIO I ,   8 ( )
    RCC->AHB1ENR |= (1<<8);

    //     
    //          
    //      
    volatile GPIO_Struct *GPIOI = (GPIO_Struct *)(GPIO_BASE + (GPIO_OFFSET*8));  

    //     ""
    GPIOI->MODER |= (1<<2);

    //   ,   , push-pull,   .
    //   push pull
    GPIOI->OTYPER &= ~(1<<1);

    //      ,       
    GPIOI->OSPEEDR |= (2<<2);

    //   
    while(1){
        for(volatile int i = 0; i < 1000000; i++){
            // 
        }

        // ,   1   0  .
        GPIOI->ODR ^= (1<<1);
    }

    return 0;
}


Jetzt müssten wir ein Projekt zusammenstellen und zur Überprüfung auf Mikrometer werfen. Ich habe ein Repository auf Github gepostet. Alles, was getan werden muss, ist das Dienstprogramm Make auszuführen.
make


Vielen Dank für Ihre Aufmerksamkeit. Im nächsten Artikel werden wir mehr über die RCC-Einheit und ihre Arbeitsweise sprechen.

All Articles