STM32CubeIDE: Primeiros passos e CMSIS Core com GPIO

É notório todo investimento que a STMicroelectronics vem fazendo nos últimos anos para poder abraçar o mercado de microcontroladores low, medium e high-end performance com arquitetura 32 bits, porém, toda arquitetura de desenvolvimento de um sistema embarcado é passada desde de sua arquitetura de hardware à camada de software e aplicação, e neste contexto, um bom pacote SDK de desenvolvimento e uma boa IDE com ferramentas de debugging e recursos de camada HAL (Hardware Abstraction Layer) se tornam necessários para acelerarem o desenvolvimento da solução e o chamado “time-to-market”, que é o tempo de desenvolvimento de um dispositivo até chegar ao mercado. E foi pensando nesses aspectos junto ao feedback de desenvolvedores que usam suas tecnologias que a STM desenvolveu este fantástico pacote de ferramentas que hoje chamamos de STM32CubeIDE, que é basicamente um “all-in-one” com IDE, debugging, compilador e pacote de configuração de MCU gráfica, que serão detalhadas ao discorrer do presente artigo.

 

Então, partindo de uma série cronológica, em 2017 a STM adquiriu uma IDE com âmbito profissional baseada em Eclipse, chamada de Atollic TrueStudio e disponibilizou com uso gratuito junto ao compilador GCC for ARM para uso gratuito com sua gama de microcontroladores de 32 bits. Junto à liberação da IDE Atollic, o configurador gráfico do microcontrolador, o pacote de bibliotecas e ferramentas HAL foi liberado no CubeMX, com possibilidade de integração ao Atollic TrueStudio e passou então a ser seu pacote de desenvolvimento oficial. E por fim no ano de 2019, especificamente no mês de maio, chegou a liberação da STM32CubeIDE, que por definição da própria STM é uma ferramenta de desenvolvimento integrado para microcontroladores STM32. Sua interface é baseada em padrões abertos como Eclipse e GNU, possuindo ferramentas profissionais para análise avançada de software, como a análise do uso de memória e a visualização do comportamento dinâmico do sistema junto a ferramentas de depuração e gerenciamento de código, assim, definindo oficialmente a junção do Atollic TrueStudio junto ao CubeMX, gerando a STM32CubeIDE. Algumas de suas características são:

  • Completamente gratuita.
  • Baseada em Eclipse, CDT, GCC e GDB.
  • Analisadores avançados de memória.

 

Começando com STM32CubeIDE

 

Agora que já entendemos como surgiu o ecossistema da CubeIDE, vamos fazer o download da mesma e compilar nosso primeiro projeto para o microcontrolador STM32F103C8T6 que vem embarcado nas placas Bluepill, fáceis de encontrar em nosso atual mercado.

 

 

Após instalar CubeIDE, crie um repositório em um local de sua preferência e direcione para selecionar seu workspace padrão assim como exemplificado na figura 1, clique em launch para poder finalizar a escolha do repositório.

 

Figura 1. Seleção de diretório para workspace - Fonte: Autor, 2019.

 

Após definir o seu repositório para ambiente de trabalho, iremos configurar todas as ferramentas para poder trabalhar com o microcontrolador STM32F103C8T6, detalhando passo a passo para obter nosso primeiro projeto com sucesso, então, nosso próximo passo é iniciar um novo projeto STM32 como demonstrado na figura 2:

 

Figura 2. Criando um novo projeto - Fonte: Autor, 2019.

 

Neste momento irá abrir uma tela com todas as famílias de microcontroladores da família STM32 atualmente suportadas pela CubeIDE. Para poder achar nosso microcontrolador, iremos no campo de busca “part number search” e digitaremos “STM32F103C8”, que é o microcontrolador em questão para início de nosso primeiro projeto como já citado anteriormente. Ele irá aparecer no campo “MCU List”, clicaremos nele como demonstrado na Figura 3.

 

Figura 3. Seleção do MCU STM32F103C8. - Fonte: Autor, 2019.

 

É interessante notar que, na figura 3, ao selecionar o microcontrolador, conseguimos ver suas features detalhadas, seu diagrama do bloco de funcionamento e recursos de periféricos, conseguimos verificar seu datasheet, application notes (inclusive, de extrema relevância pois possui diversos exemplos de uso dos periféricos), seus manuais de referências, que iremos utilizar na maioria de nossos exemplos no dia a dia e por fim, seus canais de compras oficiais oferecidos pela STM. Percebemos aqui, o quão completa a ferramenta se tornou no auxílio do desenvolvimento. Ao clicar no microcontrolador, clique no botão next para irmos a próxima tela. 

 

