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
voidADC_Init_attiny(void)//NUR KANAL 2 INITALISIERT
/* 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
intmain(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_tpuls;
43
uint16_tpos_1;
44
uint16_tpos_2;
45
46
uint16_tz_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
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
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.
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?
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.
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?
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.
...
Eine eigene Frage?
Dieser Thread ist über 10 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.