He realizado un proyecto con un STM32F100 a 24MHz, que tiene 24 PWM a 2kHz, con un ciclo de trabajo seleccionable en el paso del 1%.
Para hacer este trabajo, tuve que usar todos los temporizadores disponibles con salida PWM, y algún pin en el modo de banda de bits activado por otro temporizador.
Por favor, preste atención a que es imposible alcanzar la alta velocidad en el modo de banda de bits. En mis pruebas, la frecuencia máxima de pwm con 24pwm en modo de banda de bits, impulsada por un temporizador en interrupción de tiempo constante, es muy baja, aproximadamente 600Hz.
Esta fue mi configuración:
/*
* PWM 1 : Bit banding
* PWM 2 : Bit banding
* PWM 3 : Bit banding
* PWM 4 : Bit banding
* PWM 5 : TIM1_CH1N (TIM1->CCR1, TIM1->CCER_CC1NP)
* PWM 6 : TIM1_CH2N (TIM1->CCR2, TIM1->CCER_CC2NP)
* PWM 7 : TIM1_CH3N (TIM1->CCR3, TIM1->CCER_CC3NP)
* PWM 8 : Bit banding
* PWM 9 : Bit banding
* PWM 10: Bit banding
* PWM 11: Bit banding
* PWM 12: TIM4_CH1 ()
* PWM 13: TIM4_CH2 ()
* PWM 14: TIM4_CH3 ()
* PWM 15: TIM4_CH4 ()
* PWM 16: TIM3_CH1 ()
* PWM 17: TIM3_CH2 ()
* PWM 18: TIM3_CH3 ()
* PWM 19: TIM3_CH4 ()
* PWM 20: Bit banding
* PWM 21: Bit banding
* PWM 22: Bit banding
* PWM 23: Bit banding
* PWM 24: Bit banding
*/
Establezca el ciclo de trabajo de pwm controlado por el temporizador.
void setPwmDrivenByTimer(uint8_t pwm, uint8_t duty) {
switch (pwm) {
case 4: TIM1->CCR1 = duty; break;
case 5: TIM1->CCR2 = duty; break;
case 6: TIM1->CCR3 = duty; break;
case 11: TIM4->CCR1 = duty; break;
case 12: TIM4->CCR2 = duty; break;
case 13: TIM4->CCR3 = duty; break;
case 14: TIM4->CCR4 = duty; break;
case 15: TIM3->CCR1 = duty; break;
case 16: TIM3->CCR2 = duty; break;
case 17: TIM3->CCR3 = duty; break;
case 18: TIM3->CCR4 = duty; break;
default: break;
}
}
Para establecer el estado del pin del pwm conducido en el modo de banda de bits, he usado este código:
/* bitband type */
typedef volatile uint32_t * const bitband_t;
/* base address for bit banding */
#define BITBAND_SRAM_REF (0x20000000)
/* base address for bit banding */
#define BITBAND_SRAM_BASE (0x22000000)
/* base address for bit banding */
#define BITBAND_PERIPH_REF (0x40000000)
/* base address for bit banding */
#define BITBAND_PERIPH_BASE (0x42000000)
/* sram bit band */
#define BITBAND_SRAM(address, bit) ((void*)(BITBAND_SRAM_BASE + \
(((uint32_t)address) - BITBAND_SRAM_REF) * 32 + (bit) * 4))
/* periph bit band */
#define BITBAND_PERIPH(address, bit) ((void *)(BITBAND_PERIPH_BASE + \
(((uint32_t)address) - BITBAND_PERIPH_REF) * 32 + (bit) * 4))
#pragma diag_suppress 1296
bitband_t pwm[] = {
{BITBAND_PERIPH(&GPIOE->ODR, 2)},
{BITBAND_PERIPH(&GPIOE->ODR, 3)},
{BITBAND_PERIPH(&GPIOE->ODR, 4)},
{BITBAND_PERIPH(&GPIOB->ODR, 12)},
{BITBAND_PERIPH(&GPIOB->ODR, 13)},
{BITBAND_PERIPH(&GPIOB->ODR, 14)},
{BITBAND_PERIPH(&GPIOB->ODR, 15)},
{BITBAND_PERIPH(&GPIOD->ODR, 8)},
{BITBAND_PERIPH(&GPIOD->ODR, 9)},
{BITBAND_PERIPH(&GPIOD->ODR, 10)},
{BITBAND_PERIPH(&GPIOD->ODR, 11)},
{BITBAND_PERIPH(&GPIOD->ODR, 12)},
{BITBAND_PERIPH(&GPIOD->ODR, 13)},
{BITBAND_PERIPH(&GPIOD->ODR, 14)},
{BITBAND_PERIPH(&GPIOD->ODR, 15)},
{BITBAND_PERIPH(&GPIOC->ODR, 6)},
{BITBAND_PERIPH(&GPIOC->ODR, 7)},
{BITBAND_PERIPH(&GPIOC->ODR, 8)},
{BITBAND_PERIPH(&GPIOC->ODR, 9)},
{BITBAND_PERIPH(&GPIOA->ODR, 8)},
{BITBAND_PERIPH(&GPIOA->ODR, 11)},
{BITBAND_PERIPH(&GPIOA->ODR, 12)},
{BITBAND_PERIPH(&GPIOC->ODR, 10)},
{BITBAND_PERIPH(&GPIOC->ODR, 11)}
};
#pragma diag_warning 1296
void setPinStatusOfPwmDrivenInBitbandMode(uint8_t pin, GPIO_PinState newStatus) {
*(pwm[pin]) = newStatus;
}
Y ahora la parte difícil pero más importante.
He dicho que tengo otro temporizador.
Yo uso este temporizador para configurar el estado del pin de la banda de bits pwm.
No he configurado el temporizador para hacer una entrada siempre al mismo tiempo, pero lo configuro para hacer una interrupción en el próximo cambio de estado de pwm pin en la banda de bits.
Para hacer esto, he usado alguna función de clasificación que hace una serie de tiempos y estados del pin.
De esta manera, la interrupción se realiza solo cuando es necesario y el núcleo tiene tiempo para hacer otra cosa.
Para hacer este trabajo, he hecho una función que:
- Cree una matriz con el estado y la hora del cambio de pin,
- Ordene esta matriz en orden de media luna
- Eliminar duplicados
- Establecer el estado del pin inicial
- Configure el temporizador para disparar una matriz en el primer cambio de estado
Todo el código es complejo y largo, es por eso que en esta respuesta solo tengo el concepto funcional.
Espero que esto ayude.