#include "MyTimer.h" #include "MyGPIO.h" #include "MyTimer.h" #include #include void MyTimer_PWM2(TIM_TypeDef * Timer, char Channel) { if (!(Timer == TIM2 || Timer == TIM3 || Timer == TIM4)) return; // Si Timer es nulo, salimos de la funcion if (Channel < 1 || Channel > 4) return; // Si Chanel no esta entre 1 y 4, salimos de la funcion Timer->CR1 |= (1 << 7); // Sirve para activar el “Auto-Reload Preload Enable” (ARPE), que habilita el buffer // del registro ARR. Sin ARPE, cuando modificamos ARR, el nuevo valor entra en efecto // inmediatamente, lo que puede generar un salto brusco en la señal PWM. Con ARPE = 1, // el valor de ARR se guarda primero en un buffer, y solo se aplica al final del ciclo // actual (cuando el contador genera un “update event”). Esto garantiza que los cambios // de periodo o de CCR sean sin discontinuidades y estén sincronizados con el inicio del // siguiente ciclo PWM. switch (Channel) { // CCMR1 (Capture/Compare Mode Register 1) controla el modo de los canales 1 y 2: // Bits [1:0] ---> CC1S: seleccionan si el canal 1 es salida (00) o entrada (01/10). // Bit [3] ------> OC1PE: preload del registro CCR1 (buffer para actualizar el duty sin saltos). // Bits [6:4] ---> OC1M: modo de salida (p. ej. 110 = PWM1). // (Análogos para canal 2 en bits [9:8], [11], [14:12]). // CCMR2 hace lo mismo pero para los CHANELs 3 y 4. // CCER (Capture/Compare Enable Register) controla la habilitación y polaridad de los canales de salida: // Bit [0] ---> CC1E: habilita la salida del canal 1 (1 = activo, 0 = deshabilitado). // Bit [1] ---> CC1P: polaridad de salida (0 = activo alto, 1 = activo bajo). // (Análogos para los demás canales: CC2E/CC2P en los bits [4]/[5] del mismo registro CCER, CC3E/CC3P // en [8]/[9], y CC4E/CC4P en [12]/[13]). En modo PWM normal, CCxP=0 (activo alto) y CCxE=1 (canal habilitado). // CCMRx = ..., OCxM, OCxPE, ..., CCxS case 1: // CHANNEL1 (bits bajos de CCMR1) Timer->CCMR1 &= ~( (1<<0)|(1<<1)|(1<<3)|(1<<4)|(1<<5)|(1<<6) ); // limpia CC1S[1:0], OC1PE, OC1M[2:0] -> Todo a cero Timer->CCMR1 |= (1<<3)|(1<<5)|(1<<6); // salida (00), preload (1), PWM1 (110) Timer->CCER &= ~(1<<1); Timer->CCER |= (1<<0); // CC1P=0, CC1E=1 break; case 2: // CHANNEL2 (bits altos de CCMR1) Timer->CCMR1 &= ~( (1<<8)|(1<<9)|(1<<11)|(1<<12)|(1<<13)|(1<<14) ); // limpia CC2S[1:0], OC2PE, OC2M[2:0] -> Todo a cero Timer->CCMR1 |= (1<<11)|(1<<13)|(1<<14); // salida (00), preload (1), PWM1 (110) Timer->CCER &= ~(1<<5); Timer->CCER |= (1<<4); // CC2P=0, CC2E=1 break; case 3: // CHANNEL3 (bits bajos de CCMR2) Timer->CCMR2 &= ~( (1<<0)|(1<<1)|(1<<3)|(1<<4)|(1<<5)|(1<<6) ); // limpia CC3S[1:0], OC3PE, OC3M[2:0] -> Todo a cero Timer->CCMR2 |= (1<<3)|(1<<5)|(1<<6); // salida (00), preload (1), PWM1 (110) Timer->CCER &= ~(1<<9); Timer->CCER |= (1<<8); // CC3P=0, CC3E=1 break; case 4: // CHANNEL4 (bits altos de CCMR2) Timer->CCMR2 &= ~( (1<<8)|(1<<9)|(1<<11)|(1<<12)|(1<<13)|(1<<14) ); // limpia CC4S[1:0], OC4PE, OC4M[2:0] -> Todo a cero Timer->CCMR2 |= (1<<11)|(1<<13)|(1<<14); // salida (00), preload (1), PWM1 (110) Timer->CCER &= ~(1<<13); Timer->CCER |= (1<<12); // CC4P=0, CC4E=1 break; } } void MyTimer_PWM_Start(TIM_TypeDef *Timer, char Channel, uint16_t ccr) { if (!(Timer == TIM2 || Timer == TIM3 || Timer == TIM4)) return; if (Channel < 1 || Channel > 4) return; switch (Channel) { case 1: Timer->CCR1 = ccr; break; case 2: Timer->CCR2 = ccr; break; case 3: Timer->CCR3 = ccr; break; case 4: Timer->CCR4 = ccr; break; } Timer->EGR |= (1<<0); // Update Generator: transferimos los registros ARR/CCR preloaded al shadow register Timer->SR &= ~(1<<0); // Satus Register: limpiamos UIF (Update Interrupt Flag) por higiene Timer->CR1 |= (1<<0); // Control Register 1: CEN (Counter Enable) = 1, empezamos la PWM } void MyTimer_PWM_SetDuty(TIM_TypeDef *Timer, char Channel, uint16_t ccr) { if (!(Timer == TIM2 || Timer == TIM3 || Timer == TIM4)) return; if (Channel < 1 || Channel > 4) return; // Cargamos el nuevo valor de comparación (duty) switch (Channel) { case 1: Timer->CCR1 = ccr; break; case 2: Timer->CCR2 = ccr; break; case 3: Timer->CCR3 = ccr; break; case 4: Timer->CCR4 = ccr; break; } // Forzamos update inmediato para aplicar el nuevo CCR Timer->EGR |= (1<<0); } void MyTimer_PWM_ConfigPercent(TIM_TypeDef *Timer, char Channel, uint32_t f_pwm_hz, float duty_pct) { if (!(Timer == TIM2 || Timer == TIM3 || Timer == TIM4)) return; if (Channel < 1 || Channel > 4) return; if (f_pwm_hz == 0) return; const uint32_t f_tim = 72000000; // f_TIM = 72 MHz uint32_t psc = 0, arr = 0; for (psc = 0; psc <= 0xFFFF; ++psc) { uint32_t denom = (psc + 1) * f_pwm_hz; if (denom == 0) return; uint32_t arr_candidate = (f_tim / denom); if (arr_candidate == 0) continue; arr_candidate -= 1; if (arr_candidate <= 0xFFFF) { arr = arr_candidate; break; } } if (psc > 0xFFFF || arr == 0) return; if (duty_pct < 0.0f) duty_pct = 0.0f; if (duty_pct > 100.0f) duty_pct = 100.0f; uint32_t ccr = (uint32_t)((arr + 1) * (duty_pct / 100.0f)); if (ccr > arr) ccr = arr; MyTimer_Base_Init(Timer, (unsigned short)arr, (unsigned short)psc); MyTimer_PWM2(Timer, Channel); MyTimer_PWM_Start(Timer, Channel, (uint16_t)ccr); } void Plateau_Init2(TIM_TypeDef * Timer, int Channel){ // Config du moteur servo GPIO_configure(GPIOB, 5, 1); //config pin de direction 0 ou 1 if (Timer == TIM3) { EnableTimer(TIM3); MyTimer_Base_Init(TIM3, 159, 17); // Pour obtenir fréq de 20kHZ if (Channel == 3){ GPIO_configure(GPIOB, 0, 1); // Outut push pull alternate, config pin de consigne entre -100 et 100 MyTimer_PWM2(TIM3, 3); //TIM3 CH3 } else{ //printf("Ce pilote n'existe pas"); } } else{ //printf("Ce pilote n'existe pas"); } } void Plateau_SetCommande(signed char cmd) { if (cmd > 100) cmd = 100; if (cmd < -100) cmd = -100; if (cmd >= 0) { allume_led(GPIOB, 5); } else { eteindre_led(GPIOB, 5); cmd = -cmd; } float duty = (float)cmd; float duty_cycle = (uint16_t)((TIM3->ARR + 1) * duty / 100.0f); MyTimer_PWM_SetDuty(TIM3, 3, duty_cycle); }