Exploring STC 8051 Microcontrollers – Coding

Using Multi Channel Enhanced PWM to Drive an RGB LED

Since we now have seen how to use single channel enhanced PWM, we must also see how to use multichannel PWM. Multi-channel PWM has many applications. For example, multi-channel PWM is used in SMPSs, inverters, motor controllers, LED drivers, etc.

Code

 #include "STC8xxx.h"
#include "BSP.h"
 
#define steps 33
 
void setup(void);
void PWM_idle(void);
 
void main(void)

  signed char i = 0x00;
  signed char j = 0x00;
 
  const unsigned int duty1_value[steps] = {0, 950, 1892, 2817, 3717, 4582, 5407, 6182, 6901, 7558, 8146,
                                           8660, 9096, 9450, 9718, 9898, 9988, 9995, 9897, 9717, 9449, 9095,
                                           8658, 8144, 7555, 6898, 6179, 5403, 4579, 3713, 2813, 1888 ,946};
 
  const unsigned int duty2_value[steps] = {8658, 8144, 7555, 6898, 6179, 5403, 4579, 3713, 2813, 1888 ,946,
                                           0, 950, 1892, 2817, 3717, 4582, 5407, 6182, 6901, 7558, 8146,
                                           8660, 9096, 9450, 9718, 9898, 9988, 9995, 9897, 9717, 9449, 9095};
 
  const unsigned int duty3_value[steps] = {8660, 9096, 9450, 9718, 9898, 9988, 9995, 9897, 9717, 9449, 9095,
                                           8658, 8144, 7555, 6898, 6179, 5403, 4579, 3713, 2813, 1888 ,946,
                                           0, 950, 1892, 2817, 3717, 4582, 5407, 6182, 6901, 7558, 8146};
  
  setup();
  
  while(1)
  {
    for(j = 0; j < 6; j++)
    {
      for(i = 0; i < steps; i++)
      {
          PWM_set_PWM0_T2(duty1_value[i]);
          PWM_set_PWM1_T2(duty2_value[i]);
          PWM_set_PWM2_T2(duty3_value[i]);
          delay_ms(60);
      }
    }
    
    PWM_idle();
    
    for(j = 0; j < 6; j++)
    {
      for(i = 0; i < steps; i++)
      {
          PWM_set_PWM0_T2(duty1_value[i]);
          PWM_set_PWM1_T2(duty1_value[i]);
          PWM_set_PWM2_T2(duty1_value[i]);
          delay_ms(60);
      }
    }
    
    PWM_idle();
  };
}
 
void setup(void)
{
  CLK_set_sys_clk(IRC_24M, 2, MCLK_SYSCLK_no_output, MCLK_out_P54);
 
  PWM_clk_set(PWM_clk_sys_PS, PWM_clk_ps_sys_clk_div_2);
  
  PWM_set_counter(10000);
  
  PWM0_setup(PWM_pin_is_PWM_output, \
             PWM_init_lvl_low, \
             PWM_0_pin_P10, \
             PWM_level_normal);
  
  PWM1_setup(PWM_pin_is_PWM_output, \
             PWM_init_lvl_low, \
             PWM_1_pin_P11, \
             PWM_level_normal);
             
  PWM2_setup(PWM_pin_is_PWM_output, \
             PWM_init_lvl_low, \
             PWM_2_pin_P12, \
             PWM_level_normal);
             
  PWM_set_PWM0_T1(0);
  PWM_set_PWM0_T2(0);
  
  PWM_set_PWM1_T1(0);
  PWM_set_PWM1_T2(0);
  
  PWM_set_PWM2_T1(0);
  PWM_set_PWM2_T2(0);
  
  
  PWM_start_counter;
}
 
void PWM_idle(void)
{
  PWM0_hold_level(PWM_HLD_L_low);
  PWM1_hold_level(PWM_HLD_L_low);
  PWM2_hold_level(PWM_HLD_L_low); 
  delay_ms(100);
  PWM0_hold_level(PWM_level_normal);
  PWM1_hold_level(PWM_level_normal);
  PWM2_hold_level(PWM_level_normal);  
  delay_ms(100);
}

Schematic

Explanation

