Forum: Mikrocontroller und Digitale Elektronik Atmega328P Frequenzerzeugung


von Marco S. (marco_elec)


Lesenswert?

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
unsigned int Scalefactor = 500;
2
unsigned int counterstart = 131;
3
int main(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
  static int counter = 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

​
​

von Adam P. (adamap)


Lesenswert?

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)

: Bearbeitet durch User
von Marco S. (marco_elec)


Lesenswert?

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

von Insasse (Gast)


Lesenswert?


von Veit D. (devil-elec)


Lesenswert?

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.

von Marco S. (marco_elec)


Lesenswert?

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.

von Marco S. (marco_elec)


Lesenswert?

Nachtrag:

Es ist ein Atmega 328PB

von Adam P. (adamap)


Lesenswert?

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?

von Joachim B. (jar)


Lesenswert?

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!

: Bearbeitet durch User
von Rätsel Rater (Gast)


Lesenswert?

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 .....

von Marco S. (marco_elec)


Lesenswert?

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.

von Marco S. (marco_elec)


Lesenswert?

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?

von Rätsel Rater (Gast)


Lesenswert?

Marco S. schrieb:
> Vielleicht kann mir da jemand nochmal helfen.

Vielleicht nochmal einen Beitrag höher schauen.

von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von Rätsel Rater (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

denkst du beim lesen auch mit oder pöpelst du nur rum?

von Marco S. (marco_elec)


Lesenswert?

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
int main(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.

݂

von Veit D. (devil-elec)


Lesenswert?

Hallo,

du musst noch die Interrupts generell einschalten.
sei()

Edit:
Pin toggeln geht noch etwas schneller mit
PIND = (1 << PD5);
bzw.
PIND = _BV(PD5);

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

und wenn man die richtigen Register nimmt, dann klappt das auch.  :-)
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
int main(void)
5
{
6
    DDRD   = _BV(PD5);
7
    TIMSK1 = _BV(OCIE1A);
8
    OCR1A  = 77;
9
    TCCR1B = _BV(WGM12) | _BV(CS10) | _BV(CS12); // set CTC Mode und Prescaler 1024
10
    
11
    sei();
12
    
13
    while (1)
14
    {
15
        
16
    }
17
    
18
}
19
20
ISR(TIMER1_COMPA_vect)
21
{
22
   PIND = _BV(PD5);
23
}

von Stefan S. (chiefeinherjar)


Lesenswert?

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?

von Joachim B. (jar)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

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.

von Adam P. (adamap)


Lesenswert?

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

von Adam P. (adamap)


Lesenswert?

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.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

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.

von Stefan S. (chiefeinherjar)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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?

von Einer K. (Gast)


Lesenswert?

Stefan S. schrieb:
> Aber das Datenblatt spricht vom PINx
Ersetze x durch B oder D oder was es da sonst noch für Ports geben 
mag...

von Adam P. (adamap)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Adam, nun mal ganz sachte. Du könntest selbst im Manual nachlesen. Das 
ist schon seit Jahren bekannt. In meinem Manual 17.2.2.

von Stefan S. (chiefeinherjar)


Lesenswert?

Joachim B. schrieb:
> Bist du sicher das du es ohne XOR schaffst?
> Oder ist das ein Trick den nur wenige kennen?

Wie gesagt, im Datenblatt ist der Trick erwähnt.
https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
Seite 58 vorletzter Absatz.

Finde ich persönlich aber auch etwas... unglücklich. Ich bevorzuge ein 
Makro oder das XOR - da sieht man auf dem ersten Blick, dass der Pin 
*um*geschaltet und nicht nur *ein*geschaltet werden soll.

von Adam P. (adamap)


Lesenswert?

Veit D. schrieb:
> In meinem Manual 17.2.2.

OMG, was hab ich da bitte bei den ATmegas verpasst...
...aber braucht man das echt?

von Einer K. (Gast)


Lesenswert?

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.

von Stefan S. (chiefeinherjar)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Adam P. (adamap)


Lesenswert?

http://ww1.microchip.com/downloads/en/DeviceDoc/40001906A.pdf

17.2.2 Toggling the Pin
Writing a '1' to PINxn toggles the value of PORTxn, independent on the 
value of DDRxn. The SBI instruction can be used to toggle one single bit 
in a port.

von Veit D. (devil-elec)


Lesenswert?

Arduino Fanboy D. schrieb:
> Joachim B. schrieb:
>> Bist du sicher das du es ohne XOR schaffst?
> Veit kann das und ich auch.
> Dokumentiert ist es auch.

Danke.  :-)

