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


von Fragender4 (Gast)


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;
}


____________________________________________________

von Fragender4 (Gast)


Lesenswert?

..und jetzt mit formatierten code ;)


CODE:

_______________________________________________
1
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
//
3
//
4
// Possible Impulse Frequence: Min (Period): 8.4ms     Max (Period):
5
400µs
6
//
7
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
8
9
10
#ifndef F_CPU
11
/* prevent compiler error by supplying a default */
12
#define F_CPU 1000000UL
13
#endif
14
15
#include <util/delay.h>
16
#include <avr/interrupt.h>
17
#include <avr/io.h>
18
19
volatile char pulselength = 0;  //Volatile aufgrund von Interrupt..
20
char S1 = 0;
21
char S2 = 0;
22
char Direction = 1;
23
24
volatile uint16_t AdcValue = 0;
25
26
27
/////////////////////////////////////////////////////////////////////////////////////////////
28
// ADC Conversion read out
29
/////////////////////////////////////////////////////////////////////////////////////////////
30
ISR(ADC_vect)
31
{
32
  //Nur die MSB werden übernommen//
33
  pulselength = ADCH;
34
35
}
36
/////////////////////////////////////////////////////////////////////////////////////////////
37
38
39
/////////////////////////////////////////////////////////////////////////////////////////////
40
// Timer  0 interrupt ADC Conversion
41
/////////////////////////////////////////////////////////////////////////////////////////////
42
ISR(TIMER0_OVF_vect ) {
43
    ADCSRA |= (1<<ADSC);        // ADC Messung Starten
44
  TCNT0 = 130;     //130 => jede 1ms ADC-Abfrage
45
}
46
/////////////////////////////////////////////////////////////////////////////////////////////
47
48
49
/////////////////////////////////////////////////////////////////////////////////////////////
50
// Timer 2 interrupt Pulse length  -> calculate next signal
51
/////////////////////////////////////////////////////////////////////////////////////////////
52
ISR(TIMER2_OVF_vect )
53
{
54
55
56
  //Vorwärtsbetrieb
57
  if (Direction)
58
  {
59
    if ((S1==1) && (S2==1)) { S1 = 0; S2 = 1; }  else
60
    if ((S1==1) && (S2==0)) { S1 = 1; S2 = 1; }  else
61
    if ((S1==0) && (S2==0)) { S1 = 1; S2 = 0; }  else
62
    if ((S1==0) && (S2==1)) { S1 = 0; S2 = 0; }
63
   }
64
65
  // Rückwärtsbetrieb
66
  else if (!Direction)
67
  {
68
    if ((S2==1) && (S1==1)) { S2 = 0; S1 = 1; }  else
69
    if ((S2==1) && (S1==0)) { S2 = 1; S1 = 1; }  else
70
    if ((S2==0) && (S1==0)) { S2 = 1; S1 = 0; }  else
71
    if ((S2==0) && (S1==1)) { S2 = 0; S1 = 0; }
72
  }
73
74
75
  if (S1) { PORTD |= (1<<PD1); } else { PORTD &= ~(1<<PD1); }
76
  if (S2) { PORTD |= (1<<PD2); } else { PORTD &= ~(1<<PD2); }
77
78
  TCNT2 = pulselength; //Zählregister
79
80
}
81
/////////////////////////////////////////////////////////////////////////////////////////////
82
83
84
/////////////////////////////////////////////////////////////////////////////////////////////
85
// Initialisierung
86
//
87
/////////////////////////////////////////////////////////////////////////////////////////////
88
void init() {
89
90
  //::::::Port Configuration:::::://
91
  //PortD Data Direction Register:
92
93
  //Ouputs: PD0 = Hall1_R, PD1 = Hall2_R, PD2 = Hall1_L, PD3 = Hall2_L
94
  DDRD |= (1 << PD0) | (1 << PD1) | (1 << PD2) | (1 << PD3);
95
96
  //Inputs: PD4 = Open, PD5 = Close, PD6 = SetHall_High, PD7 =
97
SetHall_Low
98
  DDRD &= ~((1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7));
99
100
101
  // Init Timer 0
102
  TCCR0 = ( 1 << CS01 );          // Teiler: 8 //1ms ADC Value messung
103
    TIMSK = ( 1 << TOIE0 );         // Overflow Interrupt einschalten
104
    TCNT0 = 130;      // init Wert: Zähle 125 bis ersten overflow (1ms)
105
106
  // Init Timer 2
107
  TCCR2  = ( 1 << CS21 );         //prescaler 8
108
  TIMSK  |= (1 << TOIE2);         // Overflow Interupt einschalten
109
  //OCR2=200;       // Vergleichsregister
110
  TCNT2= pulselength; //200        // Zählregister
111
112
  //ADC Configuration
113
///////////////////////////////////////////////////////////////////////////////////
114
  ADMUX   = 0;                          // Kanal waehlen (ADC0)
115
    ADMUX  |= (0<<REFS1) | (1<<REFS0) | (1<<ADLAR); // AVCC als
116
Referenzspannung nutzen, left adjustet ADC Register (ADLAR)
117
    ADCSRA |= (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADIE);
118
    ADCSRA &= ~((1<<ADPS2)|(1 << ADFR));
119
120
121
  //// First Read Out um den ADC warmlaufen zu lassen
122
////////////////////////////////
123
    ADCSRA |= (1<<ADSC);                              // Start
124
ADC-Wandlung
125
    while ( ADCSRA & (1<<ADSC) ) {                  // Warten bis ADC
126
Wandlung fertig ist
127
     ;
128
    }
129
130
131
}
132
/////////////////////////////////////////////////////////////////////////////////////////////
133
134
135
136
/////////////////////////////////////////////////////////////////////////////////////////////
137
/////////////////////////////////////////////////////////////////////////////////////////////
138
//                MAIN
139
/////////////////////////////////////////////////////////////////////////////////////////////
140
/////////////////////////////////////////////////////////////////////////////////////////////
141
int main()
142
{
143
  init();
144
145
  sei();                // Enable Interrupts
146
147
  while ( 1 ) {}
148
149
return 0;
150
}

