I've made a project with a STM32F100 @ 24MHz, that have 24 PWM at 2kHz, with selectable duty cycle at step of 1%.
For make this work, I had to use all the available timers with PWM output, and some pin in bitband mode activated by another timer.
Please make attention that is impossible to reach hi speed in bitband mode. In my tests, the maximum pwm frequency with 24pwm in bitband mode, driven by a timer in constant time interrupt, is very low, about 600Hz.
This was my configuration:
/*
* 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
*/
Set the duty cycle of pwm driven by timer it's easy..
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;
}
}
For set the pin status of the pwm driven in bitband mode, i've used this code:
/* 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;
}
And now the difficult but most important part.
I've told that I've another timer.
I use this timer for setting the pin status of the bitband pwm.
I've not set the timer for make an interrut always at the same time, but I set the timer for make an interrupt on next bitband pwm pin state change.
For make this, I have used some sorting function thath make an array of timing and status of the pin.
In this way, the interrupt are made only when necessary and the core have time for doing something else.
For make this work, I've make a function that:
- Create an array with status and time of pin change,
- Order this array in crescent order
- Remove duplicates
- Set the initial pin state
- Set the timer for firing an array at first status change
All the code is complex and long, this is why I've in this answer only the functional concept.
Hope this helps.