Professional Documents
Culture Documents
Introduction
Details
o
10-bit PWM
bit10PWM
Scientific Applications
o
Pupillometry
Silent substitution
Commercial products
Introduction
Here a simple introduction to LED intensity control using pulse-width modulation (PWM) with Arduino is
presented. For more detailed discussion, following sites are recommended: Secrets of Arduino PWM by
Ken Shirriff, Adjusting PWM Frequencies in Arduino, ATmega328 (datasheet) of Arduino Uno, and PWM
(Wikipedia)
Demonstration of LED intensity control with PWM-signal from Arduino Uno using graphical Python frontend.
Details
8-bit PWM (default)
To demonstrate the simplest case of LED light control needed in the pupillometry application described in
the accompanying paper (DOI). The Arduino sketch arduinoLED-simpleGUI.ino was developed based on
the code provided by John Meichle for controlling RGB LEDs, as a part of his neuroelec library for
Arduino.
Screen capture of the Geany view of the code with the GUI for 8-bit LED Control
Main the loop() is given below:
void loop() {
if(Serial.available() >= 2){
// The cases are given from the .py file (or some other frontend)
switch( byte( Serial.read() )) {
case 'r':
ledOut_ch1 = Serial.read();
break;
case 'g':
ledOut_ch2
break;
case 'b':
ledOut_ch3
break;
case 'w':
ledOut_ch4
break;
case 'v':
ledOut_ch5
case 'y':
ledOut_ch6
}
= Serial.read();
= Serial.read();
= Serial.read();
= Serial.read();
= Serial.read();
}
// write the PWM values now to all the channels
analogWrite(ledOut_ch1Pin, ledOut_ch1);
analogWrite(ledOut_ch2Pin, ledOut_ch2);
analogWrite(ledOut_ch3Pin, ledOut_ch3);
analogWrite(ledOut_ch4Pin, ledOut_ch4);
analogWrite(ledOut_ch5Pin, ledOut_ch5);
analogWrite(ledOut_ch6Pin, ledOut_ch6);
// delay in each loop, in milliseconds
delay(20); // going too low maybe lead to unstable behavior
// default value 20 ms worked well
}
Where if(Serial.available() >= 2){ checks whether serial commands are sent to Arduino, and if it has
been sent then the program advances the switch conditional determining which channel of the 6 LED
channels the user wanted to control.
In the sending end of Python code, the code (GuiArduinoLED.py) looks like the following for one channel:
if name == "Channel1":
self.ser.write("r" + chr(int(val)))
where the first character "r" specifies the desired controlled channel (ledOut_ch1 variable in above
Arduino sketch), to which is added (+) the desired value for the channel. The variable val is first
converted to integer and then to character (8 bits).
In the Arduino sketch all channels are now updated either with the updated value or the existing value
independent of whether there was any change. The latency of 20 ms (with delay function) is added for
more stable behavior of the loop.
A simple example for Matlab is provided to control the light intensity using the MATLAB Support Package
for Arduino (aka ArduinoIO Package)written by Giampiero Campa.
Change the following line (line 42) of the GuiArduinoLED.m to match the serial port of your Arduino,
typically like 'COM2' in Windows and '/dev/ttyACM0' in Linux. Also remember to upload the
file adiosrv.pde sketch to your Arduino from the ArduinoIO package to make the demo work.
% initialize Arduino
port = '/dev/tty.usbmodemfa131';
Screencap of controlling LED intensity (1-channel example) with Arduino from LED
Demonstration of LED intensity control with PWM-signal from Arduino Uno using graphical LabVIEW VI
front-end.
You need to upload the Arduino sketch LVIFA_base.pde in order to make Arduino responsive to
commands sent from LabVIEW:
The block diagram of the Virtual Instrument ("LabVIEW program") looks the following:
VI Block diagram for 8-bit LED intensity control with PWM for 2-channels
And the front panel (GUI) as the following:
VI Front panel. The knob buttons control the intensity (0 to 255, 8 bit resolution) and from below them one
can choose the pins to which the LEDs are connected. Debug loop count is useful when testing your
Arduino and making sure that there is nothing slowing down the program and while loop is actually being
executed.
10-bit PWM
The default 8-bit resolution of Arduino PWM can be extended for limited amount of pins using for example
the 3rd party timer1 library developed by Jesse Tane. Before being able to upload the accompanying
sketch bit10PWM.ino to Arduino board, the timer1 need to be added for your system folder. For
instructions how to do it, see Arduino Playground's tutorial. Alternatively you can just place the external
library files to the same folder as your Arduino sketch (see how).
bit10PWM
The use of a library instead of "brute-force" low-level implementation simplifies the Arduino sketch
(bit10PWM.ino) slightly as seen in the following segment:
void setup()
{
// Set the baud rate
Serial.begin(57600);
Now the library can be called with more 'human-readable' calls such as Timer1.pwm() with two input
parameters pinOut specifying to which pin you have connected the anode of your LED, and
`led_dutyCycle_Out' describing the wanted output intensity as 10-bit duty cycle (0 is 0% of the time ON,
and 1023 is 100% of the time ON).
The case handling of which LED channel want to be controlled from the Python GUI is the same as for
the above described 8-bit PWM, but now special treatment is required for the value sent from Python as it
is 10-bit and single characters are always 8-bit ones in serial communication.
The sending Python code (bit10PWM_demo.py) looks like the following:
def on_changed(self, widget):
val = widget.get_value()
name = widget.get_name()
c = int(val) >> 8 # The value of x shifted 8 bits to the right, creating coarse.
f = int(val) % 256 # The remainder of x / 256, creating fine.
print(c)
print(f)
if name == "Channel1":
self.ser.write("c" + chr(int(c)) + chr(int(f)))
else:
print "ERROR: Invalid widget name, in on_changed function"
Where the 10-bit value is divided to coarse and fine part of the integer (see for further details about
splitting 16-bit integer into two 8-bit integers). And now three characters ( "c", chr(int(c)), chr(int(f))) are
sent to Arduino, the first indicating the case, the second the coarse part, and the third part the fine part.
The receiving end in the Arduino sketch (bit10PWM.ino) looks like this then:
// The cases are given from the .py file (or some other frontend)
switch( byte( Serial.read() )) {
case 'r':
// now the intensity is encoded as two 8 bit integers
// so we need to recombine
// read the 8 bit ones
int highByte = Serial.read();
int lowByte = Serial.read();
// combine
// e.g.
unsigned int led_dutyCycle_Out = highByte * 256 + lowByte;
Serial.println(highByte);
Serial.println(lowByte);
Serial.println(led_dutyCycle_Out);
break;
}
// Output the PWM
Timer1.setPwmDuty(pinOut, led_dutyCycle_Out); // setup pwm on pin X
Now the highByte is used for the coarse value, and lowByte for the fine value to introduce common
terminology for novice users (the variable names are also Arduino functions highByte() and lowByte()).
Screen capture of the Geany view of the code with the GUI for LED Control. Notice that now for each
slider value two bytes are sent for Arduino (coarse and fine bytes)
Now the PWM is updated inside the loop every time the user wants to change the LED intensity using the
library call .setPwmDuty.