Objetivo
Neste artigo ensinarei como configurar o Intellisense do VSCode, para desenvolvimento embarcado em microcontroladores da STMicroelectronics. A configuração utilizará um projeto base do STM32CubeIDE. Apesar de parecer algo simples, é algo que pode tomar algumas horas do seu dia para deixar 100% redondo, até porque, esta configuração possui um “pulo do gato” que é geralmente ignorada e irei mostrar aqui.
Como o Intellisense configurado, você poderá usufruir no VSCode de recursos como:
- Linting (verificação de código, como detecção de erros de sintaxe sem ter que compilar o código)
- Auto-completar inteligente
- Pular para definições de símbolos (funções, variáveis, macros…)
Este artigo se restringe ao Intellisense, em artigos futuros irei mostrar como compilar e carregar o projeto diretamente do VSCode.
Apesar de eu utilizar um projeto da placa STM32VLDISCOVERY no artigo, vários conceitos aqui mostrados podem ser aplicados para a configuração de outros projetos C/C++ (embarcados ou não) no VSCode.
Motivação
Eu estou com uma placa de desenvolvimento STM32-VL-DISCOVERY (essa aqui) que comprei em 2012 e nunca utilizei. Agora estou considerando utilizar algum microcontrolador da linha STM32 em um projeto novo e comecei a brincar com a placa para me familiarizar e avaliar as ferramentas de desenvolvimento da ST.
Na época que comprei a placa, a IDE STM32CubeIDE não existia, e atualmente ela é a IDE recomendada para novos projetos. Uma das vantagens da STM32CubeIDE é a ferramenta “Device Configuration Tool” que permite a configuração inicial de periféricos do microcontrolador via interface gráfica, uma das partes mais chatas de se fazer na mão no desenvolvimento embarcado. Essa ferramenta também é disponibilizada stand-alone, mas como já vem integrada no IDE não cheguei a testar para ver se tem alguma diferença.
Apesar de ser uma IDE interessante, uma vez feita a configuração inicial, não somos obrigados a usar a STM32CubeIDE. Nos últimos anos, tenho usado o VSCode nos meus projetos e já tenho meus atalhos, extensões e temas configurados da maneira que acho mais produtiva. Sendo assim, tenho duas alternativas, personalizar o STM32CubeIDE ou configurar o projeto no VSCode. Acabei optando pela segunda opção.
Softwares Utilizados
Foram utilizadas as seguintes versões de software/hardware:
- Windows 10
- STM32CubeIDE 1.4.0
- VisualStudio Code 1.48.2
- Extensão C/C++ Microsoft 0.29.0
- Placa STM32VLDISCOVERY
Passo 0: Escolher um projeto do STM32CubeIDE
Você pode criar um projeto novo ou usar um projeto existente, o importante é que o projeto esteja funcionando e buildando (compilando) normalmente.
Neste artigo criei um projeto de teste do zero e incluí o código básico para ligar os LEDs da placa STM32VLDISCOVERY alternadamente.
Passo 1: Criar um projeto novo no VSCode
Vamos começar criando um novo projeto no VSCode e abrindo a pasta do projeto base criado no STM32CubeIDE. No meu caso a pasta é:
C:\Users\Andriy\STM32CubeIDE\workspace_1.4.0\vldiscovery
Ao criar um projeto novo, o Intellisense ainda não está configurado e apresenta vários erros. Como pode ser visto no arquivo Core/Src/main.c
Nos próximos passos vamos configurar o VSCode para nos livrarmos de todos os erros de Intellisense.
Configurar as include folders
O principal motivo dos erros do plugin C/C++ é que o VSCode não consegue achar as pastas onde se encontra o código fonte. Ao utilizar um diretiva #include
, como #include “main.h”
, o plugin tenta encontrar o arquivo main.h
na pasta do arquivo e nos diretórios configurados (include folders). Como não há nenhum diretório configurado, o plugin não tem sucesso em achar o arquivo.
Mas quais diretórios devem ser configurados? Neste artigo vamos copiar os diretórios configurados no projeto do STM32CubeIDE. Verificando os Includes do projeto, encontramos:
As três primeiras pastas, são pastas de bibliotecas utilizadas globalmente. As pastas seguintes, estão todas dentro da pasta do projeto.
No meu caso, tenho apenas um código de teste que pisca os LEDs da placa de desenvolvimento. Provavelmente suas pastas serão diferentes, você pode estar usando outros periféricos, outra versão do STM32CubeIDE ou ter instalado em outra pasta. Sendo assim, você deve adaptar a configuração a seguir e usar as pastas do seu projeto.
Para adicionar estas pastas no VSCode, vamos editar o arquivo de configuração .vscode/c_cpp_properties.json
.
Se o arquivo não existir, é só acessar a opção “C/C++: Edit Configurations (JSON)”, que ele será criado automaticamente.
No meu projeto, o arquivo possui inicialmente este conteúdo:
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}
Code language: JSON / JSON with Comments (json)
Adicionado as pastas das bibliotecas fica:
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/arm-none-eabi/include",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/lib/gcc/arm-none-eabi/7.3.1/include",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/lib/gcc/arm-none-eabi/7.3.1/include-fixed",
"${workspaceFolder}/Core/Inc",
"${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include",
"${workspaceFolder}/Drivers/CMSIS/Include",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}
Code language: JSON / JSON with Comments (json)
Com esta configuração feita, se voltarmos ao arquivo main.c
, veremos que o erro na diretiva #include “main.h”
desapareceu, entretanto, vários outros erros surgiram!
Na verdade, todos esses erros já estavam aí anteriormente, só que não estávamos vendo. O plugin parava no erro da diretiva #include “main.h”
e não continuava processando o restante dos erros.
Uma coisa “estranha” destes erros é que ao utilizar o “Go to Definition” nestes símbolos, o VSCode encontra a definição normalmente.
Por exemplo, o símbolo GPIOC, apresenta o erro identifier “GPIOC” is undefined
.
Mas ao passarmos o mouse com o botão CTRL pressionado, a definição é encontrada sem problemas.
Como isso é possível?
Isso ocorre pois o VSCode está rodando duas engines simultaneamente. Estas engines são o Intellisense e o “Tag Parser”. O Intellisense é extremamente preciso, mas mais complexo de configurar. Já o Tag Parser é mais burro e é utilizado como fallback em caso de erro do Intellisense. Sendo assim os erros vem do Intellisense, mas ao tentarmos ir para a definição dos símbolos, o Tag Parser é utilizado como alternativa e consegue encontrar a definição.
Idealmente, queremos ter o Intellisense funcionando corretamente, pois ele leva em conta todo o código para linting e sugestões do auto-complete. Se tivermos um trecho de código protegido por diretivas #ifdef
, o Intellinsense é capaz de avaliar os resultados dos valores nas diretivas e determinar se o pré-processador irá incluir o código para compilação. Ou seja, isso vai torná-lo extremamente preciso. Entretanto, o Intellisense precisa saber TODAS as flags de compilação, macros e arquivos incluídos para funcionar corretamente. E isso nem sempre é fácil ou possível de ser configurado, uma vez que estas flags pode estar definidas no meio de um processo de build mais “obscuro”.
Por outro lado, o Tag Parser (também conhecido como a engine fuzzy), não leva em conta todos esses detalhes e o pré-processamento dos arquivos. Seu único trabalho é varrer o código e indexar todos os símbolos presentes, o que é uma tarefa bem mais simples. Se houver uma diretiva #ifdef 0
cercando um trecho de código, este código será removido na etapa de pré-processamento. Mas isso é irrelevante para o Tag Parser, pois a diretiva #ifdef 0
será ignorada e o código será indexado mesmo assim.
Sendo assim, apesar da configuração correta do Intellisense ser a ideal, caso não seja possível, o Tag Parser irá dar conta do trabalho, apenas de uma maneira não tão precisa.
Configurar os #defines (se livrando da maioria dos tracinhos vermelhos)
Como explicado, o Intellisense precisa ser totalmente configurado para que todos os tracinhos vermelhos de erro desapareçam. Agora vamos incluir a configuração das flags de compilação.
Copiando as flags da configuração do STM32CubeIDE, o arquivo c_cpp_properties.json
fica:
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/arm-none-eabi/include",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/lib/gcc/arm-none-eabi/7.3.1/include",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/lib/gcc/arm-none-eabi/7.3.1/include-fixed",
"${workspaceFolder}/Core/Inc",
"${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include",
"${workspaceFolder}/Drivers/CMSIS/Include",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy"
],
"defines": [
"DEBUG",
"STM32F100xB",
"USE_HAL_DRIVER"
],
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}
Code language: JSON / JSON with Comments (json)
(As flag estão no campo defines).
Voltando ao arquivo main.c vemos que os erros sobre o símbolo GPIOC sumiram. Cada vez mais estamos chegando na configuração completa do Intellisense.
Configurar as flags internas do GCC (se livrando dos tracinhos vermelhos restantes)
Essa etapa é o pulo do gato, e me custou algumas horas para descobrir. Geralmente essa configuração é ignorada, mas é ela que vai permitir um Intellisense 100% funcional.
Podemos ver que algumas funções ainda apresentam erro, mas por que será? Já que configuramos todas as pastas e flags de compilação.
Teremos que cavar o motivo destes erros. E após analisar, percebi que o erro está especificamente sobre o tipo uint32_t
.
Em resumo, a definição deste tipo está no código do toolchain, protegida por um #ifdef
, cujo valor não aparece em mais nenhum lugar do código, nem mesmo no código da toolchain! Para que o tipo uint32_t
seja realmente definido existem mais algumas flags internas do GCC que devem ser configuradas!
Neste artigo, o autor utiliza o comando echo | arm-none-eabi-gcc -dM -E –
para listar todos os defines do GCC e configurar no VSCode. No caso dele foram 344 defines. Aqui no meu ambiente são 408! E para configurá-los não é apenas copiar e colar, existem macros no meio e definições com strings, tanto que o autor do artigos teve que processar o arquivo no excel para conseguir configurar tudo.
Por sorte, encontrei uma maneira mais fácil!
A maneira que encontrei é simplesmente configurar o caminho do compilador no VSCode, deste modo, o Intellisense consulta diretamente o compilador ao fazer sua indexação. No meu ambiente o caminho é:
C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/bin/arm-none-eabi-gcc.exe
Configurando no arquivo c_cpp_properties.json
fica:
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/arm-none-eabi/include",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/lib/gcc/arm-none-eabi/7.3.1/include",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/lib/gcc/arm-none-eabi/7.3.1/include-fixed",
"${workspaceFolder}/Core/Inc",
"${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include",
"${workspaceFolder}/Drivers/CMSIS/Include",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy"
],
"defines": [
"DEBUG",
"STM32F100xB",
"USE_HAL_DRIVER"
],
"compilerPath": "C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/bin/arm-none-eabi-gcc.exe",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "${default}"
}
],
"version": 4
}
Code language: JSON / JSON with Comments (json)
(Atentar que a chave intelliSenseMode foi alterada para “${default}"
)
Configuração completa do arquivo c_cpp_properties.json
Segue o arquivo c_cpp_properties.json
com sua configuração completa.
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/arm-none-eabi/include",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/lib/gcc/arm-none-eabi/7.3.1/include",
"C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/lib/gcc/arm-none-eabi/7.3.1/include-fixed",
"${workspaceFolder}/Core/Inc",
"${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include",
"${workspaceFolder}/Drivers/CMSIS/Include",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy"
],
"defines": [
"DEBUG",
"STM32F100xB",
"USE_HAL_DRIVER"
],
"compilerPath": "C:/ST/STM32CubeIDE_1.4.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.4.0.202007081208/tools/bin/arm-none-eabi-gcc.exe",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "${default}"
}
],
"version": 4
}
Code language: JSON / JSON with Comments (json)
Deixe um comentário