Fab2016

Sibu's FabAcademy 2016 Documentation Home

LED Indicator Display.

Recently I had a chance to play with Amazon Echo and I found that the LED indicator on the device interesting. Well, this is just one of the many aspects of the device that caught my attention. I thought this will be a great thing to make, and it could be used as an indicator for the temperature of our (me and Yadu) soldering station project. This will be a great addition to the project. I plan to install a display for a more detailed manipulation, like calibration, fine tuning the temperature etc. But I want to use the LED display for indicating the present temperature and the target temperature in a nice animated form.

The Design, Schematic and Board

I decided to make the LED board separate from the main controller, and to make a 10 LED semi-circular display with Tiny44 as a controller. Even if I dedicate one pin for communication and leave the RESET pin alone, I'll have 10 digital I/O pins for the LED. Even without multiplexing I can have 10 LEDs in the indicator.

I could implement software PWM code to enable dimming of any LED of my choice to indicate fractional values. For example if I want to indicate 6.5, I'll turn on 6 LEDs and put the 7th one at half the brightness.

It's better to use the INT0 or PB2 pin for the communication, PB3 is RESET. So, that leaves me with PB0 and PB1 from PORTB and all of PORTA PA0-7 for the LEDs. Hence I'll be assigning PB0 to LED1, PB1 to LED2, PA0 to LED3, PA1 to LED4......

Now it's time to make the PCB, so lets get the eagle and design it.
And it is!

The Schematic.
The Board Layout.

These are the Eagle Files for the LED Indicator Board.


Schematic Board

Stuffing

