Avr Multitasking

Go To Last Post
46 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Recently i have been thinking about many things such as(nested timer interrupts and countdown made using timer1). Although i have been partially successful in creating a countdown timer but i have no idea on how to use the nested interrupts. If have checked the site for quite some time but not got anything of my use.

#include <avr/io.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <avr/interrupt.h>
#include "I2C_Master_H_file.h"
#include "LCD16x2_4Bit.h"

#define Device_Write_address	0xD0	/* Define RTC DS1307 slave write address */
#define Device_Read_address	0xD1	/* Make LSB bit high of slave address for read */

#define BUTTON1 PD0 // button switch connected to port B pin 1
#define BUTTON2 PD1
#define BUTTON3 PD2

#define LED1 PD3 // Drain
#define LED2 PD7 // Pump

#define DEBOUNCE_TIME 25 // time to wait while "de-bouncing" button
#define LOCK_INPUT_TIME 300 // time to wait after a button press

void init_ports_mcu()
{
	DDRD &= ~(1<<PD0); //on/off manual button
	PORTD |= (1<<PD0);

	DDRD &= ~(1<<PD1); //sensor 1 button
	PORTD |= (1<<PD1);

	DDRD &= ~(1<<PD2); //sensor 2 button
	PORTD |= (1<<PD2);

	DDRD |= (1<<PD7); //pump
	PORTD &= ~(1<<PD7);

	DDRD |= (1<<PD3);//drain
	PORTD &= ~(1<<PD3);
}

unsigned char button_state_1() //button one
{
	/* the button is pressed when BUTTON1 bit is clear */
	if (!(PIND & (1<<BUTTON1)))
	{
		_delay_ms(DEBOUNCE_TIME);
		if (!(PIND & (1<<BUTTON1))) return 1;
	}
	return 0;
}

unsigned char button_state_2()//button two
{
	/* the button is pressed when BUTTON1 bit is clear */

	if (!(PIND & (1<<BUTTON2)))
	{
		_delay_ms(DEBOUNCE_TIME);
		if (!(PIND & (1<<BUTTON2))) return 1;
	}

	return 0;
}

unsigned char button_state_3()//button three
{
	/* the button is pressed when BUTTON1 bit is clear */

	if (!(PIND & (1<<BUTTON3)))
	{
		_delay_ms(DEBOUNCE_TIME);
		if (!(PIND & (1<<BUTTON3))) return 1;
	}
	return 0;
}

int second,minute,hour,day,date,month,year;
#define degree_sysmbol 0xdf
volatile uint8_t m=0;
volatile uint8_t s=0;
volatile uint8_t timer_running=0;

void RTC_Read_Clock(char read_clock_address)
{
	I2C_Start(Device_Write_address);/* Start I2C communication with RTC */
	I2C_Write(read_clock_address);	/* Write address to read */
	I2C_Repeated_Start(Device_Read_address);/* Repeated start with device read address */

	second = I2C_Read_Ack();	/* Read second */
	minute = I2C_Read_Ack();	/* Read minute */
	hour = I2C_Read_Nack();		/* Read hour with Nack */
	I2C_Stop();			/* Stop i2C communication */
}

void RTC_Read_Calendar(char read_calendar_address)
{
	I2C_Start(Device_Write_address);
	I2C_Write(read_calendar_address);
	I2C_Repeated_Start(Device_Read_address);

	day = I2C_Read_Ack();		/* Read day */
	date = I2C_Read_Ack();		/* Read date */
	month = I2C_Read_Ack();		/* Read month */
	year = I2C_Read_Nack();		/* Read the year with Nack */
	I2C_Stop();			/* Stop i2C communication */
}

void ADC_Init(){
	DDRA = 0x00;	        /* Make ADC port as input */
	ADCSRA = 0x87;          /* Enable ADC, with freq/128  */
	ADMUX = 0x40;           /* Vref: Avcc, ADC channel: 0 */
}

int ADC_Read(char channel)
{
	ADMUX = 0x40 | (channel & 0x07);   /* set input channel to read */
	ADCSRA |= (1<<ADSC);               /* Start ADC conversion */
	while (!(ADCSRA & (1<<ADIF)));     /* Wait until end of conversion by polling ADC interrupt flag */
	ADCSRA |= (1<<ADIF);               /* Clear interrupt flag */
	_delay_ms(1);                      /* Wait a little bit */
	return ADCW;                       /* Return ADC word */
}

int main(void)
{

	char Temperature[10];
	float celsius;
	char buffer[40];
	unsigned char n_led = 1; // LED number is on now
	init_ports_mcu();

	I2C_Init();			/* Initialize I2C */
	lcdinit();			/* Initialize LCD16x2 */
	ADC_Init();
	TCCR1B=(1<<WGM12)|(1<<CS12)|(1<<CS10); //CTC Mode prescaller 1024
	OCR1A=976;
	TIMSK|=(1<<OCIE1A);
	lcd_print_xy(0, 0, "WELCOME");
	_delay_ms(100);
	lcd_clear();
	_delay_ms(500);

	while(1)
	{

		RTC_Read_Clock(0);	/* Read clock with second add. i.e location is 0 */
		sprintf(buffer, "%02x:%02x:%02x ", hour, minute, second);
		lcd_print_xy(0,0,buffer);

		RTC_Read_Calendar(3);	/* Read calendar with day address i.e location is 3 */

			sprintf(buffer, "%02x/NOV/%02x  ", date, year);
			lcd_print_xy(0,11,buffer);

		lcd_print_xy(1, 0,"TEMPERATURE");
		celsius = (ADC_Read(0)*4.88);
		celsius = (celsius/10.00);
		sprintf(Temperature,"%d%cC  ", (int)celsius, degree_sysmbol);
		lcd_print_xy(1,12,Temperature);
		memset(Temperature,0,10);
		lcd_print_xy(2, 0, "LEVEL : 0  00:00");
	    lcd_print_xy(3, 0, "PUMP OFF  DRAIN OFF");

		//Here i have operated the switches with delay but they prevents the avr
		//from multitasking and hence countdown doesnt shows
		if (button_state_1()) // Manual ON/OFF
		{

			switch(n_led){
				case 1:
				lcd_print_xy(2, 8, "0");//for the indication of 0th level
				lcd_print_xy(3, 0, "PUMP ON  DRAIN OFF");
				PORTD ^= (1<<LED1); // toggling the current state of the pin LED1. LED1 is turn off.
				break;
				n_led=0;  // reset LED number
				break;
			}
			_delay_ms(LOCK_INPUT_TIME);
		}

		if (button_state_2()) //Washing button //will turn on both the outputs for 30secs and then turn them off
		{

			switch(n_led){
				case 1:
				lcd_print_xy(2, 8, "1"); //for indication of 1st level
				lcd_print_xy(3, 0, "PUMP ON  DRAIN  ON ");
				PORTD |= (1<<LED1); // toggling the current state of the pin LED1. LED1 is turn off.
				PORTD |= (1<<LED2);
				_delay_ms(30000);  //Time changer
				PORTD &= ~(1<<LED1);
				PORTD &= ~(1<<LED2);
				break;

				n_led=0; // reset LED number
				break;
			}
			_delay_ms(LOCK_INPUT_TIME);
		}

		if (button_state_3()) // Sensor input//will turn the LED1 for 4 mins and when there is 1 min left on the countdown then LED2 should lightup
		{

			switch(n_led){
				case 1:
				lcd_print_xy(2, 8, "2");//for indication of second level
				lcd_print_xy(3, 0, "PUMP ON  DRAIN OFF");
				PORTD |= (1<<LED1);
				_delay_ms(4000);
				lcd_print_xy(3, 0, "PUMP ON  DRAIN  ON ");
				PORTD |= (1<<LED2);
				_delay_ms(2000);
				PORTD &= ~(1<<LED1); //after all this both of them should turn off 
				PORTD &= ~(1<<LED2);

				break;

				//second press missing

				break;
			}

			_delay_ms(LOCK_INPUT_TIME);
		}

	}
	return (0);

	}

	ISR(TIMER1_COMPA_vect) // for the countdown timer
	{
		if(!timer_running)
		return;

		if(s==0)
		{
			if(m==0)
			{
				//off
				timer_running=0;
			}
			else
			{
				m--;
				s=59;
			}
		}
		else
		{
			s--;
		}

	}