von Marco S. (marco_elec)


Lesenswert?

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

von Adam P. (adamap)


Lesenswert?

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 S. Landolt (Gast)


Lesenswert?

> ...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.

von Stefan S. (chiefeinherjar)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von wendelsberg (Gast)


Lesenswert?

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

von Adam P. (adamap)


Lesenswert?

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.

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

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...

von Asdf (Gast)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von m.n. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von m.n. (Gast)


Lesenswert?

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?
;-)

von Peter D. (peda)


Lesenswert?

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.

von Adam P. (adamap)


Angehängte Dateien:

Lesenswert?

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
void init_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
Hz      T (µs)        Flanke t (µs)
2
19900   50,25125628   25,12562814
3
20000   50            25
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.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

Viel zu kompliziert gedacht. TCCR0A bietet doch passende Bits, um PD5 
synchron zu verändern.

von Adam P. (adamap)


Lesenswert?

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".

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

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)

von m.n. (Gast)


Lesenswert?

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 vorbereiten

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.

von Adam P. (adamap)


Lesenswert?

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)

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

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.

von Adam P. (adamap)


Lesenswert?

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.
1
ISR (TIMER0_COMPA_vect)
2
{
3
  if (!t_cnt)
4
  {
5
    OCR0A = 199;
6
    t_cnt++;
7
  }
8
  else
9
  {
10
    OCR0A = 201;
11
    PORTD ^= (1 << PORTD5);
12
    t_cnt = 0;
13
  }
14
}
15
16
/******************************************************************************/
17
void init_timer(void)
18
{
19
  TCCR0A = (1 << WGM01);
20
  TCCR0B = (1 << CS00);
21
22
  OCR0A = 201;
23
24
  TIMSK0 = (1 << OCIE0A);
25
}
---
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.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

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

von Adam P. (adamap)


Lesenswert?

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.

von Ein Freund (Gast)


Lesenswert?

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?

von m.n. (Gast)


Lesenswert?

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.

von Adam P. (adamap)


Lesenswert?

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.

: Bearbeitet durch User
von Arno (Gast)


Lesenswert?

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

von Adam P. (adamap)


Lesenswert?

Arno schrieb:
> Mal es dir mal auf. Das funktioniert.

Ja ich weiß schon, hab da nur nicht dran gedacht.

von Marco S. (marco_elec)


