mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Prescaler,InterruptRoutinen und Zeiten berechnen


Autor: Fragender4 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

ich benutze einen Atmega8 um in Abhängigkeit des ADC-Kanals variable 
Impulslängen zu erzeugen. Ich benutze dabei ADC-Interrupt, Timer0 und 
Timer1-Interrupt. Der ADC wird alle 1ms ausgelesen (siehe code).
Was ich eigentlich vor habe, ist Impulse zu erzeugen mit Periodendauern 
von ca. mindestens 10ms und 500 us maximal. Um dies zu realisieren habe 
ich folgenden Code geschrieben und auch getestet. funktioniert 
eigentlich super, ABER: Nun hab ich vor die Zeiten zu ändern, sprich ca. 
mindestens 15ms bis maximal 2ms (oder 1,5ms). Wie kann man das 
kontrolliert machen ohne irgendwelche Prescaler werte einzustellen und 
zu messen?
Ich meine, gibt es eine Formel, mit der man es ausrechnen kann? Komme 
mit den ganzen Prescalern und Faktoren durcheinander...

Zusatzfrage: Müssen die Variablen S0, S1 auch volatile, da sie sich auch 
in der ISR befinden?

Vielen dank schonmal für eure Tipps!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


CODE:

_________________________________________________

//////////////////////////////////////////////////////////////////////// 
////////////////////////////////////////////////
//
//
// Possible Impulse Frequence: Min (Period): 8.4ms     Max (Period): 
400µs
//
//////////////////////////////////////////////////////////////////////// 
////////////////////////////////////////////////


#ifndef F_CPU
/* prevent compiler error by supplying a default */
#define F_CPU 1000000UL
#endif

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

volatile char pulselength = 0;  //Volatile aufgrund von Interrupt..
char S1 = 0;
char S2 = 0;
char Direction = 1;

volatile uint16_t AdcValue = 0;


//////////////////////////////////////////////////////////////////////// 
/////////////////////
// ADC Conversion read out
//////////////////////////////////////////////////////////////////////// 
/////////////////////
ISR(ADC_vect)
{
  //Nur die MSB werden übernommen//
  pulselength = ADCH;

}
//////////////////////////////////////////////////////////////////////// 
/////////////////////


//////////////////////////////////////////////////////////////////////// 
/////////////////////
// Timer  0 interrupt ADC Conversion
//////////////////////////////////////////////////////////////////////// 
/////////////////////
ISR(TIMER0_OVF_vect ) {
    ADCSRA |= (1<<ADSC);        // ADC Messung Starten
  TCNT0 = 130;     //130 => jede 1ms ADC-Abfrage
}
//////////////////////////////////////////////////////////////////////// 
/////////////////////


//////////////////////////////////////////////////////////////////////// 
/////////////////////
// Timer 2 interrupt Pulse length  -> calculate next signal
//////////////////////////////////////////////////////////////////////// 
/////////////////////
ISR(TIMER2_OVF_vect )
{


  //Vorwärtsbetrieb
  if (Direction)
  {
    if ((S1==1) && (S2==1)) { S1 = 0; S2 = 1; }  else
    if ((S1==1) && (S2==0)) { S1 = 1; S2 = 1; }  else
    if ((S1==0) && (S2==0)) { S1 = 1; S2 = 0; }  else
    if ((S1==0) && (S2==1)) { S1 = 0; S2 = 0; }
   }

  // Rückwärtsbetrieb
  else if (!Direction)
  {
    if ((S2==1) && (S1==1)) { S2 = 0; S1 = 1; }  else
    if ((S2==1) && (S1==0)) { S2 = 1; S1 = 1; }  else
    if ((S2==0) && (S1==0)) { S2 = 1; S1 = 0; }  else
    if ((S2==0) && (S1==1)) { S2 = 0; S1 = 0; }
  }


  if (S1) { PORTD |= (1<<PD1); } else { PORTD &= ~(1<<PD1); }
  if (S2) { PORTD |= (1<<PD2); } else { PORTD &= ~(1<<PD2); }

  TCNT2 = pulselength; //Zählregister

}
//////////////////////////////////////////////////////////////////////// 
/////////////////////