Here i have four things running simultaneously an rtc date and time system , temperature readings , if any on the button is pressed then outputs should light up accordingly and when the outputs are turned on a countdown should also run stating the time left for the output to turn off.

Please help i have been struck on it for many hours.

Don't worry for the rtc date and time and temperature readings they work good.

Buttons also work but they hangs the avr and hence countdown and  rtc time stops.

I am a little bit confused on where to place the countdown.Screen output

This topic has a solution.

anshumaan kumar

Last Edited: Thu. Nov 28, 2019 - 10:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Most AVR programs avoid nested interrupts as if they were the plague. 

 

The rule is: keep interrupts short. Use them to set or clear flags that control execution in other places of the program. 

 

Much easier to debug. Much easier to get right.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can I have an example or a simple pseudo code although i am trying still. I can use the interrupt to light up a single LED but i  have  a problem doing this with multiple buttons and multiple outputs

 

anshumaan kumar

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Have you read my tutorial i wrote many years ago? Check out the tutorials section.

Using delays is not a good idea. Nor is your method of debouncing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. thanks let me check your article.

 

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Allowing nested interrupts, is often a disaster waiting (not very long) to happen.  Once you open the door to do so, you open the stack depth to possibly being exceeded (runaway nesting)---crashing your program & cutting off the free popcorn.

Stay sane & stamp out nested IRQ's wherever you find them.

 

Simply think of completing an IRQ as completing (or starting) some small task or a step in a larger task.   Debounce the switch, done...update the PWM, done...update the encoder pins, started... toggle the popcorn ready led, done  

    

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Quote:
switch(n_led){
case 1:
lcd_print_xy(2, 8, "0");//for the indication of 0th level
lcd_print_xy(3, 0, "PUMP ON DRAIN OFF");
PORTD ^= (1<<LED1); // toggling the current state of the pin LED1. LED1 is turn off.
break;
n_led=0; // reset LED number
break;
}

The n_led=0; // reset LED number statement in that block of code will never be executed because the preceeding break; will exit the case 1: of that switch()


