Sourcecode

Here the briefly explanation for the source code. Project description can be found under Documents section.

Download source code here ---> Hex file:


 * I. Block diagram **

We use a jumper to switch between 2 operation mode: Accelerometer and Remote Control. Jumper is connected to PD.0. When jumper is open, PD.0 is pull-up to VCC. When jumper is close, PD.0 is grounded.
 * II. Programming **
 * 1. Switch**

Define modes: code format="cpp" code In main loop, we will check the value of PD0 to know which mode is in use: code format="cpp" pmode = 2;   //previous mode, 2 is unsed
 * 1) define MODE_ACCLER   1        //Accelerometer mode
 * 2) define MODE_REMOTE   0        //Remote mode

while(1) {   mode = (PIND & 0x01);    //read value at PD0 if (mode != pmode)   //update previous mode if mode changed {       pmode = mode; setup_mode(mode); } } code To initialize a mode when mode change: code format="c" void setup_mode(uint k) { if (k==MODE_REMOTE)    //remote {

//TO DO: //disable accelerometer //enable IR receiver

}   else     //accelerometer {

//TO DO: //disable IR receiver //enable accelerometer

} } code

The accelerometer is used to detect the motion of the board and base on that will change the color of the LEDs appropriately. The accelerometer we are using is MMA7260Q, which is a 3-axis, analog output accelerometer. However in this project we only need to use 2 axises X and Y (Z is unused). Output X and Y will be connected to PC.0 (ADC0) and PC.1 (ADC1) correspondingly.
 * 2. Accelerometer**

The first step is to configure ADC hardware by setting some bits in control registers for ADC. code format="c" ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescaler to 128 code code format="c" ADMUX |= (1 << ADLAR);    // Left adjust ADC result to allow easy 8 bit reading code code format="c" void init_ADC(void) {   ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescaler to 128 ADMUX |= (1 << ADLAR);    // Left adjust ADC result to allow easy 8 bit reading ADCSRA |= (1 << ADATE); // Enable auto trigger
 * Set the prescalar for the ADC: The ADC clock is derived from the system clock, and need to be set to 50 KHz to 200 KHz. We are using 16 MHz crystal, so a prescaler of 128 will result in an ADC frequency of 125 Khz. The prescaling is set by the ADPS bits in the ADCSRA register.
 * The ADC has 10-bit resolution, stored in 2 8-bit registers, ADCH and ADCL. By default, the lowest 8 bits of the ADC value are found in ADCL, with the upper two being the lowest two bits of ADCH. By setting the ADLAR bit in the ADMUX register, we can left align the ADC value. This puts the highest 8 bits of the measurement in the ADCH register, with the rest in the ADCL register. If we then read the ADCH register, we get an 8 bit value that represents our 0 to 5 volt measurement as a number from 0 to 255.
 * Reference voltage is AREF connected to VCC by default, so nothing to change.
 * Then we need to enable ADC and start conversion. The whole code for initializing ADC:

DIDR0 |= (0x3F);    //turn off digital buffer

ADCSRA |= (1 << ADEN); // Enable ADC

ADCSRA |= (1 << ADIE); // Enable ADC Interrupt ADCSRA |= (1 << ADSC); // Start A2D Conversions } code code format="c" ISR(ADC_vect) {    //only use 8 bit, hence take ADCH
 * Handling the interrupt when a conversion is finished:

if (ADCH < 65) set_color(0, 0, 127); else if (ADCH < 100) set_color(0, 127, 0); else set_color(127, 0, 0);

} code
 * The ADC conversion is put in free-running mode. Hence as soon as it finish a conversion and the interrupt handler handle it, ADC will continue to convert another value.

This is the second mode of operation in which we can use a remote control to control the colors/pattern/brightness/speed... of the LEDs. The remote we use is an remote for a CD player which doesn't have much buttons (a TV remote may have 50 buttons or more) We actually don't know with type of encoding is used for this remote. However we use another way to sample the waveform for each button. An **IR emitter LED** (emitter, NOT receiver) is plugged to the microphone input on the computer (connect each pin of the LED to each wire of the 3.5mm audio jack and plug the jack to the microphone input). The IR emitter LED can be used as the receiver. A software is used on the computer to record “sound” (Audacity is used in this test). Aim the remote to the IR emitter LED, start the software to record and press a button, we see something like this
 * 3. IR receiver**