//////////////////////////////////////////////////////////////////////// 
/////////////////////
// Initialisierung
//
//////////////////////////////////////////////////////////////////////// 
/////////////////////
void init() {

  //::::::Port Configuration:::::://
  //PortD Data Direction Register:

  //Ouputs: PD0 = Hall1_R, PD1 = Hall2_R, PD2 = Hall1_L, PD3 = Hall2_L
  DDRD |= (1 << PD0) | (1 << PD1) | (1 << PD2) | (1 << PD3);

  //Inputs: PD4 = Open, PD5 = Close, PD6 = SetHall_High, PD7 = 
SetHall_Low
  DDRD &= ~((1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7));


  // Init Timer 0
  TCCR0 = ( 1 << CS01 );          // Teiler: 8 //1ms ADC Value messung
    TIMSK = ( 1 << TOIE0 );         // Overflow Interrupt einschalten
    TCNT0 = 130;      // init Wert: Zähle 125 bis ersten overflow (1ms)

  // Init Timer 2
  TCCR2  = ( 1 << CS21 );         //prescaler 8
  TIMSK  |= (1 << TOIE2);         // Overflow Interupt einschalten
  //OCR2=200;       // Vergleichsregister
  TCNT2= pulselength; //200        // Zählregister

  //ADC Configuration 
//////////////////////////////////////////////////////////////////////// 
///////////
  ADMUX   = 0;                          // Kanal waehlen (ADC0)
    ADMUX  |= (0<<REFS1) | (1<<REFS0) | (1<<ADLAR); // AVCC als 
Referenzspannung nutzen, left adjustet ADC Register (ADLAR)
    ADCSRA |= (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADIE);
    ADCSRA &= ~((1<<ADPS2)|(1 << ADFR));


  //// First Read Out um den ADC warmlaufen zu lassen 
////////////////////////////////
    ADCSRA |= (1<<ADSC);                              // Start 
ADC-Wandlung
    while ( ADCSRA & (1<<ADSC) ) {                  // Warten bis ADC 
Wandlung fertig ist
     ;
    }


}
//////////////////////////////////////////////////////////////////////// 
/////////////////////



//////////////////////////////////////////////////////////////////////// 
/////////////////////
//////////////////////////////////////////////////////////////////////// 
/////////////////////
//                MAIN
//////////////////////////////////////////////////////////////////////// 
/////////////////////
//////////////////////////////////////////////////////////////////////// 
/////////////////////
int main()
{
  init();

  sei();                // Enable Interrupts

  while ( 1 ) {}

return 0;
}


____________________________________________________

Autor: Fragender4 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
..und jetzt mit formatierten code ;)


CODE:

_______________________________________________
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// Possible Impulse Frequence: Min (Period): 8.4ms     Max (Period):
400µs
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


#ifndef F_CPU
/* prevent compiler error by supplying a default */
#define F_CPU 1000000UL
#endif

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

volatile char pulselength = 0;  //Volatile aufgrund von Interrupt..
char S1 = 0;
char S2 = 0;
char Direction = 1;

volatile uint16_t AdcValue = 0;