If n_led is supposed to only have the values 0 or 1 then I would simply use if ( n_led == 1 ){ instead of switch(n_led){

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

It is probably best to take all of your many hard-fought learnings, and a clearer picture of things to do. Throw this version in the trash & begin round II.   It will be 10 times better & created 5x faster than the last version; if you try editing what you have, it will likely become a crazy twisted mess.  When you restart with nothing, you have the advantage of starting with no errors & can cleanly follow your latest line of thinking through the logic.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sun. Nov 24, 2019 - 05:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's how I would usually divide the tasks up:

 

 - read inputs and scale/filter etc

 - alarm logic/flash lights etc

 - display

 - setup menu

 

where the above are separate tasks or functions doesn't make much difference. The concept is to separate the acquisition, display and logic. This will make writing the code easier and easier to debug.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:
... a countdown should also run stating the time left for the output to turn off.

Please help i have been struck [sic] on it for many hours.

 

On Nested Interrupts

No - No - No. These are not the interrupts you are looking for. Pretend you'd never read about them.

 

Countdown Timer

You could have actually made some attempt at coding it. All I see is a hard-coded print of countdown time and no code to actually set any countdown. Your comment says 4 minutes countdown, I expected to see m=4; somewhere in your code.

lcd_print_xy(2, 0, "LEVEL : 0  00:00");

 

Long Delays

    _delay_ms(30000);  //Time changer
    PORTD &= ~(1<<LED1);
    PORTD &= ~(1<<LED2);

Ignoring the comment for now, I don't understand it.

Any time you write a delay longer than a few milliseconds, you should think differently about how else you can achieve your programming intention, because it leads to unresponsiveness.

Here it is easy to overcome; you have a 30s delay and some GPIO stuff afterwards. You already know about coding a countdown timer in your 1-second ISR, so consider this 30s delay simply as just another 30 countdown timer. It will add possibly 5 lines of code into your existing ISR.

 

 

Last Edited: Sun. Nov 24, 2019 - 10:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here, let me correct that for you...

N.Winterbottom wrote:

Any time you write a delay longer than a few milliseconds microseconds, you should think differently about how else you can achieve your programming intention, because it leads to unresponsiveness.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

_delayXXX functions are, or want to be, blocking. Typically, interrupts are turned off and nothing happens anywhere else until that delay is done. Instead, you COULD be checking switch states and other things during that time.

 

I  would argue for a simple finite state machine (FSM) and a timer that creates "ticks" that can be counted for various interval durations. There are numerous tutorials on the net on FSMs.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to say that _delay_ms/_delay_us do NOT disable interrupts (which is one of the reasons they are a "bad idea" as the actual delay could be considerably longer than advertised if interrupts hog CPU time).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That is the source of "or want to be"...

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Sun. Nov 24, 2019 - 11:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry for such a late reply my wifi was down for sometime. In the mean time i have done something and learned some things too such as debouncing which was termed not efficient, and also  I have found the Kartman's tutorial to multitask the Avr.

Thank you for guiding me to your tutorial.

avrcandies wrote:
Simply think of completing an IRQ as completing (or starting) some small task or a step in a larger task.   Debounce the switch, done...update the PWM, done...update the encoder pins, started... toggle the popcorn ready led, done  
 

Thanks avrcandies this concept is simple and is very effective and as you have suggested correctly starting from the beginning is applicable here.

Also about the long delays that i have put in my code these are used to turn the output ON for the set time because I was confused how to use the timer in place of delay.

(I know that delays hangs up the avr processor for the time being)I was trying to just make that work with what I know.

Thank you all the people because of you I am learning soo much.smiley Really appreciate for the precious replies you all you posted. 

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have been mean while and found the Kartman's post useful and modified it accordingly.

//
//	Co-operative multitasking framework tutorial code
//	(c)Russell Bull 2010. Free for any use.
//	Code built for a Mega168 @ 16MHz
//
//	flash two outputs (PORTB.4,PORTB.5) at different speeds - blinky#2.
//

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

void init_devices(void);
void timer0_init(void);
void reset_task(char tsk);
void set_task(char tsk);
void task_dispatch(void);
void task0(void);
void task1(void);
void task2(void);
void task3(void);
void task4(void);
void task5(void);
void task6(void);
void task7(void);
//--------------------------------------------------------------------------------------------------------------
#define Device_Write_address	0xD0	/* Define RTC DS1307 slave write address */
#define Device_Read_address	0xD1	/* Make LSB bit high of slave address for read */
int second,minute,hour,day,date,month,year;
//--------------------------------------------------------------------------------------------------------------
#define degree_sysmbol 0xdf
//--------------------------------------------------------------------------------------------------------------
#define NUM_TASKS 8
char task_bits = 0;  /* lsb is hi priority task */
volatile char tick_flag = 0;    /* if non-zero, a tick has elapsed */
unsigned int task_timers[NUM_TASKS]={0,0,0,0,0,0,0,0};                  /* init the timers to 0 on startup */
static const PROGMEM char bit_mask[]={1,2,4,8,16,32,64,128};            /* value -> bit mask xlate table */
//---------------------------------------------------------------------------------------------------------------
void RTC_Read_Clock(char read_clock_address)
{
	I2C_Start(Device_Write_address);/* Start I2C communication with RTC */
	I2C_Write(read_clock_address);	/* Write address to read */
	I2C_Repeated_Start(Device_Read_address);/* Repeated start with device read address */

	second = I2C_Read_Ack();	/* Read second */
	minute = I2C_Read_Ack();	/* Read minute */
	hour = I2C_Read_Nack();		/* Read hour with Nack */
	I2C_Stop();			/* Stop i2C communication */
}

void RTC_Read_Calendar(char read_calendar_address)
{
	I2C_Start(Device_Write_address);
	I2C_Write(read_calendar_address);
	I2C_Repeated_Start(Device_Read_address);

	day = I2C_Read_Ack();		/* Read day */
	date = I2C_Read_Ack();		/* Read date */
	month = I2C_Read_Ack();		/* Read month */
	year = I2C_Read_Nack();		/* Read the year with Nack */
	I2C_Stop();			/* Stop i2C communication */
}

void ADC_Init(){
	DDRA = 0x00;	        /* Make ADC port as input */
	ADCSRA = 0x87;          /* Enable ADC, with freq/128  */
	ADMUX = 0x40;           /* Vref: Avcc, ADC channel: 0 */
}

int ADC_Read(char channel)
{
	ADMUX = 0x40 | (channel & 0x07);   /* set input channel to read */
	ADCSRA |= (1<<ADSC);               /* Start ADC conversion */
	while (!(ADCSRA & (1<<ADIF)));     /* Wait until end of conversion by polling ADC interrupt flag */
	ADCSRA |= (1<<ADIF);               /* Clear interrupt flag */
	_delay_ms(1);                      /* Wait a little bit */
	return ADCW;                       /* Return ADC word */
}

int main(void) 
{ 
	char Temperature[10];
	float celsius;
	char buffer[40];

  init_devices();
//start at least one task here
//	Rtc time and date are displayed here
//  temperature is also displayed here
// I have selected these 2 to be here because they require constant data supply from the modules/sensors

RTC_Read_Clock(0);	/* Read clock with second add. i.e location is 0 */
sprintf(buffer, "%02x:%02x:%02x ", (hour), minute, second);
lcd_print_xy(0,0,buffer);

RTC_Read_Calendar(3);	/* Read calendar with day address i.e location is 3 */
sprintf(buffer, "%02x/%02x/%02x  ", date,month, year);
lcd_print_xy(0, 1, buffer);

lcd_print_xy(1, 0,"TEMPERATURE");
celsius = (ADC_Read(0)*4.88);
celsius = (celsius/10.00);
sprintf(Temperature,"%d%cC  ", (int)celsius, degree_sysmbol);
lcd_print_xy(1,12,Temperature);
memset(Temperature,0,10);




//set_task(7);	//task7 runs
//set_task(6);	//task6 runs

//      main loop 

  while(1)
    {
    if (tick_flag)
      {
      tick_flag = 0;
	  task_dispatch();              // well.... 
	  }
	}
  return 0;
} 
//
//	a task gets dispatched on every tick_flag tick (10ms)
//
void task_dispatch(void)
{
  /* scan the task bits for an active task and execute it */

  char task;
	

/* take care of the task timers. if the value ==0 skip it
	else decrement it. If it decrements to zero, activate the task associated with it */

  task=0;
  while (task < NUM_TASKS )
    {
    if (task_timers[task])
	   {
  	   task_timers[task]--;            /* dec the timer */
	   if (task_timers[task] == 0 )
	   		{
	    	set_task(task); /* if ==0 activate the task bit */
			}
	   }
    task++;
    }

  task = 0; /* start at the most significant task */
  while (task <= NUM_TASKS )
    {
	  if ((task_bits & pgm_read_byte(&bit_mask[task])))
	  		{
	  		break; /* if activate task found..*/
			}
      task++;         /* else try the next one */
    }
  switch(task)            /* if task bit is active..execute the task */
    {
    case 0:
      task0();
      break;
    case 1:
      task1();
      break;
    case 2:
      task2();
      break;
    case 3:
      task3();
      break;
    case 4:
      task4();
      break;
    case 5:
      task5();
      break;
    case 6:
      task6();
      break;
    case 7:
      task7();
      break;
    default:
      break;                  /* no task was active!! */
    }                       
}

// enable a task for execution
void set_task(char tsk)
{
  task_bits |= pgm_read_byte(&bit_mask[tsk]);       /* sets a task bit */
}
// disable a task from executing
void reset_task(char tsk)
{
  task_bits &= (~pgm_read_byte(&bit_mask[tsk]));  /* resets a task bit */
}

void task0(void)
{
  reset_task(0);
}
void task1(void)
{
  reset_task(1);
}
void task2(void)
{
  reset_task(2);
}
void task3(void)
{
  reset_task(3);
}
void task4(void)
{
  reset_task(4);
}
void task5(void)
{
  reset_task(5);
}
//
//	flash PORTB.4 at 2hz
//
void task6(void)
{
PORTB ^= (1<<4);
task_timers[6] = 25;		//every 250ms
reset_task(6);
}
//
//	flash PORTB.5 at 1hz
//
void task7(void)
{
PORTB ^= (1<<5);
task_timers[7] = 50;		//every 500ms  
reset_task(7);
}
//call this routine to initialize all peripherals
void init_devices(void)
{
 //stop errant interrupts until set up
 cli(); //disable all interrupts


 DDRB = 0x30;	//port 4 & 5 as outputs

 timer0_init();
 
 MCUCR = 0x00;
 EICRA = 0x00; //extended ext ints
 EIMSK = 0x00;
 
 TIMSK0 = 0x02; //timer 0 interrupt sources
 
 PRR = 0x00; //power controller
 sei(); //re-enable interrupts
 //all peripherals are now initialized
}
//TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 10mSec
// actual value: 10.048mSec (-0.5%)
void timer0_init(void)
{
  TCCR0B &= 0B11111000; //stop
  TCNT0 = 0x00; //set count
  TCCR0A |= (1 << WGM01);	//CTC mode
  OCR0A = 0x9C; 
  TCCR0B |= (1<<CS0)|(1<<CS2) //start timer
}

ISR(TIMER0_COMPA_vect)
{
 //TIMER0 has overflowed
 	tick_flag = 1;
}

Please check for any errors I was not able to run it because I was not able to translate the timer of atmega168 to atmega32 I am using because I had recently started to learn timers of the avr. Things for Noobs like me are hard to understand at once even if they are simple

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was not able to translate the timer of atmega168 to atmega32 I am using because I had recently started to learn timers of the avr.

 

If you are just barely getting started with AVRs, why are you starting with something so complicated?  Get good first, then tackle the complex stuff.

 

If you are learning how to use bricks, build a fireplace, not a skyscraper! 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you got the two leds flashing? That is the first thing to achieve. If you want help with the timer, then you need to tell us how fast your mega32 is clocked at.

 

Once you have the two leds flashing, then move onto adding your extra stuff. Don't make a simple problem more complicated by adding stuff first up.

 

 

As well, you don't put your task code in main() - put your task code into a taskx() function. The whole concept is to break your code up into little tasks that run in 10ms or less and then we can run these tasks on demand or at timed intervals.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//
//	Co-operative multitasking framework tutorial code
//	(c)Russell Bull 2010. Free for any use.
//	Code built for a Mega168 @ 16MHz
//
//	flash two outputs (PORTB.4,PORTB.5) at different speeds - blinky#2.
//

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

void init_devices(void);
void timer0_init(void);
void reset_task(char tsk);
void set_task(char tsk);
void task_dispatch(void);
void task0(void);
void task1(void);
void task2(void);
void task3(void);
void task4(void);
void task5(void);
void task6(void);
void task7(void);
//--------------------------------------------------------------------------------------------------------------
#define DS1307_WRITE_ADDR	0xD0	/* Define RTC DS1307 slave write address */
#define DS1307_READ_ADDR	0xD1	/* Make LSB bit high of slave address for read */
int second,minute,hour,day,date,month,year;
//--------------------------------------------------------------------------------------------------------------
#define DEGREE_SYMBOL 0xdf
//--------------------------------------------------------------------------------------------------------------
#define NUM_TASKS 8
char task_bits = 0;  /* lsb is hi priority task */
volatile char tick_flag = 0;    /* if non-zero, a tick has elapsed */
unsigned int task_timers[NUM_TASKS]={0,0,0,0,0,0,0,0};                  /* init the timers to 0 on startup */
static const PROGMEM char bit_mask[]={1,2,4,8,16,32,64,128};            /* value -> bit mask xlate table */
//---------------------------------------------------------------------------------------------------------------
void RTC_Read_Clock(char read_clock_address)
{
	I2C_Start(DS1307_WRITE_ADDR);/* Start I2C communication with RTC */
	I2C_Write(read_clock_address);	/* Write address to read */
	I2C_Repeated_Start(DS1307_READ_ADDR);/* Repeated start with device read address */

	second = I2C_Read_Ack();	/* Read second */
	minute = I2C_Read_Ack();	/* Read minute */
	hour = I2C_Read_Nack();		/* Read hour with Nack */
	I2C_Stop();			/* Stop i2C communication */
}

void RTC_Read_Calendar(char read_calendar_address)
{
	I2C_Start(DS1307_WRITE_ADDR);
	I2C_Write(read_calendar_address);
	I2C_Repeated_Start(DS1307_READ_ADDR);

	day = I2C_Read_Ack();		/* Read day */
	date = I2C_Read_Ack();		/* Read date */
	month = I2C_Read_Ack();		/* Read month */
	year = I2C_Read_Nack();		/* Read the year with Nack */
	I2C_Stop();			/* Stop i2C communication */
}

void ADC_Init(){
	DDRA = 0x00;	        /* Make ADC port as input */
	ADCSRA = 0x87;          /* Enable ADC, with freq/128  */
	ADMUX = 0x40;           /* Vref: Avcc, ADC channel: 0 */
}

int ADC_Read(char channel)
{
	ADMUX = 0x40 | (channel & 0x07);   /* set input channel to read */
	ADCSRA |= (1<<ADSC);               /* Start ADC conversion */
	while ((ADCSRA & (1<<ADSC)));     /* Wait until end of conversion by polling ADC interrupt flag */
	return ADCW;                       /* Return ADC word */
}

int main(void) 
{ 

  init_devices();
//start at least one task here

    set_task(5);

//set_task(7);	//task7 runs
//set_task(6);	//task6 runs

//      main loop 

  while(1)
    {
    if (tick_flag)
      {
      tick_flag = 0;
	  task_dispatch();              // well.... 
	  }
	}
  return 0;
} 
//
//	a task gets dispatched on every tick_flag tick (10ms)
//
void task_dispatch(void)
{
  /* scan the task bits for an active task and execute it */

  char task;
	

/* take care of the task timers. if the value ==0 skip it
	else decrement it. If it decrements to zero, activate the task associated with it */

  task=0;
  while (task < NUM_TASKS )
    {
    if (task_timers[task])
	   {
  	   task_timers[task]--;            /* dec the timer */
	   if (task_timers[task] == 0 )
	   		{
	    	set_task(task); /* if ==0 activate the task bit */
			}
	   }
    task++;
    }

  task = 0; /* start at the most significant task */
  while (task <= NUM_TASKS )
    {
	  if ((task_bits & pgm_read_byte(&bit_mask[task])))
	  		{
	  		break; /* if activate task found..*/
			}
      task++;         /* else try the next one */
    }
  switch(task)            /* if task bit is active..execute the task */
    {
    case 0:
      task0();
      break;
    case 1:
      task1();
      break;
    case 2:
      task2();
      break;
    case 3:
      task3();
      break;
    case 4:
      task4();
      break;
    case 5:
      task5();
      break;
    case 6:
      task6();
      break;
    case 7:
      task7();
      break;
    default:
      break;                  /* no task was active!! */
    }                       
}

// enable a task for execution
void set_task(char tsk)
{
  task_bits |= pgm_read_byte(&bit_mask[tsk]);       /* sets a task bit */
}
// disable a task from executing
void reset_task(char tsk)
{
  task_bits &= (~pgm_read_byte(&bit_mask[tsk]));  /* resets a task bit */
}

void task0(void)
{
  reset_task(0);
}
void task1(void)
{
  reset_task(1);
}
void task2(void)
{
  reset_task(2);
}
void task3(void)
{
  reset_task(3);
}
void task4(void)
{
  reset_task(4);
}
void task5(void)
{
//	Rtc time and date are displayed here
//  temperature is also displayed here
// I have selected these 2 to be here because they require constant data supply from the modules/sensors

	char Temperature[10];
	float celsius;
	char buffer[40];


    RTC_Read_Clock(0);	/* Read clock with second add. i.e location is 0 */
    sprintf(buffer, "%02x:%02x:%02x ", (hour), minute, second);
    lcd_print_xy(0,0,buffer);

    RTC_Read_Calendar(3);	/* Read calendar with day address i.e location is 3 */
    sprintf(buffer, "%02x/%02x/%02x  ", date,month, year);
    lcd_print_xy(0, 1, buffer);

    lcd_print_xy(1, 0,"TEMPERATURE");
    celsius = (float)ADC_Read(0)*4.88 / 10.0;
    sprintf(Temperature,"%d%cC  ", (int)celsius, DEGREE_SYMBOL);
    lcd_print_xy(1,12,Temperature);

    reset_task(5);
    task_timers[5] = 50;    //run twice per second
}
//
//	flash PORTB.4 at 2hz
//
void task6(void)
{
PORTB ^= (1<<4);
task_timers[6] = 25;		//every 250ms
reset_task(6);
}
//
//	flash PORTB.5 at 1hz
//
void task7(void)
{
PORTB ^= (1<<5);
task_timers[7] = 50;		//every 500ms  
reset_task(7);
}
//call this routine to initialize all peripherals
void init_devices(void)
{
 //stop errant interrupts until set up
 cli(); //disable all interrupts


 DDRB = 0x30;	//port 4 & 5 as outputs

 timer0_init();
 
 sei(); //re-enable interrupts
 //all peripherals are now initialized
}
//TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 10mSec
// actual value: 10.048mSec (-0.5%)
void timer0_init(void)
{
  TCCR0  = 0; //stop
  OCR0A = 77;   //my guess is you're using 8MHz. Change to suit the actual clock rate 
  TCCR0 = (1<<WGM01) | (1<<CS0) | (1<<CS2) //start timer
  
  TIMSK |= (1<<OCIE0);
}

ISR(TIMER0_COMP_vect)       //make sure this is correct - the compiler will complain if it's not
{
 //TIMER0 has overflowed
 	tick_flag = 1;
}

The above is my attempt at 'fixing' the code.

Note - I haven't tested it.

 

Suggestions for making the code better - 

 

write two functions - one to read the DS1307 into a structure. the other to write a structure into the DS1307.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:
If you are just barely getting started with AVRs, why are you starting with something so complicated?
Sorry my condition is a little bit confusing to me. Codes that i can see i can understand them almost all of it but then if i try to build something i have to face a lot of problems which are termed simple clearly this shows lack of practice and you are right in a way that is why i was experimenting with the timer code since yesterday and got quite impressive results.Thanks for guiding me.

Thanks Kartman you are really a kind person that helps beginners soo much I have also worked and was able to use the buttons using the interrupts found on PD2 and PD3 as i need only two buttons..Here is the updated code.
Now i need three things:
1. Transistion of the timer part of this code from atmega168 to atmega32(clocked at 8MHz).
2. To make the outputs behave in the way i have stated below, basically i want to add long delays to the outputs.
3. To use the countdown whenever the buttons are switched ON.
Please give me suggestions and don't do it by yourself. For you guys it could be simple but i want to learn it by myself.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

void init_devices(void);
void timer0_init(void);
void reset_task(char tsk);
void set_task(char tsk);
void task_dispatch(void);
void task0(void);
void task1(void);
void task2(void);
void task3(void);
void task4(void);
void task5(void);

//--------------------------------------------------------------------------------------------------------------
#define DS1307_WRITE_ADDR	0xD0	/* Define RTC DS1307 slave write address */
#define DS1307_READ_ADDR	0xD1	/* Make LSB bit high of slave address for read */
int second,minute,hour,day,date,month,year;
//--------------------------------------------------------------------------------------------------------------
#define DEGREE_SYMBOL 0xdf
//--------------------------------------------------------------------------------------------------------------
#define NUM_TASKS 8
char task_bits = 0;  /* lsb is hi priority task */
volatile char tick_flag = 0;    /* if non-zero, a tick has elapsed */
unsigned int task_timers[NUM_TASKS]={0,0,0,0,0,0,0,0};                  /* init the timers to 0 on startup */
static const PROGMEM char bit_mask[]={1,2,4,8,16,32,64,128};            /* value -> bit mask xlate table */
//---------------------------------------------------------------------------------------------------------------
void RTC_Read_Clock(char read_clock_address)
{
	I2C_Start(DS1307_WRITE_ADDR);/* Start I2C communication with RTC */
	I2C_Write(read_clock_address);	/* Write address to read */
	I2C_Repeated_Start(DS1307_READ_ADDR);/* Repeated start with device read address */

	second = I2C_Read_Ack();	/* Read second */
	minute = I2C_Read_Ack();	/* Read minute */
	hour = I2C_Read_Nack();		/* Read hour with Nack */
	I2C_Stop();			/* Stop i2C communication */
}

void RTC_Read_Calendar(char read_calendar_address)
{
	I2C_Start(DS1307_WRITE_ADDR);
	I2C_Write(read_calendar_address);
	I2C_Repeated_Start(DS1307_READ_ADDR);

	day = I2C_Read_Ack();		/* Read day */
	date = I2C_Read_Ack();		/* Read date */
	month = I2C_Read_Ack();		/* Read month */
	year = I2C_Read_Nack();		/* Read the year with Nack */
	I2C_Stop();			/* Stop i2C communication */
}

void ADC_Init(){
	DDRA = 0x00;	        /* Make ADC port as input */
	ADCSRA = 0x87;          /* Enable ADC, with freq/128  */
	ADMUX = 0x40;           /* Vref: Avcc, ADC channel: 0 */
}

int ADC_Read(char channel)
{
	ADMUX = 0x40 | (channel & 0x07);   /* set input channel to read */
	ADCSRA |= (1<<ADSC);               /* Start ADC conversion */
	while ((ADCSRA & (1<<ADSC)));     /* Wait until end of conversion by polling ADC interrupt flag */
	return ADCW;                       /* Return ADC word */
}

int main(void)
{ 

  init_devices();
//start at least one task here

    set_task(5);

//      main loop 

  while(1)
    {
    if (tick_flag)
      {
      tick_flag = 0;
	  task_dispatch();              // well....
	  }
	}
  return 0;
}
//
//	a task gets dispatched on every tick_flag tick (10ms)
//
void task_dispatch(void)
{
  /* scan the task bits for an active task and execute it */

  char task;

/* take care of the task timers. if the value ==0 skip it
	else decrement it. If it decrements to zero, activate the task associated with it */

  task=0;
  while (task < NUM_TASKS )
    {
    if (task_timers[task])
	   {
  	   task_timers[task]--;            /* dec the timer */
	   if (task_timers[task] == 0 )
	   		{
	    	set_task(task); /* if ==0 activate the task bit */
			}
	   }
    task++;
    }

  task = 0; /* start at the most significant task */
  while (task <= NUM_TASKS )
    {
	  if ((task_bits & pgm_read_byte(&bit_mask[task])))
	  		{
	  		break; /* if activate task found..*/
			}
      task++;         /* else try the next one */
    }
  switch(task)            /* if task bit is active..execute the task */
    {
    case 0:
      task0();
      break;
    case 1:
      task1();
      break;
    case 2:
      task2();
      break;
    case 3:
      task3();
      break;
    case 4:
      task4();
      break;
    case 5:
      task5();
      break;
    default:
      break;                  /* no task was active!! */
    }
}

// enable a task for execution
void set_task(char tsk)
{
  task_bits |= pgm_read_byte(&bit_mask[tsk]);       /* sets a task bit */
}
// disable a task from executing
void reset_task(char tsk)
{
  task_bits &= (~pgm_read_byte(&bit_mask[tsk]));  /* resets a task bit */
}

void task0(void)
{
  reset_task(0);
}
void task1(void)
{
  reset_task(1);
}
void task2(void)
{
  reset_task(2);
}
void task3(void)
{
  reset_task(3);
}
void task4(void)
{
  reset_task(4);
}
void task5(void)
{
//	Rtc time and date are displayed here
//  temperature is also displayed here
// I have selected these 2 to be here because they require constant data supply from the modules/sensors

	char Temperature[10];
	float celsius;
	char buffer[40];

    RTC_Read_Clock(0);	/* Read clock with second add. i.e location is 0 */
    sprintf(buffer, "%02x:%02x:%02x ", (hour), minute, second);
    lcd_print_xy(0,0,buffer);

    RTC_Read_Calendar(3);	/* Read calendar with day address i.e location is 3 */
    sprintf(buffer, "%02x/%02x/%02x  ", date,month, year);
    lcd_print_xy(0, 1, buffer);

    lcd_print_xy(1, 0,"TEMPERATURE");
    celsius = (float)ADC_Read(0)*4.88 / 10.0;
    sprintf(Temperature,"%d%cC  ", (int)celsius, DEGREE_SYMBOL);
    lcd_print_xy(1,12,Temperature);

    reset_task(5);
    task_timers[5] = 50;    //run twice per second
}
//
//	flash PORTB.4 at 2hz
//

    ISR(INT0_vect)//PD2
    {
	PORTD ^= (1<<PD6);		/* Toggle PIND6 */
	_delay_ms(50);  	/* Software debouncing control delay */
        //When switched on this output must turn on for 8mins.
     }

ISR(INT1_vect)//PD3
{
	PORTD ^= (1<<PD7);
	_delay_ms(50);
       //when turned on this output should turn on for 3mins
       // as you can see that is impossible to achieve using delays.
}

//call this routine to initialize all peripherals
void init_devices(void)
{
 //stop errant interrupts until set up
 cli(); //disable all interrupts

 DDRB = 0x30;	//port 4 & 5 as outputs

  timer0_init();
  GICR=0xc0;   //Enable External Interrupts INT0 and INT1
  MCUCR=0x08;  //Configure INT0 active low level triggered and INT1 as falling edge

 sei(); //re-enable interrupts
 //all peripherals are now initialized
}
//TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 10mSec
// actual value: 10.048mSec (-0.5%)
void timer0_init(void)
{
  TCCR0  = 0; //stop
  OCR0A = 77;   //my guess is you're using 8MHz. Change to suit the actual clock rate
  TCCR0 = (1<<WGM01) | (1<<CS0) | (1<<CS2) //start timer

  TIMSK |= (1<<OCIE0);
}

ISR(TIMER0_COMP_vect)       //make sure this is correct - the compiler will complain if it's not
{
 //TIMER0 has overflowed
 	tick_flag = 1;
}

I have written the Countdown code but its not working as it never decreases the minutes and also when the seconds go to 0 they start again from 99 which is confusing as they should return with 59.

ISR(TIMER1_COMPA_vect)
{
	clock_millisecond++;
	if(clock_millisecond==1000)
	{
		clock_second--;
		clock_millisecond=0;
		if(clock_second==-1)
		{
			clock_minute--;
			clock_second=59;

			if(clock_minute==-1)
			{
				clock_hour--;
				clock_minute=59;
			}
		}
	}
}

Thanks for the precious answers you have posted really appreciate you guys..

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Firstly, do not use interrupts for your pushbuttons! For some reason, many beginners think this is what interrupts are for! You might want to read my other tutorial on the 'traps when using interrupts'. Put simply - interrupts are for when you need microsecond response to events and detecting button presses is in the range of 10's of milliseconds. The other compelling reason is that mechanical switches 'bounce' and for each press/release of the button, you get a number of interrupts.

 

The whole basis of my little multitasking framework is to give you a way of managing timing. Timer0 is already giving you 10ms ticks so why use another hardware timer to give you 1ms ticks that you divide down to seconds? You've consumed another hardware timer and used another interrupt source for no gain.

 

 

For your countdown code, you have two architectural choices - do you manage the countdown as a count of seconds? ie: use one variable or use multiple variables like you have. Either way is valid, but you might want to consider how you use this. Personally, I'd would lean towards using one variable as a seconds downcounter. Managing the countdown is easy:

uint32_t secondsCountdown = 0;
#define TICKS_PER_SECOND 100

//in main where you start the tasks..

    task_timers[0] = TICKS_PER_SECOND;


// run every 1 second
void task0(void)
{
    if (secondsCountdown)
        {
            secondsCountdown--;
        }
    task_timers[0] = TICKS_PER_SECOND;
    reset_task(0);
}

 

To handle the debounce of your pushbuttons, read the port pins every 10 milliseconds and create a downcounter for each input. If the button is not pressed, load the downcounter, with, say 5 (5 time 10ms = 50ms). If the button is pressed, decrement the downcounter to 0.  You other code can read the downcounter. If it is 0, then the button is pressed and debounced. No need for any extra timers or interrupts. As well, because your code runs as a single thread, there is no issue with 'atomicity' so you avoid a number of tricky problems.

 

 

Think of the process as to how a chef works - he divides the meal into a number of small tasks - whilst something is in the oven, he can be chopping the vegetables. He can check the oven regularly and when that is done, he can put whatever on the bench to cool. Whilst cooling he can be boiling the vegetables and so on. This way he can do many things at 'once'

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Russell, you can come and cook at our place anytime you get the urge. cheeky

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Ross, I think my skills are more aligned to the 'Swedish chef'!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello to everyone again I have been guided again and again to divide all the programming (tasks) into chunks which means to use the task_n fucntions Kartman has created. I have come a long way and learned many things here is what i have done to debounce the switches.


#include <avr/io.h>
#include <avr/interrupt.h>

typedef unsigned char	u8;

#define KEY_PIN		PIND
#define KEY0		PD0
#define KEY1		PD1

#define LED_DDR		DDRD
#define LED0		PD6
#define LED1		PD7

u8 key_state;				// debounced and inverted key state:
// bit = 1: key pressed
u8 key_press;				// key press detect

ISR (TIMER0_OVF_vect)			// every 10ms
{
	static u8;
	u8 i;

	TCNT0 = (u8)-(8000000 / 1024 * 10e-3 + 0.5);	// preload for 10ms

	i = key_state ^ ~KEY_PIN;		// key changed ?
	key_state ^= i;			// then toggle debounced state
	key_press |= key_state & i;		// 0->1: key press detect
}

u8 get_key_press( u8 key_mask )
{
	cli();					// read and clear atomic !
	key_mask &= key_press;                        // read key(s)
	key_press ^= key_mask;                        // clear key(s)
	sei();
	return key_mask;
}

int main( void )
{
	TCCR0 = 1<<CS02^1<<CS00;			// divide by 1024
	TIMSK = 1<<TOIE0;				// enable timer interrupt

DDRD = 0xf0;	// 0=In  1=In  2=In  3=In  4=Out 5=Out 6=Out 7=Out
PORTD = 0x0f;	// 0=Hi  1=Hi  2=Hi  3=Hi  4=Low 5=Low 6=Low 7=Low
	sei();

	while(1){					// main loop
		// single press

		if( get_key_press( 1<<KEY0 ))
		{
		DDRD ^= (1<<LED0);
		}

		if( get_key_press( 1<<KEY1 ))
		{
		DDRD ^= (1<<LED1);
		}

	}
}

Please tell me why this compiles without an error but doesn't toggle the outputs.

 

Another thing that I have implemented is to use a seconds downcounter which is an awesome idea, one of my earlier projects ran flawlessly due to this advice(Thanks to Kartman and David.prentice), I have found a code written by Clawson in another discussion on the site and used it as given below:-

#include <avr/io.h>
#include <avr/interrupt.h>

#include "lcd.h"

//Connection of Load
#define LOAD_DDR DDRC
#define LOAD_PORT PORTC
#define LOAD_POS PC0

//Global variable for the clock system

volatile char        clock_second=0;
volatile char        clock_minute=0;
uint32_t seconds_to_go;

void Wait(uint8_t n)
{

	uint8_t i,temp;
	temp=n*28;

	for(i=0;i<temp;i++)
	_delay_loop_2(0);
}

void LoadOn()
{
	LOAD_PORT|=(1<<LOAD_POS);
}

void LoadOff()
{
	LOAD_PORT&=(~(1<<LOAD_POS));
}
int main(void)
{

	while(1)
	{
		LOAD_DDR|=(1<<LOAD_POS);

		LoadOff();

		//Enable Pullups on Keypad

		PORTB|=((1<<PB2)|(1<<PB1)|(1<<PB0));

		int8_t min,sec; //Target Time
		min=sec=0;

		//Initialize the LCD Subsystem
		InitLCD(0);
		//Clear the display

		LCDClear();

		//Set up the timer1 as described in the
		//tutorial
		TCCR1B=(1<<WGM12)|(1<<CS11)|(1<<CS10);
		OCR1A=250;

		//Enable the Output Compare A interrupt

		TIMSK|=(1<<OCIE1A);

		//Enable interrupts globally
		sei();

		LCDClear();
		LCDWriteString("    Welcome     ");
		LCDWriteStringXY(0,1,"   Relay Timer  ");

		Wait(4);

		LCDClear();
		LCDWriteString("Set Time - 00:00");
		LCDWriteStringXY(0,1," Start     ^");

		uint8_t selection=1;
		uint8_t old_pinb=PINB;

		while(1)
		{
			while((PINB & 0b00000111) == (old_pinb & 0b00000111));

			//Input received

			if(!(PINB & (1<<PINB2)) && (old_pinb & (1<<PB2)))
			{
				//Selection key Pressed

				selection++;
				if(selection==3)
				selection =0;
			}

			if(!(PINB & (1<<PINB1)) && (old_pinb & (1<<PB1)))
			{
				//Up Key Pressed

				if(selection == 1)
				{

					//Hour is selected so increment it
					min++;

					if(min == 100)
					min =0;
				}

				if(selection == 2)
				{

					//Min is selected so increment it

					sec++;

					if(sec == 60)
					sec =0;
				}

				if(selection == 0)
				{
					//Start Selected
					break;
				}

			}

			if(!(PINB & (1<<PINB0)) && (old_pinb & (1<<PB0)))
			{
				//Down Key Pressed

				if(selection == 1)
				{

					//Hour is selected so decrement it
					min--;

					if(min == -1)
				 min =99;
				}

				if(selection == 2)
				{

					//Min is selected so decrement it

					sec--;

					if(sec == -1)
					sec =59;
				}

				if(selection == 0)
				{
					//Start Selected
					break;
				}

			}

			old_pinb=PINB;

			//Update Display

			LCDClear();
			LCDWriteString("Set Time - 00:00");
			LCDWriteStringXY(0,1," Start    ");

			//Hour
			LCDWriteIntXY(11,0,min,2);

			//Minute

			LCDWriteIntXY(14,0,sec,2);

			if(selection == 0)
			LCDWriteStringXY(0,1,">");

			if(selection == 1)
			LCDWriteStringXY(11,1,"^");

			if(selection == 2)
			LCDWriteStringXY(14,1,"^");

			_delay_loop_2(0);
			_delay_loop_2(0);
			_delay_loop_2(0);
			_delay_loop_2(0);

			_delay_loop_2(0);
			_delay_loop_2(0);
			_delay_loop_2(0);
			_delay_loop_2(0);
		}

		//Start the Load

		LoadOn();

		//Now start the timer
		clock_minute = min;
		clock_second = sec;
		seconds_to_go = ((min * 60) + sec);

		LCDClear();
		LCDWriteString("  Power Off In ");

		while(1)
		{
			clock_minute = seconds_to_go / 60;
			clock_second = seconds_to_go % 60;
			LCDWriteIntXY(4,1,clock_minute,2);
			LCDWriteString(":");
			LCDWriteIntXY(7,1,clock_second,2);

			if((clock_minute == 0) && (clock_second == 0))
			{
				//Time Out

				LoadOff();

				LCDClear();
				LCDWriteString("Load Turned Off");

				while(1)
				{
					LCDWriteStringXY(0,1,"*Press Any Key*");

					Wait(1);

					LCDWriteStringXY(0,1,"                ");

					Wait(1);

					if((~PINB) & 0b00000111)
					break;

				}

				break;

			}

			_delay_loop_2(0);
			_delay_loop_2(0);
			_delay_loop_2(0);
			_delay_loop_2(0);
		}
		//Continue again
	}
}

//The output compate interrupt handler
//We set up the timer in such a way that
//this ISR is called exactly at 1ms interval
ISR(TIMER1_COMPA_vect)
{
	--seconds_to_go;
	if(clock_second == 0)
	{
		--clock_minute;
		clock_second = 59;
	}

}

This has a problem it doesn't reach zero although i have told it to do so.

Thanks again for replying and giving me your precious time I can feel that i am very near to execute this project flawlessly.)

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ignore the button debouncing code i have posted above because that one doesn't work because i havn't use the timer correctly now i have corrected that and made that debouncing code work.Here is the updated code:-


#include <avr/io.h>
#include <avr/interrupt.h>

typedef unsigned char	u8;
typedef signed short	s16;

#define	XTAL		8e6		// 8MHz

#define KEY_PIN		PIND
#define KEY0		0
#define KEY1		1

#define LED_DDR		DDRD
#define LED_PORT    PORTD
#define LED0		6
#define LED1		7

#define REPEAT_MASK	(1<<KEY0^1<<KEY1)	// repeat: key1, key2

u8 key_state;				// debounced and inverted key state:
// bit = 1: key pressed
u8 key_press;				// key press detect

ISR (TIMER0_OVF_vect)			// every 10ms
{
	static u8 ct0, ct1;
	u8 i;

	TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);	// preload for 10ms
	//10e-3 means (10 * (10^-3))

	i = key_state ^ ~KEY_PIN;		// key changed ?
	ct0 = ~( ct0 & i );			// reset or count ct0
	ct1 = ct0 ^ (ct1 & i);		// reset or count ct1
	i &= ct0 & ct1;			// count until roll over ?
	key_state ^= i;			// then toggle debounced state
	key_press |= key_state & i;		// 0->1: key press detect

}

