STM32 Parte 3: Primeiro Projeto

Observamos uma sociedade cada vez mais dependente de máquinas, mas ao mesmo tempo as usa de maneira mais ineficiente. - Douglas Rushkoff


Esta frase deve servir de motivação para todo programador. Afinal, é você quem decide como a máquina usa seus recursos. Mas, desde o início dos tempos, uma pessoa confia seu direito de decidir com terceiros em troca da maneira mais fácil. Antes de me perguntar sobre os benefícios dos meus artigos quando há um "Cubo", pergunte-se por que o "cubo" decide por mim.

STM32 Parte 1: Básico do
STM32 Parte 2: Inicialização

Então, vamos continuar nossa aventura. Já escrevemos o script de inicialização, descobrimos o vinculador e o compilador. É hora de piscar o LED. Neste artigo, abordaremos os conceitos básicos do bloco RCC e GPIO e adicionaremos alguns cabeçalhos, que usaremos em projetos futuros. Vai.

RCC - Reset e Clock Control, um bloco de registros que controlam o relógio do processador e periféricos. Várias fontes de relógio são controladas por esta unidade: HSI (interno de alta velocidade), HSE (externo de alta velocidade), PLL / PLLSAI / PLLI2S (bloqueio de loop em fases). Todas essas três fontes de clock podem ser usadas pelo processador, é claro, se os registros do bloco RCC estiverem configurados corretamente. Falaremos sobre isso em detalhes no próximo artigo.

GPIO - Entrada e Saída de Uso Geral. O bloco de registros criado para LEDs intermitentes, ou seja, é um bloco para controlar as pernas de nossos mícrons, um portal para o mundo real. Diferente do familiar Atmega328p (Arduino), o stm32 oferece uma personalização mais ampla das pernas. Por exemplo, a função de pé de drenagem aberta não está disponível no mega. Além disso, o controle da velocidade com que o Reino Unido aumenta a tensão na perna pode ser configurado.

Então, vamos piscar, sim. Primeiro, precisamos de dois cabeçalhos nos quais descrevemos a estrutura desses registros. Portanto, obtemos o manual de referência e o estudamos em busca das informações de que precisamos. Eu trabalho no STM32F746NG - Discovery, para todo o código desta placa.

A primeira coisa que precisamos é dos endereços desses blocos, básicos. Vamos começar com o bloco RCC e criar o cabeçalho rcc.h. Nele, criaremos a macro RCC_BASE, descreveremos também a estrutura do bloco de registro e criaremos um ponteiro para a estrutura.

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)


Vamos fazer a mesma operação com o bloco de registro GPIO, apenas sem um ponteiro e adicionar outra macro, GPIO_OFFSET. fale sobre isso abaixo.

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;


Vamos ver qual é o problema aqui e por que precisamos de estruturas. O fato é que podemos acessar o registro através de um ponteiro para uma estrutura, não precisamos inicializá-lo, pois ele já existe fisicamente. Por exemplo:

RCC->AHB1ENR = 0; 


Isso economiza espaço na memória e, às vezes, não é necessário. Mas não sobre economizar hoje.

Portanto, temos dois cabeçalhos prontos, resta aprender a pular com a ajuda desses registros e criar int main();. É tudo simples e não é bem assim. No STM32, para acessar o bloco de registro, devemos primeiro aplicar um relógio a ele, caso contrário os dados o alcançarão. Não vou aprofundar a estrutura do pneu, mas apenas dizer como é. Os blocos são colocados em pneus diferentes. Nossa unidade está localizada no ônibus AHB1. Ou seja, precisamos habilitar uma porta específica no barramento AHB1, no meu caso, é a porta I. Para iniciantes, é claro que precisamos da função principal.

Vamos atualizar um pouco nosso arquivo de inicialização e adicionar int main (); e, em seguida, crie o próprio arquivo main.c

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);
}


E agora criamos o próprio arquivo main.c. No arquivo, tentei pintar tudo nos comentários do código, para que lêssemos e nos aprofundássemos nele. Se você tiver dúvidas, escreva nos comentários abaixo que eu responderei.

#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;
}


Agora teríamos que coletar o projeto e lançá-lo em mícrons, para fins de verificação. Postei um repositório no Github, tudo o que precisa ser feito é executar o utilitário Make.
make


Obrigado a todos pela atenção, no próximo artigo falaremos mais sobre a unidade RCC e como trabalhar com ela.

All Articles