Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Timer Interrupt of AVR

Status
Not open for further replies.
Hi,

With your formula you can calculate R for a given C and a given cutoff frequency.

With my formula fc is an independent parameter i called filter constant, it only describes the behavior of the function.
It is not tau. (Tau = R x C).
The output valus of my function are similar to an RC filter, increasing and decreasing.

If you tell me if how often you want to calculate the filtered value, then i can calculate the parameter.

Klaus
 
Dear KlausST,

This filter would be connected to input of ADC channel and please calculate its parameter.
 

Hi Imran,

now i know that the ADC clock is 125kHz.
But i need to know how often per second do you start a conversion, this is called sampling frequency.

I think it is only some times per second. so try to replace fc = 0.125 in my formula.

Good luck
Klaus
*****************
Some background information:
Usually when i do measurement with ADC, then i set up a timer that starts the ADC with a fixed timing, lets say 200 times per second.

With the M8 this can be done by setting up a timer in ctc mode, within the ISR start the ADC conversion and leave the ISR (this needs only a few us).

When the ADC conversion is finished another ISR starts. Within this ISR read in the ADCvalue and do simple filter calculations. (some 10 us).
After every 50 conversions (four times per second) set a flag to indicate to the main loop that its time to display a new value(some additional us). And leave the ISR.

Within the main loop poll the flag and do a display update when flag is set, then clear the flag.

The benifit is:
* The ADC has a fixed timing. It converts 200 times per second. = exactely every 5ms.
* the display update is also with a fixed timing.
* Because of the filtering you get a better resolution than the ADC has. You can easily get 14 bits of resolution from a 10 bit ADC. (but not necessarily better accuracy)
* Often one has problems with influence of mains frequency, with a fixed samplefrequency of an integer multiple of lines frequency you can filter that easily out.
* There is plenty of time in the main loop to do what you want, you just have to be sure to update the display every 250ms.
* Also this is a good solution for battery powered tools because you can set the ucontroller into sleep mode as often as possible, and in the same
time you can be sure not to miss a single conversion.
 

Dear KlausST,

Extremely definable.
Thank you.

1. The ADC clock is set to 125kHz means time period=8us and ADC set to Single conversion mode.
2. First conversion needs 25 clock cycles and then next takes 13 clock cycles.
3. 8x25=200us for first conversion and 8x13=104us for next conversions.
4. Do you means that first ISR do ADC conversion and next ISR reads ADC_value

After every 50 conversions (four times per second) set a flag to indicate to the main loop that its time to display a new value(some additional us). And leave the ISR.

Please more clarify above quote.
 

Hi Imran,

With your adc you have a resolution of 1024 steps. With a 5V Ref this means about 5mV. If you test your voltmeter you will see different voltage readings every time you do a conversion.
Maybe 334, 331, 333, 335, 334, 333, 334, 332, 334, 333....
Here you can see values from 331 to 335 this is not nice and often confusing,

To smooth this you can do several readings and filter them.
With my filter and fc value of 0.125 you get the folowing readings (Dv starts with 0)
dv(old); adcvalue; dv(new) --> display
0; 334; 41.75
41.75; 331; 77.90
77.90; 333; 109.79
109.79; 335; 137.94
137.94; 334; 162.45
162.94; 333; 183.77
183.77; 334; 202.55
202.55; 332; 218.73
218.73; 334; 233.14
233.14; 333; 245.62
And so on...

So look on the values on the right column. This is what i thought you want as you wrote " slowly increasing display" .
I'm sorry if i misunderstood...

Because the disussion is o. t. we should go on with p.m.

Klaus
 

Dear KlausST,

Thank you very much now I am understanding that how to control display ,isr , adc efficiently with your co-operation.

I wrote its code and discuss with you.

Please let me know that what is your country.

Our time is GMT+5.
 

Dear KlausST,

I set the interrupt in ctc mode every 5ms.

Code:
unsigned char  flag, dv=0, av=330, fc=0.125;