u8 get_key_press( u8 key_mask )
{
	cli();					// read and clear atomic !
	key_mask &= key_press;                        // read key(s)
	key_press ^= key_mask;                        // clear key(s)
	sei();
	return key_mask;
}

int main( void )
{
	TCCR0 = 1<<CS02^1<<CS00;			// divide by 1024
	TIMSK = 1<<TOIE0;				// enable timer interrupt
	LED_PORT = 0x0F;
	LED_DDR = 0xF0;

	sei();

	for(;;){					// main loop
		// single press

		if( get_key_press( 1<<KEY0 ))
		LED_PORT ^= 1<<LED0;

		if(get_key_press(1<<KEY1))
		LED_PORT  ^= 1<<LED1;
	}
}

Please tell me if there is room for any change.

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

One step forward, two steps back! You had a perfectly good example of how to set the timer up and then you go and do something completely different. I also gave you an example of how to avoid running code in the interrupt context.

I think my contribution ends here.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know about anyone else but I find that code impossible to read/follow. There is a term in programming called "cyclomatic complexity" and this code way exceeds a safe threshold meaning that it is over-nested and impossible to (easily) follow the flow of execution. In fact pretty much any code that puts two while(1)s in the body of a third while(1) probably has something seriously wrong in the design.

 

I'd consider refactoring to a coherent design with "jobs" split into functions and one, clear, main path of execution.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the posts, by the advice of both of you, you guys  are clear that I require more practice to execute this code in a manner that i need it to behave. May be I should start with something simple next time and behave like a good newbie..By the way thank you guys for the precious time you have given , I am closing this discussion and marking Kartman's reply #4 as the solution as according to him and even I feel that that code is enough for multitasking the Avr. Thanks once again.smiley

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One last thing that I want to say is that can someone please give me a list or a order of things that i must learn in order to learn avr. Now I try to learn everything because of the problem that i face and its hard to overcome problems because some basics are left. Can someone help me?? I am asking for you know that you must learn first the I/o s and then the lcd interface sort of list you must be getting what i want.smiley

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:
One last thing that I want to say is that can someone please give me a list or a order of things that i must learn in order to learn avr.
Well I'd start by learning general programming technique and you can do that on any platform - not just AVR. The key thing is to take time out up-front to plan the design of any program before rushing to attempt some implementation because if you try to "bolt bits in afterwards" you end up with confused/complex code that it's difficult to follow and maintain (for you or anyone else).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Take a look here, this will get you started for AVR

