Forum: Mikrocontroller und Digitale Elektronik Timer Problem Attiny45


von Andi (Gast)


Lesenswert?

Hallo zusammen.

ich habe ein Problem und komme einfach nicht weiter.
Ich bin ein Neuling im Attinyprogrammieren also bitte verhaut mich 
nicht.

Zu meinem Problem:
ich möchte mit dem Attiny am Pin PB0 (OCRA0) ein Servosignal erzeugen.
Der Attiny läuft auf 8Mhz Intern (Das Div-Fuse ist abgeschaltet).

Jetzt bekomme ich den Hardwaretimer nicht so hin, dass ich eine 
Periodendauer von 20ms erreiche.

Also habe ich mir geholfen in dem ich den Puls mit delays hingebogen 
habe:
1
#define   F_CPU 8000000
2
3
#include <util/delay.h>
4
#include <stdio.h>
5
#include <avr/interrupt.h>
6
#include <avr/io.h>
7
8
//EINGABE (Taster)
9
#define S_1 (!(PINB & (1<<PB1)))    // Signal_auf
10
#define S_2  (!(PINB & (1<<PB2)))    // Signal_zu
11
12
 void ADC_Init_attiny(void) //NUR KANAL 2 INITALISIERT
13
 {
14
 
15
  uint16_t result;
16
 // ADMUX = (0<<REFS1) | (1<<REFS0);      // AVcc als Referenz benutzen
17
  ADMUX |= (1<<REFS2) | (1<<REFS1);      // interne Referenzspannung nutzen
18
  ADCSRA = (1<<ADPS2) |(0<<ADPS1) | (0<<ADPS0);     // Frequenzvorteiler
19
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
20
 // ADCSRB = (0<<ADTS2)| (0<<ADTS1)|(0<<ADTS0);
21
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
22
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
23
 
24
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
25
  while (ADCSRA & (1<<ADSC) ) {}        // auf Abschluss der Konvertierung warten
26
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
27
     Wandlung nicht übernommen. */
28
  result = ADCW;
29
}
30
31
32
int main(void)
33
{
34
35
//PWM_init_PTT_Attiny();
36
37
ADC_Init_attiny();
38
    //76543210 
39
DDRB =  0b00000001;    
40
PORTB = 0b00000110;    //PB0= OCR0A
41
42
uint16_t puls;
43
uint16_t pos_1;
44
uint16_t pos_2;
45
46
uint16_t z_puls;
47
48
49
  while (1)
50
  {
51
52
if (S_1)
53
{
54
 pos_1= ADC_Read_Avg(2,3);
55
 puls = (100+(pos_1 /45));
56
57
}
58
59
if (S_2)
60
{
61
 pos_2= ADC_Read_Avg(3,3);
62
 puls = (10+(pos_2 /45));
63
}
64
if(z_puls <= puls)
65
    { 
66
    PORTB = (1<<PB0);
67
    z_puls= z_puls+1; 
68
    _delay_us(1);
69
    }
70
71
    else
72
    {
73
    PORTB = (0<<PB0);
74
    z_puls= 0;
75
    _delay_ms(20);
76
    }
77
  }
78
}

Das funktioniert soweit auch, aber die Zeit der ADC wandlung dauert ca. 
20ms. Das ist so lange das ein Puls am Ausgang fehlt, und der Servo 
dadurch einen Ruck macht und dann erst wieder in die Stellung fährt in 
die er soll.

Ich komme einfach nicht weiter.

Danke schon mal für euren Denkanstoß.

Gruß Andi

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Die ganz delay Idee ist Unsinn.
Denn du hast ja nicht pro Durchlauf durch die Schleife immer konstant 
1µs.
Da kommt dann ja tatsachlich nicht die ADC Zeit dazu (die aber bei 
weitem keine 20ms ausmachen wird). Dazu kommt noch dass ein
1
    _delay_us(1);
bei 8 Mhz wohl auch nicht einigermassen gut stimmen wird.

die 20ms sind übrigens das aller Unwichtigste bei einer 
Servoansteuerung. DIe Pulslänge muss stimmen! Die Pause dazwischen ist 
völlig uninteressant, solange sie sich im vernünftigen Bereich bewegt 
(ca 10ms bis ca 20ms. Circa!)

> Jetzt bekomme ich den Hardwaretimer nicht so hin, dass ich eine Periodendauer 
von 20ms erreiche.

Auf welche Zykluszeiten kommst du denn rechnerisch?
Vergiss die 20ms. Du musst die Pulslänge mit dem Timer einstellen 
können. Wenn dann noch eine vernünftige Pause übrig bleibt, dann reicht 
das völlig.

Modellbauservo Ansteuerung

: Bearbeitet durch User
von Andi (Gast)


Lesenswert?

Danke für den Link. Habe es verstanden.
Die eine Frage habe ich aber noch.

Wenn ich PB0 und PB1 als eingänge nehme, wie kann ich dann an PB1(ORC1A) 
noch den Servo ansteuern? Oder ist das Tutorial auf einen Anderen Atmega 
geschieben mit anderer Pinbelegung?Da konnte ich keinen Hinweis finden 
also bin ich vom Atmega8 ausgegangen.

Die Zykluszeit rechne ich morgen, die ich hatte. waren aber glaub ich 
mit der Prescaler einstellung 30ms mit 1mhz oder so. Aber sauber lief 
der Servo damit nich.

von Andi (Gast)


Lesenswert?

Hallo,
habe es jetzt nach dem Tut umgesetzt. es geht auch.
Aber 1 Problem habe ich.
Die Auflösung der 1,5-2ms sind nur 10 Zähler. also kann ich den Servo 
nur in 18°schritten bewegen. (er macht eine gesamtbewegung von 180°)