Lesenswert?

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_t i = 100;
2
int main(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
  char zustand = 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

von Adam P. (adamap)


Angehängte Dateien:

Lesenswert?

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.

von Adam P. (adamap)


Lesenswert?

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.

von Marco S. (marco_elec)


Lesenswert?

Tja das mit den 500 Hz habe ich auch erst im Nachhinein erfahren. Das 
wurde wohl vergessen in die Aufgabenstellung zu übernehmen.

von Adam P. (adamap)


Lesenswert?

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.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

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.

von leo (Gast)


Lesenswert?

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

von Einer K. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Peter D. schrieb:
> Schau einfach mal ins Listing, so schlimm ist das nicht.

Testcode: ATMega328p
1
ISR(TIMER1_COMPA_vect)
2
{
3
   PIND = _BV(PD5);
4
}
5
6
ISR(TIMER1_COMPB_vect)
7
{
8
   PORTD ^= (1 <<PORTD5);
9
}
10
11
12
int main() 
13
{
14
}


Kompilat:
1
00000080 <__vector_11>:
2
3
ISR(TIMER1_COMPA_vect)
4
{
5
  80:  8f 93         push  r24
6
   PIND = _BV(PD5);
7
  82:  80 e2         ldi  r24, 0x20  ; 32
8
  84:  89 b9         out  0x09, r24  ; 9
9
}
10
  86:  8f 91         pop  r24
11
  88:  18 95         reti
12
13
0000008a <__vector_12>:
14
15
ISR(TIMER1_COMPB_vect)
16
{
17
  8a:  8f 93         push  r24
18
  8c:  8f b7         in  r24, 0x3f  ; 63
19
  8e:  8f 93         push  r24
20
  90:  9f 93         push  r25
21
   PORTD ^= (1 <<PORTD5);
22
  92:  8b b1         in  r24, 0x0b  ; 11
23
  94:  90 e2         ldi  r25, 0x20  ; 32
24
  96:  89 27         eor  r24, r25
25
  98:  8b b9         out  0x0b, r24  ; 11
26
}
27
  9a:  9f 91         pop  r25
28
  9c:  8f 91         pop  r24
29
  9e:  8f bf         out  0x3f, r24  ; 63
30
  a0:  8f 91         pop  r24
31
  a2:  18 95         reti

von Adam P. (adamap)


Lesenswert?

Arduino Fanboy D. schrieb:
> Kompilat:

OK, das ist echt weniger Code...hätte ich nicht gedacht.

von Marco S. (marco_elec)


Lesenswert?

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
void set_LED(uint16_t step);
7
void erase_LED(uint16_t step);
8
void increase_frequency();
9
void decrease_frequency();
10
                          
11
uint16_t i = 100;
12
int main(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
  char zustand_PD2 = 0; 
22
  char zustand_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
void increase_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
void decrease_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
void set_LED(uint16_t step)
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
void erase_LED(uint16_t step)
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.

von Mark S. (voltwide)


Lesenswert?

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.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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:  8f 93         push  r24                 ;  2
4
  8c:  8f b7         in  r24, 0x3f  ; 63       ;  1
5
  8e:  8f 93         push  r24                 ;  2
6
  90:  9f 93         push  r25                 ;  2
7
   PORTD ^= (1 <<PORTD5);
8
  92:  8b b1         in  r24, 0x0b  ; 11       ;  1
9
  94:  90 e2         ldi  r25, 0x20  ; 32      ;  1
10
  96:  89 27         eor  r24, r25             ;  1
11
  98:  8b b9         out  0x0b, r24  ; 11      ;  1 
12
}
13
  9a:  9f 91         pop  r25                  ;  2
14
  9c:  8f 91         pop  r24                  ;  2
15
  9e:  8f bf         out  0x3f, r24  ; 63      ;  1
16
  a0:  8f 91         pop  r24                  ;  2  
17
  a2:  18 95         reti                      ; --
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)...

von Marco S. (marco_elec)


Lesenswert?

1
#define F_CPU +6000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
void set_LED(uint16_t step);    //set LED bar graph for rising frequency sweap
7
void erase_LED(uint16_t step);    //erase LED bar graph for decreasing frequency sweap
8
void increase_frequency();
9
void decrease_frequency();
10
                          
11
uint16_t i = 100;
12
int main(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);   //set PD2 and PD3 to input for Buttons
19
  DDRB = 0xFF;              //set SFR DDRD to output
20
  sei();                                //set global interrupt   
21
  char zustand_PD2 = 0;          //flag control for button PD2 
22
  char zustand_PD3 = 0;          //flag control for button PD3                 
23
  
24
    while (1) 
25
    {  
26
    if((PIND & (1<<PIND2)) && (PIND & (1<<PIND3)))  //Button double edge detection
27
    {
28
      DDRD ^= (1<<PORTD5);            //toggls signal on/off
29
    }
30
    if((PIND & (1<<PIND2)) && zustand_PD2 == 0)
31
    {
32
      if(i <= 20000)                //maximum increase value
33
      {
34
        zustand_PD2 = 1;            //set flag value
35
        set_LED(i);                //call function for LED bar graph
36
        increase_frequency();            //call funcion for frequency step up
37
      }            
38
    }
39
    if(!(PIND & (1<<PIND2)) && zustand_PD2 == 1)  //detection if button is not longer pressed
40
    {
41
      zustand_PD2 = 0;              //reset flag value
42
      _delay_ms(100);
43
      
44
    }
45
    
46
    if((PIND & (1<<PIND3)) && zustand_PD3 == 0)
47
    {
48
      if(i >= 100)
49
      {
50
        zustand_PD3 = 1;
51
        erase_LED(i);
52
        decrease_frequency();
53
      }      
54
    }
55
    if(!(PIND & (1<<PIND3)) && zustand_PD3 == 1)
56
    {
57
      zustand_PD3 = 0;
58
      _delay_ms(100);
59
    }    
60
  }  
