| Autor |
|
delay mit C bei AVR-Programmierung |
|
|
|
|
BID = 393126
Humus Aus Forum ausgetreten
|
|
Hey,
da ich zurzeit dabei bin von Bascom auf C umzusteigen bzw. mein wissen zu erweitern bei der Programmierung meiner AVRs ist natürlich gleich ein großes Problem auf getreten, und zwar kann ich mit "_delay_ms()" auch länger als 100ms warten? Hab mir nämlich mal den Code angeschaut der hinter der header datei <avr/delay.h> steht. und da steht dann nur etwas mit knapp 100ms.
Gibt es da eine möglichkeit, oder muss ich mir mein delay selber stricken? d.h. mit schleifen?
|
|
BID = 393149
DonComi Inventar
     
Beiträge: 8604 Wohnort: Amerika
|
|
Da das abhängig von der CPU-Taktung ist, kann man deine Frage pauschal nicht beantworten.
Wenn Warteschleifen wirklich eingesetzt werden sollen, dann kann man das am besten und saubersten mit Assembler, in diesem Fall also mit Inline-Assembler machen:
Man zählt dazu z.b. ein 16-Bit-Register von einem Startwert bis 0:
sbiw xl, 1
brne pc-1
Da man weis, dass sowohl sbiw als auch brne je zwei Taktzyklen brauchen und die Taktfrequenz auch bekannt ist, kann man sich die Zahl im Register X ausrechnen:
XH:XL = f_cpu * T/4 (T ist die Periodendauer, also der Kehrwert von der Frequenz, die verzögert werden soll, angegeben in S. Wenn du mit Millisekunden rechnest, hast du einen Bruch durch 4000.
Dadurch ergibt sich ein Maximaler Wert für X. Wenn man also eine höhere Verzögerung haben will, muss man zusätzlich mit irgendwelchen Befehlen oder sogar einer Schleife in der Schleife einen höheren Teiler erreichen:
ldi r16, 0xff
dec r16
brne pc-1
sbiw xl, 1
brne pc-4
Damit erhält man einen recht hohen Teiler -> Die Verzögerung wird recht groß.
PS: Sowas macht man aber nur für rel. kurze Verzögerungen, wenn man größere Will, nimmt man am besten den Timer, weil dann das Programm noch weiterlaufen kann. Wenn man das nicht will, macht man halt 'ne Schleife und pollt das Timer-Flag.
_________________
|
|
BID = 393257
Humus Aus Forum ausgetreten
|
Kann man das auch mit C einigermaßen genau lösen, oder muss ich das mit Assembler lösen?
Es geht um ein einfaches Blinklicht, dass mir einfach nur simbolisieren soll, dass mein AVR noch Arbeitet und sich nicht aufgehängt hat!
Wenn ich ein Blinklicht mit genau 1 Hz haben will muss ich mit Assembler arbeiten, oder?
Aber wie schaut es oben bei meiner Anwendung aus? Kann ich das nicht auch schön mit C lösen und eine ca. zeit errechnen? Oder geht das mit C gar nicht das man da eine ca. Zeit errechnen kann?
|
BID = 393311
DonComi Inventar
     
Beiträge: 8604 Wohnort: Amerika
|
Doch, klar geht das. Wenn man weis, wie lange die CPU braucht, um einen Befehl zu verarbeiten.
Das Problem ist, dass ein Befehl nicht exakt einen Taktzyklus braucht (in der Regel nicht), wenn z.b. eine Variable hochgezählt wird, muss immer ein kleines Makro diesen Ablauf steuern (RAM in Register laden, Register vorher auf Stack sichern, Operation durchführen, RAM zurückschreiben, Register wieder herstellen,...)
Das dauert viel länger als ein
inc r16.
Deshalb sollten kleinere Schleifen reichen, wie man dass im Endeffekt exakt berechnen kann, kann ich dir nicht genau sagen, da ich sowas so noch nicht gemacht habe; ich nutze dafür meist Assembler.
Aber du kannst es ja mal so machen:
(unsigned short i=0;)
for(i=0; i<=100; i++)
{}
Je nach Taktung erhält man da schon eine "sehbare" Verzögerung. Sonst nimmst du halt noch einen größeren Wert für i<=100. Mit dem Simulator kannst du die Zeit ablesen, die der codeschnipsel braucht: Vorher die "Watch resetten"  und dann mit F11 die For-Schleife anspringen.
Das gelbe vom Ei ist das aber definitiv nicht, da muss man dann wissen, wie der Compiler das macht, also wie langer er exakt für einen Durchlauf braucht. Das kannst du am besten bei der Disassemblierung feststellen. Ich habe übrigens extra Short genommen, da das 16-Bit-große Zahlen sind, das dauert dan länger  .
_________________
|
BID = 393312
Humus Aus Forum ausgetreten
|
Das mit der for-schleife ist ja klar... =)
Vielen dank! =)
Muss ich dann halt doch Assembler nehmen wenn ich es genau haben will!
|
BID = 393313
DonComi Inventar
     