Gibt es da mit dem 8Bit timer ne möglichkeit oder brauch ich einen
MC mit 16bit timer?

von c-hater (Gast)


Lesenswert?

Andi schrieb:

> habe es jetzt nach dem Tut umgesetzt. es geht auch.
> Aber 1 Problem habe ich.
> Die Auflösung der 1,5-2ms sind nur 10 Zähler. also kann ich den Servo
> nur in 18°schritten bewegen. (er macht eine gesamtbewegung von 180°)

Normalerweise wird der volle Stellumfang so ungefähr zwischen 1 und 2ms 
Duty durchlaufen. 1,2..2ms wäre wohl auch noch im Bereich des 
"Üblichen", aber 1,5..2ms ist schon recht seltsam. Da stimmt vermutlich 
irgendwas nicht. Messung? Berechnung? Keine Ahnung.

> Gibt es da mit dem 8Bit timer ne möglichkeit.

Ja, klar, man muß ihn nur etwas anders als üblich benutzen, also der 
beschränkten Hardwarefunktionalität mit etwas zusätzlicher Software auf 
die Beine helfen.

Die grundsätzliche Idee dabei ist: man benutzt die Timer-Hardware 
abwechselnd_ in zwei _unterschiedlichen Konfigurationen. Man muss also 
die Konfiguration des Timers zyklisch ändern und zwar idealerweise 
synchron zu dem Timing, das die Timerhardware selber erzeugt. Dazu 
benutzt man sinnvollerweise geeignete Interupts des Timers, denn genau 
dafür gibt es die nämlich.

Alles weitere steht eigentlich im Datenblatt als Funktionsbeschreibung 
des Timers. Lesen, durchdenken und zweckentsprechend anwenden. Genau das 
ist, was Programmieren eigentlich ausmacht.

von andi (Gast)


Lesenswert?

Hallo C-hater,

verstanden habe ich es, aber ich werde aus dem Datenblatt einfach nicht 
schlau.

Im grunde muss ich mir doch zb eine Periodenhälfe druch die Erste 
konfiguration abfangen(ORCA bleibt 0) und die zweite Periodenhelfte mit
dem Wackler (1.2-2.2ms) ausgeben. Also die zweite Hälfte so im CTC-Mode 
wie ich es habe, aber die erste und die Interrups komm ich nicht drauf.

ablauf habe ich mir so vorgestellt.

timer läuft 10ms--->Interrupt---->umstellen auf CTC-----> 
10ms-(1.2-2.2ms)-->
Interrupt--->Umstellen auf ???--->timer 10ms.

Ist das so richtig?

von Hannes (Gast)


Lesenswert?

Einfaches Gedankenspiel dazu:

Betreibt man den Tiny25/45/85 mit 1 MHz, so dauert bei Timer-Vorteiler 
1:1 der Umlauf eines 8-Bit-Timers 256 µs, denn 1 Timer-Schritt dauert ja 
1 µs. Das reicht uns nicht, um 1000 bis 2000 µs darin abzubilden.

Bei Vorteiler 1:8 dauert 1 Timer-Schritt schon 8 µs, ein Umlauf mit 256 
Schritten demnach 2048 µs. Das passt schonmal, um die Dauer von 
Servoimpulsen darin abzubilden. Das Raster beträgt 8 µs, 1,0 ms 
entspricht also 125 Timerschritte, 2,0 ms 250 Timerschritte. Damit ist 
der Servo-Weg auf 125 Schritte gerastert. Wenn Du nicht gerade 
Präzisions-Kunstflug betreiben willst, sollte das ausreichen.

Die Gesamtperiode von etwa 20 ms würde ich mit einem Zähler realisieren, 
der vom Überlauf des Timers getaktet wird.

Um betreffs Pins flexibel zu bleiben und um mehrere Servos ansteuern zu 
können, würde ich auf eine Hardware-PWM verzichten und die Servo-Pins 
per Software setzen und löschen.

Die Programmstruktur sähe dann so aus:

Reset_Routine:
Einrichten von Ports,
ADC (Free-Run mit Vorteiler 1:8, 125 kHz)
Timer0 mit Vorteiler 1:8, Overflov-Interrupt und Compare0A-Interrupt
Pausenzähler auf Startwert (9 oder 10)

ISR_Ovf0:
Pausenzähler runterzählen
Wenn 0 erreicht, dann
  Pausenzähler auf Startwert setzen
  Impuls einschalten
  ADC auslesen, daraus Impulsdauer besrechnen (/2 und etwas Offset)
  Impulsdauer in OCR0A schreiben
  Sollten mehrere Servos bedient werden, dann:
    Servonummer auf 1 setzen und
    ADMUX auf nächsten Eingang umschalten
fertig...

ISR_Timer0CompA:
Impuls ausschalten (bei mehreren Servos alle ausschalten)
Sollten mehrere Servos bedient werden, dann:
  Servonummer auf Gültigkeit (Überlauf) prüfen
  Wenn Servonummer noch gültig, dann:
    ADC auslesen,
    Impulsdauer berechnen
    Impulsdauer auf OCR0A aufaddieren
    nächsten Servopin auf H setzen
  oder:
    ADMUX auf Eingang für erstes Servo
fertig...

In C notieren musst du es selbst, C ist mir zu kryptisch. In Assembler 
oder Basic werde ich es nicht notieren, da ich keinen Wert auf die 
darauf folgenden Kommentare der C-Verfechter lege.

...

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.