von Fragender4 (Gast)


Lesenswert?

...ich meinte Timer2 ;)

von STK500-Besitzer (Gast)


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.

von Fragender4 (Gast)


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.?:
1
....
2
3
TCNT2 = pulselength; //Zählregister
4
5
if(TCNT2 < 100)
6
{
7
  TCCR2  = ( 1 << CS21 );  //Prescaler 8
8
}
9
10
else 
11
{
12
TCCR2  |= ( 1 << CS21 ) | ( 1 << CS20 ) ;  //Prescaler 32
13
}

vermutlich mudd man den Prescaler in der Ini entfernen?

von Karl H. (kbuchegg)


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.

von STK500-Besitzer (Gast)


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?

von Fragender8 (Gast)


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:
1
/////////////////////////////////////////////////////////////////////////////////////////////
2
// Timer 2 interrupt Pulse length  -> calculate next signal
3
/////////////////////////////////////////////////////////////////////////////////////////////
4
ISR(TIMER2_OVF_vect )
5
{
6
7
8
  //Vorwärtsbetrieb
9
  if (Direction)
10
  {
11
    if ((S1==1) && (S2==1)) { S1 = 0; S2 = 1; }  else
12
    if ((S1==1) && (S2==0)) { S1 = 1; S2 = 1; }  else
13
    if ((S1==0) && (S2==0)) { S1 = 1; S2 = 0; }  else
14
    if ((S1==0) && (S2==1)) { S1 = 0; S2 = 0; }
15
   }
16
17
  // Rückwärtsbetrieb
18
  else if (!Direction)
19
  {
20
    if ((S2==1) && (S1==1)) { S2 = 0; S1 = 1; }  else
21
    if ((S2==1) && (S1==0)) { S2 = 1; S1 = 1; }  else
22
    if ((S2==0) && (S1==0)) { S2 = 1; S1 = 0; }  else
23
    if ((S2==0) && (S1==1)) { S2 = 0; S1 = 0; }
24
  }
25
26
27
  if (S1) { PORTD |= (1<<PD1); } else { PORTD &= ~(1<<PD1); }
28
  if (S2) { PORTD |= (1<<PD2); } else { PORTD &= ~(1<<PD2); }
29
30
 if( pulselength < 100)  // bei Schwellwert 100 sind es ca. 8ms
31
  {
32
  TCCR2  |= ( 1 << CS21 ) | ( 1 << CS20 ) ;  //Prescaler 32
33
  
34
  TCNT2 = pulselength;     //Zählregister
35
  }
36
 else 
37
  {
38
  TCCR2  = ( 1 << CS21 );  //Prescaler 8
39
  pulselength = (pulselength - 30) //um bei Schwellwert > 100 bei der   //selben Pulsdauer (ca. 8ms) zu beginnen..
40
 TCNT2 = pulselength;
41
  }

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

Gruss



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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.