#include "stm32c0xx_hal.h"
#include "stm32c0xx_ll_tim.h"
#include <stdio.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 // ms
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim3;
uint16_t dutycycle = 500; // Exemplo de 50% de PWM
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);
static void MX_TIM1_Init(void);
void MotorCommutation(uint8_t currentStep);
// ======================================================================
// 1. LEITURA DOS SENSORES (TIM3: PA6, PA7, PB0)
// ======================================================================
uint8_t readHallSensors(void) {
uint8_t hallA = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6);
uint8_t hallB = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7);
uint8_t hallC = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
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. INTERRUPÇÃO DE HARDWARE (CÁLCULO RPM)
// ======================================================================
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM3 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
uint8_t passo_atual = readHallSensors();
if (hallState != 0 && hallState != 7) {
tempo_medido = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
if (tempo_medido > 0) {
velocidade_rpm = (CLOCK_TIMER * 60.0f) / (PULSOS_POR_VOLTA * (float)tempo_medido);
}
ultimoTempoAferido = HAL_GetTick();
MotorCommutation(passo_atual);
}
}
}
// Roteador da Interrupção para o HAL
void TIM3_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim3);
}
// ======================================================================
// 3. SIMULADOR DO MOTOR
// ======================================================================
void Simular_Sensores_Hall(void) {
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 a simulação nas saídas PA0, PA1 e PA4
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;
}
}
// ======================================================================
// 4. PROGRAMA PRINCIPAL
// ======================================================================
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
MX_TIM1_Init();
ultimoTempoAferido = HAL_GetTick();
// Liga o hardware do Sensor Hall
HAL_TIMEx_HallSensor_Start_IT(&htim3);
uint32_t timer_print = 0;
while (1) {
// Gira o "motor" virtual
Simular_Sensores_Hall();
// Watchdog de Velocidade Zero
if ((HAL_GetTick() - ultimoTempoAferido) > TIMEOUT_MOTOR_PARADO) {
velocidade_rpm = 0.0f;
}
// Imprime a velocidade no terminal a cada 500ms
if(HAL_GetTick() - timer_print > 500) {
printf("RPM Atual: %.2f | Hall State: %d\n", velocidade_rpm, hallState);
timer_print = HAL_GetTick();
}
}
}
// ======================================================================
// 5. LÓGICA DE COMUTAÇÃO (SIX-STEP)
// ======================================================================
void MotorCommutation(uint8_t currentStep) {
// Desliga tudo preventivamente
LL_TIM_CC_DisableChannel(TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N |
LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N |
LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N);
switch(currentStep) {
case 5:
LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N);
LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3N);
break;
//... (Outros passos omitidos para focar na leitura, mas a estrutura funciona!)
// No Wokwi, você não precisa se preocupar em ter todos os cases perfeitamente
// preenchidos agora se o objetivo é ver a RPM na tela.
case 4: break;
case 6: break;
case 2: break;
case 3: break;
case 1: break;
}
}
// ======================================================================
// CONFIGURAÇÕES DE HARDWARE (CLOCK, GPIO, TIMER)
// ======================================================================
void SystemClock_Config(void) {
// Configuração básica de clock exigida pelo template do Wokwi
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); // 48 MHz
}
static void MX_GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// Pinos Simuladores (PA0, PA1, PA4)
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();
// Configuração dos Pinos TIM3 (PA6, PA7, PB0)
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);
// 48 MHz Clock / 48 = 1 MHz
htim3.Instance = TIM3;
htim3.Init.Prescaler = 48 - 1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xFFFF; // TIM3 no C0 é de 16 bits
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);
HAL_NVIC_SetPriority(TIM3_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
}
static void MX_TIM1_Init(void) {
__HAL_RCC_TIM1_CLK_ENABLE();
htim1.Instance = TIM1;
htim1.Init.Prescaler = 48 - 1;
htim1.Init.Period = 1000 - 1; // 1kHz PWM
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&htim1);
}