When I first designed and milled the board I was in a hurry and I made a few mistakes, I forgot to connect MOSI, MISO and SCK of the ISP to the Tiny44. So I had to use wires to bridge them. I also forgot a 10uF filter capacitor across the VCC-GND. All these mistakes and a few more(I'll talk about this later) is resolved in the above schematic and board layout, that's why you will see that the board layout doesn't match with what you will be seeing now.

Here is the 'Hero Shot' of my board. I know that everyone hates wires, so am I. It's a mess. But mistakes are inevitable, especially when you do something in a hurry, as we are human. The wires connect the ISP to MISO, MOSI and SCk there are few more wires which connects additional components I added later, like the pull-down resistor and a filter capacitor for the "data_in" pin.

Testing

First let's see if all the LEDs are working.

Blinking the LEDs

                  //timer CTC interrupt test
                  #define F_CPU 8000000UL
                  #include <avr/io.h>
                  #include <avr/interrupt.h>

                  int temp=0;
                  int main(void)
                  {
                    DDRB = 0b00000011;
                    DDRA = 0xff;
                  // initialize Timer1 the 16bit timer
                    cli();				// disable global interrupts
                    TCCR1A = 0;
                  	TCCR1B = 0;		// same for TCCR1*

                    // set compare match register to desired timer count:
                    OCR1A = 10000;

                    // turn on CTC mode:
                    TCCR1B |= (1 << WGM12);

                    TCCR1B |= (1 << CS11);

                    // enable timer compare interrupt:
                    TIMSK1 |= (1 << OCIE1A);

                    sei();			// enable global interrupts:
                  }

                  ISR(TIM1_COMPA_vect)
                  {
                    if (temp==1)
                    {
                      PORTB = (0b00000011);
                      PORTA = 0xff;
                      temp=0;
                  	}
                    else
                    {
                      PORTB = (0b00000000);
                      PORTA = 0x00;
                      temp=1;
                  	}
                  }

click to download the above code



Single LED Sweep.

Testing sweeping pattern, one LED is on at a time.

                #define F_CPU 8000000UL
                #include <avr/io.h>
                #include <util/delay.h>
                #define BLINK_DELAY_MS 75

                int temp = 0;
                int inc = 1;

                int main (void)
                {
                /* set (PB5) of PORTB for output*/
                	DDRA = 0xff; //does the same thing as above line
                  DDRB = 0b00000011;

                  while(1)
                  {
                    _delay_ms(BLINK_DELAY_MS);
                    switch (temp)
                    {
                      case 1:
                      	PORTB = 0b00000001;
                        PORTA = 0x00;
                        break;
                      case 2:
                        PORTB = 0b00000010;
                        PORTA = 0x00;
                        break;
                      case 3:
                        PORTB = 0x00;
                        PORTA = 0b00000001;
                        break;
                      case 4:
                      	PORTA = 0b00000010;
                        break;
                      case 5:
                        PORTA = 0b00000100;
                        break;
                      case 6:
                        PORTA = 0b00001000;
                        break;
                      case 7:
                      	PORTA = 000010000;
                        break;
                    	case 8:
                      	PORTA = 0b00100000;
                        break;
                      case 9:
                      	PORTA = 0b01000000;
                        break;
                      case 10:
                      	PORTA = 0b10000000;
                        inc*=-1;
                        break;
                      default:
                      	temp = 0;
                        inc*=-1;
                    }
                    temp+=inc;
                  }
                }

click to download the above code

Testing Sweep with Dimming

Here is a test code that's very close to being the final code, here I'm testing the software PWM (explained in Input/Output module) functions to adjust the brightness of the last LED to indicate fractional values.
During this test I had learned that the human eye doesn't respond linearly to the energy of the light source (well I knew this before, about sound, as I knew that the sound is measured in dBs and they are logarithmic). That is, if I reduce the power of the light source by half, or reduce the PWM duty cycle to .5 (on half the time, off the other half, reducing the energy delivered averaged over time to half), my eye is receiving half the power, but I won't be seeing half the intensity, I would be seeing less than that.
Most of our sensory organs works this way, they follow a power law, of the form. p(I) = kIa. Where p(I) is the perceived intensity/strength of the physical quantity I, k is the proportionality constant, a is the exponent. More details here.

Anyway, I didn't use the accurate numbers, instead just went for square function. 50% brightness means 25% duty cycle. But I found that my results are good, and on further inspection I found that my function is close to what many people suggested at different forums.
For example, these are the values for a 32-step, 10-bit PWM.
0, 1, 3, 6, 10, 16, 23, 32, 43, 56, 71, 88, 107, 129, 154, 181, 210, 242, 277, 315, 356, 400, 447, 498, 551, 608, 668, 732, 799, 869, 944, 1022. Here is a plot of this function along with what I'm using.

The Red curve represents square function I use and the Black curve represents the above data.
    #define F_CPU 8000000UL
    #include <avr/io.h>
    #include <avr/interrupt.h>

    int temp=0;
    int val=0;
    int prev_val = 0;
    int last_led_val;
    unsigned int last_led=0;
    unsigned int shift;

    int main(void)
    {
    	DDRB = 0b00000011;
    	DDRA = 0xff;
    	// initialize Timer1 the 16bit timer
    	cli();				// disable global interrupts
    	TCCR1A = 0;
    	TCCR1B = 0;
    	TCCR0A = 0;
    	TCCR0B = 0;

    	// set compare match register to desired timer count:
    	OCR0B = 200;

    	TCCR0B |= (1 << CS11);;

    	// enable timer compare interrupt:
    	TIMSK0 |= (1 << OCIE0A);
    	TIMSK0 |= (1 << OCIE0B);

    	sei();			// enable global interrupts:

    	while(1)
    	{
    		while (prev_val  != val)
    		{
    			last_led = val/10;
    			last_led_val =  (val - 10*last_led);
    			last_led_val = last_led_val * last_led_val  * 2;
    			PORTB = 0x00;
    			PORTA = 0x00;
    			if (last_led < 2)
    			{
    				shift = 8 - last_led;
    				PORTB = 0b11000000 << shift;
    			}
    			else
    			{
    				PORTB = 0b00000011;
    				shift = 10 - last_led;
    				PORTA = 0xff << shift;
    			}
    			OCR0A = last_led_val+1;
    			prev_val =val;
    		}

    		if (temp > 500)
    		{
    			temp = 0;
    			val++;
    			if (val > 98)
    			{
    				val = 0;
    			}
    		}
    	temp ++;
    	}
    	while(0)
    	{
    		OCR0A = 20;
    	}
    }

    ISR(TIM0_COMPA_vect)
    {
    	if (last_led < 2)
    	{
    		PORTB &= 0xff^ (1 << last_led) ;
    	}
    	else
    	{
    		PORTA &= 0xff^ (1 << (last_led-2)) ;
    	}
    }

    ISR(TIM0_COMPB_vect)
    {
    	if (last_led < 2)
    	{
    		PORTB |= (1 << last_led) ;
    	}
    	else
    	{
    		PORTA |= (1 << (last_led-2));
    	}
    	TCNT0 = 0;
    }

click to download the above code

The Final Code without identity and The Test Result

I need to sent values, which are the duty cycle to the board, I thought of various methods like software serial communication as done in my interfacing board (basically Neil's hello.ftdi.c code), sending as BCD (Binary-coded decimal) pulses....
But for something this simple, I don't need to. So I'll be sending the data as pulses who's duration will be the data to be sent. Something like, 2ms pulse represents zero and 102ms represents 100. The 16 bit timer counter will be used to measure the length of the pulse and the INT0 will be configured to trigger interrupt on logic level change, both high-to-low and low-to-high. The timer will start counting on low-to-high and will stop at high-to-low, measuring the pulse duration, after which it will be reset to zero again on the next low-to-high pulse.

Code

#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>

int temp=0;
 int val=1;
 int prev_val = 100;
 int last_led_val;
unsigned int last_led=0;
unsigned int shift;

int main(void)
{
	DDRB = 0b00000011;
	DDRA = 0xff;
	// initialize Timer1 the 16bit timer which measures the pulse length
	cli();				// disable global interrupts

	//enble pin change interrupts on int0
	GIMSK |= (1<<INT0);
	MCUCR= (1<<ISC00) | (1<<ISC00);

	TCCR1A = 0;
	TCCR1B = 0;
	TCCR0A = 0;
	TCCR0B = 0;

	// set compare match register to desired timer count:
	OCR0B = 200;

	TCCR0B |= (1 << CS01);
	TCCR1B |= (1 << CS11);

	// enable timer compare interrupt:
	TIMSK0 |= (1 << OCIE0A);
	TIMSK0 |= (1 << OCIE0B);

	sei();			// enable global interrupts:

	while(1)
	{
		last_led = val/10;
		last_led_val =  (val - 10*last_led);
		last_led_val = last_led_val * last_led_val  * 2;
		PORTB = 0x00;
		PORTA = 0x00;
		if (last_led < 2)
		{
			shift = 8 - last_led;
			PORTB = 0b11000000>>shift;
		}
		else
		{
			PORTB = 0b00000011;
			shift = 10 - last_led;
			PORTA = 0xff>>shift;
		}
		if (prev_val  != val )
		{
			OCR0A = last_led_val+1;
		}
		prev_val =val;

	}
}

ISR(INT0_vect)
{
		if (PINB && 0b00000100)
		{
			TCNT1 = 0;
		}
		else
		{
			val = TCNT1/2;
			TCNT1 = 0;
		}
}

ISR(TIM0_COMPA_vect)
{
	if (last_led < 2)
	{
		PORTB &= 0xff^ (1<< last_led) ;
	}
	else
	{
		PORTA &= 0xff^ (1<< (last_led-2)) ;;
	}
}

ISR(TIM0_COMPB_vect)
{
	if (last_led < 2)
	{
		PORTB |= (1 << last_led) ;
	}
	else
	{
		PORTA |= (1 << (last_led-2));
	}
	TCNT0 = 0;
}

click to download the above code

Problems

While testing this code, I had one major issue, whatever I do, I wasn't getting the results, I knew that there was nothing wrong in the code, after-all, most of these codes are bits and pieces from my own codes, written before, they are all tested and proven.

So only one possibility is left, NOISE.

I examined with DSO and function generator, where the function generator is used to make pulses of the duration within the limits of what the code expects. And it was working perfectly. So I was sure that the problem is the noise. I have been using the Stepper-Servo-Tester as the master board, which will sent the data as PWM pulses via the servo controller pin to the slave board(The LED Display board we are discussing about)

The issue was the noise for sure, I left the input pin of the slave and the output of the master floating, Bad idea!. I should have put a pull down resistor here. Also I didn't have a bypass/decoupling capacitor (a low value capacitor close to the input pin, between the input pin and the ground, to ground any high frequency noise signal).

The fix.

We now know the issue, so the fix is easy, add a pull down resistor and a capacitor close to the input pin.
I chose a 10nF capacitor and 5K resistor.
Now the circuit is working beautifully.

Result


The values like the prescaler for Timer1, the multiplier at the end of the the expression last_led_val = last_led_val * last_led_val * 2; etc. could be adjusted to make the circuit work in different range of pulse durations.

Identity

Identity is an important aspect of any networked system, if there were only two nodes, we might not need an identity, but when there are more than two nodes, we have to fins a mechanism to tell what is meant for whom.

The identities can be specified by may methodes. some of the methodes that comes to my mind are

So far, the program do not give any identity to the slaves, so if we have many boards connected to the same network, all of the slaves respond similarly to the commands from the master.
To resolve this, we have to find a way to add an entry in the data stream which will let the slaves decide if the data is meant for them or not. Basically this is an address, something like a code, name, pattern to distinguish one slave from another. For this to work, obviously we need to divide the data stream into packets.

Each of these packets will have some way to identify the beginning and end, also these packets will contain the address. A simple example would be, if we are using UART (software/bit-banged or hardware) for the communication, as we know it has a few bits dedicated to mark the beginning of a packet, end of a packet and parity, that is some way to detect error in transmission. I found this sparkfun article on serial communication quite informative. But clearly these protocol doesn't have a method for addressing, that is there is no way for the slave to know what is meant for whom.

For my need, I have to sent only decimal numbers, and if I use UART, except the 10 combinations meant for the 10 digits, all other combinations are left for any purpose of my choice. So I can dedicate, say A to Z for addressing and some special key like # for marking the end of the current stream.
so, A83# when sent, will go to all the slaves, but only one slave that is programmed to listen/accept command formatted like AXX# will take any action.

If I use A-Z and a-z for addressing, I can have 52 unique identities, in fact if I use 8 bit data packets, I can have 256-10=246 unique identities, if I'm just sending only numbers to all the slaves. So let's write a code shall we?

Code for Communicating with Identity.

I will be using Neil's software serial code for serial listening function of the LED board. No surprise here, Neil already did what I'm planning to do, except for the "#" to signify the end of message. Thinking about it further, I don't need to signify the end of stream, if I'm just sending plain numbers. The alphabets signifies the end of one stream and the beginning of the next.

While testing the reliability of this communication method with the following code, I found that I'll have to repeat the characters, so that the slave get's it. This could be because there is no buffer in the software serial. There are some ideas I have to solve this, will talk about it later.


#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <string.h>

#define output(directions,pin) (directions |= pin) // set port direction for output
#define input(directions,pin) (directions &= (~pin)) // set port direction for input
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
#define bit_delay_time 98// bit delay for 9600 with overhead
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay

#define serial_port PORTB
#define serial_direction DDRB
#define serial_pins PINB
#define serial_pin_in (1 << PB2)
//#define serial_pin_out (1 << PB4)

/////////////////////////////////////////
#define identity = 'A'

int temp=0;
 int val=1;
 int prev_val = 100;
 int last_led_val;
unsigned int last_led=0;
unsigned int shift;
unsigned int val_done = 0;

void get_char(volatile unsigned char *pins, unsigned char pin, char *rxbyte)
{
	// This function is written by Neil Gershenfeld
	// read character into rxbyte on pins pin
	//  assumes line driver (inverts bits)
	//
	*rxbyte = 0;
	while (pin_test(*pins,pin))
		// wait for start bit
		// delay to middle of first data bit
		half_bit_delay();
	bit_delay();
	//
	// unrolled loop to read data bits
	//
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 0);
	else
		*rxbyte |= (0 << 0);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 1);
	else
		*rxbyte |= (0 << 1);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 2);
	else
		*rxbyte |= (0 << 2);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 3);
	else
		*rxbyte |= (0 << 3);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 4);
	else
		*rxbyte |= (0 << 4);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 5);
	else
		*rxbyte |= (0 << 5);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 6);
	else
		*rxbyte |= (0 << 6);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 7);
	else
		*rxbyte |= (0 << 7);
	//
	// wait for stop bit
	//
	bit_delay();
	half_bit_delay();
}


