How to dim LEDs using the Arduino and VEX

From ROBOTC API Guide
< Tutorials‎ | Arduino Projects/Mobile Robotics/VEX
Revision as of 13:50, 27 July 2012 by Dwest (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
ArduinoArduino Tutorials and Guided ProjectsVEX + Arduino, Mobile Robotics Platform → How to dim LEDs using the Arduino and VEX

How to Adjust the Brightness

While being able to turn LEDs on and off is great, there are times when it might be desirable to have various levels of brightness. This could be achieved using multiple pins and various different levels of resistors to adjust the current and voltage flowing through the LED. However, this method requires additional components and costs more money, not to mention that it clutters your breadboard. Fortunately there is a better way of changing the current going to the LED, known as Pulse-Width Modulation, or PWM for short.

What is PWM?

PWM works by rapidly turning the circuit on and off. By adjusting the ratio of the time on and the time off, it is possible to adjust the brightness of the LED. This ratio is called the Duty-Cycle and is the percentage of time the output is high with respect to the time of one cycle (when the output goes high then low then back to high). If the Duty-Cycle is 100% then the output is high all the time and thus the LED would be a full brightness. If the Duty-Cycle is 0%, the output is off all the time, so the LED would just be off. Now if the Duty-Cycle is set to 50%, the output is on 50% of the time and off the other 50%. In this setup, the LED would appear be at 50% of its full brightness and thus appear to be dimmed. PWM has several other interesting properties that will be discussed in later sections.

PWM on a Microcontroller

Due to the high frequency that PWM operates at, getting an accurate signal using code would basically prevent you from doing anything else with the chip. Fortunately, chip manufacturers included hardware to generate the PWM signal for you. However, not every I/O pin supports PWM output. For the Arduino UNO, only digital pins 3, 5, 6, 9, 10, and 11, support it. This is shown on the Arduino itself with a ~ before the pin number. ROBOTC also has a tab in the Motors and Sensors Setup window that lists the pins that can be used. In addition, it handles all of the complex options that need to be set for the hardware PWM to work.

Programming

In this code, we want the LEDs to dim in and out over a period of 10 seconds.

Configure ROBOTC

Again, we need to start with a new source code file and tell ROBOTC which Arduino board we are using. Then we need to configure the pins by going into the Motors and Sensors Setup. Up to this point everything is the same as for the flashing LED configurations. In order to be able to set PWM output we need to configure pins 5 and 6 each as a Variable Intensity LED. To do this we go into the Motors tab, not the Digital 0-13 tab. We want to configure motor_5 and motor_6, which map to digital pins 5 and 6 respectively. Give motor_5 the name "led1" and motor_6 the name "led2". Now set the type of both to Variable Intensity LED and click OK.

Notepad.gif NOTE: At the time of writing, this version of the RobotC Arduino firmware does not work with "Variable Intensity LED". It is possible to make the circuit work by configuring the pin as "Ext. HBridge - Single PWM" until the bug is fixed.


Pins 5 and 6 configured for LED PWM

The file should have the following code at the top.

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Motor,  motor_5,         led1,          tmotorVariableIntensityLED, openLoop, reversed, IOPins, dgtl5, None)
#pragma config(Motor,  motor_6,         led2,          tmotorVariableIntensityLED, openLoop, reversed, IOPins, dgtl6, None)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

Programming task main()

First, add task main() to the code. After that, we will program the desired behaviors.

To tell the Arduino what Duty-Cycle to output to led1, for example, we would use this code:

motor[led1] = level;

where level is intensity level from -127 to 127, with 0 mapping to 0% and both -127 and 127 mapping to 100%.

To make the LED dim in and out, we will have the code slowly step the values up and down over time. For demonstration purposes we will increase or decrease the Duty-Cycle by 10% (level value: 12.7) every 500ms. Unfortunately, the motor command used for this only accepts whole numbers. Since we are hard coding the values, this is not a problem, as we can round by hand. So after doing the math, we get the following values:

Time (ms) led1 value led2 value
0 127 0
500 114 13
1000 102 25
1500 89 38
2000 76 51
2500 64 64
3000 51 76
3500 38 89
4000 25 102
4500 13 114
5000 0 127
5500 13 114
6000 25 102
6500 38 89
7000 51 76
7500 64 64
8000 76 51
8500 89 38
9000 102 25
9500 114 13

Combining the times and values with the commands to set the outputs and the pauses, you get

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Motor,  motor_5,         led1,          tmotorVariableIntensityLED, openLoop, reversed, IOPins, dgtl5, None)
#pragma config(Motor,  motor_6,         led2,          tmotorVariableIntensityLED, openLoop, reversed, IOPins, dgtl6, None)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//
 
