BE_VOILIER/Pilotes/Sources/MYPWMv2.c
Mygi27 d9d147e342 harmonisation des pilotes de l'USART et de la PWM
j'ai utilisé les pilotes de Oskar
2025-12-16 18:17:59 +01:00

206 lines
7.2 KiB
C

#include "MyTimer.h"
#include "MyGPIO.h"
#include "MyTimer.h"
#include <stdbool.h>
#include <stdint.h>
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);
}