Building the LED chasing effect stands out as an engaging project for many beginners and enthusiasts in the embedded systems domain or hobbyist circles. This project entails leveraging both the Arduino IDE and Atmel AVR Studio to program the AVR ATMega168 microcontroller, thereby offering an opportunity to familiarize oneself with these widely recognized Integrated Development Environments (IDEs) for project programming.
The LED Chasing Project
To undertake this project, you will require either the AVRJazz 28PIN board available from the ermicro store or an Arduino board along with eight 470 Ohm resistors, eight 3 mm LEDs, a 10K Ohm trimmer potentiometer (trimpot), and a breadboard, as depicted in the image below:
Now let’s start with the Arduino sketch first version of the LED Chasing Effect:
/***************************************************************************** // File Name : LED_Chase.pde // Version : 1.0 // Description : LED Chasing Effect Project // Author : RWB // Target : AVRJazz 28PIN Board (ATMega168) // Compiler : AVR-GCC 4.3.2; avr-libc 1.6.4 (WinAVR 20081205) // IDE : Arduino 0022 // Programmer : AVRJazz 28PIN STK500 v2.0 Bootloader // : Arduino 0022 - avrdude // Last Updated : 18 February 2012 *****************************************************************************/ byte LEDPin[]={0,1,2,3,4,5,6,7}; char LEDDirection=1; char LEDOn=0; unsigned int LEDDelay; void setup() { // Set the digital PIN (0-7) to the Output Mode for (byte count=0; count <= 7; count++) { pinMode(LEDPin[count],OUTPUT); } } void loop() { // Turn off all the digital Output (0 - 7) for (byte count=0; count <= 7; count++) { digitalWrite(LEDPin[count],LOW); } // Turn ON one LED digitalWrite(LEDPin[LEDOn],HIGH); // Changed to the next LED LEDOn += LEDDirection; // Change the direction when reach 0 or 7 if (LEDOn >= 7) LEDDirection=-1; if (LEDOn <= 0) LEDDirection=1; // LED Chasing Delay LEDDelay=analogRead(0); // Select Analog PIN 0 for the Trimpot Input delay(LEDDelay); } /* EOF: LED_Chase.pde */
Once the setup() function is invoked during program execution, the Arduino library proceeds to continuously call the loop() function. Consequently, we integrate the LED chasing effect algorithm along with analog input reading within this function.
// LED Chasing Delay LEDDelay=analogRead(0); // Select Analog PIN 0 for the Trimpot Input delay(LEDDelay);
The trimpot will function as the voltage divider circuit and supply the varying voltage input (0 to 5 volt) to the Arduino board analog input 0.
Upon compiling the LED chasing program, the resulting Intel HEX code size is approximately 1236 bytes. Here, we present a more concise version of the code in Intel HEX format, totaling 842 bytes. This version utilizes a “Non Arduino” methodology for manipulating the digital output port to achieve the LED chasing effect.
/***************************************************************************** // File Name : LED_Chase02.pde // Version : 2.0 // Description : LED Chasing Effect Project // Author : RWB // Target : AVRJazz 28PIN Board (ATMega168) // Compiler : AVR-GCC 4.3.2; avr-libc 1.6.4 (WinAVR 20081205) // IDE : Arduino 0022 // Programmer : AVRJazz 28PIN STK500 v2.0 Bootloader // : Arduino 0022 - avrdude // Last Updated : 18 February 2012 *****************************************************************************/ byte LEDDirection; byte LEDOn; unsigned int LEDDelay;
void setup() {
DDRD = 0xFF; // Set PORT D as Output (digital 0 to 7)
PORTD= 0x00; // Reset PORT D (digital 0 to 7)
LEDOn= 0x01; // Initial LED Chaser
LEDDirection=0;
}
void loop() {
// Turn ON one LED
PORTD=LEDOn;
// Change the direction when reach 0 or 7
if (LEDDirection == 0) {
LEDOn=LEDOn << 1; // Shift Left 1 Bit
if (LEDOn >= 0x80) LEDDirection=1;
} else {
LEDOn=LEDOn >> 1; // Shift Right 1 Bit
if (LEDOn <= 0x01) LEDDirection=0;
}
// LED Chasing Delay
LEDDelay=analogRead(0); // Select Analog PIN 0 for the Trimpot Input
delay(LEDDelay);
}
/* EOF: LED_Chase02.pde */
Utilizing direct port manipulation offers the advantage of achieving a more compact code compared to the initial version. Within the setup() function, the DDRD register of the AVR ATMega168/328 microcontroller is employed to configure the I/O mode (digital pins 0 to 7) of the PORTD.
DDRD = 0xFF; // Set PORT D as Output (digital 0 to 7) PORTD= 0x00; // Reset PORT D (digital 0 to 7)
The hexadecimal notation 0xFF corresponds to the decimal value of 255, or “1111 1111” in binary format. When we assign the binary value “1111 1111” to the DDRD register, we are directing the AVR ATMega168/328 microcontroller to set the PORTD mode to output mode.
In this iteration, we employ the algorithm utilizing the “shift left” and “shift right” bit manipulation operators to replicate the LED chasing effect demonstrated in the initial version. The functionality of the bit manipulation shift operator involves shifting one bit left or right sequentially, as illustrated in the accompanying image below:
By incrementing or decrementing one bit at a time (either left or right), we can achieve the desired LED chasing effect instead of simply turning off all LEDs and turning on a single LED as demonstrated in the initial code. The subsequent C code illustrates the variances between these two algorithms:
The first version:
// Turn off all the digital Output (0 - 7) for (byte count=0; count <= 7; count++) { digitalWrite(LEDPin[count],LOW); } // Turn ON one LED digitalWrite(LEDPin[LEDOn],HIGH); // Changed to the next LED LEDOn += LEDDirection;
The second version:
// Turn ON one LED PORTD=LEDOn;
// Change the direction when reach 0 or 7
if (LEDDirection == 0) {
LEDOn=LEDOn << 1; // Shift Left 1 Bit
if (LEDOn >= 0x80) LEDDirection=1;
} else {
LEDOn=LEDOn >> 1; // Shift Right 1 Bit
if (LEDOn <= 0x01) LEDDirection=0;
}
Consequently, employing the second algorithm yields streamlined code and significantly reduces the size of the Intel HEX file, making it easier to load into the AVR microcontroller’s flash RAM.
The AVR Studio IDE
In the final example, we will employ a comprehensive C code within Atmel AVR Studio to execute this LED chasing project. I have adopted the “arduino style” approach, utilizing functions such as setup(), loop(), and analogRead() to enhance comprehension. The entire program for the LED chasing project is depicted in the following C code snippet:
//*************************************************************************** // File Name : LED_Chase03.c // Version : 3.0 // Description : ACRJazz 28PIN AVR ATMega168 Demo Program // Author : RWB // Target : AVRJazz 28PIN Board (ATMega168) // Compiler : AVR-GCC 4.5.1; avr-libc 1.7.1 (AVR Studio 5 ToolChain) // IDE : Atmel AVR Studio 5.0.1223 // Programmer : AVRJazz Mega168 STK500 v2.0 Bootloader // : AVR Visual Studio 5.0.1223, STK500 programmer // Last Updated : 18 February 2012 //*************************************************************************** #define F_CPU 16000000UL // AVRJazz 28PIN Used 16MHz #include <avr/io.h> #include <util/delay.h> // Define byte to unsigned char typedef unsigned char byte; // Delay Function Implementation for avr-libc 1.7.1 void delay(unsigned int __ms) { while(__ms--) { _delay_us(450); // 2 x 450us for emulating 1 ms delay _delay_us(450); } } // Analog Read Function Implementation unsigned int analogRead(unsigned char __anPort) { // Disable digital input on __anPort DIDR0 |= (1 << __anPort); // Select ADC Channel using ADMUX Register on ATMega168 // Use internal Voltage Reference (REFS1=0 and REFS0=0) // ALAR bit is 0 for Right Justified Result ADMUX=__anPort; // Start conversion by setting the ADSC bit on ADCSRA Register ADCSRA |= (1 << ADSC); // Wait until convention complete ADSC=0 -> Complete while (ADCSRA & (1 << ADSC)); return ADCW; // Return the ADC Value } // Start the C Main Function void main(){ setup(); // Call the setup() function // Enable the ADC Peripheral using ADCSRA Register on AVR ATMega168 // ADC Prescaler: 128 (ADPS2=1, ADPS1=1, and ADPS0=1) ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); ADCSRB = 0x00; // Free Running Mode ADC for(;;) { loop(); // Call the loop() function } return 0; } // Start the LED_Chase02.pde program on Arduino IDE byte char LEDDirection; byte char LEDOn; unsigned int LEDDelay; void setup() { DDRD = 0xFF; // Set PORT D as Output (digital 0 to 7) PORTD= 0x00; // Reset PORT D (digital 0 to 7) LEDOn= 0x01; // Initial LED Chaser LEDDirection=0; } void loop(){ // Turn ON one LED PORTD=LEDOn; // Change the direction when reach 0 or 7 if (LEDDirection == 0) { LEDOn=LEDOn << 1; // Shift Left 1 Bit if (LEDOn >= 0x80) LEDDirection=1; } else { LEDOn=LEDOn >> 1; // Shift Right 1 Bit if (LEDOn <= 0x01) LEDDirection=0; } // LED Chasing Delay LEDDelay=analogRead(0); // Select Analog PIN 0 for the Trimpot Input delay(LEDDelay); } /* EOF: LED_Chase03.c */
In the provided LED_Chase03.c program listing, it’s evident that the C code within the setup() and loop() functions bears resemblance to the LED_Chase02.pde in the Arduino IDE. In the AVR Studio’s C code version (utilizing version 5), we include essential elements such as headers, definitions, typedefs, and functions to ensure the proper functionality of the setup() and loop() functions.
Utilizing a complete C code within Atmel AVR Studio allowed us to notably diminish the HEX size to 346 bytes in contrast to the HEX code generated by the Arduino library in our subsequent iteration of the LED chasing program. For comprehensive insights into utilizing the analog peripheral on the Atmel AVR microcontroller, I recommend referring to the article titled “Analog to Digital Converter AVR C Programming.”
I trust this modest project will illuminate your understanding of embedded systems and inspire further exploration into the realm of embedded systems.