http://www.avr-asm-tutorial.net/avr_en/beginner/index.html

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Well I'd start by learning general programming technique and you can do that on any platform - not just AVR

Thanks i will try.

avrcandies wrote:
Take a look here, this will get you started for AVR

Thanks again.

Anshumaan

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are just starting AVR & just starting programming, NONE of your code should exceed maybe 10-20 lines of real code (ignoring defines, etc).  Doing too much at once doesn't give you the correct initial learning, as you end up spending 90% of your time tracking down troubles.

Keep your code short & simple to learn about the AVR.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:

If you are just starting AVR & just starting programming, NONE of your code should exceed maybe 10-20 lines of real code (ignoring defines, etc).  Doing too much at once doesn't give you the correct initial learning, as you end up spending 90% of your time tracking down troubles.

Keep your code short & simple to learn about the AVR.

You are absolutely correct this is what I have to do most of my time. So unfortunate but now I have the learning routine from which I can learn. Thanks to you for helping me soo much.

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another thing I want to ask here, Well as you have said that my de-bouncing was poor hence I was searching ways to improve that and today I found the danni's code for perfect debouncing and how impressively it works it was a tutorial but Kartman has said the same thing before here:-

Kartman wrote:
To handle the debounce of your pushbuttons, read the port pins every 10 milliseconds and create a downcounter for each input. If the button is not pressed, load the downcounter, with, say 5 (5 time 10ms = 50ms). If the button is pressed, decrement the downcounter to 0.  You other code can read the downcounter. If it is 0, then the button is pressed and debounced. No need for any extra timers or interrupts. As well, because your code runs as a single thread, there is no issue with 'atomicity' so you avoid a number of tricky problems.