Neste momento iremos ver a tela onde será possível dar o nome do projeto, definimos com o nome “ProjetoSTM32F103”, não sendo necessário modificar mais nada, apenas clicar em next e finish.

 

Então teremos nossa tela de carregamento do CubeMX como demonstrado na figura 4. 

 

Figura 4. Tela inicial para configuração gráfica do CubeMX - Fonte: Autor, 2019.

 

Nota-se que nenhuma configuração foi feita até o presente momento, e sabendo que iremos utilizar a placa Bluepill, precisamos configurar o uso do cristal externo de 8 MHz que possui conectada fisicamente, então nosso primeiro passo é configurar o uso do cristal externo da nossa placa de desenvolvimento. Existe uma opção no lado esquerdo como visto na Figura 4, chamada “System Core”, ao clicar nela, será aberto uma lista de periféricos para configuração, iremos configurar o periférico “RCC”, para isso é preciso clicar 2x no mesmo, como demonstrado na figura 5.

 

Figura 5. Tela de configuração do periférico RCC - Fonte: Autor, 2019.

 

Nosso próximo passo é ir na opção “High Speed Clock (HSE)” e marcar a opção, “Crystal/Ceramic Resonator” como demonstrado na figura 6.

 

Figura 6. Habilitação do HSE no RCC. - Fonte: Autor, 2019.

 

Após isso, vamos na opção “Clock configuration” para definir a frequência do cristal para 8 MHz e habilitar a multiplicação interna do RCC para HSE como demonstrado na figura 7. Após modificar estes dois parâmetros, vamos até a opção “HCLK(MHz)” e vamos digitar 72 MHz e teclar enter, depois “OK”, neste momento, o próprio CubeMX irá fazer todos os cálculos de multiplicação de PLL e configuração para que os periféricos e clock principal, possam ser rodados na frequência de 72 MHz (exceto o APB1 peripheral clock que é limitado a 36 MHz). Todos esses passos são demonstrados na figura 7.

 

Figura 7. Configuração de PLL, HSE e Frequência de opção em 72 Mhz. Fonte: Autor, 2019.

 

Nosso próximo passo é voltar para opção “Pinout & Configuration” no menu principal da barra superior, clicar novamente em “System Core” e ir até a opção “SYS”. Nesta etapa, iremos configurar qual depurador iremos utilizar, que por convenção de uso do ST-Link/V2, será escolhida a opção “Serial Wire” de acordo com a figura 8.

 

Figura 8. Configuração da Serial Wire para debug. Fonte: Autor, 2019.

 

Nota-se que os pinos PA13 e PA14 foram configurados neste momento para receber o SWCLK e SWDIO do ST-Link/V2. Outro parâmetros que temos de opção nesta etapa de configuração é o “Timebase Source”, vamos deixar-la do jeito que está, na opção “SysTick”, essa opção utiliza o timer interno de 24b para dar parâmetros de tempo para funções de delay e etc, e futuramente será utilizado em tutoriais que serão publicados nessa série em conjunto com FreeRTOS e alguns dos periféricos da linha STM32F1xx.

 

Agora chegou a grande hora de gerar a configuração final do projeto, neste momento vamos até a opção “Save ALL” que é basicamente o ícone de um disquete aninhado, ao clicar lá, será perguntado se queremos gerar o código inicial a partir do CubeMX, iremos escolher a opção “Yes” como demonstrado na figura 9.

 

Figura 9. Passo para salvar e gerar estrutura de códigos. Fonte: Autor, 2019.

 

Quando o projeto é gerado, ele trará uma estrutura demonstrada no figura 10. Onde contém o arquivo STM32F1xx.h que traz todas as estruturas de ponteiros para os registradores de 32b contidos no Reference Manual da ST chamado “RM0008”, cujo o link: https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf. Nesta mesma estrutura é contido os arquivos gerados pelo CubeMX para camada HAL de programação.

 

Figura 10. Estrutura de Arquivos gerados pelo CubeMX. Fonte: Autor, 2019.

 

Ao expandir a pasta “inc” da estrutura de arquivos HAL, será demonstrado todas as bibliotecas geradas pelo CubeMX que será necessário neste projeto. Caso você queira aprender mais sobre como são implementadas as funções de API HAL geradas pela STM e toda a arquitetura de código, podem olhar todos esses arquivos listados, tanto .h como .c, lá está toda implementação feita em cima da camada HAL.

 

Montando nosso circuito

 

Nosso primeiro passo é montar o circuito demonstrado na figura 11 em protoboard. O objetivo do circuito é acionar o led no momento em que o push button for pressionado, levando o sinal de entrada do pino PB1 para nível baixo, essa alteração de nível lógico é garantido graças ao circuito de pull-up posto no botão, é interessante mesmo que para testes, que o circuito de pull-up seja posto conforme o esquemático demonstrado na figura 11, pois isso irá garantir que o pino de entrada PB1 não fique “flutuando” sinal de entrada entre nível alto e baixo. 

