#include "stm32f103xb.h"
#include <stdint.h>
/* ==========================================================================
* MACROS Y CONSTANTES
* ========================================================================== */
// Prescaler para obtener 1 tick = 1 us (Reloj a 8 MHz)
#define TIMER_1MHZ_PSC 7
/* ==========================================================================
* VARIABLES GLOBALES VOLÁTILES
* ========================================================================== */
volatile uint32_t t_motor_us = 1000000;
volatile uint32_t delta_t_us = 250000;
volatile uint16_t temp_celsius = 25;
volatile uint8_t tdc_stage = 0; // 0: Esperando 90°, 1: Pulso activo (75%)
/* ==========================================================================
* PROTOTIPOS DE FUNCIONES
* ========================================================================== */
void SystemClock_Config(void);
void GPIO_Init(void);
void ADC1_Init(void);
void Timers_Init(void);
void EXTI_Init(void);
uint16_t ADC_Read(void);
/* ==========================================================================
* FUNCIÓN PRINCIPAL
* ========================================================================== */
int main(void) {
SystemClock_Config();
GPIO_Init();
ADC1_Init();
Timers_Init();
EXTI_Init();
while (1) {
// 1. Leer Temperatura (Sensor LM35 en PA0)
uint16_t adc_val = ADC_Read();
temp_celsius = (adc_val * 330) / 4095;
// 2. Extraer el periodo actual del motor de forma segura
__disable_irq();
uint32_t t_motor_actual = t_motor_us;
__enable_irq();
// 3. Cálculos matemáticos del profesor
uint32_t t_90_us = t_motor_actual / 4; // Límite inferior (Tmotor / 4)
uint32_t t_180_us = t_motor_actual / 2; // Límite superior (Tmotor / 2)
// Fórmula: dt = (Tmotor/4) + 200 * (85 - Temp)
int32_t compensacion = 200 * (85 - (int32_t)temp_celsius);
int32_t dt_calculado = (int32_t)t_90_us + compensacion;
// 4. Restricciones del examen
if (dt_calculado < (int32_t)t_90_us) {
dt_calculado = t_90_us;
} else if (dt_calculado > (int32_t)t_180_us) {
dt_calculado = t_180_us;
}
// 5. Guardar delta_t para los Timers
__disable_irq();
delta_t_us = (uint32_t)dt_calculado;
__enable_irq();
}
}
/* ==========================================================================
* RUTINAS DE ATENCIÓN A INTERRUPCIONES (ISR)
* ========================================================================== */
// Interrupción PB5 (Señal CKPZ - Referencia 0° del ciclo)
void EXTI9_5_IRQHandler(void) {
if (EXTI->PR & EXTI_PR_PR5) {
EXTI->PR |= EXTI_PR_PR5; // Limpiar bandera
// Medir periodo total
t_motor_us = TIM1->CNT;
TIM1->CNT = 0;
// Iniciar etapa 0: Esperar a llegar a los 90° (TDC)
tdc_stage = 0;
TIM3->ARR = (t_motor_us / 4);
TIM3->CNT = 0;
TIM3->CR1 |= TIM_CR1_CEN;
}
}
// Manejo del TDC (TIM3) - Aquí ocurre la magia del 75%
void TIM3_IRQHandler(void) {
if (TIM3->SR & TIM_SR_UIF) {
TIM3->SR &= ~TIM_SR_UIF; // Limpiar bandera
if (tdc_stage == 0) {
// ACABAMOS DE LLEGAR A TDC (90°)
GPIOB->BSRR = (1 << 6); // Encender Magenta (TDC)
// Disparar el retardo de inyección (TIM2)
TIM2->ARR = delta_t_us;
TIM2->CNT = 0;
TIM2->CR1 |= TIM_CR1_CEN;
// Configurar el Timer para que se apague en el 75% de un diente
tdc_stage = 1;
TIM3->ARR = (t_motor_us * 3) / 16; // Cálculo: (T_motor/4) * 0.75
TIM3->CNT = 0;
TIM3->CR1 |= TIM_CR1_CEN;
}
else {
// EL PULSO YA DURÓ EL 75%, SE APAGA
GPIOB->BRR = (1 << 6); // Apagar Magenta (TDC)
tdc_stage = 0;
}
}
}
// Interrupción TIM2 (Terminó el retardo - ¡Inyectar!)
void TIM2_IRQHandler(void) {
if (TIM2->SR & TIM_SR_UIF) {
TIM2->SR &= ~TIM_SR_UIF; // Limpiar bandera
TIM4->CR1 |= TIM_CR1_CEN; // Disparar Inyector (PWM 100kHz)
}
}
/* ==========================================================================
* CONFIGURACIÓN A BAJO NIVEL (REGISTROS)
* ========================================================================== */
void SystemClock_Config(void) {
// Por defecto el STM32 arranca con el HSI a 8MHz.
}
void GPIO_Init(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;
// PA0: Analógico (Sensor LM35)
GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
// PB5: Entrada Flotante (Señal 0° - EXTI5)
GPIOB->CRL &= ~(GPIO_CRL_MODE5 | GPIO_CRL_CNF5);
GPIOB->CRL |= (GPIO_CRL_CNF5_0);
// PB6: Salida Push-Pull a 50MHz (SEÑAL TDC PARA OSCILOSCOPIO)
GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6);
GPIOB->CRL |= (GPIO_CRL_MODE6_1 | GPIO_CRL_MODE6_0);
// PB7: Salida Alterna Push-Pull (PWM del Inyector - TIM4_CH2)
GPIOB->CRL &= ~(GPIO_CRL_MODE7 | GPIO_CRL_CNF7);
GPIOB->CRL |= (GPIO_CRL_MODE7 | GPIO_CRL_CNF7_1);
}
void EXTI_Init(void) {
AFIO->EXTICR[1] &= ~AFIO_EXTICR2_EXTI5;
AFIO->EXTICR[1] |= AFIO_EXTICR2_EXTI5_PB;
EXTI->IMR |= EXTI_IMR_MR5;
EXTI->FTSR |= EXTI_FTSR_TR5; // Disparar en flanco de bajada
NVIC_EnableIRQ(EXTI9_5_IRQn);
NVIC_SetPriority(EXTI9_5_IRQn, 1);
}
void ADC1_Init(void) {
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
ADC1->CR2 |= ADC_CR2_ADON;
for (volatile int i = 0; i < 1000; i++);
ADC1->CR2 |= ADC_CR2_CAL;
while(ADC1->CR2 & ADC_CR2_CAL);
}
uint16_t ADC_Read(void) {
ADC1->SQR3 = 0; // Canal 0 (PA0)
ADC1->CR2 |= ADC_CR2_SWSTART;
while (!(ADC1->SR & ADC_SR_EOC));
return ADC1->DR;
}
void Timers_Init(void) {
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM4EN;
// TIM1: Cronómetro libre (1 us por tick)
TIM1->PSC = TIMER_1MHZ_PSC;
TIM1->ARR = 0xFFFF;
TIM1->CR1 |= TIM_CR1_CEN;
// TIM3: Timer One-Shot para retardo TDC y ancho de pulso
TIM3->PSC = TIMER_1MHZ_PSC;
TIM3->CR1 |= TIM_CR1_OPM;
TIM3->DIER |= TIM_DIER_UIE;
NVIC_EnableIRQ(TIM3_IRQn);
NVIC_SetPriority(TIM3_IRQn, 2); // Prioridad media
// TIM2: Timer One-Shot para retardo Delta_t
TIM2->PSC = TIMER_1MHZ_PSC;
TIM2->CR1 |= TIM_CR1_OPM;
TIM2->DIER |= TIM_DIER_UIE;
NVIC_EnableIRQ(TIM2_IRQn);
NVIC_SetPriority(TIM2_IRQn, 3); // Prioridad baja
// TIM4: PWM Inyector (100kHz, 20% DC, One-Shot con reloj a 8MHz)
TIM4->PSC = 0; // Sin prescaler (Corre a 8MHz)
TIM4->ARR = 79; // 8MHz / 80 = 100kHz exactos
TIM4->CCR2 = 16; // 20% de 80 = 16
TIM4->CCMR1 |= (TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2);
TIM4->CCER |= TIM_CCER_CC2E;
TIM4->CR1 |= TIM_CR1_OPM;
}