Sorry Kartman at that time I was not able to understand what you have said but in the recent days I have learned about timers and have done the following to the Multitask code that was posted in the tutorial, Please anyone can explain that why this compiles without error but lcd cries that it was busy when it received another command ??I had no idea??

//
//    Co-operative multitasking framework tutorial code
//    (c)Russell Bull 2010. Free for any use.
//    Code built for a Mega168 @ 16MHz

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include "I2C_Master_H_file.h" //for rtc
#include "lcd.h"  //for the lcd 
#include "debounce.h" //for button debounce

void init_devices(void);
void timer_0_init(void);
void reset_task(char tsk);
void set_task(char tsk);
void task_dispatch(void);
void task0(void);
void task1(void);
void task2(void);
void task3(void);
void task4(void);
void task5(void);
void task6(void);
void task7(void);
//--------------------------------------------------------------------------------------------------------------
#define DS1307_WRITE_ADDR    0xD0    /* Define RTC DS1307 slave write address */
#define DS1307_READ_ADDR    0xD1    /* Make LSB bit high of slave address for read */
int second,minute,hour,day,date,month,year;
//--------------------------------------------------------------------------------------------------------------
#define DEGREE_SYMBOL 0xdf
//--------------------------------------------------------------------------------------------------------------
#define NUM_TASKS 8
char task_bits = 0;  /* lsb is hi priority task */
volatile char tick_flag = 0;    /* if non-zero, a tick has elapsed */
unsigned int task_timers[NUM_TASKS]={0,0,0,0,0,0,0,0};                  /* init the timers to 0 on startup */
static const PROGMEM char bit_mask[]={1,2,4,8,16,32,64,128};            /* value -> bit mask xlate table */
//---------------------------------------------------------------------------------------------------------------
void RTC_Read_Clock(char read_clock_address)
{
    I2C_Start(DS1307_WRITE_ADDR);/* Start I2C communication with RTC */
    I2C_Write(read_clock_address);    /* Write address to read */
    I2C_Repeated_Start(DS1307_READ_ADDR);/* Repeated start with device read address */

    second = I2C_Read_Ack();    /* Read second */
    minute = I2C_Read_Ack();    /* Read minute */
    hour = I2C_Read_Nack();        /* Read hour with Nack */
    I2C_Stop();            /* Stop i2C communication */
}

