Pular para o conteúdo principal

Laboratório 07

Documentação de Apoio para Atividade de Laboratório

A documentação desta atividade de laboratório é complementar a documentação principal da disciplina.

Utilize como base a descrição apresentada no Moodle da disciplina.

TarefaTemplateInícioFimConteúdo
LAB07TPL0711-May-202618-May-2026ADC com DMA; Uso do STM32CubeMonitor; Manipulação de variáveis;

Conteúdo​

Esta atividade de laboratório tem como objetivo utilizar o DMA, MQTT e STM32CubeMonitor. Cada item equivale a 10% da nota.

  • Crie um repositório (LAB07) via template (https://github.com/ELT73A-LAB-TPL/LAB07) na organização e clone;
  • Importe e compile o código da pasta TIM2EXTI0ADC1PWMDMA
  • Verifique a configuração dos periféricos;
  • Configure o executável e probes no STM32CubeMonitor;
  • Configure o MQTT no Node-RED;
  • Teste de injeção de dados no MQTT;
  • Comfiguração do APP para MQTT;
  • Acionamento do LED via MQTT;
  • Commit e push do Node-RED export flow;
  • Envie o link do repositório no GitHub (hyperlink);

Avaliação​

  • Uso do template e código - 20%
  • Configuração do STM32CubeMonitor - 20%
  • Configuração e teste do MQTT - 20%
  • Comfiguração do APP e acionamento do LED - 20%
  • Commit e push do Node-RED export flow - 20%
  • Link do repositório no GitHub (hyperlink) - 10%

Verifique o seu ambiente de desenvolvimento​

Git is the most widely used source-code management tool among professional developers.

  • Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
winget install --id Git.Git -e --source winget

Configurações do git:

git config --list --show-origin
Configure a ferramenta git

Configure o nome de usuário para todos os repositórios locais ligados às suas transações de commit:

git config --global user.name "Your Name"

Configure o email de usuário para todos os repositórios locais ligados às suas transações de commit:

git config --global user.email "you@example.com"

É recomendado verificar se a instalação do seu Git não está realizando nenhuma transformação entre LFs e CRLFs.

git config --global core.autocrlf false

Configure o git para usar o Visual Studio Code como editor padrão para tarefas como escrever mensagens de commit ou rebases interativos

git config --global core.editor "code --wait"

Habilite a coloração automática da saída da linha de comando do Git:

git config --global color.ui auto

Configura o Git para usar main como o nome do branch padrão sempre que você inicializar um novo repositório localmente:

git config --global init.defaultBranch main

Liste as configurações aplicadas:

git config --list --show-origin

STM32CubeMX is an initialization code generator.

  • STM32CubeMX is a graphical tool that allows a very easy configuration of STM32 microcontrollers and microprocessors, as well as the generation of the corresponding initialization C code for the Arm® Cortex®-M core or a partial Linux® Device Tree for Arm® Cortex®-A core, through a step-by-step process.

Crie um novo repositório com base no template do LAB07​

Escolha o Grupo e entre com o comando abaixo para criar o repositório no GitHub:

Cole o comando abaixo no prompt aberto na pasta de trabalho!

Use o comando abaixo para criar o repositório no GitHub com base no template do laboratório LAB07:

gh repo create ELT73A-S22-2026-2-A/LAB07 --private -p ELT73A-LAB-TPL/LAB07 -c

Visualize o repositório:

gh repo view ELT73A-S22-2026-2-A/LAB07 --web

Abra o repositório clonado no VS Code:

code LAB07 --profile "STM32"
Como fazer commit da atualizações

Verifique o status do repositório:

git status

Adicione os arquivos modificados:

git add .

Realize o commit das alterações:

git commit -m "Descrição breve das alterações realizadas!"

Envie para o repositório remoto (GitHub):

git push

Vizualize o log de alterações:

git log

Vizualize no GitHub:

gh repo view --web

Diagrama de pinos do STM32F4x1​


STM32F4x1 microcontroller pinout diagram;

O debugger ST-LINK/V2 possue um conector IDC de 10 pinos. A pinagem é descrita na figura a seguir.

ST-LINK/V2 Clone


Acesso Direto à Memória​

O DMA (Direct Memory Access, ou Acesso Direto à Memória) no microcontrolador STM32F411CEU6 é um periférico projetado para transferir dados em alta velocidade entre memórias e periféricos sem a intervenção da CPU. Isso é especialmente útil para operações que exigem grandes volumes de dados, como leitura de sensores, comunicação serial ou manipulação de buffers de áudio/vídeo, onde a CPU pode ficar sobrecarregada se tiver que gerenciar cada byte de dados. O DMA pode ser configurado para transferir dados de uma fonte (como um periférico ou memória) para um destino (outra memória ou periférico) de forma eficiente, liberando a CPU para realizar outras tarefas enquanto a transferência ocorre em segundo plano.

User Code​

Src/main.c​

Implemente a lógica para acionar o LED azul via flag BLUELED:

Src/main.c
/* USER CODE BEGIN Includes */
#include <stdbool.h>
/* USER CODE END Includes */
Src/main.c
/* USER CODE BEGIN PV */
bool BLUELED = 0;
uint32_t AD_RES_BUFFER[2];
uint16_t ADC1IN1,ADC1IN2;
float voltage1,voltage2;
/* USER CODE END PV */

Inicialize o Timer 2 com suporte a rotina de interrupção e saída PWM no canal 1:

Src/main.c
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC1_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1);
/* USER CODE END 2 */