void InitTimer1(){
  
  TCCR1B = 0x09;
  OCR1AH = 0x9C; 
  OCR1AL = 0x3F;   
  TIMSK  = 0x10; 
}
uint16_t read_adc(uint8_t ch)
{
   
   //Select ADC Channel ch must be 0-7
     ADMUX =  (ADMUX & 0xF8) | ch;
 
     return(ADCW);
}

ISR(TIMER1_COMPA_vect) 
 { 
     ADCSRA|=(1<<ADSC);
     while(!(ADCSRA & (1<<ADIF)));
     ADCSRA|=(1<<ADIF);
	
	 dv=read_adc(1); 
	 dv = dv + ( av - dv) * fc ;	  
      }
main()
{
while(1)
{
Print1(dv);
}

}
 

Hi Imran,

I´m not that familiar in writing C code, although i can read it :-(
So you have to translate my writing in true C code. Sorry for that. i marked the lines with an #

*****


Code:
unsigned char  flag, dv=0, av=0, fc=0.01;
#mcount uint8 = 0
#dispv float =0

void InitTimer1(){
  
  TCCR1B = 0x09;
  OCR1AH = 0x9C; 
  OCR1AL = 0x3F;   
  TIMSK  = 0x10; 
}
uint16_t read_adc(uint8_t ch)
{
   
   //Select ADC Channel ch must be 0-7
     ADMUX =  (ADMUX & 0xF8) | ch;
 
     return(ADCW);
}

ISR(TIMER1_COMPA_vect) 
 { 
     ADCSRA|=(1<<ADSC);
     while(!(ADCSRA & (1<<ADIF)));
     ADCSRA|=(1<<ADIF);
	
	 av=read_adc(1); 
	 dv = dv + ( av - dv) * fc ;	
# mcount ++ ; increment counter
# if mcount == 100 ; 100 x 5ms = 500ms, 100 adcvalues are filtered and now ready to display
# {
# mcount = 0 ; clear counter
# dispv = dv ; give the value to the main loop
# flag = 1 ; infor the main loop to display new value
# }  
      }
main()
{
while(1)
{
# if flag == 1 ; wait until flag is set (every 100 adc readings = 500ms)
# { 
# Print1(dispv); Mind this is a float and you can display it as "0000.0" because of better resolution
# flag == 0 ; clear the flag
# }
}

}
 
Thank you.

Dear KlausST, In my voltmeter, I do not need floating values to display only integer is sufficient for reading.

- - - Updated - - -

Dear KlausST,

I treated it as you describe but display shows 0 volts and I am :thinker:

Code:
unsigned char  flag, dv=0, av=0;
unsigned char mcount  = 0
float dispv =0 , fc=0.01;

void InitTimer1(){
  
  TCCR1B = 0x09;
  OCR1AH = 0x9C; 
  OCR1AL = 0x3F;   
  TIMSK  = 0x10; 
}
uint16_t read_adc(uint8_t ch)
{
   
   //Select ADC Channel ch must be 0-7
     ADMUX =  (ADMUX & 0xF8) | ch;
 
     return(ADCW);
}

ISR(TIMER1_COMPA_vect) 
 { 
     ADCSRA|=(1<<ADSC);
     while(!(ADCSRA & (1<<ADIF)));
     ADCSRA|=(1<<ADIF);
	
	 av=read_adc(1); 
	 dv = dv + ( av - dv) * fc ;	  
         mcount++;
	 if(mcount==100)
	 {
	 mcount=0;
         dispv=dv;
         flag=1;
	 }  
         }
main()
{
while(1)
   {
   if(flag==1)
   {
   Print1(dispv);
   flag=0;  
   }	
   }   
}
 

Hi,

Because of calculating with values of 0.01 i think dv must be a float.
Is char a 16 bit or 8bit ? av has to be 16 bit.

Klaus

- - - Updated - - -

Hi,

Because of calculating with values of 0.01 i think dv must be a float.
Also fc must be a float.
Is char a 16 bit or 8bit ? av has to be 16 bit.

Klaus
 

Dear KlausST,

char is 8-bit and integer i.e int is 16-bit
Ok I try.

- - - Updated - - -

Dear KlausT,

The only float of any value is showed but in my code it is not display any voltages
 

Hi,

Can you check if your display update happens exactely every 500ms.

Please confirm that dv, fc is float
Av is 16 bit int.

Klaus
 

Dear KlausST,

Yes it is working thank you dear.

Code:
//unsigned char  flag;
unsigned char mcount=0;
unsigned int av=0;
float dispv=0,fc=0.01,dv=0;

void InitTimer1(){
  
  TCCR1B = 0x09;
  OCR1AH = 0x9C; 
  OCR1AL = 0x3F;   
  TIMSK  = 0x10; 
}
uint16_t read_adc(uint8_t ch)
{
   
   //Select ADC Channel ch must be 0-7
     ADMUX =  (ADMUX & 0xF8) | ch;
 
     return(ADCW);
}

ISR(TIMER1_COMPA_vect) 
 { 
     ADCSRA|=(1<<ADSC);
     while(!(ADCSRA & (1<<ADIF)));
     ADCSRA|=(1<<ADIF);
	
	 av=read_adc(1); 
	 dv = dv + ( av - dv) * fc ;	  
       
         mcount++;
	 if(mcount==50)
	 {
	 mcount=0;
         dispv=dv;
         } 
 

         }
main()
{
while(1)
{
    
Print1(dispv);
 
}   
}
 
Last edited:

Hi Imran,

Things getting better ...

If you want more time for reading the display, then let mcount count up to 200. Then you get a new value every second

If you want to increase the value more slowly, then decrease fc. Try 0.003.

Just play around with different values and see what happens.

Klaus

- - - Updated - - -

Hi,

I just saw that you don't use the mcount and flag....
--> For sure it is very fast then.....

How often do you like to "print". I recommend to do this with fixed delay.
Otherwise you may loose data or get corrupt data..

Klaus
 

How often do you like to "print". I recommend to do this with fixed delay.
Otherwise you may loose data or get corrupt data..

Dear KlausST,

Please let me know more clearly about above quote.

- - - Updated - - -

Dear KlausST,

Where you live?
Do you tell me your Email ID or Skype or facebook ID?

- - - Updated - - -

Dear KlausST,

One thing please let me know that if I want to read two or three channels simultaneously so how treat it.


I just saw that you don't use the mcount and flag....
--> For sure it is very fast then.....

I used mcount but flag eliminated.
 

Hi Imran,

I live in germany, now we have gmt+2, dls included.

.......

... How often do you want to print....
With your code the "print1" function is called very often, hundred times per second. Your eyes are not fast enough to recognize every single update. Und the ucontroller is busy all the time.
dv is changed every 500ms, and it is enough to run the "print1" function only one time per change.

Corrupt data: dv is written within the ISR, but read in the main loop. Now dv is not a single byte but 4 bytes (i think). Now imagine: in the main loop your function reads dv: 1st byte, 2nd byte [and now ISR runs and canges dv] , 3rd byte, 4th byte...
You see the fitrst two bytes are from old dv and the last two bytes are fromnew dv. This means garbage.
This is a pitfall with ISR.
To avoid this you must perform an "atomic read of dv"
In main loop it looks like this:
...
# Assembler inline Cli
# tmpdv = dv
# assembler inline sei
# print1(tmpdv)
...
With this the isr is disabled during read of dv, only a very short time, then enabled again. Now all four bytes are read without interruption.

..........
This a solution that works all the time.
The solution with the flag has this benifit included, because immediately after dv is canged the flag is set ind it takes 500ms time to the next change of dv. In main loop the flag is recognized within us and dv is read in us also, once. Then the flag is cleared and dv is not read anymore... 500ms later dv and flag is changed again....

.....
Read several channels...
It depend on the timing when changing the input MUX.
I have to look into the datasheet, i dont have by hand.....

in two hours i will send you a pm...

Bye
KLAUS
 

Dear KlausST ,

I am very very thankful to you because of excellent co-operation.

I am doing this with my code as you describe and now I am understand.
 

Dear KlausST,

Both channels are read by two pre-built functions Print1 for digits 1-3 and Print for digits 4-6.
but both channels value are not displayed.I don't know why.I did as you told.

Code:
unsigned char mcount=0;
unsigned int av=0;
float dispv=0,fc=0.01,dv=0,dv1=0,dispv1=0,tmp1=0,tmp=0;

void InitTimer1(){
  
  TCCR1B = 0x09;
  OCR1AH = 0x9C; 
  OCR1AL = 0x3F;  
  TIMSK  = 0x10; 
}
void InitADC()
{
   ADMUX |=(1<<REFS0);          // For Aref=AVcc;
   ADCSRA|=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1);// Prescalar div factor = 64
   ADCSRA|=(1<<ADSC);
}