void RTC_Read_Calendar(char read_calendar_address)
{
    I2C_Start(DS1307_WRITE_ADDR);
    I2C_Write(read_calendar_address);
    I2C_Repeated_Start(DS1307_READ_ADDR);

    day = I2C_Read_Ack();        /* Read day */
    date = I2C_Read_Ack();        /* Read date */
    month = I2C_Read_Ack();        /* Read month */
    year = I2C_Read_Nack();        /* Read the year with Nack */
    I2C_Stop();            /* Stop i2C communication */
}

void ADC_Init(){
    DDRA = 0x00;            /* Make ADC port as input */
    ADCSRA = 0x87;          /* Enable ADC, with freq/128  */
    ADMUX = 0x40;           /* Vref: Avcc, ADC channel: 0 */
}

int ADC_Read(char channel)
{
    ADMUX = 0x40 | (channel & 0x07);   /* set input channel to read */
    ADCSRA |= (1<<ADSC);               /* Start ADC conversion */
    while ((ADCSRA & (1<<ADSC)));     /* Wait until end of conversion by polling ADC interrupt flag */
    return ADCW;                       /* Return ADC word */
}

int main(void) 
{ 

  init_devices();
//start at least one task here

    set_task(5);

//set_task(7);    //task7 runs
//set_task(6);    //task6 runs

//      main loop 

  while(1)
    {
    if (tick_flag)
      {
      tick_flag = 0;
      task_dispatch();              // well.... 
      }
    }
  return 0;
} 
//
//    a task gets dispatched on every tick_flag tick (10ms)
//
void task_dispatch(void)
{
  /* scan the task bits for an active task and execute it */

  char task;
    

/* take care of the task timers. if the value ==0 skip it
    else decrement it. If it decrements to zero, activate the task associated with it */

  task=0;
  while (task < NUM_TASKS )
    {
    if (task_timers[task])
       {
         task_timers[task]--;            /* dec the timer */
       if (task_timers[task] == 0 )
               {
            set_task(task); /* if ==0 activate the task bit */
            }
       }
    task++;
    }

  task = 0; /* start at the most significant task */
  while (task <= NUM_TASKS )
    {
      if ((task_bits & pgm_read_byte(&bit_mask[task])))
              {
              break; /* if activate task found..*/
            }
      task++;         /* else try the next one */
    }
  switch(task)            /* if task bit is active..execute the task */
    {
    case 0:
      task0();
      break;
    case 1:
      task1();
      break;
    case 2:
      task2();
      break;
    case 3:
      task3();
      break;
    case 4:
      task4();
      break;
    case 5:
      task5();
      break;
    case 6:
      task6();
      break;
    case 7:
      task7();
      break;
    default:
      break;                  /* no task was active!! */
    }                       
}