/////////////////////////////////////////////////////////////////////////////////////////////
// ADC Conversion read out
/////////////////////////////////////////////////////////////////////////////////////////////
ISR(ADC_vect)
{
  //Nur die MSB werden übernommen//
  pulselength = ADCH;

}
/////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////////
// Timer  0 interrupt ADC Conversion
/////////////////////////////////////////////////////////////////////////////////////////////
ISR(TIMER0_OVF_vect ) {
    ADCSRA |= (1<<ADSC);        // ADC Messung Starten
  TCNT0 = 130;     //130 => jede 1ms ADC-Abfrage
}
/////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////////
// Timer 2 interrupt Pulse length  -> calculate next signal
/////////////////////////////////////////////////////////////////////////////////////////////
ISR(TIMER2_OVF_vect )
{


  //Vorwärtsbetrieb
  if (Direction)
  {
    if ((S1==1) && (S2==1)) { S1 = 0; S2 = 1; }  else
    if ((S1==1) && (S2==0)) { S1 = 1; S2 = 1; }  else
    if ((S1==0) && (S2==0)) { S1 = 1; S2 = 0; }  else
    if ((S1==0) && (S2==1)) { S1 = 0; S2 = 0; }
   }

  // Rückwärtsbetrieb
  else if (!Direction)
  {
    if ((S2==1) && (S1==1)) { S2 = 0; S1 = 1; }  else
    if ((S2==1) && (S1==0)) { S2 = 1; S1 = 1; }  else
    if ((S2==0) && (S1==0)) { S2 = 1; S1 = 0; }  else
    if ((S2==0) && (S1==1)) { S2 = 0; S1 = 0; }
  }


  if (S1) { PORTD |= (1<<PD1); } else { PORTD &= ~(1<<PD1); }
  if (S2) { PORTD |= (1<<PD2); } else { PORTD &= ~(1<<PD2); }

  TCNT2 = pulselength; //Zählregister

}
/////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////////
// Initialisierung
//
/////////////////////////////////////////////////////////////////////////////////////////////
void init() {

  //::::::Port Configuration:::::://
  //PortD Data Direction Register:

  //Ouputs: PD0 = Hall1_R, PD1 = Hall2_R, PD2 = Hall1_L, PD3 = Hall2_L
  DDRD |= (1 << PD0) | (1 << PD1) | (1 << PD2) | (1 << PD3);

  //Inputs: PD4 = Open, PD5 = Close, PD6 = SetHall_High, PD7 =
SetHall_Low
  DDRD &= ~((1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7));


  // Init Timer 0
  TCCR0 = ( 1 << CS01 );          // Teiler: 8 //1ms ADC Value messung
    TIMSK = ( 1 << TOIE0 );         // Overflow Interrupt einschalten
    TCNT0 = 130;      // init Wert: Zähle 125 bis ersten overflow (1ms)

  // Init Timer 2
  TCCR2  = ( 1 << CS21 );         //prescaler 8
  TIMSK  |= (1 << TOIE2);         // Overflow Interupt einschalten
  //OCR2=200;       // Vergleichsregister
  TCNT2= pulselength; //200        // Zählregister

  //ADC Configuration
///////////////////////////////////////////////////////////////////////////////////
  ADMUX   = 0;                          // Kanal waehlen (ADC0)
    ADMUX  |= (0<<REFS1) | (1<<REFS0) | (1<<ADLAR); // AVCC als
Referenzspannung nutzen, left adjustet ADC Register (ADLAR)
    ADCSRA |= (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADIE);
    ADCSRA &= ~((1<<ADPS2)|(1 << ADFR));


  //// First Read Out um den ADC warmlaufen zu lassen
////////////////////////////////
    ADCSRA |= (1<<ADSC);                              // Start
ADC-Wandlung
    while ( ADCSRA & (1<<ADSC) ) {                  // Warten bis ADC
Wandlung fertig ist
     ;
    }


}
/////////////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
//                MAIN
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
int main()
{
  init();

  sei();                // Enable Interrupts

  while ( 1 ) {}

return 0;
}

Autor: Fragender4 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...ich meinte Timer2 ;)

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann es sein, dass du zwei Zehnerpotenzen verwechselst?
µ ist etwa tausendmal kleiner als m...

Wenn du Probleme mit dem Vorteiler hast, dann mußt du halt eine 
"Schaltschwelle" programmieren, an der der Vorteiler umgeschaltet wird.

Die Schwelle kannst du ziemlch einfach herausfinden, da du ja einen 
proportionalen Zusammenhang zwischen ADC-Wert und Periodendauer erzeugen 
willst.
Für einen bestimten Bereich eine bestimmten Vorteiler-Faktor und für 
einen anderen einen anderen.
Dann muß man nur noch den Vergleichswert berechnen, mit dem der 
Timerwert verglichen werden soll.

Autor: Fragender4 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo

nein, eigentlich micht: Ich meinte 10ms und und 500 us
umgesetzt sind ca 8,4ms und 400 us.

hmmm..daran hab ich nicht gedacht. stimmt, man könnte den prescaler 
umschalten, nachdem ein bereich des Timer Zählerns überschritten wird
gibt es denn beispiele wie sowas aussehen könnte?

so z.B.?:

....

TCNT2 = pulselength; //Zählregister

if(TCNT2 < 100)
{
  TCCR2  = ( 1 << CS21 );  //Prescaler 8
}

else 
{
TCCR2  |= ( 1 << CS21 ) | ( 1 << CS20 ) ;  //Prescaler 32
}


vermutlich mudd man den Prescaler in der Ini entfernen?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fragender4 wrote:

>
> TCNT2 = pulselength; //Zählregister
>
> if(TCNT2 < 100)
> {
>   TCCR2  = ( 1 << CS21 );  //Prescaler 8
> }
>
> else
> {
> TCCR2  |= ( 1 << CS21 ) | ( 1 << CS20 ) ;  //Prescaler 32
> }
>
> [/c]


