I assume you use this module.
PSM is often used because of it's higher efficiency with light loads. It's also better for AC loads, since they should get a complete AC voltage and not a DC voltage (when you cut half of the wave).
The module you have, is build to support you with implementing PSM correctly. It has a zero-crossing detection, which gives you a signal on the Z-C pin, every time the AC sine wave crosses zero voltage (which is the best time to switch). You can sense the signal with an interrupt in your Arduino. The website does not explain exactly, what is outputted by the Z-C pin on a zero crossing, but you can check this easily yourself, by monitoring both AC and Z-C voltage in operation. Most likely you will have to use either a FALLING
or a CHANGE
interrupt.
PSM works with modulating the number of pulses (in this case full sine wave periods) are transmitted to the load. Imagine you want to dim the light in 10 distinct values.
- For full power you always set the pin marked with PWM to high, when a full sine wave is starting (this is at every second zero crossing). This means the PWM pin is always high.
- For half power you turn the PWM pin high for 5 full periods and then turning it off for further 5 periods.
- For 10% (a value of 1) you turn the PWM pin high for 1 full period and then off for 9 periods.
The downside of this method is, that you might see the light flickering when having low values.
When implementing this, you can use the attachInterrupt()
function with the Z-C pin. In the callback function you toggle a global (volatile!) boolean variable, to indicate, if a new full period is starting, or if the zero crossing signal is only indicating the start of the negative part of the sine wave. Also use a global (volatile!) counter variable, that counts (increments every full period) to the value, you preset (Here something between 0 and 10). When the counter variable is 0 (and the desired value is greater than 0) you write HIGH to the PWM pin of your module. When the counter reaches the desired value, you write LOW to the PWM pin. When the counter reaches the maximum value, you have reset it to 0.
In code something like this:
volatile byte counter=0;
volatile boolean new_period = true;
volatile byte value=5;
#define MAX_VALUE 10
void setup(){
// set pinModes for the correct pins
// active interrupt. Note, that you may have to change FALLING to a value fitting the output of the module (see explanation above)
attachInterrupt(digitalPinToInterrupt(Z_C_pin), zero_crossing, FALLING);
}
void zero_crossing(){
if(new_period){
if(counter==0 && value>0) digitalWrite(PWM_pin,HIGH);
if(counter==value) digitalWrite(PWM_pin,LOW);
counter++;
if(counter > MAX_VALUE) counter=0;
}
new_period=!new_period;
}
Note: Especially when having a great range of possible values, some of the values can be exchanged by a better behavior, that would prevent a bit of the flickering. For example, when using 100 distinct dimming stages and wanting to dim to 50%, you can either (as depicted above) turn the light on for 50 periods and then turning it of for another 50 periods. Or you can turn on during every second period. Though this will not help you with really small values. So I left it out for simplicity.