Beiträge: 8604 Wohnort: Amerika
|
Der Compiler erzeugt mir aus dem Teil
for(i=0; i<=100; i++)
{}
das hier:
+0000006D: 91800100 LDS R24,0x0100 Load direct from data space
+0000006F: 91900101 LDS R25,0x0101 Load direct from data space
+00000071: 3685 CPI R24,0x65 Compare with immediate
+00000072: 0591 CPC R25,R1 Compare with carry
+00000073: F450 BRCC PC+0x0B Branch if carry cleared
+00000074: 91800100 LDS R24,0x0100 Load direct from data space
+00000076: 91900101 LDS R25,0x0101 Load direct from data space
+00000078: 9601 ADIW R24,0x01 Add immediate to word
+00000079: 93900101 STS 0x0101,R25 Store direct to data space
+0000007B: 93800100 STS 0x0100,R24 Store direct to data space
+0000007D: CFEF RJMP PC-0x0010 Relative jump
Du kannst also jetzt, wenn du Lust dazu hast  genau rausfinden, wie lange die CPU diesen Teil wiederholt. Dann kannst du die Formel oben auch hier anwenden, um deine Variable i mit dem richtigen Wert zu laden.
PS: Sry, ich bin grad etwas neben mir, meine Formulierungen sind wieder geil...
Ich meinte oben natürlich, dass nicht das ganze RAM neugeschireben wird  sondern nur die entsprechende Adresse im RAM...
_________________
|
BID = 393429
photonic Schreibmaschine
    
Beiträge: 1301 Wohnort: Zürich, Schweiz
|
Ein delay von mehr als 100 ms (mit delay.h, sehr ineffizienter Zeitverschwendungscode drin) geht nur mit einem CPU-Takt von weniger als 10 MHz.
Sonst machst du am besten eine Timer-Interruptroutine die eine ausreichend grosse Variable hochzählt und bei erreichen eines Schwellenwertes die LED toggelt. So kann der Prozessor in der Zwischenzeit noch andere Dinge erledigen anstelle bloss blöd vor sich hin zu warten. Man braucht keinen Assembler um sehr präzise zu programmieren, das geht mit C genauso gut, wenn du es unbedingt brauchst auch Taktzyklen-genau. Den Delay mit einer For-Schleife zu basteln ist ineffizient, für den Speicher und für die Rechenzeitverschwendung.
|
BID = 393439
Midnight Stammposter
   
Beiträge: 256
|
Hallo,
vielleicht habe ich auch einfach das ganze nicht genau verstanden.
Die LED soll also symbolisieren das der AVR noch läuft.
Bei der Steuerung mit dem Delay wäre es aber doch egal ob der AVR noch läuft oder sich aufgehangen hat. Ob er nun hängt oder 100% seiner Rechenzeit zum blinken einer LED aufbringt wäre doch gleich, was anderes kann er eh nicht mehr machen.
Besser wäre da wohl dann echt ein Timer oder aber den Watchdog (je nach AVR) zu bemühen.
Gruß
Simon
|
BID = 393486
Humus Aus Forum ausgetreten
|
Genau die LED soll mir anzeigen, dass mein AVR noch läuft und sich nicht aufgeängt hat.
Also das ganze soll folgendermaßen ausschauen:
-LED Bit gesetzt
-Eingaben
-Ausgaben
LED Bit zurück gesetzt
-Eingaben
-Ausgaben
So in der Art, vielleicht auch immer zwischen Eingabe/Ausgbae bzw Ausgabe/Eingabe!
Somit sehe ich dann gleich ob mein AVR sich an irgendetwas aufgehängt hat und wo ich suchen kann bzw. ohne langes rumprobieren an was es vielleicht am PC (Hyperterminal) etc. liegen kann.
Oder gibt es dafür eine bessere Lösung?
|
BID = 393511
DonComi Inventar
     
Beiträge: 8604 Wohnort: Amerika
|
Ja, wie gesagt wurde, auch schon von mir, den internen Timer.
Wie das geht, steht im Datenblatt.
_________________
|
BID = 393605
Humus Aus Forum ausgetreten
|
Die Timer sind schon alle eingebunden!
|
BID = 393616
photonic Schreibmaschine
    
Beiträge: 1301 Wohnort: Zürich, Schweiz
|
Du hast keinen freilaufenden Timer dem du noch einen Interrupt abzwacken könntest?
Sonst machst du halt dasselbe im Mainloop falls dieser mit einer konstanten Geschwindigkeit läuft.
Oder du nimmst den Watchdog gegen Controllerabstürze, dann resettet er sich von selbst wenn er sich aufgehängt haben sollte. So sollte die Blinkled überflüssig sein.
|
BID = 393705
Humus Aus Forum ausgetreten
|
Gut das du den Watchdog erwähnst... Irgndwie hab ich gar nicht an den gedacht. Den gibt es ja auch noch! =) Was doch so alles in einem Controller steckt!
Denn müsste ich mir mal genauer anschauen, aber wenn du das schon so erwähnst das der dafür geeignet ist =) Muss ich mich dann mal ein lesen. =D
Dankeschön!!
|