In this demo, a RGB LED is lit with multi-channel PWM. This results in smooth color shifting of the LED. The concepts are same as that of single channel PWM example.

The system clock is set to 12MHz.

 CLK_set_sys_clk(IRC_24M, 2, MCLK_SYSCLK_no_output, MCLK_out_P54); 

The PWM frequency is set to 600 Hz.

Since we are using a RGB LED, we need three PWM channels and so their GPIOs are also set. Lastly, the initial duty cycle is set 0 and the PWM counter is started.

 PWM_clk_set(PWM_clk_sys_PS, PWM_clk_ps_sys_clk_div_2);
  
PWM_set_counter(10000);
  
PWM0_setup(PWM_pin_is_PWM_output, \
           PWM_init_lvl_low, \
           PWM_0_pin_P10, \
           PWM_level_normal);
  
PWM1_setup(PWM_pin_is_PWM_output, \
           PWM_init_lvl_low, \
           PWM_1_pin_P11, \
           PWM_level_normal);
             
PWM2_setup(PWM_pin_is_PWM_output, \
           PWM_init_lvl_low, \
           PWM_2_pin_P12, \
           PWM_level_normal);
             
PWM_set_PWM0_T1(0);
PWM_set_PWM0_T2(0);  
PWM_set_PWM1_T1(0);
PWM_set_PWM1_T2(0);
PWM_set_PWM2_T1(0);
PWM_set_PWM2_T2(0);
    
PWM_start_counter;

To achieve smooth fading the duty cycle of each LED is mapped using a sine table and the duty cycles are varied according to the formula as have already seen.

However, there are phase shifts in all of these tables, meaning that the three PWM channels do not have the same duty cycle at the same time.

const unsigned int duty1_value[steps] = {0, 950, 1892, 2817, 3717, 4582, 5407, 6182, 6901, 7558, 8146,
                                         8660, 9096, 9450, 9718, 9898, 9988, 9995, 9897, 9717, 9449, 9095,
                                         8658, 8144, 7555, 6898, 6179, 5403, 4579, 3713, 2813, 1888 ,946};
 
const unsigned int duty2_value[steps] = {8658, 8144, 7555, 6898, 6179, 5403, 4579, 3713, 2813, 1888 ,946,
                                         0, 950, 1892, 2817, 3717, 4582, 5407, 6182, 6901, 7558, 8146,
                                         8660, 9096, 9450, 9718, 9898, 9988, 9995, 9897, 9717, 9449, 9095};
 
const unsigned int duty3_value[steps] = {8660, 9096, 9450, 9718, 9898, 9988, 9995, 9897, 9717, 9449, 9095,
                                         8658, 8144, 7555, 6898, 6179, 5403, 4579, 3713, 2813, 1888 ,946,
                                         0, 950, 1892, 2817, 3717, 4582, 5407, 6182, 6901, 7558, 8146};

In the main, the PWM channels are driven as per their respective sine tables.

 for(j = 0; j < 6; j++)
{
for(i = 0; i < steps; i++)
  {
      PWM_set_PWM0_T2(duty1_value[i]);
      PWM_set_PWM1_T2(duty2_value[i]);
      PWM_set_PWM2_T2(duty3_value[i]);
      delay_ms(60);
  }
}
    
PWM_idle();
    
for(j = 0; j < 6; j++)
{
  for(i = 0; i < steps; i++)
  {
     PWM_set_PWM0_T2(duty1_value[i]);
     PWM_set_PWM1_T2(duty1_value[i]);
     PWM_set_PWM2_T2(duty1_value[i]);
     delay_ms(60);
  }
}
    
PWM_idle();

Note there is new function in this example unlike the last one. The PWM_idle function ensures that PWM channels are reset once it is called.

 void PWM_idle(void)
{
  PWM0_hold_level(PWM_HLD_L_low);
  PWM1_hold_level(PWM_HLD_L_low);
  PWM2_hold_level(PWM_HLD_L_low); 
  delay_ms(100);
  PWM0_hold_level(PWM_level_normal);
  PWM1_hold_level(PWM_level_normal);
  PWM2_hold_level(PWM_level_normal);  
  delay_ms(100);
}

Demo

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

Related Posts

13 comments

Leave a Reply

Your email address will not be published. Required fields are marked *