Toggle no LED interno via interrupção do User Key:

Scr/main.c
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// Verify BLUELED Flag
if (BLUELED)
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // LED ON
else
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // LED OFF
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */

}
/* USER CODE END 3 */

Src/stm32f4xx_it.c​

Src/stm32f4xx_it.c
/* USER CODE BEGIN Includes */
#include <stdbool.h>
/* USER CODE END Includes */
Src/stm32f4xx_it.c
/* USER CODE BEGIN PV */
extern bool BLUELED;
extern uint32_t AD_RES_BUFFER[2];
extern uint16_t ADC1IN1,ADC1IN2;
extern float voltage1,voltage2;
/* USER CODE END PV */
Src/stm32f4xx_it.c
void EXTI0_IRQHandler(void)
{
/* USER CODE BEGIN EXTI0_IRQn 0 */
// Toggle BLUELED FLAG
BLUELED = !BLUELED;
/* USER CODE END EXTI0_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(User_KEY_EXTI0_Pin);
/* USER CODE BEGIN EXTI0_IRQn 1 */

/* USER CODE END EXTI0_IRQn 1 */
}

Inicie o ADC1 na interrupção do Timer 2:

Src/stm32f4xx_it.c
void TIM2_IRQHandler(void)
{
/* USER CODE BEGIN TIM2_IRQn 0 */

/* USER CODE END TIM2_IRQn 0 */
HAL_TIM_IRQHandler(&htim2);
/* USER CODE BEGIN TIM2_IRQn 1 */
HAL_ADC_Start_DMA(&hadc1, AD_RES_BUFFER, 2);
/* USER CODE END TIM2_IRQn 1 */
}
  • HAL_ADC_ConvCpltCallback() é uma função de alto nível fornecida pela ST (STMicroelectronics) para facilitar a vida do desenvolvedor. Ela é chamada automaticamente quando uma conversão ADC é concluída e os dados estão prontos para serem processados. O uso dessa função é recomendado porque ela abstrai a complexidade de lidar diretamente com as interrupções de ADC e o gerenciamento de buffers, permitindo que o desenvolvedor se concentre na lógica de aplicação em vez de detalhes de baixo nível.
Src/stm32f4xx_it.c
/* USER CODE BEGIN 1 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
// Conversion Complete & DMA Transfer Complete As Well
ADC1IN1 = AD_RES_BUFFER[0];
ADC1IN2 = AD_RES_BUFFER[1];
voltage1 = (ADC1IN1 * 3.3) / 4095;
voltage2 = (ADC1IN2 * 3.3) / 4095;
TIM2->CCR1 = ADC1IN1; // PWM CH1 duty cycle update based on ADC1IN1
}
/* USER CODE END 1 */

SQLite​

CREATE TABLE IF NOT EXISTS sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
ADC1IN1 INTEGER,
voltage1 REAL,
ADC1IN2 INTEGER,
voltage2 REAL,
BLUELED INTEGER
);
let ADC1IN1 = Math.floor(Math.random() * 4096);
let ADC1IN2 = Math.floor(Math.random() * 4096);
let voltage1 = (ADC1IN1 * 3.3) / 4095;
let voltage2 = (ADC1IN2 * 3.3) / 4095;
let BLUELED = Math.round(Math.random());

// Create the SQL Insert Statement
// Replace 'sensor_data' with your actual table name
msg.topic = `INSERT INTO sensor_data (ADC1IN1, voltage1, ADC1IN2, voltage2, BLUELED)
VALUES (${ADC1IN1}, ${voltage1}, ${ADC1IN2}, ${voltage2}, ${BLUELED});`;

return msg;

The SQL Query​

SELECT * FROM sensor_data ORDER BY timestamp DESC LIMIT 1;

SQLite always returns data as an Array of Objects, even if there is only one result.

// Check if data actually exists to avoid errors
if (msg.payload.length > 0) {
// Take the first element of the array
// msg.payload = msg.payload[0];
msg.payload = msg.payload[0].voltage1;
return msg;
}

SQLite to insert data into the database​

const d = msg.payload;

