Hallo,
bei meinem aktuellen Projekt soll ein Attiny85 zwei leicht verschiedene
PWM-Signale erzeugen.
Da es am einfachsten war, die Pins 2 und 3 (PB3 und PB4) zu nehmen,
dachte ich an eine Lösung mittels Software PWM.
Dabei wird über einen Timer (alle 2 us Interrupt) eine Variable
(counter) hochgezählt. Erreicht diese bestimmte Werte, so werden die
entsprechenden Pins geschaltet.
1
volatileintcounter=0;
2
3
constintmag_freq=6240;
4
constintmag_duty=256;
5
6
constintled_freq=6211;
7
constintled_duty=5;
8
9
constintmag=PB3;// vllt
10
constintled=PB4;// tauschen
11
12
intmain{
13
//Set timer0
14
//Interrupt every 2 us
15
cli();
16
TCCR0A=(1<<WGM01);// set ctc mode
17
TCCR0B=(1<<CS00);// set no prescaler
18
OCR0A=15;// set ctc value
19
TIMSK=(1<<OCIE0A);// enable interrupt
20
21
sei();
22
23
DDRB=(1<<mag)|(1<<led);// set outputs
24
25
while(1){
26
27
if(counter%mag_freq==0){// mag on
28
PORTB|=(1<<mag);
29
}
30
elseif(counter%mag_freq==mag_duty){// mag off
31
PORTB&=~(1<<mag);
32
}
33
if(counter%led_freq==0){//led on
34
PORTB|=(1<<led);
35
}
36
elseif(counter%led_freq==led_duty){// led off
37
PORTB&=~(1<<led);
38
}
39
}
40
}
41
42
ISR(TIMER0_COMPA_vect){
43
++counter;
44
}
Leider, und für mich unverständlicherweise, funktioniert der Code so
nicht. Auf PB3 erhalte ich gar kein Signal und auf PB4 ein durchgängiges
HIGH.
Da es ein Weihnachtsgeschenk werden soll, wäre ich für schnelle
Antworten sehr dankbar!
Gruß Alex
Hallo,
2µs das ist sportlich, rechne dir bitte die Interrupt Reponsezeit und
den Zeitbedarf einer ISR aus.
Den Timer0 muss man dann auch konfigurieren.
Bei FCPU = 8MHz sind das nur 16 Takte für alles.
Wählt man 16Mhz ist es mit 32 Takten immer noch sehr eng.
Danke für die Antwort!
Karl M. schrieb:> Den Timer0 muss man dann auch konfigurieren.
Das habe ich am Anfang der main gemacht
Karl M. schrieb:> 2µs das ist sportlich
Ja, ich versuchs mal mit einem größeren Intervall
Alexander W. schrieb:> const int mag = PB3;> const int led = PB4;Karl M. schrieb:> bist Du dir sicher, das dies korrekten Code erzeugt ?> DDRB = (1 << mag) | (1 << led);
Müsste doch eigentlich? "mag" und "led" sind ja vorher deklariert...
an Karl M.
Es sind immer 16 Takte, denn
> TCCR0B = (1 << CS00); // set no prescaler> OCR0A = 15; // set ctc value
Und in der Tat, ob das in C so ohne weiteres klappt?
Ja, 16 Takte ist wahrscheinlich zu knapp...
Hab jetzt gerade mal das Timerintervall auf 128 Takte hochgesetzt und
dementsprechend die konstanten Werte oben runtergesetzt (alle durch 8
geteilt).
Jetzt erhalte ich auch einen Output an beiden Pins, jedoch weder sehr
regelmäßig, noch mit der gewünschten Frequenz (gewünscht ~80 Hz,
tatsächlich ~unter 10 Hz).
Aus meiner Sicht scheint es tatsächlich an den zu kurzen
Timerintervallen zu liegen, oder?
Hallo,
Rechne es Dir doch anhand des .lss Listings aus.
Vermutungen helfen Dir nicht weiter.
Das .lss Listing, zeigt des weitern auch noch, was der avr gcc wie
übersetzt hat und wo man noch etwas optimieren kann.
Vielleicht gibt es aber auch einen ganz anderen Ansatz, den ich bis
jetzt übersehen habe, deshalb jetzt mal mein Ziel:
Der hier
(https://github.com/cubic-print/timeframe/blob/master/software/TimeFrame_3_0_simple_instructables.ino)
zu findene Code funktioniert auf meinem Arduino ganz wunderbar, es kommt
zum gewünschten Effekt.
Da ich einen Arduino aber nicht im Projekt unterbringen kann (zu groß),
versuche ich den Code auf einen Attiny85 zu bringen, leider bis jetzt
ohne wirklichen Erfolg...
Karl M. schrieb:> Rechne es Dir doch anhand des .lss Listings aus.
Ich benutze die Arduino IDE zum programmieren, ich glaube die erzeugt
keine solche Datei
S. Landolt schrieb:> was benötigt diese '%'-Operation?
Damit sollen die unterschiedlichen Frequenzen mit einem counter
bearbeitet werden können. Immer wenn der counter % einem bestimmten Wert
== 0, ist eine Periode um
S. Landolt schrieb:> Nein, die Frage war nicht, was sie bewirkt, sondern wieviele Takte sie> verbraucht.
Genau kann ich es dir leider nicht sagen, aber auf jeden Fall viel zu
viele (zumindest in diesem Fall).
Habe die % Operation jetzt ersetzt, durch zwei unabhängige Counter
Variablen, die mit dem Höchstwert verglichen und bei Übereinstimmung
zurückgesetzt werden.
Jetzt funktioniert alles so wie es soll!
Vielen Dank für eure Hilfe und schöne Feiertage!
Gruß Alex
Alexander W. schrieb:> Jetzt erhalte ich auch einen Output an beiden Pins, jedoch weder sehr> regelmäßig, noch mit der gewünschten Frequenz (gewünscht ~80 Hz,> tatsächlich ~unter 10 Hz).>> Aus meiner Sicht scheint es tatsächlich an den zu kurzen> Timerintervallen zu liegen, oder?
Und aus meiner Sicht scheint es an der CKDIV8 Fuse zu liegen.
Dein Tiny läuft wahrscheinlich mit 1MHz.
Alexander W. schrieb:> Jetzt erhalte ich auch einen Output an beiden Pins, jedoch weder sehr> regelmäßig, noch mit der gewünschten Frequenz (gewünscht ~80 Hz,> tatsächlich ~unter 10 Hz).
Du erwartest was mit 80 Hz und hast was mit rund 10 Hz? Das klingt nach
einem aktiven CLKDIV8-Fuse. Setze mal bei der Initialisierung folgendes
zu beginn:
Hallo,
sollte man auf die counter Variable nicht Interrupt geschützt zugreifen?
Stichwort "atomic". Davon abgesehen würde ich die PWM Funktion der Timer
nutzen. Einen Timer haste dafür sowieso schon in der Mangel.
Veit D. schrieb:> Davon abgesehen würde ich die PWM Funktion der Timer> nutzen. Einen Timer haste dafür sowieso schon in der Mangel.
Da hat er das Problem, dass er PB3 und PB4 benutzen will (warum auch
immer, schrieb er oben). Die sind PWM-mäßig aber gekoppelt (PB3: !OC1A,
PB4: OC1A). Die PWM-Signale sollen aber (leicht) verschieden sein. Mit
der Hardware-PWM sehe ich da erstmal keinen Weg dass so zu realisieren.
Gut wäre es natürlich wenn ein Pin sich ändern können würde, z.B. statt
PB3 PB1 zu benutzten. Dann ginge das mit der Harware-PWM.
Alexander W. schrieb:> Dabei wird über einen Timer (alle 2 us Interrupt) eine Variable> (counter) hochgezählt. Erreicht diese bestimmte Werte, so werden die> entsprechenden Pins geschaltet.
...
> Leider, und für mich unverständlicherweise, funktioniert der Code so> nicht. Auf PB3 erhalte ich gar kein Signal und auf PB4 ein durchgängiges> HIGH.
Wie schon von anderen erwähnt, der Interrupt verbrät fast die ganze
CPU-Zeit. Was in der Hauptschleife übrig bleibt soll dann für 4
16Bit-Modulo-Operatoren ("%") ausreichen?
Der 500kHz Counter wird nämlich mit einer wesentlich kleineren Frequenz
abgetastet. Allein die 4 Modulos brauchen (ohne Interrupt) schon 60μs.
Da wäre man bei 16kHz, der Interrupt wird grob geschätzt nicht mehr als
10% Rechenzeit übrig lassen, dann sind es 1,6kHz.
Dabei muß aber der exakte Wert 0 als Modulo-Ergebnis errechnet werden,
so steht es aktuell da, was bei der Abtastung mit einen 50stel der
"Eingangsfrequenz" einem Wunder gleichen würde.
Andere Idee: der Timer setzt nur sein Int-Flag, das die Hauptschleife
als Takt benutzt. Wartet auf Int-Flag, dieses rücksetzen und einen
Durchlauf machen. Statt Modulo-Operation auf einem "Zähler", bekommt
jeder Kanal seinen eigenen, der je Durchlauf um 1 erhöht wird und bei 0
einschaltet, bei PWM-Wert ausschaltet und bei MAX-Wert auf 0
zurückgesetzt wird. Also einfach 2 16-Bit-Timer in Software, wenn sie
die HW schon nicht hat.
Dafür werden vermutlich die 32 Takte (8MHz; 2μs "Zähler-Takt) nicht ganz
reichen, deshalb langsam anfangen und um das "Warten auf HW-Timer" herum
einen Pin toggeln, um die "übriggebliebene Zeit" zu messen. Vermutlich
werden 10μs pro Durchlauf hinzubekommen sein.
M. K. schrieb:> Veit D. schrieb:>> Davon abgesehen würde ich die PWM Funktion der Timer>> nutzen. Einen Timer haste dafür sowieso schon in der Mangel.>> Da hat er das Problem, dass er PB3 und PB4 benutzen will (warum auch> immer, schrieb er oben). Die sind PWM-mäßig aber gekoppelt (PB3: !OC1A,> PB4: OC1A). Die PWM-Signale sollen aber (leicht) verschieden sein. Mit> der Hardware-PWM sehe ich da erstmal keinen Weg dass so zu realisieren.> Gut wäre es natürlich wenn ein Pin sich ändern können würde, z.B. statt> PB3 PB1 zu benutzten. Dann ginge das mit der Harware-PWM.
Er braucht auch einen 16-Bit-Timer, eher 2, wollte er das mit HW-PWM
machen, die der kleine 8-Poler gar nicht hat.
Hallo,
ich würde erstmal das besagte abarbeiten. Dein µC Takt oder Timer Konfig
scheint nicht zu stimmen. Denn mit 80Hz Solltakt kommt man in keine
zeitlichen Probleme.
Desweiteren sollte der atomic Zugriff auf counter beachtet werden!
Ob Software oder Hardwaretimer. Er hätte zwei 8Bit Timer zur Verfügung.
Können also unterschiedlich konfiguriert werden. Aber gut, kommt auf den
Zweck und Anforderung an.