61
}
62
ISR(TIMER1_COMPA_vect)          //interrupt of compare match Timer1
63
{
64
  PORTD ^= (1<<PORTD5);        //ceate he signal at PD5 at h match
65
}
66
67
void increase_frequency()
68
{
69
  /****************************
70
  *calculation of ORC1A is given by the relationship from the manual in given chapter.
71
  *OCRA = clock_frequency/2*prescaler*output_frequency
72
  ****************************************************/
73
  if(i <= 500)
74
  {
75
    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
void decrease_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
void set_LED(uint16_t step)
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
void erase_LED(uint16_t step)
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

von S. Landolt (Gast)


Lesenswert?

> //switch prescaler to 1
Wie auch schon m.n. frage ich mich, wo der Timer1-Takt auf /8 
umgeschaltet wird.

von Ein Freund (Gast)


Lesenswert?

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).

von Marco S. (marco_elec)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

Und wenn man danach wieder unter 500 Hz geht?

von S. Landolt (Gast)


Lesenswert?

... dann wird beim OCR1A mit /8 gerechnet, ohne dass der Timer-Takt dazu 
passt.

von S. Landolt (Gast)


Lesenswert?

PS:
Ich meine, schon rein optisch fehlt da die Symmetrie im Programm.

von Ein Freund (Gast)


Lesenswert?

Es ist wohl mein Schicksal, von den selbsternannten "Experten" dieses 
Forums ignoriert zu werden ( fatum, ut esse neglecta )

Mir doch egal :-)

von c-hater (Gast)


Lesenswert?

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...

von Ein Freund (Gast)


Lesenswert?

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?

von Marco S. (marco_elec)


Lesenswert?

1
#define F_CPU +6000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
void set_LED(uint16_t step);    //set LED bar graph for rising frequency sweap
7
void erase_LED(uint16_t step);    //erase LED bar graph for decreasing frequency sweap
8
void increase_frequency();
9
void decrease_frequency();
10
                          
11
uint16_t i = 100;
12
int main(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);   //set PD2 and PD3 to input for Buttons
19
  DDRB = 0xFF;              //set SFR DDRD to output
20
  sei();                                //set global interrupt   
21
  char zustand_PD2 = 0;          //flag control for button PD2 
22
  char zustand_PD3 = 0;          //flag control for button PD3                 
23
  
24
    while (1) 
25
    {  
26
    if((PIND & (1<<PIND2)) && (PIND & (1<<PIND3)))  //Button double edge detection
27
    {
28
      DDRD ^= (1<<PORTD5);            //toggls signal on/off
29
    }
30
    if((PIND & (1<<PIND2)) && zustand_PD2 == 0)
31
    {
32
      if(i <= 20000)                //maximum increase value
33
      {
34
        zustand_PD2 = 1;            //set flag value
35
        set_LED(i);                //call function for LED bar graph
36
        increase_frequency();            //call funcion for frequency step up
37
      }            
38
    }
39
    if(!(PIND & (1<<PIND2)) && zustand_PD2 == 1)  //detection if button is not longer pressed
40
    {
41
      zustand_PD2 = 0;              //reset flag value
42
      _delay_ms(100);
43
      
44
    }
45
    
46
    if((PIND & (1<<PIND3)) && zustand_PD3 == 0)
47
    {
48
      if(i >= 100)
49
      {
50
        zustand_PD3 = 1;
51
        erase_LED(i);
52
        decrease_frequency();
53
      }      
54
    }
55
    if(!(PIND & (1<<PIND3)) && zustand_PD3 == 1)
56
    {
57
      zustand_PD3 = 0;
58
      _delay_ms(100);
59
    }    
60
  }  
