// ==========================
// STM32C0 + SysTick (polling) + PWM por software trifásico
// - Sem declarar IRQ handlers (evita colisões com o core)
// - GPIO: RCC->IOPENR, GPIOx->MODER, GPIOx->BSRR
// - SysTick: LOAD/VAL/CTRL, usa COUNTFLAG a cada 10 us
// - ADC: bare-metal opcional ou fallback analogRead() (padrão para Wokwi)
// ==========================
#include <Arduino.h>
#include <math.h>
#include "stm32c0xx.h"
// ======= Config =======
static const float F_SINE_HZ = 60.0f; // senoide 60 Hz
static const uint16_t PWM_TOP = 200; // 0..199 → ~500 Hz (com tick 10 us)
static const uint16_t TICK_US = 10; // “tick” virtual de 10 us
static const uint16_t UPDATE_MS = 1; // recalcula senoide a cada 1 ms
// ======= PINOUT (ajuste se necessário) =======
// Entradas analógicas
#define ADC_CH_A0 0 // PA0
#define ADC_CH_A1 1 // PA1
#define ADC_CH_A2 4 // PA4
// Saídas (fases A/B/C) — aqui: D3/D5/D6 = PB3/PB5/PB6
#define PHASEA_GPIO GPIOB
#define PHASEA_PIN 3
#define PHASEB_GPIO GPIOB
#define PHASEB_PIN 5
#define PHASEC_GPIO GPIOB
#define PHASEC_PIN 6
// ======= ADC bare-metal? (1 = sim; 0 = usar fallback analogRead) =======
#define USE_BARE_ADC 0
// ======= Estado =======
volatile uint16_t dutyCount[3] = {100,100,100};
volatile uint16_t pwmCount = 0;
volatile float theta = 0.0f;
// ======= Utils =======
static inline float clamp01(float x){ if(x<0) return 0; if(x>1) return 1; return x; }
// -------- GPIO (registradores) --------
static void gpio_enable_clock(GPIO_TypeDef* gpio){
if(gpio==GPIOA) RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
else if(gpio==GPIOB) RCC->IOPENR |= RCC_IOPENR_GPIOBEN;
#ifdef GPIOC
else if(gpio==GPIOC) RCC->IOPENR |= RCC_IOPENR_GPIOCEN;
#endif
}
static void gpio_pin_output(GPIO_TypeDef* gpio, uint8_t pin){
uint32_t sh = pin*2U;
gpio->MODER &= ~(0b11UL<<sh);
gpio->MODER |= (0b01UL<<sh); // output
}
static inline void gpio_write(GPIO_TypeDef* gpio, uint8_t pin, bool high){
gpio->BSRR = high ? (1UL<<pin) : (1UL<<(pin+16U));
}
// -------- ADC --------
#if USE_BARE_ADC
static void adc_enable_clock_and_calibrate(){
RCC->APBENR2 |= RCC_APBENR2_ADCEN;
ADC1->CR |= ADC_CR_ADCAL; while(ADC1->CR & ADC_CR_ADCAL){}
ADC1->CR |= ADC_CR_ADEN; while(!(ADC1->ISR & ADC_ISR_ADRDY)){}
ADC1->CFGR1 = 0;
ADC1->SMPR = 0x7; // amostragem “longa”
}
static uint16_t adc_read_channel(uint8_t ch){
ADC1->CHSELR = (1UL<<ch);
ADC1->ISR = ADC_ISR_EOC | ADC_ISR_EOS | ADC_ISR_OVR;
ADC1->CR |= ADC_CR_ADSTART;
while(!(ADC1->ISR & ADC_ISR_EOC)){}
return (uint16_t)ADC1->DR;
}
#endif
static float adc_read_normA0(){
#if USE_BARE_ADC
return (float)adc_read_channel(ADC_CH_A0)/4095.0f;
#else
analogReadResolution(12);
return (float)analogRead(A0)/4095.0f;
#endif
}
static float adc_read_normA1(){
#if USE_BARE_ADC
return (float)adc_read_channel(ADC_CH_A1)/4095.0f;
#else
analogReadResolution(12);
return (float)analogRead(A1)/4095.0f;
#endif
}
static float adc_read_normA2(){
#if USE_BARE_ADC
return (float)adc_read_channel(ADC_CH_A2)/4095.0f;
#else
analogReadResolution(12);
return (float)analogRead(A2)/4095.0f;
#endif
}
// -------- SysTick (polling) --------
static void systick_init_polling(uint32_t tick_us){
uint32_t reload = (SystemCoreClock/1000000UL)*tick_us - 1UL;
SysTick->LOAD = reload;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; // sem IRQ
}
// -------- Constantes trig --------
#ifndef PI
#define PI 3.14159265358979323846f
#endif
static const float TAU_F = 2.0f*PI; // 2π
static const float PH_B = -2.0f*PI/3.0f; // -120°
static const float PH_C = +2.0f*PI/3.0f; // +120°
void setup(){
// GPIO saídas
gpio_enable_clock(PHASEA_GPIO);
gpio_enable_clock(PHASEB_GPIO);
gpio_enable_clock(PHASEC_GPIO);
gpio_pin_output(PHASEA_GPIO, PHASEA_PIN);
gpio_pin_output(PHASEB_GPIO, PHASEB_PIN);
gpio_pin_output(PHASEC_GPIO, PHASEC_PIN);
#if USE_BARE_ADC
adc_enable_clock_and_calibrate();
#endif
// tempo-base 10 us
systick_init_polling(TICK_US);
}
void loop(){
// aguarda “estouro” do SysTick (COUNTFLAG)
if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk){
// --- PWM tick (10 us) ---
pwmCount++; if(pwmCount >= PWM_TOP) pwmCount = 0;
gpio_write(PHASEA_GPIO, PHASEA_PIN, pwmCount < dutyCount[0]);
gpio_write(PHASEB_GPIO, PHASEB_PIN, pwmCount < dutyCount[1]);
gpio_write(PHASEC_GPIO, PHASEC_PIN, pwmCount < dutyCount[2]);
// --- Atualização senoide a cada 1 ms ---
static uint16_t acc_us = 0;
acc_us += TICK_US;
if(acc_us >= (UPDATE_MS*1000U)){
acc_us = 0;
float Aa = adc_read_normA0(); // 0..1
float Ab = adc_read_normA1(); // 0..1
float Ac = adc_read_normA2(); // 0..1
float dA = 0.5f + 0.5f*Aa*sinf(theta);
float dB = 0.5f + 0.5f*Ab*sinf(theta + PH_B);
float dC = 0.5f + 0.5f*Ac*sinf(theta + PH_C);
dA = clamp01(dA); dB = clamp01(dB); dC = clamp01(dC);
dutyCount[0] = (uint16_t)(dA * PWM_TOP);
dutyCount[1] = (uint16_t)(dB * PWM_TOP);
dutyCount[2] = (uint16_t)(dC * PWM_TOP);
theta += TAU_F*F_SINE_HZ*0.001f; // Δt = 1 ms
if(theta >= TAU_F) theta -= TAU_F;
}
}
}