I need to drive a Peltier element for thermal stabilization. Typically they are driven by a linear source and it's even recommended to do it that way. I did use MAX1968 as a bipolar driver, but that has a few drawbacks - hardware PID controller is fixed so there's very little room for thermal load variations; limited current/voltage. So I decided to try out driving an H-bridge with PWM and LC filter. While looking at PWM module in STM32F429 I noticed an interesting "complimentary" feature. I tested it out and here we go.
Advanced timers on F429 (TIM1 and TIM8) apart from being wider, also have "complimentary" feature, where a single TIM channel can drive two GPIOs, one in being an opposite of another.
But first things first. Let's try to set up a simple PWM first. As usual there are a few basic steps one has to perform:
- Configure the timer
- Configure PWM output of the timer
- Feed clock to the timer
- Clock up GPIOs
- Configure those as outputs
- Enable timer on particular channel
Due to TIM1 outputs on my trusty Discovery board being used by other things, I decided to go with TIM8. So, all the configuration go into tim.c:
What we are doing here is telling TIM8 to:
- count upwards from 0 until "Period" value is reached
- setting up TIM8 clock frequency to 72MHz / 7200 = 100kHz or 10us
- setting up single Period duration to 100 * 10us = 1ms. This is our PWM frequency where both high and low periods are summed
- setting up Dead Time Step period (needed later)
- feeding the timer its clock from APB2
- setting "active" period "Pulse" value to 65 * 10us = 650 us. This is the duration when output will be of OCPolarity, in our case LOW
- telling that Active is LOW, while Idle is HIGH. This can also be inverted by selecting PWM Mode2
MspPostInit() function just sets up GPIOs:
Then we need to enable the PWM by calling HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);
Now complementary will be on the same timer, but using Channel 2 (PC7 and PB0 as inverse). We'll add to the same MX function before MspPostInit() call:
Again we set up:
- PWM mode 1 - to have active while counting
- Polarities same with idle states opposite, just to see what's happening
- Dead time is inserted to prevent transistor shoot-trough in H-bridge connection. FETs tend to have large input capacitances which means that it takes time to close the gate. If one FET hasn't discharged before other one opens, we get power supply short to ground through 2 FETs. To avoid this we need to give FETs some time to close up, before opening another one. This is called Dead time. DT calculations are a bit involved, but I described it in code comments.
- Channel 2 is our PWM Channel 3, which is inverted 1 ms long with 650 us being low and 350 us being high.
- Channels 3 and 4 are our complimentary outputs. As you can see, they are HIGH ~450 us, LOW are 550 us with 50 us overlap when both are low. This overlap is our dead time.
As usual, code in GitHub. I refactored it a bit, to have features in separate branches. This will live in "pwm" branch