Hallo an alle,
ich habe ein Projekt, welches ich als Laborprojekt in der Uni bearbeiten
soll. Leider fällt die Vorlesung dieses Semester durch corona aus und
ich bin auf mich alleine gestellt. Toll oder?
Also hier zum Problem:
Ich habe einen Atmega328PB mit 16MHz.
Die Erste Aufgabe lautet:
"Generieren Sie am PIN PD5 ein Signal mit einer Frequenz von 100Hz.
Mittels eines Tasters an PD2 soll die Frequenz in 100Hz-Schritten
erhöht, mittels eines Tasters an PD3 in 100Hz-Schritten verringert
werden. Die maximale Frequenz sei 20kHz."
So nun habe ich mich erstmal schlau gemacht. Ich bin bisher so weit
gekommen, dass ich einen Timer nutzen sollte. Nun habe ich das ganze mal
für eine Frequenz von 1Hz versucht:
8-Bit Timer0 auf Normal Mode, Prescaler auf 256, einen Scalefactor von
500 und einen TCNT0 von 131. Kommt genau 1Hz raus. Funktioniert auch.
Den Code habe ich mal zugefügt:
1
unsignedintScalefactor=500;
2
unsignedintcounterstart=131;
3
intmain(void)
4
{
5
TCCR0A=0;//set timer0 to normal mode
6
TCCR0B|=(1<<CS02);//set prescaler to 256
7
TIMSK0|=(1<<TOIE0);//set overflow interrupt
8
TCNT0=counterstart;
9
sei();//set global interrupt flag
10
DDRD=0xFF;
11
/*PORTD |= (1 << PORTD5);*/
12
13
while(1)
14
{
15
}
16
17
}
18
ISR(TIMER0_OVF_vect)
19
{
20
staticintcounter=0;
21
TCNT0=counterstart;
22
counter++;
23
if(counter==Scalefactor)
24
{
25
PORTD^=(1<<PORTD5);
26
counter=0;
27
}
28
}
Jetzt kommt aber mein Problem. Ich habe einfach keine Ahnung wie ich das
jetzt auf 100Hz bekomme. Theoretisch habe ich folgende Gleichung
erstellt:
fWunsch * Scalefaktor=(Systemtakt * x)/(Scalefactor *
prescaler⋅(256−Startwert)
Gut, wenn ich nun für x = 100, 200, 300, ...., 20000 einsetze, dann
komme ich auf die gewünschte Frequenz. Aber wie programmiere ich das?
Klar kann ich eine abfrage machen.
IF PD2 THEN x += 100. IF PD3 THEN -= 100 (so in etwa)
Aber wie übergebe ich das der ISR damit der Timer dann den Overflow auch
richtig setzt. Ich kann ihm ja nicht einfach x übergeben.
Leider bin ich komplett ins kalte Wasser geworfen worden und als Laie
recht alleine damit. Ich hoffe sehr, dass ihr mir etwas helfen könnt und
wir gemeinsam eine Lösung finden können.
Ich möchte nur nochmal anmerken, dass ich kompletter Laie in der
Programmierung bin. Ich versuche selbst sehr viel und lese viel dazu,
aber leider bekomme ich das alleine nicht hin.
Danke euch allen schon mal im Voraus,
Marco
Hey,
ich würde auf die ganzen Variablen in der ISR verzichten (auf den ganzen
Inhalt der ISR).
Damit sollte sich dein letztes Problem lösen.
Du kannst den Timer so einstellen das er auf PD5 (OC0B) Pin automatisch
einen Pin-Change machte, z.B. bei jedem Überlauf oder erreichen des
Wertes.
Dann musst du den neu eingegebenen Wert dem Timer als Reload-Value
setzen.
Das sollte schon reichen...(natürlich vorher errechnet)
Hi,
erstmal danke für deine Antwort. Leider verstehe ich nur Bahnhof haha.
Also meinst du, dass ich den Timer nicht im normal mode, sondern im CTC
mode starte? OC0B nehme ich dann als Compare match value?
Wenn es das ist was du meinst, dann weiß ich nur nicht, wie ich das
ausrechnen kann.
Ich weiß, dass ich 16Mhz habe. Durch 256 ergibt das 62500 Overflows pro
sekunde ohne prescaler.
Aber wie mache ich weiter und was ist einer Preload value?
Sorry für die Fragen aber ich stehe leider komplert auf dem Schlauch
Hallo,
Hallo,
Du musst dir erstmal einen Timer Mode raussuchen. Dann kennst du schon
die notwendigen WGM Bits. Für den TOP Wert hat jeder Timer Mode eine
eigene Formel. Steht alles im Manual. Die Formel stellst du dir passend
um. Bei meinem Frequenzgenerator habe ich mir eine Funktion geschrieben,
die eine Wunschfrequenz entgegen nimmt und damit den passenden Prescaler
für den größtmöglichen TOP Wert ermittelt. Bis hierher haste du freie
Wahl des Modi. Funktioniert praktisch mit allen.
Wenn der Taktpin PD5 direkt vom Timer gesteuert werden soll, brauchste
einen PWM Mode. Dann konfigurierst noch den Compare Output. Dann steuert
der Timer den Pin direkt. Du musst aber etwas tricksen. Ich weiß nicht
ob dein Prof. den Pin PD5 bewusst oder unbewusst gewählt hat. Bei den
gängigen Timer Modi kannst du TOP ändern und damit die PWM Frequenz
einstellen. Das wäre der OCR0A Pin. PD5 ist jedoch der OCR0B Pin. Du
kannst jedoch dafür sorgen, dass das OCR0B Register immer den halben
Wert von OCR0A zugewiesen bekommt. Die Taktdifferenz korrigierst du in
deiner TOP Wert Ermittungsformel/Funktion.
Es gibt viele Möglichkeiten wie man an die Sache rangeht. Und achte
drauf ob du nun einen Atmega328P oder ...PB hast.
Hi,
os erstmal wieder danke für die Antwort. Ich weiß leider einfach
nichtwelchen Timer mode ich nehmen soll. Die Formeln habe ich, aber ich
bekomme beim rechnen dauernd krumme Werte heraus.
100Hz und 200Hz geht ja noch. Bei 300Hz kommt dann eine Dezimalzahl für
OCR.
Vielleicht fangen wir mal ganz von vorne an.
Ich hatte mir überlegt den CTC Modus zu nehmen. Würde diese Aufgabe
damit realisierbar sein? Oder schlagt ihr einen anderen Modus vor, mit
dem es einfacher geht.
Marco S. schrieb:> Vielleicht fangen wir mal ganz von vorne an.Veit D. schrieb:> Hallo,>> Hallo,>> Du musst dir erstmal einen Timer Mode raussuchen.
Eine sehr ausführliche Antwort :)
Erklärt alles.
Marco S. schrieb:> Ich weiß leider einfach> nichtwelchen Timer mode ich nehmen soll.Veit D. schrieb:> Ich weiß nicht> ob dein Prof. den Pin PD5 bewusst oder unbewusst gewählt hat.
Elegant wäre es, den zu nehmen wo PD5 dran hängt, also was war das -
TC0?
Marco S. schrieb:> Nachtrag:
du bist angemeldeter User innerhalb 1 Minute (sogar bis 60 Minuten) ohne
Postings dazwischen kannst du deinen Nachtrag direkt reineditieren!
Das bläht den Thread dann nicht unnötig auf.
Sollte etwas Zeit vergangen sein und du hast Angst das keiner dein Edit
mitbekommt dann kopiere deinen Text, lösche das Posting und stelle den
Text mit Edit ein, dann bleibt es 1 Eintrag und alle bekommen das NEU zu
sehen, sogar auf Wunsch mit Meldung!
Marco S. schrieb:> Vielleicht fangen wir mal ganz von vorne an.
Ja.
Du brauchst einen 16-Bit-Timer. Nicht Timer 0 sondern Timer 1.
Mit dem Prescaler erzeugst du eine Clock-Frequenz die mindestens
doppelt so hoch ist wie die höchste Frequenz die du erzeugen
musst. Dann lasse den Zähler mit dieser Frequenz soweit laufen
dass er eine halbe Periode deiner zu erzeugenden Frequenz zählt
und damit den Augangs-Pin toggelt. Mit dem 16-Bit-Timer hast
du die Möglichkeit die Ziel-Periodendauer fein aufzulösen.
Bisschen rechnen darfst du selbst noch .....
Ahhhh,
ok jetzt sehe ich es am Datenblatt. Das wäre dann Timer 1 und nicht
Timer 0.
Macht beio der Zahl auch Sinn einen 16Bit Timer zu nehmen anstatt 8 Bit.
Soo, jetzt bin ich aber immer noch nicht schlauer wie ich das jetzt
berechne. Vielleicht kann mir da jemand nochmal helfen.
Rätsel Rater schrieb:> Ja.>> Du brauchst einen 16-Bit-Timer. Nicht Timer 0 sondern Timer 1.> Mit dem Prescaler erzeugst du eine Clock-Frequenz die mindestens> doppelt so hoch ist wie die höchste Frequenz die du erzeugen> musst. Dann lasse den Zähler mit dieser Frequenz soweit laufen> dass er eine halbe Periode deiner zu erzeugenden Frequenz zählt> und damit den Augangs-Pin toggelt. Mit dem 16-Bit-Timer hast> du die Möglichkeit die Ziel-Periodendauer fein aufzulösen.> Bisschen rechnen darfst du selbst noch .....
Danke für deine Antwort. Sollte ich dafür dann den CTC Mode nehmen oder
ist das mit einem PWM Mode einfacher zu lösen?
Hallo,
das ist im Prinzip erstmal egal ob Timer 0 oder Timer 1. Die Wahl ist
nur davon abhängig ob man PD5 direkt oder indirekt takten lässt.
Natürlich hat man mit Timer 1 eine höhere Auflösung. Aber auch hier
wirst du deine Werte runden müssen. Das geht gar nicht anders. Die
Abweisung ist nur kleiner.
Okay, meinetwegen CTC Mode mit Timer 1. Der Pin schaltet dann in einer
ISR um. Dann schreib jetzt einmal den Code dafür das der Pin PD5 mit
100Hz taktet, sprich toggelt.
Veit D. schrieb:> Der Pin schaltet dann in einer ISR um.
Zum Togglen eines Pins braucht es keine ISR. Wäre auch für einen
AVR bei 20 kHz etwas mühselig.
Hallo Veit,
vielen Dank für deine nette Antwort.
mich machen diese ganzen Werte und bezeichnungen einfach verrückt.
So mal kurz zum durchgehen:
Nehmen wir mal an, dass ich den TIMER1 im CTC mode nehme.
Dann hätte ich einen TOP vom OCR1A laut Datenblatt. Jetzt ist aber laut
Datenblatt an PD5 OC0B. Ich verstehe nicht was diese Werte jetzt genau
bedeuten. OCR1A ist ja die obere Grenze bei der der Timer dann den
Overflow ausruft und von vorne anfängt. OCR1A gebe ich vor.
Habe ich also einen Takt von 16Mhz und teile dies nun mal durch einen
Prescaler von 1024 erhalte ich 15625Hz.
Nun weiß ich, dass der 16Bit Timer genau 65535 Schritte zählt.
Das bedeutet, dass er alle 65535/15625 = 4.19sec einen Overflow auslöst.
Ich hoffe bis hierher ist es richtig.
Wenn ich nun eine frequenz von 100Hz haben möchhte nehme ich folgende
Gleichung aus dem Datenblatt:
f = (fclk)/2*prescaler*(1+OCR).
Nach OCR umgestellt ergibt sich: ORC = 77.1 also 77.
Ich habe nun folgenden Code geschrieben:
1
intmain(void)
2
{
3
4
TCCR1A|=(1<<WGM12);//set CTC mode
5
TCCR1B|=(1<<CS10)|(1<<CS12);//set prescaler to 1024
6
TIMSK1|=(1<<OCIE1A);
7
OCR1A=77;
8
DDRD=0xFF;
9
10
while(1)
11
{
12
13
14
}
15
16
}
17
ISR(TIMER1_COMPA_vect)
18
{
19
20
PORTD^=(1<<PORTD5);
21
22
}
leider bleibt die LED komplett aus. Jetzt verstehe ich überhaupt nichts
mehr.
݂
Hallo,
du musst noch die Interrupts generell einschalten.
sei()
Edit:
Pin toggeln geht noch etwas schneller mit
PIND = (1 << PD5);
bzw.
PIND = _BV(PD5);
Veit D. schrieb:> PIND = (1 << PD5);
Öh... Ich habe bestimmt komplett etwas übersehen oder stehe EXTREM auf
dem Schlauch... aber damit wird doch die LED nur eingeschaltet?!
Wo/wie wird sie denn abgeschaltet? Das Bit wird ja nur gesetzt und nicht
mehr gelöscht - oder übersehe ich da vollkommen etwas?
Veit D. schrieb:> du musst noch die Interrupts generell einschalten.> sei()
stimmt
Veit D. schrieb:> PIND = _BV(PD5);Stefan S. schrieb:> .. aber damit wird doch die LED nur eingeschaltet?!
stimmt auch
ich rate mal gemeint war:
Marco S. schrieb:> PORTD ^= (1 <<PORTD5);
also zum Toggeln XOR
Hallo,
wenn der Pin auf Ausgang konfiguriert ist, kann man in einem Taktzyklus
den Pin toggeln, in dem man das Bit für den Input Port setzt. Jedes
erneute setzen des Bits toggelt den Pin. Genaueres kann man im Manual
nachlesen.
Stefan S. schrieb:> Öh... Ich habe bestimmt komplett etwas übersehen oder stehe EXTREM auf> dem Schlauch... aber damit wird doch die LED nur eingeschaltet?!>> Wo/wie wird sie denn abgeschaltet? Das Bit wird ja nur gesetzt und nicht> mehr gelöscht - oder übersehe ich da vollkommen etwas?
Hast recht....das exOR vom TO war schon richtig.
PORTD ^= (1 <<PORTD5);
Veit D. schrieb:> Jedes> erneute setzen des Bits toggelt den Pin.
Seit wann das denn bitte???
Das würde ja die ganze Logik durcheinander werfen...
Bin für neues offen, aber bitte zeig mir wo das steht?
Wenn überhaupt, dann macht es diese Zeile:
PORTD ^= (1 <<PORTD5);
Der µC toggelt da gar nix automatische...es geht um die Logik-Operatoren
die man anwendet.
Stefan S. schrieb:> Veit D. schrieb:>> PIND = (1 << PD5);>> Öh... Ich habe bestimmt komplett etwas übersehen oder stehe EXTREM auf> dem Schlauch... aber damit wird doch die LED nur eingeschaltet?!
Da hast du was übersehen!
Oder der extreme Schlauch...
Egal.
Joachim B. schrieb:> Veit D. schrieb:>> PIND = _BV(PD5);>> Stefan S. schrieb:>> .. aber damit wird doch die LED nur eingeschaltet?!>> stimmt auch
Nein, stimmt nicht.
Du: Auch noch mal ins Datenblatt schauen.
Veit D. schrieb:> den Pin toggeln, in dem man das Bit für den Input Port setzt. Jedes> erneute setzen des Bits toggelt den Pin. Genaueres kann man im Manual> nachlesen.
Ach... da war was. Aber das Datenblatt spricht vom PINx-Register:
1
However, writing a logic one to a bit in the PINx register, will result in a toggle in the corresponding bit in the data register.
Veit D. schrieb:> ISR(TIMER1_COMPA_vect)> {> PIND = _BV(PD5);> }Veit D. schrieb:> wenn der Pin auf Ausgang konfiguriert ist, kann man in einem Taktzyklus> den Pin toggeln, in dem man das Bit für den Input Port setzt
ich glaube ich verstehe was du meinst, aber toggeln ohne XOR sehe ich
nicht.
mir fehlt halt das Rücksetzen!
Ich denke gerade an DDR Data Register auf out und in umschalten aber
auch das sehe ich nicht.
Bist du sicher das du es ohne XOR schaffst?
Oder ist das ein Trick den nur wenige kennen?
Stefan S. schrieb:> Ach... da war was. Aber das Datenblatt spricht vom PINx-Register:
Ja vom OUT hab ich das noch net gehört...
würde mir aber auch in der Programmierung sehr "missfallen"...ich setze
und er macht was er vorher hatte kombiniert mit dem was ich ihm sage :-D
strange
Joachim B. schrieb:> Bist du sicher das du es ohne XOR schaffst?
Veit kann das und ich auch.
Dokumentiert ist es auch.
> Oder ist das ein Trick den nur wenige kennen?
Wenn die "wenigen" das Datenblatt verstehend lesen können... ja.
Die anderen bleiben außen vor.
Veit D. schrieb:> In meinem Manual 17.2.2.
In dem von mir verlinkten Datenblatt stehen da nur die Definitionen für
den Timer2 drin.
Besser verlink, welches Datenblatt du meinst, damit jeder auf dem
gleichen Stand ist.
In dem Microchip Datenblatt steht unter 17.2.2 nur folgendes:
1
Many register and bit references in this document are written in general form. A lower case “n” replaces the Timer/Counter number, in this case 2. However, when using the register or bit defines in a program, the precise form must be used, i.e., TCNT2 for accessing Timer/Counter2 counter value and so on.
Hallo,
brauchen tut man gar nichts. Aber der TO möchte ja bis 20kHz takten, da
sollte man die ISR schon mit offiziellen Mitteln beschleunigen. Wenn dir
XOR lieber ist, dann bleibste bei XOR. Kostet nur ein paar mehr Takte.
Ok jetzt verstehe ich leider gar nichts mehr.
Warum hast du WGM12 in Register TCCR1B gesetzt.
Laut Datenblatt wird dieses in TCCR1A gesetzt.
Ich habe jetzt den Code für 1Hz. Angeschlossen habe ich eine
Piezo-Lautsprecher zum testen. Er tickt 2mal pro sekunde. Ist das nun
1Hz oder 1/2Hz? Weiß nicht genau ob der auch beim Low Pegel tickt
Veit D. schrieb:> Kostet nur ein paar mehr Takte.
Ja aber mit deinem Vorschlag mit 77 und 1024 kommst auch net perfekt
raus.
um die 100Hz Schritte bei 19.9kHz 20.0kHz hinzubekommen muss man etwas
genauer sein.
> ...aber braucht man das echt?
Es kann sehr praktisch sein - in dem Beispiel hier schreibt ein
Assemblerprogrammierer in die ISR nur dieses 'sbi PIND,5 reti', kein
Sichern eines Registers, kein Sichern von SREG, nicht mal ein rjmp, da
die zwei Befehle beim ATmega328 bequem in die Vektortabelle passen.
Marco S. schrieb:> Ich habe jetzt den Code für 1Hz. Angeschlossen habe ich eine> Piezo-Lautsprecher zum testen. Er tickt 2mal pro sekunde. Ist das nun> 1Hz oder 1/2Hz? Weiß nicht genau ob der auch beim Low Pegel tickt
Er wird sicherlich bei jeder Flankenänderung klicken. Denn bei jeder
Flanke bewegt sich die Membran - und genau das ist, was du hörst.
Pro Sekunde zwei Flanken H->L und L->H heißt du hast zwei Interrupts pro
Sekunde oder eben eine Frequenz von 1Hz. Denn eine Periode sieht so aus:
___|----|
0,5 Sekunden low, 0,5 Sekunden high. Und dann geht's von vorne los.
Das kannst du auch einfach mit einem Oszilloskop oder einem einfachen
Multimeter mit Frequenzmesser kontrollieren.
0,5Hz hieße eine Periode pro zwei Sekunden. Ergo: 1s low; 1s high. Also
ein Klick pro Sekunde.
Marco S. schrieb:> Ok jetzt verstehe ich leider gar nichts mehr.> Warum hast du WGM12 in Register TCCR1B gesetzt.> Laut Datenblatt wird dieses in TCCR1A gesetzt.
Lies nochmal das Manual genau durch. In meinem stehen alle benötigten
Bits im TCCB1B Register. Vom TCCR1A Register benötigen wir hier in dem
Fall keine Bits.
Stefan S. schrieb:> Das kannst du auch einfach mit einem Oszilloskop oder einem einfachen> Multimeter mit Frequenzmesser kontrollieren.
Die armen Spatzen, immer gleich die Kanone.
Ich wuerde dazu eine simple LED mit Vorwiderstand benutzen.
wendelsberg
Edit:
Das mit den 16MHz passt irgendwie nicht ...bzgl der Schrittweite.
Ich probiere es morgen früh mal aus auf einem 328PB.
Poste dann die Messung mit nem LA.
Marco S. schrieb:> 100Hz und 200Hz geht ja noch. Bei 300Hz kommt dann eine Dezimalzahl für> OCR.
Nun, da die Hardware nur ganzzahlig teilen kann, ist es unvermeidlich,
dass man nicht jede Frequenz exakt darstellen kann.
Was man immerhin machen kann: In Software den Fehler möglichst
gleichmäßig zu verteilen, so dass sich am Ende, über längere Zeiträume
betrachtet, doch die exakte Frequenz ergibt. Aber natürlich läuft auch
die Software getaktet durch die Hardware, ist also in ihren
Möglichkeiten, den Fehler zu verteilen, beschränkt.
Das Ergebnis ist Phasenjitter. Also: Frequenz passt nur über längere
Zeiträume betrachtet exakt, springt aber in Wirklichkeit zwischen zwei
diskreten Werten hin und her. In exakt dem Verhältnis, was nötig ist, um
im Mittel auf auf die Zielfrequenz zu kommen.
Aber: Bis du sowas gebacken bekommst, werden wohl noch ein paar
Wochenenden in's Land gehen...
Du solltest diese Möglichkeiten also erstmal wieder vergessen. Mir
scheint, du bekommst es nichtmal gebacken, allein mit der Hardware die
jeweils bestmögliche Näherung an die Zielfrequenz zum Spielen zu
bekommen. Dabei ist das wirklich ziemlich easy...
Die Strategie ist ganz simpel:
1) wähle denjenigen Prescalerfaktor für den Timer, bei der der mit
vollem Zählumfang gerade so noch eine Frequenz erzeugt, die kleiner ist
als die Zielfrequenz.
2) Berechne aus Systemtakt und Prescaler den OCR-Wert, bei dem die
resultierende Frequenz der gewünschten Zielfrequenz am nächsten liegt.
Das sind zwei hochtriviale Formeln und ein wenig Datenblattlektüre, um
sie zu finden und die möglichen Prescalerfaktoren des gewählten Timers.
Kinderkram. Da soltle man kein Wort drüber verlieren müssen, geschweige
denn einen Thread in einem Forum lostreten...
Ich würde das einfacher lösen.
PD2 und PD3 sind die Int0 und Int1 Pins. Damit kannst Du in der ISR
einen delay Wert berechnen. In der Main() toggelst Du den PD4 und nutzt
_delay_us() für die Frequenz. Die ISR berechnen dann nur den passenden
Eert für die delay Fkt.
Rechnen kannst du ja selbst
Adam P. schrieb:> Veit D. schrieb:>> Kostet nur ein paar mehr Takte.>> Ja aber mit deinem Vorschlag mit 77 und 1024 kommst auch net perfekt> raus.> um die 100Hz Schritte bei 19.9kHz 20.0kHz hinzubekommen muss man etwas> genauer sein.
Von wem stammt das?
Wobei ich hier nicht so pingelig bin. Zudem 19,9kHz gerundet auch 20kHz
sind. :-) Der Prof. möchte sicherlich das sich seine Studenten erstmal
mit Timer generell befassen, Taster abfragen und anwenden können. Wie
man das Optimum aus den Timersettings rausbekommt hatte ich Eingangs
kurz beschrieben. Zumindestens mache ich das so bei meinem
Frequenzgenerator. Ich würde an der Stelle vom TO darüber erstmal keinen
größeren Aufwand betreiben. Kann er später immer noch machen. Sinnvoll
im späteren Austausch mit seinen Mitstudierenden wenn jeder mit einer
anderen Lösung ankommt. Dann könnten die sich austauschen und die
optimale Lösung von allen zusammenwürfeln. Das ist mein Verständnis vom
Ziel des Prof.
Wegen Schrittweite. Darauf würde ich mich erst gar nicht festnageln
lassen.
Die Taster ändern nicht die OCR1A "Schrittweite" direkt, sondern die
Soll-Frequenz in 100Hz Sprüngen. Alles Benötigte wird frisch berechnet
und zugewiesen. Was denkst du wie einfach das wird wenn der Prof
plötzlich die Frequenzsprünge geändert haben möchte, nur so zum Spass
... der staunt nicht schlecht. :-)
Noch ein Tipp an den TO. Schreibe klare Funktionen. Das erhöht die
Lesbarkeit und die spätere Wartbarkeit vom Programm.
Hallo,
ein letzter Hinweis an den TO. Wenn du für deinen Frequenzbereich einmal
paar Werte für Presaler und TOP durchrechnest, dann wirst du sicherlich
wie ich festsellen, dass du mit einem Prescaler alles (ausreichend)
genau abdecken kannst. Das würde dir erstmal vieles erleichtern. Die
Tastersteuerung wird nämlich ungleich komplizierter.
c-hater schrieb:> Die Strategie ist ganz simpel:>> 1) wähle denjenigen Prescalerfaktor für den Timer, bei der der mit> vollem Zählumfang gerade so noch eine Frequenz erzeugt, die kleiner ist> als die Zielfrequenz.
Am geschicktesten ist es, den Vorteiler auf 1 zu belassen. Dann lassen
sich mit den Timern alle Intervalle/Perioden auf 62,5 ns genau erzeugen.
Längere Intervalle lassen sich aus Teilintervallen zusammensetzen.
Sofern PD5 Ausgang sein soll, ist Timer0 mit OCR0B die passende Wahl, um
jitterfreie Signale zu erzeugen.
Fertige Lösungen dazu finden sich zwar im Netz, aber hier wird ja eine
eigene Leistung gefordert.
Veit D. schrieb:> ISR(TIMER1_COMPA_vect)> {> PIND = _BV(PD5);> }
Solche Mikrooptimierungen braucht man nicht, laß das sein. Es erschwert
nur die Lesbarkeit.
1
PORTD^=(1<<PORTD5);
ist schon vollkommen o.k.
Es gibt genug andere Baustellen für einen Anfänger.
Die Wahl von PD5 ist natürlich optimal blöd. Wenn der Prof etwas
intelligenter gewesen wäre, hätte er besser einen Compare-Pin eines
16Bit-Timers genommen (PB1, PB2, PD0, PD1, PD2).
So muß man mit etwas Jitter leben. Exakt 100Hz Schrittweite läßt sich eh
nicht erreichen. Die Umrechnung Frequenz in Teilerfaktoren macht man am
besten in float und rundet dann.
Will man es besonders schön machen, benutzt man das DDS-Prinzip.
https://de.wikipedia.org/wiki/Direct_Digital_Synthesis
Das ist aber nur was für Fortgeschrittene.
Für die Tastenabfrage suche am besten eine professionelle Entprell-Lib
mittels Timer.
Libs mit Delay(xx) taugen in der Regel nichts und bereiten nur Ärger.
Delay != Entprellung.
Allgemein halte ich die Aufgabe für einen Anfänger schon für sehr
ambitioniert.
Man sieht sofort, daß der Prof sich selber keinerlei Gedanken gemacht
hat, wie man die Aufgabe lösen kann.
m.n. schrieb:> Sofern PD5 Ausgang sein soll, ist Timer0 mit OCR0B die passende Wahl, um> jitterfreie Signale zu erzeugen.Peter D. schrieb:> Die Wahl von PD5 ist natürlich optimal blöd.> ...> So muß man mit etwas Jitter leben.
Was stimmt denn nun?
;-)
m.n. schrieb:> Was stimmt denn nun?> ;-)
Naja, von hinten durch die Brust ins Auge mit T1 dann T0 vorbereiten,
halte ich nicht für so anfängertauglich.
Möglich ist es natürlich.
m.n. schrieb:> Am geschicktesten ist es, den Vorteiler auf 1 zu belassen. Dann lassen> sich mit den Timern alle Intervalle/Perioden auf 62,5 ns genau erzeugen.> Längere Intervalle lassen sich aus Teilintervallen zusammensetzen.> Sofern PD5 Ausgang sein soll, ist Timer0 mit OCR0B die passende Wahl, um> jitterfreie Signale zu erzeugen.
Das war auch meine Idee, aber nöö, das geht so nicht.
Timer Init. für PD5 als Output jede 0,125 µs:
1
voidinit_timer(void)
2
{
3
TCCR0A=(1<<COM0B0)|(1<<WGM01);
4
TCCR0B=(1<<CS00);
5
OCR0A=1;
6
// TIMSK0 = (1 << OCIE0A);
7
}
16 MHz ohne Prescaler bist bei 0,0625 µs.
Dann lässt ihn bis 1 hochzählen, bist bei 0,125 µs.
Ja, dies entspricht zwar der kleinsten Differenz:
1
HzT(µs)Flanket(µs)
2
1990050,2512562825,12562814
3
200005025
4
5
25,12562814-25=0,125628141µs
Dies würde aber bedeuten er müsste alle 2 Takte in die ISR.
In dieser müsstest du einen Wert zählen, vergleichen usw.
Das geht nicht...(zeitlich).
Selbst nur das hier:
1
ISR(TIMER0_COMPA_vect)
2
{
3
PORTD^=(1<<PORTD6);
4
}
Dauert ca. 2,43 µs. (siehe Anhang)
Also wird man wohl doch den 16-Bit Timer nutzen müssen,
dann kommt er maximal alle 25 µs in die ISR, in der man dann den PD5
toggelt.
m.n. schrieb:> TCCR0A bietet doch passende Bits, um PD5> synchron zu verändern.
Macht er ja auch...
Adam P. schrieb:> TCCR0A = (1 << COM0B0)
PD6 hab ich nur als 2ten Pin der per Software (ISR) gesteuert wird,
als "Vergleich".
Peter D. schrieb:> Veit D. schrieb:>> ISR(TIMER1_COMPA_vect)>> {>> PIND = _BV(PD5);>> }>> Solche Mikrooptimierungen braucht man nicht, laß das sein. Es erschwert> nur die Lesbarkeit.
Das ist keine Mikrooptimierung.
Auch kein Trick.
Sondern die im Datenblatt aufgeführte Art un Weise einen Pin zu toggeln.
Zudem die schnellste Art und Weise.
Und wenn es die "Lesbarkeit erschwert", dann weil es für dich ungewohnt
ist.
Ich setze es seit Jahren ein, und meine Lesungen stört es nicht.
Eine Empfehlung das nicht zu nutzen verstehe ich überhaupt nicht.
Da könnte man auch sagen: Scheiß auf das Datenblatt!
---
Ok, wenn du das nicht brauchst, von mir aus.
Aber aus deiner Beschränkung eine Regel/Empfehlung für andere
abzuleiten, finde ich gnadenlos *.
(bei bitte * ein Wort deiner eigenen Wahl einsetzen)
Adam P. schrieb:> m.n. schrieb:>> TCCR0A bietet doch passende Bits, um PD5>> synchron zu verändern.
Dein Beitrag kam dazwischen, ich hatte es als Antwort hierauf
geschrieben:
Peter D. schrieb:> mit T1 dann T0 vorbereitenAdam P. schrieb:> Dies würde aber bedeuten er müsste alle 2 Takte in die ISR.
Ich weiß nicht, was Du hier rechnest. Für 19900 Hz hat man 402 Takte, um
den Pegel zu wechseln.
m.n. schrieb:> Adam P. schrieb:>> Dies würde aber bedeuten er müsste alle 2 Takte in die ISR.>> Ich weiß nicht, was Du hier rechnest. Für 19900 Hz hat man 402 Takte, um> den Pegel zu wechseln.
Falls du die kleinste Differenz haben willst und das sind 0,125µs,
zwischen 19900 und 20000 Hz.
Und die 402 passen halt nicht in das 8-Bit Register vom T0.
(Hab ich den Beitrag etwa so unverständlich geschrieben :-/ ? - sorry)
Adam P. schrieb:> Falls du die kleinste Differenz haben willst und das sind 0,125µs,
Da kein 50 Tastverhältnis gefordert ist, ist die Auflösung 62,5 ns.
> Und die 402 passen halt nicht in das 8-Bit Register vom T0.
Es passen aber 2 x 201 in das Register, was den gewünschten Effekt hat.
m.n. schrieb:> Längere Intervalle lassen sich aus Teilintervallen zusammensetzen.
m.n. schrieb:> Adam P. schrieb:>> Falls du die kleinste Differenz haben willst und das sind 0,125µs,>> Da kein 50 Tastverhältnis gefordert ist, ist die Auflösung 62,5 ns.>>> Und die 402 passen halt nicht in das 8-Bit Register vom T0.>> Es passen aber 2 x 201 in das Register, was den gewünschten Effekt hat.>> m.n. schrieb:>> Längere Intervalle lassen sich aus Teilintervallen zusammensetzen.
Ich glaub wir reden aneinander vorbei...
OK, nehmen wir mal deine 2 x 201.
Ich lade den OCR0A mit 201, dann kommt mein Interrupt nach 12,625 µs.
Jetzt würde ich die 201 neu laden und erst beim nächsten Interrupt den
Pin toggeln (19900 Hz) - ok, aber in der Zeit wo ich in der ISR bin,
zählt der Timer munter weiter (bzw. Zeit vergeht), bevor die 201 das 2te
mal gesetzt werden...
Damit wirds auch nicht wirklich genau...
So kommt man zumindest auf 19,910 kHz.
---
Und wenn ich nun ja doch in der ISR alles selbst machen muss (ohne
Output Compare pin (OC0B)), dann kann ich auch direkt den 16-bit nehmen
und mir die Teilintervalle sparen und die Ungenauigkeit durch den ISR
call verschwindet auch, oder nicht.
Adam P. schrieb:> Jetzt würde ich die 201 neu laden und erst beim nächsten Interrupt den> Pin toggeln (19900 Hz) - ok, aber in der Zeit wo ich in der ISR bin,> zählt der Timer munter weiter (bzw. Zeit vergeht), bevor die 201 das 2te> mal gesetzt werden...
Das geht so:
nachladewert = 201;
...
OCR0A += nachladewert;
Adam P. schrieb:> Und wenn ich nun ja doch in der ISR alles selbst machen muss
Mußt Du nicht, das macht der µC ;-)
m.n. schrieb:> OCR0A += nachladewert;
Das bezweifel ich sehr stark...
Dir ist bewusst, dass das OCR0A nur das Vergleichsregister für das TCNT0
Register ist?
OCR0A verändert sein Wert nicht, wenn du es nicht änderst.
Also wenn da 201 drin steht und ich das ausführe
1
OCR0A+=nachladewert;
dann gibts erst ein Überlauf und dann steht in OCR0A = 146.
Marco S. schrieb:> Ich habe einen Atmega328PB mit 16MHz.> Die Erste Aufgabe lautet:> "Generieren Sie am PIN PD5 ein Signal mit einer Frequenz von 100Hz.
Steht in Deiner Aufgabe, dass der Atmega328PB mit 16 MHz laufen soll?
Falls nicht - und sowieso - schon mal über PWM nachgedacht?
Adam P. schrieb:> dann gibts erst ein Überlauf und dann steht in OCR0A = 146.
Da wir nicht in einem Kochforum sind, ist der Überlauf nichts Schlimmes.
Egal was genau in OCR0A steht. Hauptsache, es sind 201 Takte, bis der
Zähler dort hinkommt.
Aber aufpassen: OCR0A war jetzt Dein Beipiel. Genau genommen muß es hier
OCR0B sein.
m.n. schrieb:> Sofern PD5 Ausgang sein soll, ist Timer0 mit OCR0B die passende Wahl, um> jitterfreie Signale zu erzeugen.
m.n. schrieb:> Egal was genau in OCR0A steht. Hauptsache, es sind 201 Takte, bis der> Zähler dort hinkommt.>> Aber aufpassen: OCR0A war jetzt Dein Beipiel. Genau genommen muß es hier> OCR0B sein.
Ja gut, wenn du das so "missbrauchst", dann funktioniert es natürlich
schon :-)
Trotzdem irgendwie umständlich.
Adam P. schrieb:> m.n. schrieb:>> OCR0A += nachladewert;>> Das bezweifel ich sehr stark...>> Dir ist bewusst, dass das OCR0A nur das Vergleichsregister für das TCNT0> Register ist?> OCR0A verändert sein Wert nicht, wenn du es nicht änderst.>> Also wenn da 201 drin steht und ich das ausführeOCR0A += nachladewert;>> dann gibts erst ein Überlauf und dann steht in OCR0A = 146.
...und das macht genau das, was m.n. will, nämlich 201 Timer-Takte nach
der letzten ISR-Auslösung den nächsten Aufruf auslösen (wenn das
"nachladen" nicht zu lange gedauert hat und die 201 Takte schon vorbei
sind).
Mal es dir mal auf. Das funktioniert.
Ob es in dieser Situation bzw. als Lösung für die Hausaufgabe
die sinnvollste Lösung ist, weiß ich nicht. Meiner Einschätzung nach
wird der Prof/Übungsleiter das eher als Fingerübung zur Nutzung der
PWM-Hardware gedacht haben, die kann nämlich zufällig auf PD5 zugreifen
- sowohl beim 328P als auch beim 328PB.
MfG, Arno
Hallo an alle,
erstmal vielen Dank für die ganzen tollen Antworten.
Ich habe echt bis heute Nacht noch programmiert und es auch hibekommen,
dass PD2 die Frequenz hochzählt. Zumindest wird der Piezo lauter und das
auch nicht abrupt. Nach einem Gespräch mit dem Prof gestern, meinte er,
dass die Werte nicht 100% exakt sein müssen. Ein paar Prozent Abweichung
können es schon sein.
So jetzt brauche ich aber nochmal eure Hilfe. Wie in der
Aufgabenstellung ja angegeben, soll en Taster an PD2 die Frequenz
hochzählen und einer an PD3 runterzählen.
Hochzählen klappt ja. Ich habe hier mal den Code für das hochzählen.
1
uint16_ti=100;
2
intmain(void)
3
{
4
TCCR1B|=/*(1<<CS11) |*/(1<<WGM12);//set prescaler to 8
5
TIMSK1|=(1<<OCIE1A);//set compare match interrupt
6
/*EIMSK |= (1<<INT0) | (1<<INT1);
7
EICRA |= (1<<ISC01) | (1<<ISC11);*/
8
DDRD=0xFF;//set DDRD to output
9
DDRD&=~((1<<PORTD2)|(1<<PORTD3));//set PORTD2 and PORTD3 to input for switch UP and DOWN
10
PORTD|=(1<<PORTD2)|(1<<PORTD3);
11
sei();//set global interrupt
12
charzustand=0;// flag for switch
13
14
while(1)
15
{
16
17
if(!(PIND&(1<<PIND2))&&zustand==0)
18
{
19
zustand=1;
20
if(i<=500)
21
{
22
TCCR1B|=(1<<CS11);//set prescaler to 8
23
OCR1A=16000000/(2*8*i);
24
i+=100;
25
26
_delay_ms(100);
27
}
28
else
29
{
30
TCCR1B&=~(1<<CS11);
31
TCCR1B|=(1<<CS00);
32
OCR1A=(16000000/(2*1*i));//switch prescaler to 1
33
i+=100;
34
zustand=1;
35
_delay_ms(100);
36
}
37
}
38
39
if((PIND&(1<<PIND2))&&zustand==1)
40
{
41
zustand=0;
42
_delay_ms(50);
43
}
44
45
}
46
47
}
48
ISR(TIMER1_COMPA_vect)//ISR toggles PD5 for creating the signal at this Pin.
49
{
50
PORTD^=(1<<PD5);
51
52
}
53
54
/*ISR(INT0_vect)
55
{
56
if(i <= 500)
57
{
58
TCCR1B |= (1<<CS11);
59
OCR1A = 16000000/(2*8*i);
60
i += 100;
61
}
62
else
63
{
64
TCCR1B &= ~(1<<CS11);
65
TCCR1B |= (1<<CS00);
66
OCR1A = (16000000/(2*1*i));
67
i += 100;
68
69
}
70
}
71
72
ISR(INT1_vect)
73
{
74
if(i < 500)
75
{
76
TCCR1B |= (1<<CS11);
77
OCR1A = 16000000/(2*8*i);
78
i -= 100;
79
}
80
else
81
{
82
TCCR1B &= ~(1<<CS11);
83
TCCR1B |= (1<<CS00);
84
OCR1A = (16000000/(2*1*i));
85
i -= 100;
86
}
87
}*/
Das Problem ist nur, dass wenn ich die If Verzweigung nun für PD3
nochmals einfüge und zum runterzählen ändere, ganz komische Sachen
passieren. PD2 zählt nur hoch, wenn man PD3 gedrückt hält. Oder es
treten Sprünge auf. Teilweise passiert auch mal gar nichts. Oder es
zählt hoch aber anders runter.
Unten habe ich das gleiche auch mit den externen Interrupts auf neative
Edge versucht. Gleiches Resultat.
Vielleicht hat jemand hier einen Tipp, wie ich das mit den Tastern lösen
kann, sodaß es vernünftig hoch und runterzählt.
Ach ja, den Prescaler habe ich nicht zum Spaß gewechselt. Das soll so
sein. Laut Prof sollte dieser ab 500 auf 1 gewechselt werden, damit das
OCR Register immer schön voll ist. Naja, meine Idee war es nicht.
Danke euch schon mal
Also ich hätte da jetz was mit dem 16-bit T1 (siehe Anhang).
Marco S. schrieb:> Ach ja, den Prescaler habe ich nicht zum Spaß gewechselt. Das soll so> sein. Laut Prof sollte dieser ab 500 auf 1 gewechselt werden, damit das> OCR Register immer schön voll ist.
Was ist denn nun eigentlich die Vorgabe?
Gestern war die Aufgabe eine Frequenz zu erzeugen, da hast du nichts von
den 500 erwähnt.
Marco S. schrieb:> if(!(PIND & (1<<PIND2)) && zustand == 0)> {> zustand = 1;> if(i <= 500)> {> TCCR1B |= (1<<CS11); //set prescaler to 8> OCR1A = 16000000/(2*8*i);> i += 100;>> _delay_ms(100);> }
Das sieht allgemein zu wirsch aus...
Trenne am besten die einzelnen Aufgaben.
1. Taster Entprellen
2. Entprelltes Signal auf Click prüfen
3. Click-Event für das ändern der freq-Variable nutzen
4. Variable an eine set-Funktion für den Timer übergeben.
Als erstes: Ich hab mich so sehr auf die 62,5ns fixiert, dass meine
Berechnung viel zu komplex geworden ist, ich werde es noch überarbeiten.
Marco S. schrieb:> 500 Hz
Was sollen diese 500 Hz bedeuten, für was sollen die sein?
Ich versteh den Zusammenhang nicht so wirklich.
OK...du nutzt Sie als untere Grenze um den Prescaler auf 8 zu setzen.
Ja, denn mit Prescaler 1 kommst halt bis 200 Hz runter, 100 gehen dann
nicht mehr, deshalb hab ich auf das umschalten verzichtet und lass ihn
2x in die ISR laufen für 100 Hz.
Aber die 500 sind ja willkürlich?
Marco S. schrieb:> Laut Prof sollte dieser ab 500 auf 1 gewechselt werden, damit das> OCR Register immer schön voll ist.
Was genau hat er den gesagt?
Was meint er mit "schön voll"...
Das ist dem OCR Register doch egal :-D außerdem ist es "voller" je
geringer die Frequenz.
Peter D. schrieb:> Veit D. schrieb:>> ISR(TIMER1_COMPA_vect)>> {>> PIND = _BV(PD5);>> }>> Solche Mikrooptimierungen braucht man nicht, laß das sein. Es erschwert> nur die Lesbarkeit.>
1
>PORTD^=(1<<PORTD5);
2
>
> ist schon vollkommen o.k.> Es gibt genug andere Baustellen für einen Anfänger.
Hallo Peter,
so sehr ich sonst deine Antworten und Code schätze, so sehr bin ich über
diese Antwort von dir enttäuscht. Du verpflichtest damit einen Anfänger
mit angezogener Handbremse zufahren. Obwohl das kein umständlicher
Syntax ist. Sondern ein ganz einfacher der dazu noch einen großen
Vorteil bietet. Man muss ihn nur einmal gelesen/verstanden haben das der
Atmega das kann.
Veit D. schrieb:> Man muss ihn nur einmal gelesen/verstanden haben das der> Atmega das kann.
Das gilt fuer alle "neueren" AVR Chips. Ich habe aber kein Datum
gefunden, wann das eingefuehrt wurde. E.g. aus dem ATtiny25 Datenblatt:
10.2.2 Toggling the PinWriting a logic one to PINxn toggles the value of
PORTxn, independent on the value of DDRxn. Note that the SBIinstruction
can be used to toggle one single bit in a port.
Man findet das sehr schnell mit der Suche nach "toggle" im Datenblatt.
leo
leo schrieb:> Man findet das sehr schnell mit der Suche nach "toggle" im Datenblatt.
Das alte Atmelstudio hat *.xml Dateien zur µC Beschreibung im Bauch.
Da findet sich die Toggle Fähigkeit bei den Daten für den Simulator.
Bei den neueren Atmel Studio ist das leider nicht mehr so dabei.
Es gibt auch keine offizielle Liste, welche AVR das können.
Veit D. schrieb:> Du verpflichtest damit einen Anfänger> mit angezogener Handbremse zufahren.
Schau einfach mal ins Listing, so schlimm ist das nicht.
Der Interrupt-Overhead beim AVR-GCC ist schonmal 25 Zyklen. Der
Unterschied zwischen beiden Varianten beträgt also nur 31 zu 37 Zyklen.
Veit D. schrieb:> Obwohl das kein umständlicher> Syntax ist.
Du kannst nicht erwarten, daß ein Anfänger das Datenblatt in- und
auswendig kennt. Da sind andere Sachen viel wichtiger für die Aufgabe.
Veit D. schrieb:> Sondern ein ganz einfacher der dazu noch einen großen> Vorteil bietet.
Das kleine bischen Zeiteinsparung ist vielleicht nett, aber mehr auch
nicht.
Hallo,
also nochmals danke für die hilfreichen Tipps.
Ich habe nun den Code fertig. Ich poste ihn hier mal.
1
#define F_CPU +6000000UL
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
#include<util/delay.h>
5
6
voidset_LED(uint16_tstep);
7
voiderase_LED(uint16_tstep);
8
voidincrease_frequency();
9
voiddecrease_frequency();
10
11
uint16_ti=100;
12
intmain(void)
13
{
14
TCCR1B|=(1<<CS11)|(1<<WGM12);//set prescaler to 8
15
TIMSK1|=(1<<OCIE1A);//set compare match interrupt
16
DDRD=0xFF;//set DDRD to output
17
DDRD&=~((1<<PORTD2)|(1<<PORTD3));//set PORTD2 and PORTD3 to input for switch UP and DOWN
18
PORTD|=(1<<PORTD2)|(1<<PORTD3);
19
DDRB=0xFF;
20
sei();//set global interrupt
21
charzustand_PD2=0;
22
charzustand_PD3=0;
23
24
while(1)
25
{
26
if((PIND&(1<<PIND2))&&zustand_PD2==0)
27
{
28
if(i<=20000)
29
{
30
zustand_PD2=1;
31
set_LED(i);
32
increase_frequency();
33
}
34
}
35
if(!(PIND&(1<<PIND2))&&zustand_PD2==1)
36
{
37
zustand_PD2=0;
38
_delay_ms(100);
39
40
}
41
42
if((PIND&(1<<PIND3))&&zustand_PD3==0)
43
{
44
if(i>=100)
45
{
46
zustand_PD3=1;
47
erase_LED(i);
48
decrease_frequency();
49
}
50
}
51
if(!(PIND&(1<<PIND3))&&zustand_PD3==1)
52
{
53
zustand_PD3=0;
54
_delay_ms(100);
55
}
56
}
57
}
58
ISR(TIMER1_COMPA_vect)
59
{
60
PORTD^=(1<<PORTD5);
61
}
62
63
voidincrease_frequency()
64
{
65
if(i<=500)
66
{
67
OCR1A=16000000/(2*8*i);
68
i+=100;
69
_delay_ms(100);
70
}
71
else
72
{
73
TCCR1B&=~(1<<CS11);
74
TCCR1B|=(1<<CS10);
75
OCR1A=(16000000/(2*1*i));
76
i+=100;
77
_delay_ms(100);
78
}
79
}
80
voiddecrease_frequency()
81
{
82
if(i>500)
83
{
84
TCCR1B&=~(1<<CS11);
85
TCCR1B|=(1<<CS10);
86
OCR1A=(16000000/(2*1*i));
87
i-=100;
88
_delay_ms(100);
89
}
90
else
91
{
92
OCR1A=16000000/(2*8*i);
93
i-=100;
94
_delay_ms(100);
95
}
96
97
98
99
}
100
101
voidset_LED(uint16_tstep)
102
{
103
if(step==100)
104
{
105
PORTB|=(1<<PORTB0);
106
}
107
if(step>=1000)
108
{
109
PORTB|=(1<<PORTB1);
110
}
111
if(i>=9000)
112
{
113
PORTB|=(1<<PORTB2);
114
}
115
if(i>=15000)
116
{
117
PORTB|=(1<<PORTB3);
118
}
119
if(i>=20000)
120
{
121
PORTB|=(1<<PORTB4);
122
}
123
124
}
125
voiderase_LED(uint16_tstep)
126
{
127
if(step==100)
128
{
129
PORTB|=(1<<PORTB0);
130
}
131
if(step<1000)
132
{
133
PORTB&=~(1<<PORTB1);}
134
if(i<9000)
135
{
136
PORTB&=~(1<<PORTB2);
137
}
138
if(i<15000)
139
{
140
PORTB&=~(1<<PORTB3);
141
}
142
if(i==20000)
143
{
144
PORTB&=~(1<<PORTB4);
145
}
An für sich funktioniert alles. PD2 erhöht die Frequenz und PD3
verringert sie.
Als Zusatz:
Die Funktionen setLED und eraseLED sind die Lösung von Aufgabenteil b).
Klar es gibt ja nicht nur einen. "KOTZ"
Dabei soll eben einfach nur eine LED-Leiste leuchten , wenn verschiedene
Frequenzen erreicht sind. Ok das ist nicht schwer.
Was mich nun noch etwas stört ist, dass das runterzählen nicht so klappt
wie das hochzählen. Ich habe das Gefühl, dass das Programm manche Werte
verschluckt. Am Ende bin ich zwar wieder auf 100Hz und das kennt man ja
nach 2 Tagen fiepen aber zwischendrin habe ich mal andere Töne
dazwischen. Als ob es dann mal kurz lauter wird und sich dann wieder
einreiht.
Leider habe ich kein Oszilloskop um den Frequnzgang wirklich zu testen
und ob man nun 6Khz von 8Khz auf 100Hz am Ton erkennt wage ich zu
bezweifeln.
Habt ihr noch eine Idee was da vielleicht das Problem sein köännte? Ich
denke als Laie und absoluter Anfänger habe ich wirklich bisher versucht
was ich kann.
Peter D. schrieb:>> Will man es besonders schön machen, benutzt man das DDS-Prinzip.> https://de.wikipedia.org/wiki/Direct_Digital_Synthesis> Das ist aber nur was für Fortgeschrittene.>
Na endlich!
Die Vorgabe der linearen Frequenzschrittweite legt doch nahe, dass es
genau das ist, worauf der Prof hinaus will.
Genaus so etwas habe ich mit dem Attiny auch schon mal programmiert (und
vor Jahrzehnten mit dem HD64180)
Du brauchst eine Sinustabelle mit z.B. 256 Stützstellen.
Einen 8 oder 16 bit Zeiger auf diese Tabellenzellen.
In einem festen Zeitraszter (25us-Interrrupt aka 40kHz) liest dieser
Zeiger diese Tabelle. Wobei zwischen zwei samples ein festgelegter
Offset zur vorigen Adresse aufaddiert wird. Dieser Offset ist der
Phasensprung.
Und damit sind wir beim Kern des DDS-Verfahrens: Je größer der
Phasensprung, desto höher die Frequenz - genauer: Die Frequenz ist
proportional zum Adressoffset/Phasensprung. Dies ermöglicht - beliebig
feine, lineare unterteilte Frequenzen - alles nur eine Frage der
RechenGenauigkeit des AddressAddierers.
In Deinem Interrupt mußt Du lediglich die neue Adresse berechnen und den
zugehörigen Momentanwert der Schwingung ausgeben. Das sollte in <25us zu
schaffen sein.
Das Hauptprogramm fragt die NutzerEingaben ab und berechnet den aktuell
gültigen AddressOffset.
Marco S. schrieb:> Leider habe ich kein Oszilloskop
Das brauchst Du unbedingt!
Marco S. schrieb:> OCR1A = 16000000/(2*8*i);
Das verstehe ich nicht.
Es fehlen jegliche Kommentare, wo was passiert und warum. Kommentiere
ruhig in Deutsch ;-)
Adam P. schrieb:> OK, das ist echt weniger Code...hätte ich nicht gedacht.
Das zeigt vor allem eins: der Compiler ist Scheisse, denn das geht
natürlich sehr viel effizienter auch dann, wenn das Toggle-Feature nicht
vorhanden ist (oder aus unerfindlichen Gründen nicht verwendet werden
soll).
Statt dieses Compiler-Bullshits:
1
ISR(TIMER1_COMPB_vect)
2
{
3
8a:8f93pushr24;2
4
8c:8fb7inr24,0x3f;63;1
5
8e:8f93pushr24;2
6
90:9f93pushr25;2
7
PORTD^=(1<<PORTD5);
8
92:8bb1inr24,0x0b;11;1
9
94:90e2ldir25,0x20;32;1
10
96:8927eorr24,r25;1
11
98:8bb9out0x0b,r24;11;1
12
}
13
9a:9f91popr25;2
14
9c:8f91popr24;2
15
9e:8fbfout0x3f,r24;63;1
16
a0:8f91popr24;2
17
a2:1895reti;--
18
;18
könnte man ohne große Klimmzüge auch machen (d.h.: auch der Compiler
(genauer: der Codegenerator) könnte es ohne große Probleme so machen,
wenn er denn das passende "Makro" für das Togglen einen einzelnen Bits
in einem SFIO-Register in seinem "Wissensschatz" hätte, was er aber
offensichtlich nicht hat...):
1
push r24 ; 2 2
2
in r24, 0x0b ; 1 1
3
sbrc r24, 5 ; 1 2
4
rjmp clearbit ; 2
5
sbr r24, 0x20 ; 1
6
out 0x0b, r24 ; 1
7
pop r24 ; 2
8
reti
9
clearbit:
10
cbr r24, 0x20 ; 1
11
out 0x0b,r24 ; 1
12
pop r24 ; 2
13
reti ; -- --
14
; 10 9
Das würde für alle IO-Register funktionieren, auf die sich mit in/out
zugreifen läßt, genau wie der Code, den der Compiler produziert, aber
durchschnittlich nur 52,8% der Zeit des Compilercodes brauchen. Das ist
schon eine Ansage, insbesondere für sehr häufig durchlaufene ISRs...
Aber natürlich geht es noch viel besser, wenn da nicht die störende
C-Runtime wäre, die einem am Arsch hängt wie ein Sack Steine.
Dann könnte man ein Immediate-fähiges Register für exclusiven Code als
Scratch reservieren und wäre bei:
1
in rsvd, 0x0b ; 1 1
2
sbrc rsvd, 5 ; 1 2
3
rjmp clearbit ; 2
4
sbr rsvd, 0x20 ; 1
5
out 0x0b, rsvd ; 1
6
reti
7
clearbit:
8
cbr rsvd, 0x20 ; 1
9
out 0x0b, rsvd ; 1
10
reti ; -- --
11
; 6 5
Womit wir dann bei nur noch durchschnittlich 30,6% des Compilercodes
wären.
Aber es geht noch mehr, wenn man die Verwendung des Codes einschränkt
auf den engeren Bereich der SFIO-Register, die sich halt auch mit
Instruktionen wie sbic,sbis,sbi und cbi ansprechen lassen. Auch das
könnte der Compiler tun, denn er weiß sehr wohl, ob das anzusprechende
Register dieser Bedingung genügt. Es würde dann herauskommen:
1
sbic 0x0b, 5 ; 1 2
2
rjmp clearbit ; 2
3
sbi 0x0b, 5 ; 2
4
reti
5
clearbit:
6
cbi 0x0b, 5 ; 2
7
reti ; -- --
8
; 5 4
Womit wir dann bei durchschnittlich 25% des Compilercodes wären. Es
ginge noch mehr, wenn man den ISR-Frame dadurch optimieren kann, dass
man den Code direkt in der Vektortabelle platziert, aber diese
Betrachtung lasse ich mal weg. Das nutze ich auch nur, wenn es denn
unbedingt erforderlich ist oder der ISR-Code vollständig in den Vector
passt, was hier nicht der Fall ist.
Aber klar, jetzt werden die C-Apologeten wieder anfangen, von unnötigen
"Mikroptimierungen" zu schwadronieren. Ja meine Herren, dann rechnet
doch bitte mal durch, was diese "Mikrooptimierung" auf 25% denn wohl
bedeuten mag, wenn die ISR z.B. mit 115200Hz auf einem Target mit 16MHz
Systemtakt aufgerufen wird...
Man kommt also schnell dahinter: Kleinvieh macht auch Mist (wenn es nur
oft genug scheißt)...
OCR1A=16000000/(2*8*i);//OCR1A with selected prescaler 8
76
i+=100;//increment i
77
_delay_ms(100);
78
}
79
else
80
{
81
TCCR1B&=~(1<<CS11);//switch prescaler to 1
82
TCCR1B|=(1<<CS10);
83
OCR1A=(16000000/(2*1*i));//OCR1A with swtiched prescaler 1
84
i+=100;
85
_delay_ms(100);
86
}
87
}
88
voiddecrease_frequency()
89
{
90
if(i>500)
91
{
92
TCCR1B&=~(1<<CS11);
93
TCCR1B|=(1<<CS10);
94
OCR1A=(16000000/(2*1*i));
95
i-=100;
96
_delay_ms(100);
97
}
98
else
99
{
100
OCR1A=16000000/(2*8*i);
101
i-=100;
102
_delay_ms(100);
103
}
104
105
106
107
}
108
109
voidset_LED(uint16_tstep)
110
{
111
if(step==100)
112
{
113
PORTB|=(1<<PORTB0);
114
}
115
if(step>=1000)
116
{
117
PORTB|=(1<<PORTB1);
118
}
119
if(i>=9000)
120
{
121
PORTB|=(1<<PORTB2);
122
}
123
if(i>=15000)
124
{
125
PORTB|=(1<<PORTB3);
126
}
127
if(i>=20000)
128
{
129
PORTB|=(1<<PORTB4);
130
}
131
132
}
133
voiderase_LED(uint16_tstep)
134
{
135
if(step==100)
136
{
137
PORTB|=(1<<PORTB0);
138
}
139
if(step<1000)
140
{
141
PORTB&=~(1<<PORTB1);}
142
if(i<9000)
143
{
144
PORTB&=~(1<<PORTB2);
145
}
146
if(i<15000)
147
{
148
PORTB&=~(1<<PORTB3);
149
}
150
if(i==20000)
151
{
152
PORTB&=~(1<<PORTB4);
153
}
154
155
}
Danke dir nochmal. Ich habe jetzt etwas mehr kommentiert. Sorry, ich
muss das leider auf Englisch kommentieren, da das Studium in Englisch
gehalten wird. Ich denke aber mal, dass das gut lesbar sein sollte.
Ich hoffe jemand kann mir noch bei dem zählproblem helfen
c-hater schrieb:> Aber klar, jetzt werden die C-Apologeten wieder anfangen, von unnötigen> "Mikroptimierungen" zu schwadronieren. Ja meine Herren, dann rechnet> doch bitte mal durch, was diese "Mikrooptimierung" auf 25% denn wohl> bedeuten mag, wenn die ISR z.B. mit 115200Hz auf einem Target mit 16MHz> Systemtakt aufgerufen wird...
Hast Du mal darüber nachgedacht, was eine PWM (in diesem Bereich sogar
ideal mit den 16 MHz Takt bei 100 Hz bis 20 kHz in 100 Hz Schritten) mit
OC0B (PD5) so machen könnte - ohne ISR?
Dazu braucht man nicht mal Assembler-Kenntnisse :-) -nur Timer 1 - und
die Ausführungs-Geschwindigkeit (mit Ausnahme der Taster-Abfrage) wird
sicher identisch sein (und deutlich genauer wie die ISR-Toggle-Methode).
S. Landolt schrieb:> Wie auch schon m.n. frage ich mich, wo der Timer1-Takt auf /8> umgeschaltet wird.
Der wurde schon oben in der Main als Basis prescaler festgelegt. Ich
hatte den direkt am Anfang festgelegt, da ich mit 8 sowieso starte und
erst ab 500Hz auf 1 umschalte.
Ein Freund schrieb:> Hast Du mal darüber nachgedacht, was eine PWM (in diesem Bereich sogar> ideal mit den 16 MHz Takt bei 100 Hz bis 20 kHz in 100 Hz Schritten) mit> OC0B (PD5) so machen könnte - ohne ISR?
Das kann man nicht wirklich. Da bleibt ganz sicher (für fast jede(!!!)
Zielfrequenz) ein Frequenzfehler.
Den könnte man ausmerzen (zumindest zu Phasenjitter reduzieren). Aber
eben auch wieder nur unter Benutzung einer ISR. Und bei einem solchen
Unterfangen kommt die Unvollkomenheit des Compilers noch sehr viel
stärker zum Tragen als bei dem gezeigten einfachen Beispiel dafür...
Das war so gewählt, damit es auch Dummies verstehen können, wenn sie
sich ein wenig anstrengen...
c-hater schrieb:> Das kann man nicht wirklich. Da bleibt ganz sicher (für fast jede(!!!)> Zielfrequenz) ein Frequenzfehler.
Dann teile doch mal (aus Spaß) 16 MHz durch 32 (Prescaler) und versuche
damit (mit einem 16-Bit-Zähler) 100 Hz und 20 kHz zu erreichen. Klingelt
da etwas? Wenn beides glatt aufgeht werden wohl auch die
Zwischenschritte glatt aufgehen - oder?
OCR1A=16000000/(2*8*i);//OCR1A with selected prescaler 8
78
i+=100;//increment i
79
_delay_ms(100);
80
}
81
else
82
{
83
TCCR1B&=~(1<<CS11);//switch prescaler to 1
84
TCCR1B|=(1<<CS10);
85
OCR1A=(16000000/(2*1*i));//OCR1A with swtiched prescaler 1
86
i+=100;
87
_delay_ms(100);
88
}
89
}
90
voiddecrease_frequency()
91
{
92
if(i>500)
93
{
94
TCCR1B&=~(1<<CS11);
95
TCCR1B|=(1<<CS10);
96
OCR1A=(16000000/(2*1*i));
97
i-=100;
98
_delay_ms(100);
99
}
100
else
101
{
102
TCCR1B|=(1<<CS11);
103
TCCR1B&=~(1<<CS10);
104
OCR1A=16000000/(2*8*i);
105
i-=100;
106
_delay_ms(100);
107
}
108
109
110
111
}
112
113
voidset_LED(uint16_tstep)
114
{
115
if(step==100)
116
{
117
PORTB|=(1<<PORTB0);
118
}
119
if(step>=1000)
120
{
121
PORTB|=(1<<PORTB1);
122
}
123
if(i>=9000)
124
{
125
PORTB|=(1<<PORTB2);
126
}
127
if(i>=15000)
128
{
129
PORTB|=(1<<PORTB3);
130
}
131
if(i>=20000)
132
{
133
PORTB|=(1<<PORTB4);
134
}
135
136
}
137
voiderase_LED(uint16_tstep)
138
{
139
if(step==100)
140
{
141
PORTB|=(1<<PORTB0);
142
}
143
if(step<1000)
144
{
145
PORTB&=~(1<<PORTB1);}
146
if(i<9000)
147
{
148
PORTB&=~(1<<PORTB2);
149
}
150
if(i<15000)
151
{
152
PORTB&=~(1<<PORTB3);
153
}
154
if(i==20000)
155
{
156
PORTB&=~(1<<PORTB4);
157
}
158
159
}
So und nochmal ein Danke. Jetzt hat sich auch das Problem mit dem
"Verschlucken" geklärt.
Klar wenn man Bits in einem Register setzt, sollte man sie auch wieder
löschen, wenn man sie nicht mehr braucht. Mein Problem war also, dass
dann nicht mit prescaler 8 sondern 64 gerechnet wurde. Nur weil ich das
Bit nicht gelöscht habe.
Das nennt man Lerneffekt.
So letzte Frage die ich noch habe bisher:
Wenn ich den Taster PD2 drücke wird ja die Frequenz hochgezählt. Wenn
ich nun, sagen wir mal, bei 1kHz stoppe und PD3 drücke um wieder
runterzuzählen, dann bewirkt das Drücken von PD3 erstmal ein weiteres
hochzählen bevor dann runtergezählt wird. Das Gleiche bei PD2.
Drück ich also increment wird erst einmal dekrementiert und dann
inkrementiert.
An was kann das denn liegen? Die If-Verzweigungen sind doch meiner
Meinung nach eindeutig. Das ist das letzte Problem was ich noch habe
dann ist das Ding fertig.
Ich möchte euch auch nochmal ein Lob aussprechen. Ihr gebt super Tipps
und ich finde dieses Forum klasse. Es werden nicht einfach nur stupide
Lösungen reingeworfen sondern auch verlangt, selbst zu denken. Perfekt
zum lernen. Danke dafür
c-hater schrieb:> Das zeigt vor allem eins: der Compiler ist Scheisse, denn das geht> natürlich sehr viel effizienter auch dann, wenn das Toggle-Feature nicht> vorhanden ist (oder aus unerfindlichen Gründen nicht verwendet werden> soll).
C-hater, mein Sohn!
Wir beide wissen, dass es dumm ist, ein solches "fast pin Toggle
feature", links liegen zu lassen.
Das wäre dümmer, als sich ein Compiler anstellen könnte.
Zudem wissen wir beide über deinen Genius Bescheid.
Dummer weise setzt du diesen hier ein, um über die Arbeit anderer zu
schimpfen.
Du könntest die Compiler verbessern.
Eine kleine Alternative kann ich dir bieten:
Einfach die ISR in ASM verfassen, so lässt sie sich in jedes C/C++
Programm binden. (auch in Arduino Programme)
Und dein Gejaule hat sich so ziemlich erledigt.
Arduino Fanboy D. schrieb:> Eine kleine Alternative kann ich dir bieten:> Einfach die ISR in ASM verfassen, so lässt sie sich in jedes C/C++> Programm binden. (auch in Arduino Programme)> Und dein Gejaule hat sich so ziemlich erledigt.
Eine weitere Alternative ist: Nachdenken.
Marco S. schrieb:> Ich möchte euch auch nochmal ein Lob aussprechen. Ihr gebt super Tipps> und ich finde dieses Forum klasse. Es werden nicht einfach nur stupide> Lösungen reingeworfen sondern auch verlangt, selbst zu denken. Perfekt> zum lernen.
Wenn Du das sagst (schreibst) ...
Arduino Fanboy D. schrieb:> Ein virtuelles Weihnachtsgeschenk, von mir, für dich: Ein Aluhut!
Für mich für Dich - eine Lesehilfe. Leider nur über die Krankenkasse
realisierbar (das kann ich mir nicht leisten) - aber der Wille zählt.
Ein Freund schrieb:> Arduino Fanboy D. schrieb:>> Ein virtuelles Weihnachtsgeschenk, von mir, für dich: Ein Aluhut!>> Für mich für Dich - eine Lesehilfe. Leider nur über die Krankenkasse> realisierbar (das kann ich mir nicht leisten) - aber der Wille zählt.
Arduino Fanboy D. schrieb:> Ein virtuelles Weihnachtsgeschenk, von mir, für dich: Ein Aluhut!
Bisher hatte ich eine gute Meinung von Dir - das hat sich relativiert
:-)
Ein Freund schrieb:> Dann teile doch mal (aus Spaß) 16 MHz durch 32 (Prescaler) und versuche> damit (mit einem 16-Bit-Zähler) 100 Hz und 20 kHz zu erreichen. Klingelt> da etwas? Wenn beides glatt aufgeht werden wohl auch die> Zwischenschritte glatt aufgehen - oder?
Nö. Das ist nicht so. Beispiel 19900Hz:
16000000 / 32 = 500000
500000 / 19900 = 25,125628140703517587939698492462
Bestmögliche Näherung ist 25:
Es ergibt sich: 16000000 / (32 * 25) = 20000
Der Fehler (bezogen auf die Zielfrequenz, in Prozent):
(20000 - 19900) * 100 / 19900 = 0,50251256281407035175879396984925
Alles klar?
Und bei höheren Zielfrequenzen sieht das natürlich tendenziell immer
ungünstiger aus...
So letzte Frage die ich noch habe bisher:
Wenn ich den Taster PD2 drücke wird ja die Frequenz hochgezählt. Wenn
ich nun, sagen wir mal, bei 1kHz stoppe und PD3 drücke um wieder
runterzuzählen, dann bewirkt das Drücken von PD3 erstmal ein weiteres
hochzählen bevor dann runtergezählt wird. Das Gleiche bei PD2.
Drück ich also increment wird erst einmal dekrementiert und dann
inkrementiert.
An was kann das denn liegen? Die If-Verzweigungen sind doch meiner
Meinung nach eindeutig. Das ist das letzte Problem was ich noch habe
dann ist das Ding fertig.
Nicht streiten Leute. Macht das nach dieser FrageHAHAHA
c-hater schrieb:> 16000000 / 32 = 500000
500000 / 20000 = 25.
500000 / 100 = 5000
500000 / 19000 = 18224.23093...
Welcher Fehler ergibt sich?
Der Prof. ist mit
Marco S. schrieb:> Nach einem Gespräch mit dem Prof gestern, meinte er,> dass die Werte nicht 100% exakt sein müssen. Ein paar Prozent Abweichung> können es schon sein.
ein paar Prozent vollkommen zufrieden.
Der Kunde bestimmt, ob die Anforderung erfüllt wird :-)
Hier gibt es auch Leute, welche Frequenzbänder relativ sehen - wen
kümmert bes?
> So letzte Frage ...
Dazu fehlt mir jetzt der Antrieb; aber nochmals am Rande, ein Vorschlag:
mal im Datenblatt nachlesen, ob bei den OCR1A-Zuweisungen nicht ein -1
fehlt.
Marcus,
Nur ein Ratschlag am Rande:
Zur besseren Lesbarkeit gewöhne Dir bitte an zwischen Funktionen im
Source Code immer drei Leerzeilen einzufügen. Das verbessert die
optische Gruppierung der einzelnen Programmabschnitte, sonst muß man
immer die Klammerblöcke identifizieren.
Auch ein kleiner informativer Header mit funktionellen Hinweisen wie
Zweck, Parameter Verwendungshinweise oder Beschränkungen und falls
relevant diverse Return Codes wäre zur Langzeitwartung von Programmen
nicht schlecht wenn man sich später nicht mehr an die Details so gut
erinnert.
Ein Freund schrieb:> Bisher hatte ich eine gute Meinung von Dir - das hat sich relativiert> :-)Ein Freund schrieb:> Da stimmen wir merkwürdigerweise überein :-)
Da ist nix merkwürdiges dran.
Auch nix gutes.
Dein Befinden geht mir schlicht am Arsch vorbei.
Ein Freund schrieb:> Der Prof. ist mit> ein paar Prozent vollkommen zufrieden.
Was ist "ein paar"?
> Der Kunde bestimmt, ob die Anforderung erfüllt wird :-)
Nunja: spätestens, wenn irgendwelche Problem auftreten, wird der Kunde
behaupten, er hätte mit "ein paar" natürlich allenfalls "ein Paar"
gemeint oder sogar noch weniger...
Das "ein Paar" wäre mit deinem Ansatz immerhin knapp zu schaffen, der
worst case liegt bei knapp -1,9% Fehler, wenn ich mich nicht verrechnet
habe...
Ihr habt für so ne Popens-Aufgabe ein ganz schönes Fass aufgemacht. Ich
mein, hey Leute. Hier gehts um ne Aufgabe aus dem Studium. Hier soll
doch nur mit nem CTC-Mode ein Pin getoggelt werden.
Arduino Fanboy D. schrieb:> Das kann ich der Aufgabenstellung nicht "direkt" entnehmen.
Nein, das steht so direkt nicht drin aber das ist doch das
naheliegenste. Ich denke auch, dass es kein Zufall ist, dass man mit 200
Schritten den kompletten Bereich von 100 Hz bis 20.000 Hz durchlaufen
kann. Das schreit doch schon förmlich nach CTC-Mode. ;)
M. K. schrieb:> Nein, das steht so direkt nicht drin aber das ist doch das> naheliegenste. Ich denke auch, dass es kein Zufall ist, dass man mit 200> Schritten den kompletten Bereich von 100 Hz bis 20.000 Hz durchlaufen> kann. Das schreit doch schon förmlich nach CTC-Mode. ;)
Richtig, und selbst von CTC steht ja nichts in der Aufgabenstellung,
d.h. so etwas wie
1
while(1)
2
{
3
toggle(D5);// hier darf jeder seine liebste togglefunktion eintragen
4
while(delay-->0)
5
{
6
_delay_us(1);
7
}
8
}
und einer Berechnung von uint32_t delay bei einem Tastendruck wären zur
Erfüllung der Aufgabe schon ausreichend (einschließlich der zulässigen
Abweichung bei der Frequenz).
asdf schrieb:> wären zur> Erfüllung der Aufgabe schon ausreichend
Aber das macht man doch so nicht ;-)
Ein wenig eleganter darfs ja dann doch schon sein,
immerhin hat der TO ja bereits mit dem Timer angefangen und es nicht auf
diese stupide Art&Weise versucht.
Adam P. schrieb:> Ein wenig eleganter darfs ja dann doch schon sein,> immerhin hat der TO ja bereits mit dem Timer angefangen und es nicht auf> diese stupide Art&Weise versucht.
Erst mal: So reicht es ja, um die Aufgabenstellung zu erfüllen
Und elegant ist ansonsten mMn nur eine Lösung mit Timer0, da man hier
durch OCR0B den PD5 direkt von der Hardware aus schalten lassen kann.
Alles anderen mit Timer1 und dann von Hand den Port schalten ist m.E.
deutlich weniger elegant.
DDRD&=~((1<<PORTD2)|(1<<PORTD3));//set PORTD2 and PORTD3 to input for switch UP and DOWN
3
PORTD|=(1<<PORTD2)|(1<<PORTD3);//set PD2 and PD3 to input for Buttons
Sowas macht man nicht. Man setzt nicht alles pauschal auf Ausgang um
danach gezielt Eingänge zu konfigurieren. Zudem der Kommentar der 3.
Zeile falsch ist. Was bewirkt ein PORT Bit wenn es sich um einen Eingang
handelt?
Die Register sind nach Controller Reset alle genullt. Man muss vorher
nichts löschen. Zudem eine Kurzschlussverbindung zweier Eingänge keinen
Schaden verursacht. Dagegen zweier Ausgänge schon.
Entprelle die Taster mit klaren Funktionen. Ändere je nach
Tastererkennung den Wert. Berechne damit die neuen Timereinstellungen
und übernehme sie.
Dein falsches Inkrementieren/Dekrementieren liegt daran, dass du eine
Funktion in einer Funktion erstellst hast. Nochmal, schreibe klare
Funktionen die bei Bedarf aufgerufen werden. Die Led Band Spielerei
lenkt dich nur ab.
c-hater schrieb:> Das "ein Paar" wäre mit deinem Ansatz immerhin knapp zu schaffen, der> worst case liegt bei knapp -1,9% Fehler, wenn ich mich nicht verrechnet> habe...
Na dann nimm halt einen Pre4scaler von 8 - dann sind wir bei max. knapp
0,5 % Abweichung.
Hallo,
noch was @ TO:
Wenn ich schon drüber schaue, gefällt mir auch deine
Timerumkonfiguration nicht. Besser und sicherer ist es den Timer erst zu
stoppen. Danach umzukonfigurieren, dazu zählt alles, auch das ändern des
Comparewertes, weil der CTC Mode keinen Buffer hat und erst am Ende
wieder zu starten. Dabei stößt man auf ein weiteres Problem, wenn man es
genau nimmt. Wenn der aktuelle Timer Zählerstand größer ist als der neue
Compare Wert, dann läuft der Timer erstmal eine Runde durch. Muss man
für die Aufgabe sicherlich nicht dringend beachten. Würde aber mehr Sinn
machen als sich mit Led Bändern zu befassen. :-) Nur so als Hinweis.
Möchtest schließlich etwas über Timer lernen. Wünsche fröhliches
programmieren.
Mit Prescaler 1 kommt man von 20kHz bis auf 200Hz runter:
16e6 / 65536 / 2 = 122Hz.
Nur für 100Hz muß man auf 8 umschalten.
Der maximale Fehler ist <0,13%
Ganz häßlich sind die vielen verstreuten Delays und noch dazu in
Unterfunktionen. Man muß ne ganze Weile lesen, um zu sehen, daß sie in
jeder Verzweigung erfolgen. Also kann man doch einfach nur ein
gemeinsames Delay in der Mainloop ausführen.
asdf schrieb:> Und elegant ist ansonsten mMn nur eine Lösung mit Timer0, da man hier> durch OCR0B den PD5 direkt von der Hardware aus schalten lassen kann.> Alles anderen mit Timer1 und dann von Hand den Port schalten ist m.E.> deutlich weniger elegant.
Dies wäre natürlich elegant, leider geht es nicht (falls man eine genaue
Annäherung an die geforderten Frequezen möchte).
Da müsste man ebenfalls in der ISR prüfen und mehrmals "nachladen".
Adam P. schrieb:> Dies wäre natürlich elegant, leider geht es nicht (falls man eine genaue> Annäherung an die geforderten Frequezen möchte).
Das geht schon - wenn man einen geeigneten Quarz einsetzt. Aber dazu
schweigt sich der TO ja aus.
Peter D. schrieb:> Mit Prescaler 1 kommt man von 20kHz bis auf 200Hz runter:> 16e6 / 65536 / 2 = 122Hz.> Nur für 100Hz muß man auf 8 umschalten.> Der maximale Fehler ist <0,13%>> Ganz häßlich sind die vielen verstreuten Delays und noch dazu in> Unterfunktionen. Man muß ne ganze Weile lesen, um zu sehen, daß sie in> jeder Verzweigung erfolgen. Also kann man doch einfach nur ein> gemeinsames Delay in der Mainloop ausführen.
Sag ich doch, ist doch eigentlich ganz einfach. ;)