61
}
62
ISR(TIMER1_COMPA_vect)          //interrupt of compare match Timer1
63
{
64
  PORTD ^= (1<<PORTD5);        //ceate he signal at PD5 at h match
65
}
66
67
void increase_frequency()
68
{
69
  /****************************
70
  *calculation of ORC1A is given by the relationship from the manual in given chapter.
71
  *OCRA = clock_frequency/2*prescaler*output_frequency
72
  ****************************************************/
73
  if(i <= 500)
74
      {
75
        TCCR1B |= (1<<CS11);
76
        TCCR1B &=~(1<<CS10);
77
        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
    void decrease_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
void set_LED(uint16_t step)
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
void erase_LED(uint16_t step)
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

von Einer K. (Gast)


Lesenswert?

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.

von Ein Freund (Gast)


Lesenswert?

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.

von Ein Freund (Gast)


Lesenswert?

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) ...

von Einer K. (Gast)


Lesenswert?

Ein Freund schrieb:
> Eine weitere Alternative ist: Nachdenken.
Ein virtuelles Weihnachtsgeschenk, von mir, für dich: Ein Aluhut!

von Ein Freund (Gast)


Lesenswert?

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.

von Ein Freund (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> #define F_CPU +6000000UL
Nur am Rande: dies lässt das _delay_ms(100) wohl etwas kurz ausfallen.

von Ein Freund (Gast)


Lesenswert?

Ein Freund schrieb:
> Für mich

Von mir (Sch... Gast-Status ...)

von Einer K. (Gast)


Lesenswert?

S. Landolt schrieb:
> Ich meine, schon rein optisch fehlt da die Symmetrie im Programm.

 Symmetrie == Kunst für Doofe

von Ein Freund (Gast)


Lesenswert?

S. Landolt schrieb:
> Nur am Rande: dies lässt das _delay_ms(100) wohl etwas kurz ausfallen.

Nur Sub-optimal :-)

von Ein Freund (Gast)


Lesenswert?

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 
:-)

von c-hater (Gast)


Lesenswert?

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...

von Ein Freund (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Symmetrie == Kunst für Doofe

Da stimmen wir merkwürdigerweise überein :-)

von Marco S. (marco_elec)


Lesenswert?

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

von Einer K. (Gast)


Lesenswert?

Marco S. schrieb:
> An was kann das denn liegen?
Dein endlicher Automat leistet sich einen Fehltritt.

von Ein Freund (Gast)


Lesenswert?

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?

von Ein Freund (Gast)


Lesenswert?

Ein Freund schrieb:
> bes?

es?

von S. Landolt (Gast)


Lesenswert?

> 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.

von Maschinenstürmer (Gast)


Lesenswert?

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.

von Ein Freund (Gast)


Lesenswert?

Marco S. schrieb:
> An was kann das denn liegen?

Am Programm? Das macht genau, was DU vorgegeben hast. Ist halt so.

von Einer K. (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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...

von M. K. (sylaina)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

M. K. schrieb:
> Hier soll
> doch nur mit nem CTC-Mode ein Pin getoggelt werden.
Das kann ich der Aufgabenstellung nicht "direkt" entnehmen.

von M. K. (sylaina)


Lesenswert?

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. ;)

von asdf (Gast)


Lesenswert?

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).

von Adam P. (adamap)


Lesenswert?

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.

: Bearbeitet durch User
von asdf (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

@ TO:
1
DDRD = 0xFF;                          //set DDRD to output
2
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.

von Ein Freund (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Adam P. (adamap)


Lesenswert?

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".

von Ein Freund (Gast)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

Ein Freund schrieb:
> Aber dazu
> schweigt sich der TO ja aus.
Ja?

Marco S. schrieb:
> mit 16MHz.

von c-hater (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Ein Freund schrieb:
>> Aber dazu
>> schweigt sich der TO ja aus.
> Ja?
>
> Marco S. schrieb:
>> mit 16MHz.

Naja, das ist der gegebene. Allerdings ganz sicher nicht der für die 
Anwendung optimal geeignete...

von M. K. (sylaina)


Lesenswert?

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. ;)

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.