Das wird so wohl nicht funktionieren, da es ja einen Zusammenhang gibt, 
zwischen dem Wert, den du in TCNT2 laden musst, dem Vorteiler und er 
Verzögerung die du erreichen willst.

So ein Timer ist ja kein Hexenwerk.
Der zählt einfach nur still und leise vor sich hin.
Wie schnell er zählt, hängt von der Prozessortaktfrequenz ab und dem 
Vorteiler der eingestellt ist.
Ist die Taktfrequenz 1Mhz, dann zählt der Timer in 1 Sekunde bis 1 
Million (würde er gerne, wenn nicht voerher ein Overflow dazwischen 
kommen würde). Ist ein Vorteiler von 64 eingestellt, dann kommt der 
Zähler in 1 Sekunde nicht mehr bis 1 Mio, sondern nur noch bis 1000000 / 
64 = 15625

Wenn du also einen hypotetischen Timer so eingestellt hättest, dass er 
bei 2536000 etwas macht (zb einen Interrupt auslöst), dann braucht es 
bei 1Mhz Timerfrequenz 2.536 Sekunden, bis dieser Event eintritt. Bei 
einem Vorteiler von 64 dauert es aber 2536000/15625 = 162.3 Sekunden.

Das sind alles nur mathematische Schlussrechnungen, die hier notwendig 
sind. Wenn dir klar ist, wie so ein Timer eigentlich funktioniert (und 
das ist in der Tat sehr simpel), dann kann man sich die 'Formeln' ganz 
leicht selber überlegen.

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>nein, eigentlich micht: Ich meinte 10ms und und 500 us
>umgesetzt sind ca 8,4ms und 400 us.

Das muß ich immer noch nicht verstehen, oder?
Untere Grenze der Periodendauer sollen 10ms sein und obere 500µs?
Das passt nicht. 10ms sind 20mal mehr als 500µs.

Oder meinst du mit Periodendauer vielleicht die Pulsdauer?

Autor: Fragender8 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo

ja ich meinte die pulsdauer ;) also im oberen bereich impulse mit ca 
500us (Periodendauer)  erzeugen und im unteren bereich impulse mit 10 ms 
erzeugen.

hallo karl heinz,

ich verstehe nicht wo das problem liegt. ich kann doch in meiner ISR den 
Prescaler bei einer bestimmten Schwelle einfach umschalten um so andere 
pulsdauern zu erhalten:


/////////////////////////////////////////////////////////////////////////////////////////////
// Timer 2 interrupt Pulse length  -> calculate next signal
/////////////////////////////////////////////////////////////////////////////////////////////
ISR(TIMER2_OVF_vect )
{


  //Vorwärtsbetrieb
  if (Direction)
  {
    if ((S1==1) && (S2==1)) { S1 = 0; S2 = 1; }  else
    if ((S1==1) && (S2==0)) { S1 = 1; S2 = 1; }  else
    if ((S1==0) && (S2==0)) { S1 = 1; S2 = 0; }  else
    if ((S1==0) && (S2==1)) { S1 = 0; S2 = 0; }
   }

  // Rückwärtsbetrieb
  else if (!Direction)
  {
    if ((S2==1) && (S1==1)) { S2 = 0; S1 = 1; }  else
    if ((S2==1) && (S1==0)) { S2 = 1; S1 = 1; }  else
    if ((S2==0) && (S1==0)) { S2 = 1; S1 = 0; }  else
    if ((S2==0) && (S1==1)) { S2 = 0; S1 = 0; }
  }


  if (S1) { PORTD |= (1<<PD1); } else { PORTD &= ~(1<<PD1); }
  if (S2) { PORTD |= (1<<PD2); } else { PORTD &= ~(1<<PD2); }

 if( pulselength < 100)  // bei Schwellwert 100 sind es ca. 8ms
  {
  TCCR2  |= ( 1 << CS21 ) | ( 1 << CS20 ) ;  //Prescaler 32
  
  TCNT2 = pulselength;     //Zählregister
  }
 else 
  {
  TCCR2  = ( 1 << CS21 );  //Prescaler 8
  pulselength = (pulselength - 30) //um bei Schwellwert > 100 bei der   //selben Pulsdauer (ca. 8ms) zu beginnen..
 TCNT2 = pulselength;
  }

Sollte doch so funktionieren? Wüsste jetzt nicht warum es nicht gehen 
soll?

Gruss



}
//////////////////////////////////////////////////////////////////////// 
/////////////////////

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.