Project 1 More PWM
Overview
In this part, we'll take what we've already done and extend it so that we can do more mixing: and cover the entire CIE triangle colorspace with the RGB LED
Functional Abstraction

Our first step is to abstract out the actual PWM work into a function, to make it cleaner and easier to use. We will also use this opportunity to speed up the PWM frequency so that we will get less flickering and more 'resolution' to our colormixing.

00029 void delay_10us(uint8_t us)
00030 {
00031   uint8_t delay_count = F_CPU / 2000000;
00032   volatile uint8_t i;
00033 
00034   while (us != 0) {
00035     for (i=0; i != delay_count; i++);
00036     us--;
00037   }
00038 }
00039 
00040 void do_pwm_fade(uint8_t start_duty, uint8_t end_duty, uint8_t rate) {
00041   uint8_t duty;
00042   uint8_t j;
00043 
00044   duty = start_duty;
00045   while (duty != end_duty) {
00046     for (j = 0; j < rate; j++) {
00047       output_low(PORTB, RED);
00048       delay_10us(duty);
00049       output_high(PORTB, RED);
00050       delay_10us(255-duty);
00051     }
00052     if (start_duty < end_duty) {
00053       duty++;
00054     } else {
00055       duty--;
00056     }
00057   }
00058 }
00059 
00060 int main(void) {
00061   // initialize the direction of the B port to be outputs
00062   // on the 3 pins that have LEDs connected
00063   set_output(DDRB, RED);  
00064   set_output(DDRB, GREEN);
00065   set_output(DDRB, BLUE);
00066 
00067   // start with all the LEDs off
00068   output_low(PORTB, RED);
00069   output_low(PORTB, GREEN);
00070   output_low(PORTB, BLUE);
00071 
00072   // simple pulse width modulation
00073   while (1) {
00074     // go from 0% duty cycle to 100%
00075     do_pwm_fade(0, 255, 3);
00076     // now back again
00077     do_pwm_fade(255, 0, 3);
00078   }
00079 }
00080 

In this case, we have reduced the minimum timing resolution from 1ms to 10us (100 times smaller). This means we can easily extend our resolution from 25 to 255 (we could go even farther, but 255 is plenty). We have also written a function that fades from one PWM duty cycle to another, in this case pulsing an LED on and off.

Exercises
  1. Extend the function do_pwm_fade() to also take a port (which is uint16_t type) and a pin (uint8_t) so that you can easily change what LED is being faded to/from
  2. Extend the resolution from 8-bit to 16-bit
ROYGBIV

Finally we'll do what we really want, to fade smoothly from red to orange to yellow to green to blue to indigo to violet back to red.

00017 void do_pwm(uint8_t r_duty, uint8_t g_duty, uint8_t b_duty, uint8_t rate) {
00018   uint8_t i;
00019 
00020   while (rate != 0) {
00021     output_high(PORTB, RED);
00022     output_high(PORTB, GREEN);
00023     output_high(PORTB, BLUE);
00024     
00025     for (i=0; i < 255; i++) {
00026       if (i == r_duty)
00027         output_low(PORTB, RED);
00028       if (i == g_duty)
00029         output_low(PORTB, GREEN);
00030       if (i == b_duty)
00031         output_low(PORTB, BLUE);
00032     }
00033     rate--;
00034   }
00035 }
00036 
00037 int main(void) {
00038   uint8_t i;
00039 
00040   // initialize the direction of the B port to be outputs
00041   // on the 3 pins that have LEDs connected
00042   set_output(DDRB, RED);  
00043   set_output(DDRB, GREEN);
00044   set_output(DDRB, BLUE);
00045 
00046   // slowly turn red on
00047   for (i=0; i<255; i++)
00048     do_pwm(i, 0, 0, 5);
00049 
00050   while (1) {
00051     // slowly turn green on too
00052     for (i=0; i<255; i++)
00053       do_pwm(255, i, 0, 15);
00054     
00055     // now turn red off
00056     for (i=255; i>0; i--)
00057       do_pwm(i, 255, 0, 15);
00058   
00059     // slowly turn on blue
00060     for (i=0; i<255; i++)
00061       do_pwm(0, 255, i, 15);
00062 
00063     // turn off green
00064     for (i=255; i>0; i--)
00065       do_pwm(0, i, 255, 15);
00066   
00067     // turn on red
00068     for (i=0; i<255; i++)
00069       do_pwm(i, 0, 255, 15);
00070 
00071     // turn off blue
00072     for (i=255; i>0; i--)
00073       do_pwm(255, 0, i, 15);
00074   }
00075 }
00076 
We took a step back, here. Instead of having a function doing the fading, the main loop takes care of that. We only have a function to handle the actualy PWM loop, which we've extending to handle red, green and blue. We've also removed the delay call because want the code to run as fast as possible to give a clean fade.
Exercises
  1. Make do_pwm() generate your favorite color. Try also to make the purest white light you can.
  2. Try extending the resolution to more than 8 bits, or changing the speed of the fade.
  3. Try to create a full color do_pwm_fade(), that will fade from one RGB value to another. This is rather difficult because of the different rates, but it might be fun to try.
May 17, 2011 20:07