ARM com núcleo Cortex Mx (usando STM32F10x como exemplo)

O microcontrolador ARM Cortex M3 STM32F103c8t6 é amplamente utilizado como microcontrolador de 32 bits para projetos amadores. Como para quase qualquer microcontrolador, existe um SDK para ele, incluindo arquivos de cabeçalho C ++ para determinar a periferia do controlador.
E ali, a porta serial, por exemplo, é definida como uma estrutura de dados, e uma instância dessa estrutura está localizada na área de endereço reservada para registros e temos acesso a essa área através de um ponteiro para um endereço específico.
Para aqueles que não se depararam com isso antes, descreverei um pouco como é definido, os mesmos leitores que estão familiarizados com isso podem pular essa descrição.
Essa estrutura e sua instância são descritas da seguinte maneira:
typedef struct { __IO uint32_t CR1; . . . __IO uint32_t ISR; } USART_TypeDef;
Você pode ver mais detalhes aqui
stm32f103xb.h ≈ 800 kBE se você usar apenas as definições deste arquivo, precisará escrever desta forma (exemplo do uso do registro de status da porta serial):
E você deve usá-lo porque as soluções proprietárias existentes conhecidas como CMSIS e HAL são muito complexas para serem usadas em projetos amadores.
Mas se você escreve em C ++, pode escrever assim:
Uma referência mutável é inicializada com um ponteiro. Isso é um pouco de alívio, mas agradável. Melhor ainda, é claro, escrever uma pequena classe de wrapper sobre isso, enquanto essa técnica ainda é útil.
Obviamente, eu gostaria de escrever imediatamente essa classe de invólucro pela porta serial (EUSART - transmissor-ressetor universal serial estendido universal), tão atraente, com recursos avançados, um transceptor serial assíncrono e capaz de conectar nosso pequeno microcontrolador a um sistema desktop ou laptop, mas microcontroladores O Cortex é diferenciado por um sistema de clock avançado e você terá que começar com ele e, em seguida, configurar os pinos de E / S correspondentes para trabalhar com periféricos, porque na série STM32F1xx, como em patas outros microcontroladores ARM Cortex não pode simplesmente configurar os pinos da porta para entrada ou saída e trabalhar ao mesmo tempo com a periferia.
Bem, vamos começar ativando o tempo. O sistema de relógio é chamado de registros RCC para controle de relógio e também representa uma estrutura de dados, um ponteiro ao qual é atribuído um valor de endereço específico.
typedef struct { . . . } RCC_TypeDef;
Campos dessa estrutura declarados assim, em que __IO define volátil:
__IO uint32_t CR;
correspondem aos registros do RCC, e os bits individuais desses registros são ativados ou as funções de relógio da periferia do microcontrolador. Tudo isso está bem descrito na
documentação (pdf) .
Um ponteiro para uma estrutura é definido como
#define RCC ((RCC_TypeDef *)RCC_BASE)
Trabalhar com bits de registro sem usar o SDK geralmente se parece com isso:
Aqui está a inclusão da porta A.
Você pode ativar dois ou mais bits de uma só vez
Parece um pouco incomum para C ++, ou algo incomum. Seria melhor escrever de forma diferente, assim, por exemplo, usando OOP.
Parece melhor, mas no século XXI iremos um pouco mais longe, usar C ++ 17 e escrever usando modelos com um número variável de parâmetros ainda mais bonito:
Onde Rcc é definido assim:
A partir disso, começaremos a criar um invólucro ao longo do tempo. Primeiro, definimos uma classe e um ponteiro (link) para ela.
No começo, eu queria escrever no padrão C ++ 11/14 usando a descompactação recursiva dos parâmetros de um modelo de função. Um bom artigo sobre isso é fornecido no final do artigo, na seção de links.
Considere a chamada para a função de habilitação do relógio da porta:
Rcc.PortOn<GPort::A>();
O GCC o implantará em um conjunto de comandos:
ldr r3, [pc, #376] ; (0x8000608 <main()+392>) ldr r0, [r3, #24] orr.w r0, r0, #4 str r0, [r3, #24]
Isso deu certo? Verifique o próximo
Rcc.PortOn<GPort::A, GPort::B, GPort::C>();
Infelizmente, o GCC não tão ingênuo implantou a chamada de recursão à direita separadamente:
ldr r3, [pc, #380] ; (0x8000614 <main()+404>) ldr r0, [r3, #24] orr.w r0, r0, #4 ; APB2ENR |= GPort::A str r0, [r3, #24] ldr r0, [r3, #24] orr.w r0, r0, #28 ; APB2ENR |= Gport::B | GPort::C str r0, [r3, #24] #24]
Em defesa do GCC, devo dizer que esse nem sempre é o caso, mas apenas em casos mais complexos, que serão vistos ao implementar a classe de porta de E / S. Bem, o C ++ 17 tem pressa em ajudar: Reescreva a classe TRCC usando os recursos de rolagem internos.
Agora acabou:
ldr r2, [pc, #372] ; (0x800060c <main()+396>) ldr r0, [r2, #24] orr.w r0, r0, #28 ; APB2ENR |= Gport::A | Gport::B | GPort::C str r0, [r3, #24]
E o código da classe se tornou mais simples.
Conclusão: O C ++ 17 nos permite usar os modelos com um número variável de parâmetros para obter o mesmo conjunto mínimo de instruções (mesmo quando a otimização está desativada) que é obtida ao usar o trabalho clássico com o microcontrolador por meio de definições de registro, mas ao mesmo tempo obtemos todas as vantagens de uma forte digitação em C ++. durante a compilação, reutilizado através da estrutura das classes base do código e assim por diante.
Aqui está algo como isto escrito em C ++
Rcc.PortOn<Port::A, Port::B, Port::C>();
E o texto clássico sobre registros:
RCC->APB2 |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN;
desdobrar em um conjunto ideal de instruções. Aqui está o código gerado pelo GCC (otimização desativada -Og):
ldr r2, [pc, #372] ; (0x800060c <main()+396>) [ RCC] ldr r0, [r2, #0] ; r0 = RCC->APB2 // [ APB2] orr.w r0, r0, #160 ; r0 |= 0x10100000 str r0, [r2, #0] ; RCC->APB2 = r0
Agora você deve continuar trabalhando e escrever a classe da porta de entrada e saída. Trabalhar com bits de porta de E / S é complicado pelo fato de quatro bits serem alocados para a configuração de um trecho de porta e, portanto, são necessários 64 bits de configuração em uma porta de 16 bits, divididos em dois registros CRL e CRH de 32 bits. Além disso, a largura da máscara de bits se torna maior que 1. Mas aqui, percorrer o C ++ 17 mostra seus recursos.

Em seguida, a classe TGPIO será escrita, bem como as classes para trabalhar com outros periféricos, uma porta serial, I2C, SPI, DAP, temporizadores e muito mais, que geralmente está presente nos microcontroladores ARM Cortex e, em seguida, será possível piscar com esses LEDs.
Mas mais sobre isso na próxima nota.
Fontes do projeto no github .
Artigos da Internet usados para escrever notas
Modelos com um número variável de argumentos em C ++ 11 .
Inovações nos modelos .
Inovação em linguagem C ++ 17. Parte 1. Convolução e derivação .
Lista de links para a documentação dos microcontroladores STM .
Macros de parâmetros variáveisArtigos sobre Khabr que me levaram a escrever esta nota
Semáforo em Attiny13 .
Julian Assange preso pela polícia britânicaO espaço como uma vaga memóriaEscrito em 12/04/2019 - Feliz Dia da Cosmonáutica!
PS
Imagem STM32F103c8t6 do CubeMX.
Como ponto de partida, o texto criado pela extensão Eclips para trabalhar com os
microcontroladores GNU MCU Eclipse ARM Embedded e
STM CubeMX é usado , ou seja, existem arquivos das funções padrão C ++, _start () e _init (), definições de vetores de interrupção são retiradas do Eclipse MCU O ARM Embedded e o registro principal e os arquivos de trabalho do Cortex M3 são de um projeto feito pelo CubeMX.
PPSNo KDPV, a depuração com o controlador STM32F103c8t6 é representada. Nem todo mundo tem esse conselho, mas não é difícil comprá-lo; no entanto, isso está além do escopo deste artigo.