int main(void)
{
	static char chr;
	DDRB = 0b00000011;
	DDRA = 0xff;

	cli();				// disable global interrupts

	// set clock divider to /1
	CLKPR = (1 << CLKPCE);
	CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

	//enble pin change interrupts on int0
//	GIMSK |= (1<<INT0);
//	MCUCR= (1<<ISC00) | (1<<ISC00);

//	TCCR1A = 0;
//	TCCR1B = 0;
	TCCR0A = 0;

	TCCR0B = 0;

	// set compare match register to desired timer count:
	OCR0B = 200;
	TCCR0B |= (1 << CS01);
//	TCCR1B |= (1 << CS11);

	// enable timer compare interrupt:
	TIMSK0 |= (1 << OCIE0A);
	TIMSK0 |= (1 << OCIE0B);

	sei();			// enable global interrupts:

	while(1)
	{
		get_char(&serial_pins, serial_pin_in, &chr);

		 if (chr == 'A')
			val = 10;

		if (chr == 'B')
			val = 50;

		if (chr == 'C')
			val = 90;


		last_led = val/10;
		last_led_val =  (val - 10*last_led);
		last_led_val = last_led_val * last_led_val  * 2;
		PORTB = 0x00;
		PORTA = 0x00;
		if (last_led < 2)
		{
			shift = 8 - last_led;
			PORTB = 0b11000000>>shift;
		}
		else
		{
			PORTB = 0b00000011;
			shift = 10 - last_led;
			PORTA = 0xff>>shift;
		}
		if (prev_val  != val )
		{
			OCR0A = last_led_val+1;
		}
		prev_val =val;
	}
}

