Take the blue pill.

Recentemente me envolvi em um projeto onde preciso utilizar um microcontrolador para efetuar leituras de alguns sensores e acabei optando por utilizar a linha STM32 da ST Electronics.

Para construir a prova de conceito utilizei uma placa conhecida por Blue Pill.

As vantagens são o preço, a facilidade de uso com o IDE Arduino e as especificações:

-Microcontrolador STM32F103C8T6 Cortex-M3 de 32 Bits rodando a 72Mhz;
-20Kb de RAM;
-64Kb de flash;
-4 Timers
-2 SPI’s
-2 I2C’s
-2 USART’s
-até 40 GPIO’s.


Se você está habituado ao IDE do Arduino a maneira mais fácil de começar com o Blue Pill é o projeto stm32duino. Depois de configurado, podemos simplesmente abrir o exemplo Blink e enviar para a memória do dispositivo.


O problema de verdade só começa a aparecer quando adicionamos um pouco mais de código e ultrapassamos a barreira dos 64Kb da memória flash. Este simples exemplo Blink gera um binário de 12Kb, o que para projetos maiores é um desperdício.

Vou utilizar este exemplo para descrever como cheguei a 3Kb com a mesma funcionalidade e durante o processo descrever um pouco este microcontrolador.

Como diria Jack, vamos por partes.

O que o exemplo faz é:
1 - ligar o led
2 - aguardar um segundo
3 - desligar o led
4 - aguardar um segundo
5 - repetir o processo

Colocando em termos do microcontrolador, podemos dizer:

1 - ativar o GPIO PC13
2 - "aguardar" por um segundo
3 - desativar o GPIO PC13
4 - "aguardar" por um segundo
5 - repetir o processo

Ligar o led tornou-se ativar GPIO PC13 porque é neste "pino" que o led presente na placa está ligado.
A palavra "aguardar"está entre aspas porque, embora pareça algo trivial, existem diversas formas de efetuar em um microcontrolador.

Como o IDE do Arduino é focado na simplicidade para os iniciantes de eletrônica digital, praticamente inexiste opção de otimização do código.
Lendo o livro Discovering the STM32 MicroController fui apresentado a lib CMSIS, fornecida pelo fabricante do processador. Esta biblioteca tem a intenção de criar uma API de controle comum entre os vários modelos de processadores para reduzir a curva de aprendizado.

GPIO

GPIO's são os pinos do microcontrolador que podemos controlar programaticamente.
podem ser utilizados para qualquer fim desde que se respeite os limites de tensão e corrente do mesmo.
No caso do STM32 a maioria dos pinos suporta nominalmente 3.3v mas alguns são tolerantes a 5v.

Para ativar um GPIO é necessário ativar o barramento ao qual o mesmo está ligado, configurar a velocidade de operação e o modo de operação.

Por modo de operação entendemos entrada, saída ou modo de função alternativa. A função alternativa é o que possibilita que o mesmo pino que é utilizado como GPIO possa ser usado para uma conexão USART ou SPI por exemplo. Nestes modos alternativos o controlador faz uso do pino atendendo a um destes protocolos sem que você precise implementar em código o mesmo.

TIMER

Controlar o tempo é uma necessidade constante quando falamos em microcontroladores. Seja para verificar o estado de um sinal de tempos em tempos, fazer um relógio ou piscar um led necessitamos de uma forma controlada de passar o tempo.

Um simples loop grande o bastante pode causar um delay de tempo e sabendo quantas instruções o mesmo toma e a velocidade do clock interno podemos calcular quanto tempo se passou. No entanto esta é uma forma ineficiente de delay já que o processador fica ocupado apenas para passar o tempo.

No STM32 temos diversos timers e eles são distribuídos entre os barramentos do microcontrolador.
No manual do mesmo conseguimos a informação da distribuição. No caso, utilizaremos o TIM2, logo será um timer que se encontra no barramento ABP2.


Existem diversos registradores para configurar um timer já que podemos utiliza-los das formas mais diversas. Nos vídeos a seguir tem uma explicação bastante detalhada:

 

 


No nosso caso, vamos apenas configurar o timer para que uma interrupção seja gerada a cada segundo.

Sabendo que o barramento ABP1 possui um clock de 72Mhz, precisamos encontrar um divisor (PSC) e valor de contagem (ARR) que nos gere este período.

O divisor é o número pelo qual os pulsos do barramento serão divididos, ou seja, a velocidade do contador. Se dividirmos o clock 72000000 por 720000 teremos 1000 ciclos por segundo ou 1khz.
Colocando o contador para o valor de 999, teremos 1000 ciclos de contagem para ocorrer um overflow e consequentemente nossa interrupção. Se temos 1000 ciclos por segundo e contamos até 1000 para o overflow teremos então 1 overflow por segundo, ou seja, um overflow segundo a segundo.

Não podemos simplesmente utilizar um divisor com o mesmo valor do clock porque o registrador deste divisor é de apenas 16 bits. Existem formas de dividir o clock antes da aplicação do PSC, mas para fins desta explicação estes números são o bastante.

Interrupções

No STM32 o tratamento de interrupções sempre passa pelo NVIC ou Nested Vectored Interupt Controller. O NVIC é basicamente o controlador de interrupções e entre suas funções estão a ativação e escalonamento por prioridade das interrupções.


Utilizando o CMSIS podemos ativar nossa interrupção para o TIM2 com apenas uma linha de código e efetuar o tratamento simplesmente criando uma função com a assinatura:


void TIM2_IRQHandler(void);


Toda vez que uma interrupção ocorrer em quaisquer uma das atividades configuradas no TIM2, essa função será chamada.

Código

Após programar a versão utilizando apenas o CMSIS e compilar com o GCC, o resultado foi algo
frustrantemente grande, 25Kb para ser mais exato com otimização no nível 3.


Na tentativa de reduzir mais, resolvi extrair as estruturas necessárias e converter as chamadas do CMSIS para simples manipulações de registradores. O resultado finalmente chegou a meros 3Kb.


Este resultado não é nem de longe o menor possível, mas considerando o tamanho do exemplo compilado no IDE Arduino ficamos com apenas 25% do tamanho original.


Repositório com o código: https://github.com/renatoaquino/stm32blink


Comentários

Postagens mais visitadas deste blog

CFTV com Zoneminder e Linux

O problema C10K