Figura 11. Circuito esquemático para teste do código proposto. Fonte: Autor, 2019.

 

Fazendo nosso primeiro código

 

Neste momento chegamos na hora mais esperada, a hora de pôr o código em prática, que estará anexado abaixo com o código que será utilizado, bastando copiar e substituir pelo código existente no arquivo “main.c”.

 

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void delay(void)
{
	int i = 100000;
	while (i-- > 0)
	asm("nop");
}


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	int botao;

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
	//habilita o clock de periférico APB2 para as portas A e B
	RCC->APB2ENR |= 0x0C;

	//configura a porta PB0 como saída do tipo push-pull e frequência máxima de saída de 10MHz
	//configura a porta PB1 como entrada do tipo pull-up/pull-down
	GPIOB->CRL = 0x81;
	//configura a porta PB1 como pull-up
	GPIOB->ODR = 0x2;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		delay();

		//set variavel botao se PB1 == 0 (pull-down)
		botao = ((GPIOB->IDR & 0x2) == 0);

		if (botao == 1)
		{
			//aciona saída PB0 se botão for pressionado
			GPIOB->BSRR = 1;
		}
		else
		{
			//desliga saída PB0 se botão não for pressionado
			GPIOB->BSRR = 1<<16;
		}
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

O código está auto explicativo e contém vários comentários, porém, vamos falar sobre as partes mais importantes para entendimento do mesmo, até porque, na utilização da GPIO e temporização, não utilizamos nenhum dos recursos da API HAL, usamos o recursos nativo do CMSIS Core Library para demonstrar o quão possível é partir da camada de baixo nível até o API HAL que será abordado nos próximos artigos.

 

Bom, o primeiro passo a ser comentado é a função delay(), que é basicamente uma chamada do mnemônico em assembly “nop” que faz literalmente NADA a não ser gastar ciclos de máquina da CPU durante 100 mil ciclos como definido na variável i.

 

A configuração dos periféricos é normalmente inicializada a partir da chamada da estrutura do periférico RCC, onde “RCC->APB2ENR |= 0x0C”, habilita o clock de periférico APB2 para port A e B. Configuramos também a porta PB0 como saída push-pull e a porta PB1 como entrada pull-up/down a partir da chamada da estrutura GPIOB, onde “GPIOB->CRL = 0x81”. Configuramos também a porta PB1 como pull-up chamando estrutura com parâmetro “GPIOB->ODR = 0x2”. 

 

Nota-se que toda a configuração das GPIO foram feitas apenas em 3 linhas acessando diretamente a camada de baixo nível CMSIS, porém, esse tipo de configuração é trabalhosa pois para configurarmos os registradores na mão, precisamos ir no datasheet do microcontrolador STM32F103C8T6, no reference manual 0008 e arquivo “stm32f1xxx.h” la na estrutura de arquivos gerados pelo CubeMX para que com esses três recursos na mão, pudéssemos verificar tabela por tabela de registradores e fazer os cálculos de binário para hexadecimal para poder verificar cada etapa do periférico. 

 

Nosso objetivo é realizar a leitura do registrador IDR do PORT B com uma máscara binária, seguindo a chamada “botao = ((GPIOB->IDR & 0x2) == 0)” configurando em nível alto (1) a variável botão caso PB1 for igual a zero (pull-down). No momento que chegamos a condicional do botão, estamos configurando o registrador BSSR (Port bit set/reset register) de acordo com a lógica de acionamento exposta a variável “botao”, vale ressaltar que o registrador “BSSR” é de 32b e seus 16 primeiros bits fazem o SET (Nível alto) dos 16 pinos do PORTB, e seus últimos 16 bits fazem o efeito contrário, RESET (Nível baixo) nos pinos do PORTB.

 

 

Configurando processo de compilação e download do firmware via debugging mode

 

Depois de efetuar o código, faremos a compilação do código e gravaremos na placa Bluepill. Antes de fazer a compilação, precisamos configurar alguns parâmetros da CubeIDE, então vamos até a opção “Project->Properties” como demonstrada na figura 12. Esta opção se encontra na TOOLBAR superior da IDE. 

 

Figura 12. Abrindo a configuração de projeto. Fonte: Autor, 2019.

 

Será aberta a janela de configuração, siga os passos da figura 13 a seguir para chegar na opção “Tool Settings” fazendo os passos: “C/C++ Build → Settings → Tool Settings → MCU Post build outputs”. Neste momento, marcaremos o checkbox “Convert to Intel Hex File (-O ihex)” e clicaremos na opção “Apply” e “Apply and Close”.

 