ISR(TIM0_COMPA_vect)
{
	if (last_led < 2)
	{
		PORTB &= 0xff^ (1<< last_led) ;
	}
	else
	{
		PORTA &= 0xff^ (1<< (last_led-2)) ;;
	}
}

ISR(TIM0_COMPB_vect)
{
	if (last_led < 2)
	{
		PORTB |= (1 << last_led) ;
	}
	else
	{
		PORTA |= (1 << (last_led-2));
	}
	TCNT0 = 0;
}            

click to download the above code
Though not reliable, I wrote the code any way, the code resets when it finds a '#' or another alphabet in the address range.
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <string.h>

#define output(directions,pin) (directions |= pin) // set port direction for output
#define input(directions,pin) (directions &= (~pin)) // set port direction for input
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
#define bit_delay_time 98// bit delay for 9600 with overhead
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay

#define serial_port PORTB
#define serial_direction DDRB
#define serial_pins PINB
#define serial_pin_in (1 << PB2)
//#define serial_pin_out (1 << PB4)

/////////////////////////////////////////
#define identity = 'A'

//int temp=0;
 int val=1;
 int prev_val = 100;
 int last_led_val;
 int temp_val = 0;
unsigned int last_led=0;
unsigned int shift;

unsigned int forme = 0;