And here the algorithm to decode this: code format="c" //define remote code code code format="c" void init_timer1(void) {   //timer1 is operating in Input Capture mode TCCR1B |= (1<<CS11);   // prescaler = 8 (2MHz) TCCR1B |= (1<<ICES1);   // rising edge detect on ICP //TIMSK1 |= (1<<ICIE1);   // enable interrupt on ICP } code code format="c" ISR(TIMER1_CAPT_vect) {     static uint last; static uint pulse_count; static uint data; uint pulse_width;
 * Capture positive edge and measure time delay between 2 successive pos edges.
 * If the time delay is greater than a threshold, we consider that a 1, else that a 0
 * However if it take more than some time, say 0.5s, to receive next pos edge, that would be considered as "lost signal"
 * Source code:**
 * Define all codes for buttons (Code for each button is attached here --> [[file:remote_code.docx]])
 * 1) define POWER       8415
 * 2) define FUNCTION   1530
 * 3) define DISPLAY       55335
 * 4) define TIMER       53295
 * 5) define SLEEP       51255
 * 6) define INTRO       20655
 * 7) define STOP       36975
 * 8) define RANDOM       18615
 * 9) define TRACK_L       39015
 * 10) define PLAY       32895
 * 11) define TRACK_R       34935
 * 12) define BBS       10455
 * 13) define REPEAT       16575
 * 14) define PROG       22695
 * 15) define DOWN       63495
 * 16) define UP       59415
 * 17) define MUTE       2040
 * Initialize timer 1 to operate in Input Capture mode to capture rising edge
 * Handling interrupt when receiving a rising edge. In this interrupt handler, we use **static** variables so that we can store the previous values (those variables won't be cleared when return from this subroutine)

pulse_width = ICR1 - last; last = ICR1;

if (pulse_width > TIME_OUT) {       pulse_count = 0; data = 0; }   else {       data <<= 1;

if (pulse_width > LONG_PULSE) data |= 0x01; //add bit 1 pulse_count++;

if (pulse_count == 32) {           key = data; switch (key) {               //what you want to do for each key??? }       }    } } code

The LED we use is RGB LED which can be controlled by PWM on each pin to have desired color. Timer 2 is used for PWM.
 * 4. PWM**
 * Every time the 16-bit counter of timer 2 is overflow, increase ticker by 1
 * If ticker = 255 then reset ticker
 * if ticker < duty[i] turn LED on, else turn OFF where duty is a number in range 0..255


 * RED --> PD2, duty[0]
 * GREEN --> PD3, duty[1]
 * BLUE --> PD4, duty[2]

Initialize timer 2 code format="c" /************************************************************               TIMER2: PWM void init_timer2(void) {   TCCR2B = (1<<CS20);     //run at full speed 16MHz, no prescaler TIMSK2 |= (1<<TOIE2);   //enable interrupt }

code Handling counter overflow interrupt code format="c" ISR(TIMER2_OVF_vect) {   uint i;

ticker++; if (ticker==255) ticker = 0;

for (i=0; i< 3; i++) {       if (ticker <= duty[i]) PORTD &= (~(1<<(i+2))); //clear to turn on       else PORTD |= (1<<(i+2));    //set pin to turn off } } code

Here are some color pattern we defined code format="c" void random_start(uint *duty, uint *dir) {   uint i;
 * 5. Color patterns**
 * Random

for (i=0;i<3;i++) { duty[i] = rand%254 + 1; if (rand%255>128) dir[i]=3; else dir[i]=-3; } } code code format="c" void disco {   uint i;    while (1) {       my_delay(10*tdelay); if (pattern!= RANDOM) return;
 * Disco

for (i=0;i<3;i++) duty[i] = rand%254 + 1; }   return; } code code format="c" void fade(uint brightness) {   uint i, j;
 * Fade

for (i=0; i<3; i++) duty[i] = 0;   //turn off all LEDs

while (1) {       for (i=0; i<3; i++) {           //fade in            for (j=0; j0; j--) {               my_delay(tdelay); if (pattern!= INTRO) return; duty[i] = j;           } }       //PORTD ^= (1<<7);    //toogle LED }

return; } code code format="c" void smooth(uint brightness) {   uint i;
 * Smooth

while (1) {       for (i=0; i0; i--) {           my_delay(tdelay); if (pattern!= PLAY) return; duty[1] = i;       }

for (i=0; i0; i--) {           my_delay(tdelay); if (pattern!= PLAY) return; duty[0] = i;       }

for (i=0; i0; i--) {           my_delay(tdelay); if (pattern!= PLAY) return; duty[2] = i;       }

//PORTD ^= (1<<7);   //toogle LED }

return; }

code Check out how each pattern look like in Gallery


 * III. Reference**
 * ADC tutorial
 * Timer tutorial
 * Sampling remote control