Figura 13. Configuração de arquivos de compilação da CubeIDE. Fonte: Autor, 2019.

 

Agora basta compilar o projeto na opção “Project → Build” como demonstrado na figura 14.

Figura 14. Compilando o projeto. Fonte: Autor, 2019.

Agora o esperado se seguimos todos os passos recomendados até o momento do artigo, é receber a confirmação de compilado com sucesso no console da CubeIDE conforme a figura 15.

 

Figura 15. Compilação completa. Fonte: Autor, 2019.

 

Para poder rodar, fazer upload do código na Bluepill e depurar o projeto, precisamos fazer a conexão entre a Bluepill e o ST-Link/V2, seguindo o diagrama de conexão recomendado na figura 16.

 

Figura 16. Diagrama de conexão Bluepill <-> St link v2. Fonte: Autor, 2019.

 

Iremos agora configurar a ferramenta de debug e download do firmware, então seguimos os passos “Run → Debug Configurations → 2x click em STM32 MCU Debugging”. Próximo passo é configurar o campo “C/C++ Application”, referenciando o arquivo “xxxx.elf” salvo no diretório do seu projeto, como demonstrado na figura 17.

 

Figura 17. Configurando debugger para compilação e download do projeto. Fonte: Autor, 2019.

 

Após fazer a alteração, clique em “Apply”, certifique-se de que a placa Bluepill foi ligada corretamente ao depurador ST-Link/V2 conforme a figura 16, e posteriormente clique em Debug, teremos um retorno no console informando que o código foi compilado e feito o download, conforme a figura 18. Caso apareça uma opção do CubeIDE pedindo para fazer o “Switch” para o debugger mode, confirme.

 

Figura 18. Confirmação de compilação e download do código. Fonte: Autor, 2019.

 

Não será abordado nesse momento a ferramenta de depuração e recursos nativos da CubeIDE, faremos o mesmo posteriormente em outro artigo. Nossos próximos objetivos serão fazer o mesmo código saindo do CMSIS Core,partindo para API HAL, assim como a integração do Middleware do FreeRTOS e recursos mais avançados com uso de demais periféricos.

 

 

Referências

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

José Maurício Alencar FIlho
Técnico em Eletrônica pelo Instituto Federal de Alagoas - IFAL. Cursando 7º período de Engenharia Mecatrônica no Centro Universitário Tiradentes - UNIT/AL, possuo experiência em desenvolvimento de sistemas embarcados com ênfase em ecossistemas IoT e sistema de identificação com Tag RFID. No momento com interesses em P&D para as seguintes áreas: Sistemas Operacionais de Tempo Real (RTOS), Linux Embarcado, Processamento Digital de Sinais, Sistemas embarcados para indústria 4.0, Dispositivos e Sistemas Eletrônicos.

8
Deixe um comentário

avatar
 
4 Comment threads
4 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
5 Comment authors
José Maurício Alencar FIlhoAndré Lucas SilvaManoel LemosmarcusJonathan Gonzaga Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
André Lucas Silva
Membro
André Lucas Silva

Muito bom artigo, parabéns. Já instalei essa IDE e o que mais achei positivo foi a fácil instalação no Ubuntu, sem a necesidade de drivers externos. Porém, tive problemas ao tentar utilizar o FreeRTOS nativamente (baixando a versão do site do Free), quando fui inserir os arquivos, encontrei problemas para incluir os Paths dos diretórios. É um problema que ainda estou tentando resolver, mas nada grave, apenas familiarização com o ambiente.

De resto, é uma boa solução oferecida pela ST.

Manoel Lemos
Visitante
Manoel Lemos

Excelente artigo!
Realmente as soluções da ST vieram para extinguir os cores de 8 bit e dominar o mercado de 32 bit com ferramentas extremamente ágeis e excelente suporte.
Parabéns Mauricio!!!!

marcus
Visitante
marcus

SHOW DE BOLA, GOSTEI DO CONTEUDO. BEM CONSTRUIDO. PARABÉNS

Jonathan Gonzaga
Visitante
Jonathan Gonzaga

Não sei em qual ambiente vc utilizou essa IDE, mas eu tive alguns problemas com ela no Ubuntu 16.04. O debugger simplesmente parava de funcionar, no meio da depuração ela dava crash e terminava a execução. Testei várias placas diferente, uma blue pill e duas discovery. E com o mesmo código nas outras IDEs baseadas em Eclipse (SW4STM32 - AC6 e Atollic) esse erro não ocorria. Como sugestão de post, poderia falar sobre o wrapper que a ST sugere do FreeRTOS para o CMSIS RTOS, quando se usa o Cube em geral, por exemplo, funções da api equivalentes e um… Leia mais »