task main()
{
  while(true) //repeat indefinitely
  {
    motor[led1] = 127;
    motor[led2] = 0;
    wait1Msec(500);
 
    motor[led1] = 114;
    motor[led2] = 13;
    wait1Msec(500);
 
    motor[led1] = 102;
    motor[led2] = 25;
    wait1Msec(500);
 
    motor[led1] = 89;
    motor[led2] = 38;
    wait1Msec(500);
 
    motor[led1] = 76;
    motor[led2] = 51;
    wait1Msec(500);
 
    motor[led1] = 64;
    motor[led2] = 64;
    wait1Msec(500);
 
    motor[led1] = 51;
    motor[led2] = 76;
    wait1Msec(500);
 
    motor[led1] = 38;
    motor[led2] = 89;
    wait1Msec(500);
 
    motor[led1] = 25;
    motor[led2] = 102;
    wait1Msec(500);
 
    motor[led1] = 13;
    motor[led2] = 114;
    wait1Msec(500);
 
    motor[led1] = 0;
    motor[led2] = 127;
    wait1Msec(500);
 
    motor[led1] = 13;
    motor[led2] = 114;
    wait1Msec(500);
 
    motor[led1] = 25;
    motor[led2] = 102;
    wait1Msec(500);
 
    motor[led1] = 38;
    motor[led2] = 89;
    wait1Msec(500);
 
    motor[led1] = 51;
    motor[led2] = 76;
    wait1Msec(500);
 
    motor[led1] = 64;
    motor[led2] = 64;
    wait1Msec(500);
 
    motor[led1] = 76;
    motor[led2] = 51;
    wait1Msec(500);
 
    motor[led1] = 89;
    motor[led2] = 38;
    wait1Msec(500);
 
    motor[led1] = 102;
    motor[led2] = 25;
    wait1Msec(500);
 
    motor[led1] = 114;
    motor[led2] = 13;
    wait1Msec(500);
  }
}

Incrementing using a For Loop

It might seem a bit silly to write that much code just to increment LED brightness. Fortunately, there is a way to greatly simplify it, through the use of a for() loop.

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Motor,  motor_5,         led1,          tmotorVariableIntensityLED, openLoop, reversed, IOPins, dgtl5, None)
#pragma config(Motor,  motor_6,         led2,          tmotorVariableIntensityLED, openLoop, reversed, IOPins, dgtl6, None)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//
 
task main()
{
  while(true) //repeat indefinitely
  {
    for(int i=0; i < 10; i++) {
      motor[led1] = 127 - (127 * i / 10);
      motor[led2] = (127 * i / 10);
      wait1Msec(500);
    }
 
    for(int i=0; i < 10; i++) {
      motor[led1] = (127 * i / 10);
      motor[led2] = 127 - (127 * i / 10);
      wait1Msec(500);
    }
  }
}

Notepad.gif TODO: explain what a variable is.

The for() loop works by defining a variable to store a number, which in this case is "i". The for() loop sets the variable to a starting value, in this case 0 ("int i=0") and will then check that the value of the variable meets the requirements set after the first semi-colon. For this loop, "i < 10" is the requirement (the value of i is less than 10). If the requirements are met, then the code inside the curly brackets will execute. Once that is completed, the for loop looks at the code after the second semi-colon in the parentheses. This code section tells the for loop what to do with the variable between each execution. In this case, it is telling the loop to increment the value of i by 1 ("i++", "++" is a c command to increment a variable by +1).


You will notice that there is what looks like an equation after the equals sign for the motor command. This particular equation is used to calculate the value for the led at each stage of the loop.

ROBOTC follows regular order of operations when doing math, which means that the code equation is almost identical to a normal math equation.

ROBOTC equation Math equation
127 - (127 * i / 10) 127-(127×i/10)
(127 * i / 10) (127×i/10)

where i is the value of the variable i at the time of the evaluation. To help you understand, here is a table of the values of i and the LED intensities.

Loop run number i value before loop i value after loop will execute code led1 value led2 value
0 0 1 Yes 127 0
1 1 2 Yes 114 13
2 2 3 Yes 102 25
3 3 4 Yes 89 38
4 4 5 Yes 76 51
5 5 6 Yes 64 64
6 6 7 Yes 51 76
7 7 8 Yes 38 89
8 8 9 Yes 25 102
9 9 10 Yes 13 114
10 10 N/A No N/A N/A