// Constructing the query with rounded voltage values
msg.topic = `INSERT INTO sensor_readings (ADC1IN1, ADC1IN2, BLUELED, voltage1, voltage2)
VALUES (${d.ADC1IN1}, ${d.ADC1IN2}, ${d.BLUELED}, ${d.voltage1.toFixed(2)}, ${d.voltage2.toFixed(2)});`;

// Ensure payload is an empty array so the SQLite node executes the topic string
msg.payload = [];

return msg;

Create DB

CREATE TABLE IF NOT EXISTS sensor_readings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ADC1IN1 INTEGER,
ADC1IN2 INTEGER,
BLUELED INTEGER,
voltage1 REAL,
voltage2 REAL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);

Versão simplificada sem as colunas de tensão:​

const d = msg.payload;

// Constructing the query with rounded voltage values
msg.topic = `INSERT INTO sensor_readings (ADC1IN1, ADC1IN2, BLUELED)
VALUES (${d.ADC1IN1}, ${d.ADC1IN2}, ${d.BLUELED});`;

// Ensure payload is an empty array so the SQLite node executes the topic string
msg.payload = [];

return msg;

Create DB

CREATE TABLE IF NOT EXISTS sensor_readings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ADC1IN1 INTEGER,
ADC1IN2 INTEGER,
BLUELED INTEGER,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);

Calculation Node (Function after the SQLite node):

msg.topic = "SELECT ADC1IN1 FROM sensor_readings ORDER BY id DESC LIMIT 1;";
return msg;
if (msg.payload.length > 0) {
let adc_1 = msg.payload[0].ADC1IN1;

// Perform the conversion
let voltage1 = (adc_1 * 3.3) / 4095;

// Return the result rounded to 2 decimal places
msg.payload = voltage1.toFixed(2);

return msg;
}

Or:

// The math: (adc1 * 3.3) / 4095
msg.topic =
"SELECT (ADC1IN1 * 3.3 / 4095) AS voltage1 FROM sensor_readings ORDER BY id DESC LIMIT 1;";
return msg;

Average of the Last "X" Records

msg.topic = `SELECT AVG(ADC1IN1) AS mean_adc1
FROM (SELECT ADC1IN1 FROM sensor_readings ORDER BY id DESC LIMIT 5);`;
return msg;

InfluxDB Test​

let adc1Value = Math.floor(Math.random() * 4096);
let adc2Value = Math.floor(Math.random() * 4096);

msg.topic = "InfluxData";
msg.payload = {
ADC1IN1: adc1Value,
voltage1: (adc1Value * 3.3) / 4095,
ADC1IN2: adc2Value,
voltage2: (adc2Value * 3.3) / 4095,
BLUELED: Math.round(Math.random()),
};
return msg;

Ou sugerido peo Gemini:

// Generate raw values
const adc1 = Math.floor(Math.random() * 4096);
const adc2 = Math.floor(Math.random() * 4096);

// Helper to convert ADC to Voltage
const toVolts = (val) => parseFloat(((val * 3.3) / 4095).toFixed(3));

msg.topic = "sensor_data"; // Useful for organizing in Node-RED
msg.payload = {
ADC1IN1: adc1,
voltage1: toVolts(adc1),
ADC1IN2: adc2,
voltage2: toVolts(adc2),
BLUELED: Math.random() > 0.5 ? 1 : 0,
};

return msg;

VS Code Terminal Configuration​

Configure Git Bash como terminal integrado do Visual Studio Code para evitar problemas de formatação de texto e comandos:

.vscode/settings.json
"terminal.integrated.defaultProfile.windows": "Git Bash",

Crie um gh alias para deploy-debug​

gh alias set deploy-debug 'tag="$1"; cp ./build/Debug/*.elf ./firmware-debug.elf && gh release create "$tag" ./firmware-debug.elf --generate-notes && rm ./firmware-debug.elf' --shell

Como funciona:

  • Considera a etiqueta de lançamento como $1.
  • Copia o ELF de ./build/Debug/*.elf → ./firmware-debug.elf.
  • Executa gh release create com o arquivo renomeado.
  • Limpa removendo o arquivo renomeado temporário.

Uso:

gh deploy-debug v1.0.0

Rápida verificação do upload​

Assim que o comando terminar, você poderá verificar o upload diretamente em seu terminal usando:

gh release view v1.0.0 --web

Como carregar um script do STM32CubeMX com LoadMX​

Call for Help (--help, -h, /?)

LoadMX --help

Run STM32CubeMX interactively with LoadMX

Generate code with TIM2, EXT INT, ADC1 and PWM

LoadMX TIM2EXTI0ADC1PWM TIM2EXTI0ADC1PWM.txt

Generate code with TIM2, EXT INT, DMA (IN1 and IN2) and PWM

LoadMX TIM2EXTI0ADC1PWMDMA TIM2EXTI0ADC1PWMDMA.txt