206 lines
7.2 KiB
C
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);
|
|
}
|
|
|
|
|
|
|