// enable a task for execution
void set_task(char tsk)
{
  task_bits |= pgm_read_byte(&bit_mask[tsk]);       /* sets a task bit */
}
// disable a task from executing
void reset_task(char tsk)
{
  task_bits &= (~pgm_read_byte(&bit_mask[tsk]));  /* resets a task bit */
}

void task0(void)
{
    
  reset_task(0);
}
void task1(void)
{
    if (button_down(BUTTON1_MASK))
    {
      
        PORD ^= 1<<PD4;
    }
  reset_task(1);
}
void task2(void)
{
    if (button_down(BUTTON2_MASK))
    {
      
        PORTD ^= 1<<PD7;
    }
  reset_task(2);
}
void task3(void)
{
      char Temperature[10];
      float celsius;
      mylcd_string(1, 0,"TEMPERATURE");
      celsius = (float)ADC_Read(0)*4.88 / 10.0;
      sprintf(Temperature,"%d%cC  ", (int)celsius, DEGREE_SYMBOL);
      mylcd_string(1,12,Temperature);
      reset_task(3);
}
void task4(void)
{
    
    reset_task(4);
}
void task5(void)
{
//    Rtc time and date are displayed here

    char buffer[40];
    RTC_Read_Clock(0);    /* Read clock with second add. i.e location is 0 */
    sprintf(buffer, "%02x:%02x:%02x ", (hour), minute, second);
    mylcd_string(0,0,buffer);
    RTC_Read_Calendar(3);    /* Read calendar with day address i.e location is 3 */
    sprintf(buffer, "%02x/%02x/%02x  ", date,month, year);
    mylcd_string(0, 1, buffer);
    reset_task(5);
    task_timers[5] = 50;    //run twice per second
}

void task6(void)
{

reset_task(6);
}

void task7(void)
{

reset_task(7);
}

//call this routine to initialize all peripherals
void init_devices(void)
{
 //stop errant interrupts until set up
 cli(); //disable all interrupts
    DDRA = 0x00;//for temperature sensor
    DDRB = 0xff;//outputs for lcd
    PORTB = 0x00;
    PORTC = 0x00; // for rtc chip ds1307
    DDRC  = 0x00;
    PORTD = 0x0f;    //pull-up enables for 3 pushbuttons
    DDRD  = 0xf0;    //2 o/p for relays, 1 o/p for LED, 1 o/p for TXD and 3 i/p for pushbuttons

 timer_0_init();
 debounce_init();
 
 sei(); //re-enable interrupts
 //all peripherals are now initialized
}
//TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 10mSec
// actual value: 10.048mSec (-0.5%)
void timer_0_init(void)
{
 TCCR0 = 0x00; //stop
 TCNT0 = 0x00; //set count
 TCCR0 = 0x02;    //CTC mode
 OCR0 = 0x9C;
 TCCR0 = 0x05; //start timer
}

ISR(TIMER0_COMP_vect)       //make sure this is correct - the compiler will complain if it's not
{
 //TIMER0 has overflowed
     tick_flag = 1;
     debounce();
}

I am using peter's fluery lcd library and danni's library for button debounce both of which great worked on my other projects.

Also please note that it compiles with 6 warnings all of which says the same thing:

array subscript has type char

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

array subscript has type char

 

It's easier if you post the message as text - copy & paste.

 

The [-Wchar-subscripts] on the end is telling you the option which would suppress this message.

 

If you take that (without the leading hyphen), and put it into your favourite Internet Search Engine, that will take you to a description of the message.

 

The GCC Documentation wrote:

-Wchar-subscripts

Warn if an array subscript has type char. This is a common cause of error, as programmers often forget that this type is signed on some machines.

 

https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Warning-Options.html

 

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Wed. Dec 4, 2019 - 05:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

If you take that (without the leading hyphen), and put it into your favourite Internet Search Engine, that will take you to a description of the message.

 

 

oh, a new feature I learned today, I always copied the whole message and then search for the more relevant answer, 

thanks.

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anything else that I need to do to make this code (#35)running??Anyone??

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:
I always copied the whole message and then search

That also works - so you should already have had the answer to the question ... ?

 

https://www.google.com/search?q=array+subscript+has+type+%27char%27

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:
lcd cries that it was busy when it received another command

Real LCDs don't cry!

 

Presumably, you mean some Simulation ?

 

So use the facilities of the Simulator to see what's going wrong:

  1. What does your Simulator think the timing limits are?
  2. What timing does your Simulator think your code is giving?
  3. Presumably, the Simulator is correct in checking (2) against (1) ?

 

And then check:

  1. What timing is your real code giving?
  2. What are the actual timing limits of your real LCD?
  3. How do (1) and (2) compare?

 

 

EDIT

 

Separate real from Simulation

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Dec 5, 2019 - 10:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahh. Yes I have been given the  answer on how to multitask the avr chip and also got the answer about the warnings (Thanks to you, awneil). But I have tried the code suggested for multitasking and hence I want the answer what could go wrong here because according to me the code is now correct(doesn't show any error) but doesn't run on the hardware / simulation so I wanted to ask about the problem in the code. The warning stuff that I told you is because I don't know If that was causing the problem, let me give you guys what I have so that you can think the possible mistakes I could have done.

Thanks for replying I thought I have to create a new thread.laugh

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

Real LCDs don't cry!

 

Presumably, you mean some Simulation ?

Yup, you have made that sound funny I was just saying that the simulation said that the lcd received command when it was busy.

 

awneil wrote:
Presumably, the Simulator is correct in checking (2) against (1)

Yes it's correct I can vouch for that.

awneil wrote:

And then check:

  1. What timing is your real code giving?
  2. What are the actual timing limits of your real LCD?
  3. How do (1) and (2) compare?

 

1. Well I am not sure what are you talking about in "1." you can explain me a little about "1."

2.And you must be talking about the time required for the lcd to process a command well I have solved that by using a good library and this works everytime but in other projects and not this one ,

so I think I must be doing something else wrong which is hidden from me .

3.

awneil wrote:
How do (1) and (2) compare?

Didn't know about "1." I am a little bit confused about this , but for "2." I can refer to the datasheet of my lcd I am using (HD44780).

 

 

awneil wrote:
Separate real from Simulation

Ok. I will test this on my hardware today.

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anshumaan wrote:
I thought I have to create a new thread.laugh

A new thread is, obviously, better for a new question.

 

 I want the answer what could go wrong here because according to me the code is now correct(doesn't show any error) but doesn't run

The fact that code compiles is absolutely no guarantee whatsoever that it will actually work !

 

This is basic programming - nothing specific to 'C' or AVR or microcontrollers.

 

Compiling without errors just means that you have not broken any of the language syntax rules - it tells you nothing about whether the code will function as required.

 

 

 

EDIT

 

doesn't run

Of course, the code does run - it is just not (quite) doing what is needed.

 

So you need to debug it:

 

https://www.avrfreaks.net/commen...

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Dec 5, 2019 - 10:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
it tells you nothing about whether the code will function as required

Yes , but I think the simulation or the hardware can tell, as far as I am concerned I cannot detect my problem hence I have asked and as you say I will create a new thread. Thanks for guiding me .smiley

anshumaan kumar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have the simulator - so you need to use  it to investigate what is happening, and see how that differs from what should be happening.

 

That, in a nutshell, is debugging. Again, it is an inherent part of the development process; you cannot palm it off to others - it is part of your job.

 

If you have a simulator, then use it to see what is happening.

 

That's the whole point of a simulator - it is (or should be) far easier to probe and investigate than having to use real test instruments on real hardware.

 

And if the simulator is not easier - then throw it away and use use real test instruments on real hardware!

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh I see , sorry I was unable to understand you at first , yes you are right It is my job to provide what I observe in the simulator sorry for not posting that earlier, I had forgotten that.

anshumaan kumar