uint16_t read_adc(uint8_t ch)
{
      //Select ADC Channel ch must be 0-7
    
     ADMUX =  (ADMUX & 0xF8) | ch;
     return(ADCW);
}

ISR(TIMER1_COMPA_vect) 
 { 
         ADCSRA|=(1<<ADSC);
	 while(!(ADCSRA & (1<<ADIF)));
         ADCSRA|=(1<<ADIF);
	
	 
	 av=read_adc(1); 
	 dv = dv + ( av - dv) * fc ;
	 
	 av=read_adc(2);
	 dv1 = dv1 + ( 10*av - dv1) * fc ;	  
  	 
	 mcount++;
	 if(mcount==100)
	 {
	 mcount=0;
         dispv=dv;
         dispv1=dv1;
	 }}
main(){
 while(1)
   {
   cli();
   tmp1=dispv1;
   tmp=dispv;
   sei();
   Print1(tmp1);// Print1 for digit 1-3
   Print(tmp);  // Print for digit 4-6
   
   }}

First three digits displays value and other three digits displays shows same value
while I applied different voltages on two channels i.e 1&2
 
Last edited:

Hi,

I just saw that there is something mixed up...

Try it this way:
Code:
}

uint16_t read_adc(uint8_t ch)
{
      //Select ADC Channel ch must be 0-7
    
     ADMUX =  (ADMUX & 0xF8) | ch;  select channel
        ADCSRA|=(1<<ADSC);    start conversion
	 while(!(ADCSRA & (1<<ADIF))); wait until conversion finished
         ADCSRA|=(1<<ADIF); clear flag
         return(ADCW); read adc conversion result
}

