#include "stm32c0xx_hal.h"
#include <stdio.h>
#include <stdbool.h>
// ======================================================================
// DEFINIÇÕES E VARIÁVEIS GLOBAIS
// ======================================================================
#define CLOCK_TIMER 1000000.0f // Timer a 1 MHz (1 tick = 1 us)
#define PULSOS_POR_VOLTA 54.0f // 18 polos (9 pares * 6 passos)
#define TIMEOUT_MOTOR_PARADO 150 // Tempo limite em ms para considerar RPM = 0
TIM_HandleTypeDef htim3;
volatile uint32_t tempo_medido = 0;
volatile float velocidade_rpm = 0.0f;
volatile uint32_t ultimoTempoAferido = 0;
volatile uint8_t hallState = 0;
// Variáveis do Simulador
uint32_t tempo_ultimo_passo = 0;
uint8_t indice_passo = 0;
uint32_t tempo_por_passo_ms = 10; // Motor simulado dá 1 passo a cada 10ms
// ======================================================================
// PROTÓTIPOS
// ======================================================================
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM3_Init(void);
// ======================================================================
// 1. LEITURA DOS SENSORES (Loopback Interno Sem Fios)
// ======================================================================
uint8_t readHallSensors(void) {
// Para contornar os bugs de ligação do Wokwi, lemos diretamente
// os pinos que o nosso próprio simulador está a piscar.
uint8_t hallA = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
uint8_t hallB = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
uint8_t hallC = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4);
// Junta os bits para formar o estado atual
hallState = (hallA << 2) | (hallB << 1) | hallC;
switch(hallState){
case 0b101: return 4;
case 0b100: return 6;
case 0b110: return 2;
case 0b010: return 3;
case 0b011: return 1;
case 0b001: return 5;
case 0b000: return 0;
default: return 0;
}
}
// ======================================================================
// 2. SIMULADOR DO MOTOR
// ======================================================================
void Simular_Sensores_Hall(void) {
// Matriz com a sequência clássica de passos do sensor Hall
const uint8_t passos[6][3] = {
{1, 0, 1}, {1, 0, 0}, {1, 1, 0},
{0, 1, 0}, {0, 1, 1}, {0, 0, 1}
};
if ((HAL_GetTick() - tempo_ultimo_passo) >= tempo_por_passo_ms) {
tempo_ultimo_passo = HAL_GetTick();
// Escreve os estados simulados nos pinos de saída
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, passos[indice_passo][0] ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, passos[indice_passo][1] ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, passos[indice_passo][2] ? GPIO_PIN_SET : GPIO_PIN_RESET);
indice_passo++;
if (indice_passo > 5) indice_passo = 0;
}
}
// ======================================================================
// 3. PROGRAMA PRINCIPAL
// ======================================================================
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
ultimoTempoAferido = HAL_GetTick();
// Inicia o Timer 3
HAL_TIMEx_HallSensor_Start(&htim3);
uint32_t timer_print = 0;
uint8_t ultimo_estado_hall = 0;
while (1) {
// Gira o "motor" virtual
Simular_Sensores_Hall();
// 1. Lê a posição em que o motor se encontra
uint8_t passo_atual = readHallSensors();
// 2. VERIFICAÇÃO (Hardware Real vs Emulador Wokwi)
bool hardware_flag = __HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_CC1);
bool wokwi_fallback = (passo_atual != ultimo_estado_hall) && (passo_atual != 0);
if (hardware_flag || wokwi_fallback)
{
if (hardware_flag) {
// Vida real: O hardware fez tudo sozinho
__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_CC1);
tempo_medido = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);
} else {
// Wokwi: Nós lemos o contador (CNT) do Timer na mão e zeramos
tempo_medido = __HAL_TIM_GET_COUNTER(&htim3);
__HAL_TIM_SET_COUNTER(&htim3, 0);
}
ultimo_estado_hall = passo_atual;
// 3. CÁLCULO MATEMÁTICO DA VELOCIDADE
if (hallState != 0 && hallState != 7 && tempo_medido > 0) {
velocidade_rpm = (CLOCK_TIMER * 60.0f) / (PULSOS_POR_VOLTA * (float)tempo_medido);
ultimoTempoAferido = HAL_GetTick();
}
}
// 4. WATCHDOG DE VELOCIDADE ZERO
if ((HAL_GetTick() - ultimoTempoAferido) > TIMEOUT_MOTOR_PARADO) {
velocidade_rpm = 0.0f;
}
// 5. IMPRIME NA TELA (A cada 500ms)
if(HAL_GetTick() - timer_print > 500) {
printf("RPM Atual: %6.2f | Hall State: %d | Tempo Medido (us): %lu\n", velocidade_rpm, hallState, tempo_medido);
timer_print = HAL_GetTick();
}
}
}
// ======================================================================
// CONFIGURAÇÕES DE HARDWARE (CLOCK, GPIO, TIMER)
// ======================================================================
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
}
static void MX_GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
static void MX_TIM3_Init(void) {
TIM_HallSensor_InitTypeDef sConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
__HAL_RCC_TIM3_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
htim3.Instance = TIM3;
htim3.Init.Prescaler = 48 - 1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xFFFF;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim3);
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter = 5;
sConfig.Commutation_Delay = 0;
HAL_TIMEx_HallSensor_Init(&htim3, &sConfig);
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
sSlaveConfig.InputTrigger = TIM_TS_TI1F_ED;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 5;
HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig);
}