void get_char(volatile unsigned char *pins, unsigned char pin, char *rxbyte)
{
	// This function is written by Neil Gershenfeld
	// read character into rxbyte on pins pin
	//  assumes line driver (inverts bits)
	//
	*rxbyte = 0;
	while (pin_test(*pins,pin))
		// wait for start bit
		// delay to middle of first data bit
		half_bit_delay();
	bit_delay();
	//
	// unrolled loop to read data bits
	//
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 0);
	else
		*rxbyte |= (0 << 0);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 1);
	else
		*rxbyte |= (0 << 1);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 2);
	else
		*rxbyte |= (0 << 2);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 3);
	else
		*rxbyte |= (0 << 3);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 4);
	else
		*rxbyte |= (0 << 4);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 5);
	else
		*rxbyte |= (0 << 5);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 6);
	else
		*rxbyte |= (0 << 6);
	bit_delay();
	if pin_test(*pins,pin)
		*rxbyte |= (1 << 7);
	else
		*rxbyte |= (0 << 7);
	//
	// wait for stop bit
	//
	bit_delay();
	half_bit_delay();
}


int main(void)
{
	static char chr;
	DDRB = 0b00000011;
	DDRA = 0xff;

	cli();				// disable global interrupts

	// set clock divider to /1
	CLKPR = (1 << CLKPCE);
	CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

	//enble pin change interrupts on int0
//	GIMSK |= (1<<INT0);
//	MCUCR= (1<<ISC00) | (1<<ISC00);

//	TCCR1A = 0;
//	TCCR1B = 0;
	TCCR0A = 0;

	TCCR0B = 0;

	// set compare match register to desired timer count:
	OCR0B = 200;
	TCCR0B |= (1 << CS01);
//	TCCR1B |= (1 << CS11);

	// enable timer compare interrupt:
	TIMSK0 |= (1 << OCIE0A);
	TIMSK0 |= (1 << OCIE0B);

	sei();			// enable global interrupts:

	while(1)
	{
		get_char(&serial_pins, serial_pin_in, &chr);

		 if (chr == 'A') //set forme flag =1 if found my address
		 {
			 forme = 1;
			  if ( ~temp_val )
				 val = temp_val;
			temp_val = 0;
		 }
		 else
		 {
			 if (chr == '#' || (chr > 64 && chr < 91) )	 //not my address but either a stop char or the address of another slave 65 & 90 are ascii codes for A and Z
			 {
				 forme = 0;
				 if ( temp_val != 0  && chr =='#')
					 val = temp_val;
				 if(val>100)
					 val =90;
				 temp_val = 0;
			}
		}

		if (forme == 1 && (chr > 47 && chr < 58) )
		{
			temp_val =temp_val*10 +  (chr - 48);
		}

		last_led = val/10;
		last_led_val =  (val - 10*last_led);
		last_led_val = last_led_val * last_led_val  * 2;
		PORTB = 0x00;
		PORTA = 0x00;
		if (last_led < 2)
		{
			shift = 8 - last_led;
			PORTB = 0b11000000>>shift;
		}
		else
		{
			PORTB = 0b00000011;
			shift = 10 - last_led;
			PORTA = 0xff>>shift;
		}
		if (prev_val  != val )
		{
			OCR0A = last_led_val+1;
		}
		prev_val =val;
	}
}