ISR(TIMER1_COMPA_vect) 
 { 
	 av=read_adc(1); 
	 dv = dv + ( av - dv) * fc ;
	 
	 av=read_adc(2);
	 dv1 = dv1 + ( 10*av - dv1) * fc ;	  
  	 
	 mcount++;
.
.
 

Dear KlausST,

Make some changes i.e 10 eliminate then working well and runs beautifully.

Thank you dear KlausST.

Code:
uint16_t read_adc(uint8_t ch)
{
      //Select ADC Channel ch must be 0-7
    
         ADMUX =  (ADMUX & 0xF8) | ch;  select channel
         ADCSRA|=(1<<ADSC);    start conversion
	 while(!(ADCSRA & (1<<ADIF))); wait until conversion finished
         ADCSRA|=(1<<ADIF); clear flag
         return(ADCW); read adc conversion result
}

ISR(TIMER1_COMPA_vect) 
 { 
	 av=read_adc(1); 
	 dv = dv + ( av - dv) * fc ;
	 
	 av=read_adc(2);
	 dv1 = dv1 + ( av - dv1) * fc ;	//////////////// 10 eliminated here and its working well.  
  	 
	 mcount++;
         if(mcount==100)
	 {
	 mcount=0;
         dispv=dv;
         dispv1=dv1;
	 }

- - - Updated - - -

Dear KlausST,

I added new channels and declare more variables then it does not run and simulator shows program memory errors.
ATmega8 has 8kB flash memory , 512 Bytes EEPROM and 1kB internal SRAM.
Device: atmega8

Program: 4822 bytes (58.9% Full)
(.text + .data + .bootloader)

Data: 308 bytes (30.1% Full)
(.data + .bss + .noinit)

Here,

Code:
unsigned char mcount=0,p=0,swcount=0;
unsigned int av=0,RBV=0;
float fc=0.005,dispv=0,dispv1=0,dv=0,dv1=0,dv2=0,tmp1=0,tmp=0;
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top