ISR(TIM0_COMPA_vect)
{
	if (last_led < 2)
	{
		PORTB &= 0xff^ (1<< last_led) ;
	}
	else
	{
		PORTA &= 0xff^ (1<< (last_led-2)) ;;
	}
}

ISR(TIM0_COMPB_vect)
{
	if (last_led < 2)
	{
		PORTB |= (1 << last_led) ;
	}
	else
	{
		PORTA |= (1 << (last_led-2));
	}
	TCNT0 = 0;
}

click to download the above code

As expected the code is not working (well, it works once in a while, with a 90% fail rate). I'll have to make a buffer and some other reliable serial reading method.

New Serial Communication Idea Using Interrupts

The idea is to use the interrupts and the high resolution Timer1 to enable serial communication. I have two idea in my mind to achive this. The writing part is relatively easy, reading seems a bit challenging.

First idea is to use the Timer1 in Interrupt on compare match mode with interrupts being called every time the Timer1 saturates.

Sending data

Receiving Data

The only catch is in identifying the start bit. Once this is done,

Second Idea

Sending Data

Receiving Data

As before the challange is in identifying the start bit. Once done,

The Issue of Identifying the Start Bit.

In my understanding the start bit in an UART/USART communication protocol is no different from any other data bit, I don't know how then the chip identifies if the incoming bit is a start_bit or a data bit. If I were to implement the protocol, I would slightly differentiate the start bit, I would be sending a shorter pulse or a longer pulse, which would not be found in any combination of the data pulses.

One way I'm thinking about is to have a much shorter pulse to signify the start bit. In a 9600bps communication, the bit duration is about 104uS. If I just take the serial bus low and then high within 10uS, this clearly won;t cause any confusion and can easily identify the start of a new data frame.

Else if the bit duration is about 100uS, I could use a start bit if say, 50uS. Again, this will not cause any confusion.

As I said, sending the data is really easy, and I found a program which is very close to what I have planned. here is the link.

altsoftserial may also is using the technique of interrupts.