Hallo zusammen, Ich will einen Spannungsteiler mit einer 10khz Rechteckspannung bestromen. Also zu einen Zeitpunkt soll die eine Seite des Spannungsteiler mit Masse und der zweite Pol mit VCC verbunden sein. In zweiten hälfte des Taktes soll die Polung umgedreht werden. So dass sich die Stromflussrichtung ständig ändert. In der Mitte der ersten Takthälfte soll dann eine AD Wandlung durchgeführt werden. Generell hat der uC noch weitere Aufgaben zu erledigen. Teilweise dauern auch manche Interruptroutinen länger als 1000 Takte. Es handelt sich um einen Atmel Atmega. Dieser läuft mit 8 MHz. Mein Problem an der Sache ist die begrenzte Zeit. Eine AD Wandlung dauert 14 Takte. Wenn ich einen Timer aufrufe der mir die 10 khz erzeugt, dann hab ich zwischen den Timeraufrufen nur ca. 400 Takte Zeit. Wenn jetzt aber eine andere Interruptroutine länger dauert habe ich ein Problem. Kann man das ganze irgendwie per PWM oder so im Hintergrund laden lassen. Ohne grosse CPU Last? Danke
FLorian Unbekannto schrieb: > Kann man das ganze irgendwie per PWM oder so im Hintergrund laden > lassen. Ohne grosse CPU Last? Atmegas machen PWM ohne CPU-Last. Nur die alten Gurken wie Atmega8 sind da etwas eingeschränkt. Aber die verwendet ja heute kaum noch jemand. mfg.
FLorian Unbekannto schrieb: > In der Mitte der ersten > Takthälfte soll dann eine AD Wandlung durchgeführt werden. Das heisst, du brauchst einen 40 kHz Timer! Bei 0 und 2 den Ausgang umschalten, bei 1 ADC, bei 3 kann sich der Prozessor eine Verschnaufpause gönnen. Oder du rufst mit einem 10 kHz Interrupt die ADC-Wandlung auf, das musst du ja sowieso, aber dann müsstest du das Rechtecksignal mit der richtigen Phase durch eine externe Schaltung erzeugen. Ein PWM-Ausgang würde vermutlich nicht synchron mit der ADC-Wandlung laufen. Georg
Hi >Mein Problem an der Sache ist die begrenzte Zeit. Eine AD Wandlung >dauert 14 Takte. Nein, 13 Takte. Aber ADC-Takte. Und der ADC-Takt soll zwischen 50...200kHz betragen. Das passt nicht zusammen. MfG Spess
FLorian Unbekannto schrieb: > Wenn jetzt aber eine andere Interruptroutine länger dauert habe ich ein > Problem. Das hast du definitiv. Was du brauchst ist eine ISR für Rechteck und ADC, die Priorität vor allen anderen hat, also auch andere laufende ISR unterbrechen kann. Sofern das Ganze überhaupt funktioniert: mit der ADC-Wandlucg ist es ja nicht getan, irgenwie müssen die Daten ja weiterverarbeitet werden, und alle 100 µs kommen neue. Georg
Georg schrieb: > Was du brauchst ist eine ISR für Rechteck und > ADC, die Priorität vor allen anderen hat, also auch andere laufende ISR > unterbrechen kann. Tolle Idee, das gibt es auf einem Atmega nur leider nicht.
FLorian Unbekannto schrieb: > Also zu einen Zeitpunkt soll die eine Seite des > Spannungsteiler mit Masse und der zweite Pol mit VCC verbunden sein. In > zweiten hälfte des Taktes soll die Polung umgedreht werden. So dass sich > die Stromflussrichtung ständig ändert. In der Mitte der ersten > Takthälfte soll dann eine AD Wandlung durchgeführt werden. Klingt nach nem Denkfehler. "Die Polung umdrehen" bedeutet ja, dass der uC plötzlich negative Betriebsspannung liefern müsste. Wenn du die Polung umdrehen willst, musst du den einen Anschluss deines Spannungsteilers auf einen Pin (nennen wir ihn mal P1) legen und den anderen Anschluss auf den anderen Pin (P2). An P1 liegt dann das Rechtescksignal an und an P2 das ganze invertiert. edit: O.K. mein Fehler ich glaube genau das meintest du. Notiz an mich: 2 mal lesen... > Generell hat der uC noch weitere Aufgaben zu erledigen. Teilweise dauern > auch manche Interruptroutinen länger als 1000 Takte. Müssen so lange ISR wirklich sein?
1 | volatile bool my_event=false; // globale eventvariable für ??? |
2 | |
3 | ISR ( ???) |
4 | {
|
5 | my_event=true; |
6 | }
|
7 | |
8 | main
|
9 | {
|
10 | // init ...
|
11 | while(1) |
12 | {
|
13 | if (my_event) |
14 | {
|
15 | > der Kram der in der ISR stehen würde und nicht zeitkritisch ist < |
16 | my_event=false; |
17 | }
|
18 | // irgendwelche andernen sachen...
|
19 | |
20 | }
|
21 | |
22 | }
|
> Es handelt sich um > einen Atmel Atmega. Dieser läuft mit 8 MHz. Welcher ATmega? Wenn es an die PWM geht wärs ganz gut zu wissen ob man nen 8 bit Timer oder vieleicht sogar nen 12 oder 16 bit Timer zu Verfügung hat > Mein Problem an der Sache ist die begrenzte Zeit. Eine AD Wandlung > dauert 14 Takte. Wenn ich einen Timer aufrufe der mir die 10 khz > erzeugt, dann hab ich zwischen den Timeraufrufen nur ca. 400 Takte Zeit. > Wenn jetzt aber eine andere Interruptroutine länger dauert habe ich ein > Problem. > Kann man das ganze irgendwie per PWM oder so im Hintergrund laden > lassen. Ohne grosse CPU Last? > > Danke Mit einer 8 bit PWM und einem Preescaler von 2 kommst du grob überschlagen auf 15,625 kHz und bei Prescale 4 auf 7,8125 kHz. On zu Off time ist dann natürlich 1:1 Müssen es genau 10kHz sein?
:
Bearbeitet durch User
Jim Meba schrieb: > Tolle Idee, das gibt es auf einem Atmega nur leider nicht. Und was willst du damit sagen? Dass du eine bessere Idee hast? Dann her damit. Man kann sich sowas selbst zusammenprogrammieren, wenn auch nicht so effektiv wie in Hardware, man kann aber auch zum Schluss kommen, dass die Hardware für die Aufgabe unzureichend ist. Egal wie, für die beschriebene Aufgabe muss man dafür sorgen, dass die worst case latency des Interrupts kürzer ist als der für das Rechteck zulässige Jitter. Darüber ist aber eh nichts Genaues bekannt. Georg
FLorian Unbekannto schrieb: > Teilweise dauern auch manche Interruptroutinen länger als 1000 Takte. Warum? Das hört sich für mich nach "Programmfehler" an...
Bernhard F. schrieb: > > Mit einer 8 bit PWM und einem Preescaler von 2 kommst du grob > überschlagen auf 15,625 kHz und bei Prescale 4 auf 7,8125 kHz. On zu Off > time ist dann natürlich 1:1 > Müssen es genau 10kHz sein? Wieso --> Mit 8 MHz Clock Frequenz bekomme ich doch mit einem 8Bit Timer ein 10KHz Rechteck Signal generier --> Prescaler 8 CTC - Mode Anschalten Toggle OC0A on Compare Match 0CR Register auf 0x63 Ohne Gewähr und wenn ich mich nicht verrechnet habe.
Georg schrieb: > Das heisst, du brauchst einen 40 kHz Timer! Bei 0 und 2 den Ausgang > umschalten, bei 1 ADC, bei 3 kann sich der Prozessor eine > Verschnaufpause gönnen. Oder du rufst mit einem 10 kHz Interrupt die > ADC-Wandlung auf, das musst du ja sowieso, aber dann müsstest du das > Rechtecksignal mit der richtigen Phase durch eine externe Schaltung > erzeugen. > > Ein PWM-Ausgang würde vermutlich nicht synchron mit der ADC-Wandlung > laufen. Genau so meinte ich es. Nur das geht auf keinen Fall. Viel zu viel Rechenzeit. Deshalb war meine Idde das ganze irgendwie in die PWM Sache zu verlegen. Nur weiß ich nicht, ob man zwei PWM Kanäle genau gegensätzlich betreiben kann, das ganze auch noch synchronisiert. Ich könnte mich auch damit anfreuden den ADC nur mit 100 Hz aufzurufen. Nur Ka, wie ich das synchronisiert bekommen soll. Bzw ob das überhaupt möglich ist.
Hi >0CR Register auf 0x63 >Ohne Gewähr und wenn ich mich nicht verrechnet habe. Du hast dich verrechnet. Eine 10kHz Frequenz bekommst du bei 0x31. MfG Spess
spess53 schrieb: > > Du hast dich verrechnet. Eine 10kHz Frequenz bekommst du bei 0x31. > > MfG Spess ..... Schade um Faktor zwei vertan .... Kopfrechnen Schwach Religion sehr gut :-) Scherz bei seite. Aber ein Ziemlich Stabilies Rechtecksignal kann ich damit erzeugen. Danke Spess53 für die Korrektur Gruß Detlev
Hi
Ich hab mir mal die Mühe gemacht und dein Vorhaben kurz nach meinem
Verständnis skizziert. Eigentlich ist das dein Job, denn nur so ist
eindeutig, worüber hier diskutiert wird.
Ich seh da überhaupt kein zeitproblem, bei richtigem Programmaufbau und
klar,
>Teilweise dauern auch manche Interruptroutinen länger als 1000 Takte.
ist das ein Fehler im Konzept. Eine ISR sollte kurz und schmerzlos nur
das erforderliche tun- so ist es kein Problem, alle 25 µSek in einer ISR
Flags zu setzen sowie einen Zähler aufzuaddieren. Bei 1 startest du im
Hauptprogramm die Messung, Ergebnis kommt in ISR des AD-Wandlers. Bei 2
wechselst du im Hauptprogramm die Signallage der Portpins. bei 4 setzt
du den Zähler wieder auf 0 und wechselstebenfalls wieder die Signallage
der Portpins. Kurz über den Daumen kalkuliert bei 8 MHz und einer
CPU-Taktzeit von 125 nSek alles ca. in 15 bis 20 µSek. abgearbeitet.
Aufruf ISR mit Inc Zähler ca. 10 Takte
1 | ISR_Timer: ;2 Takte |
2 | IN Status_Save, SREG ; Statusregister in Statuskopie 1 Takt |
3 | INC ISR_STEP ; reserviertes Zählregister 1 Takt |
4 | OUT SREG, Status_Save ; Statusregister zurückschreiben 1 Takt |
5 | RETI ;4 Takte |
Auswertung Zähler ca. max. 30 Takte
1 | CPI ISR_Step, 1 |
2 | BREQ Start_AD |
3 | CPI ISR_Step, 2 |
4 | BREQ Swap_Portbit |
5 | CPI ISR_Step, 4 |
6 | BRLO Weiter |
7 | CLR ISR_Step |
8 | RJMP Swap_Portbit |
9 | Start_AD: |
10 | .... |
11 | RJMP weiter |
12 | Swap_PortBit: |
13 | ..... |
14 | weiter: |
Wie du siehst, keine große Sache. Ausrechnen kannst du dir das selbst. Gruß oldmax
Detlev Neumann schrieb: > Bernhard F. schrieb: >> >> Mit einer 8 bit PWM und einem Preescaler von 2 kommst du grob >> überschlagen auf 15,625 kHz und bei Prescale 4 auf 7,8125 kHz. On zu Off >> time ist dann natürlich 1:1 >> Müssen es genau 10kHz sein? > > Wieso --> Mit 8 MHz Clock Frequenz > bekomme ich doch mit einem 8Bit Timer ein 10KHz Rechteck Signal generier > > --> Prescaler 8 > CTC - Mode Anschalten > Toggle OC0A on Compare Match > > 0CR Register auf 0x63 > > Ohne Gewähr und wenn ich mich nicht verrechnet habe. Natürlich geht das so. O.K. bis auf ein kleines Detail: In dem Beispiel dürfte ein Rechteck-Puls 100us lang. Periodendauer wären aber 2 Pulse? der Timer dürfte also nur bis 49 Zählen? Also müsste OCR = 0x31 sein wenn ich mich grade nicht vertue. Ich war gedanklich noch in ein vergleichbares Projekt vertieft. Da hab ich die 2 PWM pins bei Bottom und (OCR=265/2)-1 geschaltet, den Timer bis zu 0xFF (fast pwm) hochzählen lassen, und mir einen Overflow Interrupt generiert. Ich wollte auf jeden Fall nur einen Interrupt pro Periode. Man hätte natürlich im CTC-Mode nur jeden 2. Interrupt mitnehmen können für das gleiche Ergebnis, aber das ganze war schon spitze auf Knopf gerechnet und höchst zeitkritisch.(Nach dem der Wert endlich gewandelt wurde muss er noch ausgewertet und auf LEDs ausgegeben werden und das möglichst bevor der nächste Wert aufgenommen wird) Dafür hab ich dann gerne eine feste Frequenz in Kauf genommen. Zugegeben im Nachinein würde ich das auch anders lösen, aber da der Threadsteller eh schon zu viele Interrupts hat, hab ich das gleich mal irgendwie im Hirn miteinander verbunden... Klang alleine so dahstehend bestimmt seltsam ... ich merk es selbst... Felix schrieb: Nur weiß ich nicht, ob man zwei PWM Kanäle genau > gegensätzlich betreiben kann, das ganze auch noch synchronisiert. Ich > könnte mich auch damit anfreuden den ADC nur mit 100 Hz aufzurufen. Nur > Ka, wie ich das synchronisiert bekommen soll. Bzw ob das überhaupt > möglich ist. Klar ist das möglich. OCRA und OCRB sind ja am gleichen Timer.
:
Bearbeitet durch User
oldmax schrieb: > Bei 1 startest du im > Hauptprogramm die Messung Genau dasselbe habe ich ihm ja auch schon vorgeschlagen - entweder er versteht es nicht oder er will es einfach nicht. Er hat sich wohl an der PWM festgefressen, aber ich sehe nicht, wie man Rechteck und ADC phasenrichtig steuern kann ausser wie von uns beiden beschrieben. Kann man halt nix machen. Georg
ey Georg, ich bin beratungsresistent. Hatte das bei dir nicht so verstanden und außderdem kam zu deinem Post ein anderer Post, wo drin stand, dass das nicht so geht. Bin gerade dabei das umzustetzen, wie oldmax das gesagt hat. Bzw ihr das gesagt habt;-)
O.K. kleiner Denkansatz ich bitte darum ich zu korrigieren wenn ich falsch liege. Da leider nicht bekannt ist welche Variante des AT-Mega benutzt wird hoffe ich einfach mal, dass dieser einen 16 bit timer hat. 1) Der Timer wird mittels Prescaler 8 Mode 14 Fast PWM ICRn = 0x0063 (dec 99) auf die Frequenz von 10kHz eingestellt. 2) OCnA und OCnB erhalten die Werte 0x0031 (dec49) a) OCnA set on BOTTOM Clear on Match (non-inverting) b) OCnB clear on Bottom set on compare match (inverting) -> übernehmen das Toggeln bei Pos 0 und Pos 2 3) OCnC erhält den Wert 0x0018 (dec 24) im entsprechenden TIMSK wird der Interupt für Compare Match scharf geschaltet. -> generiert einen Interrupt bei Pos 1 Könnte das so klappen?
:
Bearbeitet durch User
Bernhard F. schrieb: > Klingt nach nem Denkfehler. "Die Polung umdrehen" bedeutet ja, dass der > uC plötzlich negative Betriebsspannung liefern müsste. Da hast du einen Denkfehler. Es reicht, wenn das Signal vom µC über einen Kondensator geführt und dadurch der Nullpunkt verschoben wird ...
Ok, das mit der Polung war missverständlich ausgedrückt. Da es sich um einen Leitwertmesser handelt, ist nur die Stromflussrichtung interessant. Macht euch darum keine Gdannken. Ich hoffe es kommen jetzt nicht wieder Leute an und motzen rum, dass ich nicht alle Fakten gesagt hab. Das war aus meiner Sicht unnütze Information. Es ist ein ATmega 16 mit 16 Bit Timern.
FLorian Unbekannto schrieb: > einen Leitwertmesser handelt Ein Aquarianer, die messen 1/R immer mit Wechselstrom und am liebsten alle paar µs MfG Klaus
Georg schrieb: > Er hat sich wohl an der > PWM festgefressen, aber ich sehe nicht, wie man Rechteck und ADC > phasenrichtig steuern kann ausser wie von uns beiden beschrieben. Meine Fresse. Das ist doch nun wirklich extrem simpel. Alles, was man braucht, ist ein Timer, der die drei Register OCRA, OCRB und ICR besitzt (bzw. bei einem 16Bit-Timer jeweils die H- und L-Teilregister dieser drei Register). Dann braucht man noch den Systemtakt und die möglichen Prescalerwerte für den Timer. Nehmen wir als Beispiel einfach mal einen Mega32@8MHz. Dieser Methusalem besitzt nur einen Timer mit zwei PWM-Kanälen, den Timer1. Also nimmt man den. Der am besten für die Aufgabe geeignete Timermodus ist FastPWM mit ICR1 als TOP (Modus 14). Mögliche Prescaler für Timer1 sind: 1 1/8 1/64 1/256 1/1024 Der maximale Zählumfang eines 16-Bit-Timers ist 65536. Damit kann man erstmal den bestmöglichen Prescaler ermitteln. Das ist genau der, der bei vollem Zählumfang des Timers gerade noch eine Überlauffrequenz des Timers liefert, die kleiner ist als die gewünschte von 10kHz. Also gilt es folgende, für manche Leute scheinbar schon überaus komplizierte Rechenaufgaben zu lösen: 8.000.000/(65536*1) = 122,0703125 8.000.000/(65536*8) = 8.000.000/(65536*64) = 8.000.000/(65536*256) = 8.000.000/(65536*1024)= Nunja, hier können wir offensichtlich schon nach dem ersten Anlauf aufhören mit Rechnen, denn 122Hz sind sehr deutlich unterhalb von 10 kHz. Der bestmögliche Prescaler ist hier also 1. Der entsprechende Wert für die CS1x-Bits in TCCR1B ist (zufällig) auch 1. Damit bleibt als einziger Freiheitsgrad noch der Wert für ICR1H/L, um den korrekten PWM-Zyklus zu erreichen. Wie man den berechnet steht im Datenblatt. Auch wieder so eine überaus komplizierte Division, obendrein gefolgt von der unheimlich anspruchsvollen Aufgabe, vom Ergebnis dann noch eins abzuziehen... 8.000.0000/10.000 - 1 = 799 bzw. $31F ICR1H ist also mit 3 zu laden, ICR1L mit $1F. So, damit hat man erstmal die richtige PWM-Frequenz und zwar sogar ganz exakt. Nun soll aber ein Rechteck erzeugt werden (vermutlich ein möglichst symmetrisches, also eins mit gleich langen High- und Low-Phasen). Dafür benutzt man OCR1AH/L und OCR1BH/L. Beide bekommen den gleichen Wert, denn beide sollen das gleiche Rechtecksignal erzeugen. Damit das symmetrisch ist, muß der Wert einfach die "Hälfte" des ICR-Wertes betragen. Die korrekte Formel ist: OCR=(ICR+1)/2-1 (warum das die korrekte Formel ist, steht wieder im Datenblatt, nämlich in der Funktionsbeschreibung des Timers) Also: OCR=(799+1)/2-1=399 bzw. $18F OCR1AH und OCR1BH sind also jeweils mit 1 zu laden, OCR1AL und OCR1BL jeweils mit $8F. Damit haben wir zwei gleiche und synchrone Rechtecksignale mit der richtigen Frequenz. Nun müssen wir die bloß noch an die entsprechenden Pins lotsen und eins davon bei dieser Gelegenheit invertieren. Das geht über die COM1Ax- und COM1Bx-Bits in TCCR1A. Für den FastPWM-Modus wären die entsprechenden Bitkombinationen 0b10 bzw. 0b11 (was natürlich auch wieder dem Datenblatt zu entnehmen ist), was davon man als "normal" und "invertiert" betrachtet, liegt allein im Auge des Betrachters, wichtig ist für die konkrete Anwendung nur eins: Wenn man für den einen Kanal die eine Bitkombination verwendet und für den anderen die andere, dann erhält man auf jeden Fall bei sonst identischer Initialisierung der beiden Kanäle an einem der beiden OC1x-Pins genau das invertierte Signal des anderen. (Tatsächlich stimmt das nicht 100%ig, aber für den konkreten Fall mit symmetrischen Rechtecken stimmt es ganz sicher). So, das war die erste Hälfte der Aufgabe, der Mann hat erstmal ohne jede CPU-Last sein antisymmetrisches Rechtecksignal an zwei Pins. Bleibt noch, die ADC zu synchronisieren. Auch das geht (fast) ohne jede CPU-Last, denn die ADC läßt sich durch gewisse Timer-Ereignisse starten. Daß das passieren soll, legt man über das Setzen des ADATE-Bits in ADCSRA fest, welches Timerereignis der Trigger sein soll, legt man über die ADTSx-Bits (beim Mega32 im SFIOR-Register) fest. Für Timer1 als Quelle stehen hier drei Varianten zur Verfügung, die im Prinzip für die konkrete Anwendung alle gleichermaßen geeignet sind, es ist also in erster Näherung scheißegal, welche man davon verwendet. Overflow und Capture triggern nahezu zur gleichen Zeit, CompareMatchB einen halben PWM-Zyklus später. Fast ohne CPU-Last gilt bezüglich der Triggerung deshalb, weil ohne jede Timer-ISR die ADC-ISR selber das Trigerflag zurücksetzen muß. Naja, zwei Takte aller 100µs sollten machbar sein. Wenn es aber aus irgendwelchen Gründen sowieso eine Timer-ISR geben muß, dann wird man natürlich als Triggerquelle genau diese wählen, weil dann das manuelle Zurücksetzen des Flags entfällt. Was letztlich noch zu tun bleibt, ist noch nur eine Sache: einen geeigneten ADC-Prescaler zu wählen, der sicherstellt, daß die ADC innerhalb eines PWM-Zyklus auch sicher fertig wird. Im Datenblatt steht, wie man das berechnet... Von der ganzen CPU-Last bleibt also genau nur eine Sache: die ADC-ISR. Entweder mit oder ohne Rücksetzen des Triggerflags. Da das in der ISR alles Operationen sind, die ohne Flagveränderung auskommen können, ist die ISR sehr bescheiden bezüglich der CPU-Last. Folgendes Beispiel zeigt das für die Verwendung von OCR1B als Quelle mit Rücksetzen: adc_handler: ; 6 (Normalfall mit rcall im Vector) push R16 ; 2 Hilfsregister retten ldi R16,1<<OCF1B ; 1 Triggerflag zurücksetzen out TIFR,R16 ; 1 in R16,ADCL ; 1 ADC-Wert im Speicher ablegen sts adc_value+0,R16; 2 in R16,ADCH ; 1 sts adc_value+1,R16; 2 pop R16 ; 2 reti ; 4 ;-- ;22 Die ISR benötigt also 10.000*22=220.000 Takte pro Sekunde, um alles zu tun, was nötig ist. Von 8.000.000 verfügbaren Takten. Also benötigt die ganze Sache genau 2,75% der verfügbaren Rechenzeit. Für den Fall, daß aus irgendwelchen anderen Gründen sowieso ein Timerinterrupt gebraucht wird und damit das Rücksetzen des Triggerflags in der ADC-ISR entfallen kann, sinkt deren Rechenzeitbedarf auf 20 Takte= 2,5%. SO werden µC programmiert. Alle Features der Hardware ausnutzen. Erst, wenn man damit nicht mehr die gewünschte Funktion erreichen kann, kommt überhaupt erst Code in's Spiel. Und da erreicht man dann die maximale Performance in genau einer Sprache: Der Assemblersprache des Zielsystems.
c-Hater murmelte:
>Meine Fresse.
Meine auch...
(Isch war beim Schaahnarzscht, deshalb)
Nichts desto Rotz: Ein gute Erklärung.
MfG Paul
c-hater (Gast) super Ausführung aber nicht jeder beherrscht Assembler wenn Du es kannst sei glücklich Aber ich denke auch in einer Hochsprache ist dein Szenario programmierbar Gruß Detlev
Detlev Neumann schrieb: > Aber ich denke auch in einer Hochsprache ist dein Szenario > programmierbar Natürlich. Aber solange das hier > Generell hat der uC noch weitere Aufgaben zu erledigen. Teilweise > dauern auch manche Interruptroutinen länger als 1000 Takte. nicht abgestellt wird, ist alles andere Makulatur. Egal ob in Assembler oder in Makro-Assembler (alias 'C').
:
Bearbeitet durch User
Hey, die tausend Takte versuche ich gerade abzustellen, allerdings krieg ich es noch nicht hin. Ich setze jetzt in der ISR einen bool auf true und dann polle ich den bool in der main(). Wenn true, dann führe ich die 1000 Takte aus. Ich hoffe das ist im Prinzip richtig und sollte gehen. Allerdings funktioniret das nicht...obwohl ich den bool volatile markiert habe. @c hater: danke für die Ausführung. Das ganze in C zu machen ist kein Problem, allerdungs wird meines Verständnisses nach, der ADC direkt gestartet nach dem ein Pegelwechsel vorliegt. Also Compare match wird aufgerufen, dann Signale toggeln und sofort startet der ADC. Das wollte ich zwecks einschwingen ja eigentlich nicht. Oder versteh ich was falsch? VG
FLorian Unbekannto schrieb: > Ich setze jetzt in der ISR einen bool auf true und > dann polle ich den bool in der main(). Wenn true, dann führe ich die > 1000 Takte aus. Ich hoffe das ist im Prinzip richtig und sollte gehen. > Allerdings funktioniret das nicht...obwohl ich den bool volatile > markiert habe. Vermutlich hast du noch einen Programmierfehler - Zeile 42 schon mal genauer angeguckt? ;-) Nachdem du in der main() die Sache abgearbeitet hast, solltest du deine bool dort auf false setzen, damit die ISR wieder eine Chance hat, einen neuen Durchlauf anzufordern. Außerdem musst du dir sicher sein, dass die zusätzlichen Takte in der main() die Durchlauffrequenz nicht zu sehr verringern. Sonst könntest du die 1000 Takte in Teilaufgaben aufteilen, die in aufeinanderfolgenden Hauptschleifendurchläufen abgearbeitet werden. Dann hätten die anderen "Tasks" häufiger eine Chance, dran zu kommen.
Karl Heinz schrieb: > Aber solange das hier >> Generell hat der uC noch weitere Aufgaben zu erledigen. Teilweise >> dauern auch manche Interruptroutinen länger als 1000 Takte. > > nicht abgestellt wird, ist alles andere Makulatur. Egal ob in Assembler > oder in Makro-Assembler (alias 'C'). Nö. Eine ISR kann auch lang sein. Was möglichst kurz sein muß, ist "nur" der Teil ihrer Zeit, den sie exklusiv arbeitet, also die Zeit, in der es nicht möglich ist, daß andere Interrupts ausgelöst werden. Aber gerade diese mit "nur" apostrophierte Aufgabe stellt doch oft eine ziemliche Knobelaufgabe dar. Und wenn du abstreiten willst, daß diese sich nur in Asm mit dem besten aller denkbaren Ergebnisse lösen lassen wird, dann lügst du. In C kannst du im allerbesten Fall das gleiche Ergebnis wie in Asm erreichen. In der Praxis wirst du aber (gerade bezüglich ISRs) nur ein etwas bis sogar sehr deutlich schlechteres Ergebnis erzielen können. Je kürzer die ISR, desto größer der Nachteil für C, weil der nutzlose C-Overhead dann immer relevanter wird. Die gezielte Wahl der bestmöglichen Optimierung für das konkrete Problem ist nur in Asm möglich. Eben weil diese Optimierung oft bedeuten wird, daß man zumindest in der ISR selber auf den "Komfort" von C verzichten muß. Im Falle geistig etwas minderbemittelter Compiler, die keine explizite Reservierung von Registern für wirklich zeitkritische Operationen unterstützen, muß man ggf. sogar im ganzen Programm auf C verzichten, um das theoretisch Machbare auch praktisch umsetzen zu können und landet so auf ganz natürliche Art bei der gottgegebenen Sprache für kleine µC: Asm.
Detlev Neumann schrieb: > c-hater (Gast) super Ausführung aber nicht jeder beherrscht Assembler > wenn Du es kannst sei glücklich Das hat nix mit Glück zu tun. Ich habe mir einfach nur die Mühe gemacht, auch AVR-Asm zu lernen. Neben vielen anderen Sprachen. > Aber ich denke auch in einer Hochsprache ist dein Szenario > programmierbar Dieses ja. Andere nicht. Je enger es an die Grenzen des theoretisch Möglichen geht, desto wichtiger wird es, dem Controller notfalls auch direkt in seiner Sprache sagen zu können, wie genau er es am Besten machen soll.
FLorian Unbekannto schrieb: > allerdungs wird meines Verständnisses nach, der ADC direkt > gestartet nach dem ein Pegelwechsel vorliegt. Ja. Die ADC wird hier auf jeden Fall immer anläßlich eines Pegelwechsels an den Ausgängen gestartet. Allerdings heißt "Starten" der ADC nicht, daß auch in diesem Moment der Meßwert gesampelt wird. Das passiert erst später. Wann genau, ist berechenbar. Wie genau das berechnet wird, steht (du ahnst es sicher schon) im Datenblatt... Vielleicht liest du das verdammte Ding doch einfach mal, bevor du die Hardware benutzt, die es beschreibt?
Naja das habe ich mir schon durchgelesen. Aber 1.5 ADC Cycles ist jetzt nicht sooo die Welt...Für Einschwingvorgänge eher suboptimal.
Hi Ein kleiner Denkfehler meinerseits... Bei einem Zeitinterrupt von 25 µSek. und der von mir beschriebenen Vorgehensweise muss die Main in weniger als 25 µSek. abgearbeitet sein. Das ist nicht grad viel Zeit und deshalb macht es Sinn, die Befehle alle in die ISR zu packen. So ist auch sichergestellt, das die Zeittaktung mit dem Zähler funktioniert. Der Swap für die IO Bits ist einfach mit einer EOR-Maske erledigt und der ADC wird lediglich gestartet. Das Ergebnis liefert dann seine eigene ISR. Sollten weitere Zeitabhängige Jobs zuu erledigen sein, würde ich einen zweiten Zähler bis 40 (0-39) zählen lassen und mir dann ein mSek-Flag bilden. Das kann dann in der Main gepollt werden, denn eine Zykluszeit < 1mSek. dürfte kein Problem sein. Gruß oldmax
Hi >Ein kleiner Denkfehler meinerseits... Bei einem Zeitinterrupt von 25 >µSek. und der von mir beschriebenen Vorgehensweise muss die Main in >weniger als 25 µSek. abgearbeitet sein. Das ist nicht grad viel Zeit... Nicht viel Zeit? Das sind bei 8MHz 200000 Takte oder ca. 120000...150000 Assemblerbefehle. MfG Spess
spess53 schrieb: >>weniger als 25 µSek. abgearbeitet sein. Das ist nicht grad viel Zeit... > > Nicht viel Zeit? Das sind bei 8MHz 200000 Takte oder ca. 120000...150000 > Assemblerbefehle. µ nicht m. 'n Kaffee und nochmal :)
Hi Ja, es war noch früh am Tag, aber auch meine Rechnung hat bei 8 MHz ergeben 1/f=0,000000125Sek= 125nSek und das sind für 10 KHz 800 Impulse. Da auf 40 KHz aufgelöst wird eben nur noch 200 und bei durchschnittlich 2 Takten pro Befehl bleiben ca. 100 Befehle. Ich hoffe jetzt, ich hab mich da nicht auch noch verrechnet... Gruß oldmax
oldmax schrieb: > Hi > Ein kleiner Denkfehler meinerseits... Bei einem Zeitinterrupt von 25 > µSek. und der von mir beschriebenen Vorgehensweise muss die Main in > weniger als 25 µSek. abgearbeitet sein. Das ist nicht grad viel Zeit und > deshalb macht es Sinn, die Befehle alle in die ISR zu packen. So ist > auch sichergestellt, das die Zeittaktung mit dem Zähler funktioniert. > Der Swap für die IO Bits ist einfach mit einer EOR-Maske erledigt und > der ADC wird lediglich gestartet. Das Ergebnis liefert dann seine eigene > ISR. Sollten weitere Zeitabhängige Jobs zuu erledigen sein, würde ich > einen zweiten Zähler bis 40 (0-39) zählen lassen und mir dann ein > mSek-Flag bilden. Das kann dann in der Main gepollt werden, denn eine > Zykluszeit < 1mSek. dürfte kein Problem sein. > Gruß oldmax Das rall ich nicht. Erst hauste hier einen raus alles muss mit Hardware sein und jetzt gehste den weg von Georg, den du erst so Kacke fandest?
FLorian Unbekannto schrieb: > Erst hauste hier einen raus alles muss mit Hardware > sein und jetzt gehste den weg von Georg, den du erst so Kacke fandest? Irgendwie bist du völlig von der Rolle und wirfst alles durcheinander - ich finde nirgends, dass oldmax nicht meiner Meinung ist, im Gegenteil, er hat zeimlich genau das Gleiche wie ich (schon im 3. Post) vorgeschlagen und noch weiter ausgeführt. Mehr Hilfe geht nicht. FLorian Unbekannto schrieb: > ey Georg, > > ich bin beratungsresistent. Manchmal unterläuft dir doch versehentlich eine zutreffende Einschätzung. Georg
Hi Florian Nimm einmal zur Kenntnis, niemals würde ich eine Programmlösung "Kacke" finden, egal von wem. Vielleicht ungeeignet, so auch mein erster Vorschlag, in der ISR einfach nur zu zählen und den Zähler in der Main zu prüfen. Das funktioniert nämlich nur, wenn die Main eine Zykluszeit < 25µSek. hat. Nur in diesem Zeitraumwerden die Zählerstände sauber 0, 1, 2 und 3 immer erkannt. Braucht die Main länger, dann kommt schon mal eine Lücke vor... 1, 0, 3, 1 usw. Also nicht zuverlässig und daher in der Form ungeeignet. Das heißt aber nicht, das alles Blödsinn war, sondern nur, das die Auswertung vom Wert in ISR_Step in der ISR erfolgen muss. Das bläht aber die ISR nicht unnötig auf. Start-AD ist einfach nur das Setzen des Bits zum Starten des ADC und den Bit_Swap erledigst du mit
1 | |
2 | Push R16 |
3 | Push R17 |
4 | In R16, PortB |
5 | LDI R17, 0b00000011 |
6 | EOR R16, R17 |
7 | Out PortB, R16 |
8 | Pop R17 |
9 | Pop R16 |
Der Vorteil von Assembler ist halt, das man genau nachrechnen kann, wie lange diese ISR braucht. Bei ca. 40 Befehlen und einer mittleren Taktzahl von 1,5 landest du grob überschlagen bei 0,125*60 = 7,5 µSek. Das sind auf 25 µSek bezogen ca. 35% Leitest du dir noch einen Systemtakt ab, sind die 40% schnell erreicht oder gar überschritten. Hier ist dann schon mal eine genaue Rechnung erforderlich. Das bedeutet aber nicht, das deine Main keine zeit hat, aber die ISR wird vermutlich in einem Programmzyklus mehrfach aufgerufen und verlängert sozusagen das Programm. Ist bei jedem Rechteckssignal eine Messung erforderlich, könnte auch hier der ein oder andere Wert verloren gehen. Vielleicht ist aber auch nur jeder 5. oder 10. Wert erforderlich. Gruß oldmax
O.K. auch wenn ich vorhin ab und an mal ansatzweise wirres Zeug geschrieben hab hab ich meinen "Denkansatz" von vorhin weitergesponnen und das ganze auf einen Atmega gebracht. Eins vorweg: er läuft mit 16 MHZ darum habe ich auch ein 20kHZ Signal wie man auf den Bilder sieht. Bild 1: die beiden Rechtecke -exakt synchron -NULL Interrupts zum erzeugen -leider auf dem Bild "ineinandergeschoben"(eins um 1 V nach oben das andere um 1 V nach unten) aber ich denke man erkennt sie. -alles in Hardware Bild 2: der Triggerpunkt um die ADC zu starten. - ein Interrupt pro 100us Periode - Inhalt ist nur das manuelle starten einer ADC das ist ein Bit das gesetzt wird. Wer das in Assembler ausrechnen möchte: gerne, ich hab grade keine Lust dazu... - zum debuggen wird ein PIN an CH2 des Oszilloskopes getoggelt - CH1 ist das nicht invertierte Rechteck Bild 3: während sich der uC in der ADC-complete ISR befindet wird CH2 des Oszilloskopes runtergezogen. - ein Interrupt pro 100us Periode - inhalt ist eigentlich nur das auslesen des ADC-Wertes in einen Puffer, auch das sind nur wenige Takte Den dazugehörigen C-Code poste ich gleich, muss ihn noch bisschen kommentieren, sonst ist er nie und nimmer vorzeigbar...
:
Bearbeitet durch User
Ich habe versucht auch auf die Funktion mit den 1000 Takten einzugehen, die in der Main ist. Mein Lösungsansatz ist einfach die Messwerte so lange aufzusummiern / zu mitteln, wie die Main anderweitig beschäftigt ist. Falls dein Spannungsteiler sich nicht so schnell ändert, sollte das gehen, wenn nicht dann wars das halt... oder du lässt einfach ein paar Messwerte aus... Die ISRs jetzt noch in Assembler umschreiben wäre sicher auch eine Option. Ich hab dem C-Compiler vertraut, dass er bei so kurzen ISRs nicht zu viel Murks macht...
1 | /*
|
2 | * PWM10khz.c
|
3 | *
|
4 | * Created: 03.09.2014 13:07:57
|
5 | * Author: Bernhard F.
|
6 | */
|
7 | |
8 | |
9 | #include <avr/io.h> |
10 | #include <avr/interrupt.h> |
11 | #include <stdbool.h> |
12 | |
13 | volatile bool ADC_summe_changed=false; |
14 | volatile uint16_t ADC_summe=0; |
15 | volatile uint8_t ADC_zaehler=0; |
16 | uint16_t ADC_ergebnis=0; |
17 | |
18 | void initialisierung(); |
19 | void TIMER0_init(); |
20 | void TIMER1_init(); |
21 | void ADC_init(); |
22 | uint16_t Takte_1000(); |
23 | |
24 | |
25 | ISR (ADC_vect) // einmal pro 100us |
26 | {
|
27 | PORTC^=(1<<PC2); // debugpin toggeln |
28 | |
29 | // grundsätzlich:
|
30 | // buffer=ADC;
|
31 | |
32 | // versuch einer Mittelwertbildung wenn die Main noch nicht so weit ist
|
33 | ADC_summe_changed=true; |
34 | ADC_summe= ADC_summe +ADC; |
35 | ADC_zaehler++; |
36 | |
37 | |
38 | PORTC^=(1<<PC2); // debugpin toggeln |
39 | }
|
40 | |
41 | ISR (TIMER0_COMPB_vect) einmal pro //100us |
42 | {
|
43 | PORTA^=(1<<PA4); // debugpin toggeln |
44 | ADCSRA = ADCSRA | (1<<ADSC); //ADC start conversion |
45 | }
|
46 | |
47 | ISR (TIMER1_OVF_vect) // genau einmal zu beginn des Programms um Timer 0 auf Timer 1 zu synchronisieren |
48 | {
|
49 | TCNT0=0; |
50 | TIMSK1 &=~(1<<TOIE2); // Overflow interupt aus |
51 | }
|
52 | |
53 | |
54 | int main(void) |
55 | {
|
56 | initialisierung(); |
57 | PORTA^=(1<<PA4); |
58 | PORTC^=(1<<PC2); // debugpin toggeln |
59 | while(1) |
60 | {
|
61 | Takte_1000(); // do complex things |
62 | |
63 | ADC_summe_changed=false; // es wäre blöd wenn der interrupt während dieser Berechnung zuschlägt -> vermeiden |
64 | do
|
65 | {
|
66 | ADC_ergebnis=ADC_summe/ADC_zaehler; |
67 | |
68 | } while (ADC_summe_changed==true); |
69 | |
70 | //TODO:: Please write your application code
|
71 | }
|
72 | }
|
73 | |
74 | |
75 | void initialisierung() |
76 | {
|
77 | // Debug PINS festgelegt und als Ausgang geschaltet
|
78 | DDRA|=(1<<PA4); |
79 | DDRC|=(1<<PC2); |
80 | |
81 | TIMER1_init(); |
82 | ADC_init(); |
83 | TIMER0_init(); |
84 | |
85 | sei(); |
86 | }
|
87 | |
88 | |
89 | void TIMER0_init() |
90 | {
|
91 | |
92 | |
93 | TCCR0B &=~(1<<WGM02); |
94 | TCCR0A |= (1<<WGM01); |
95 | TCCR0A &=~(1<<WGM00); // WGM 010 CTC Mode |
96 | |
97 | |
98 | TCCR0B &=~(1<<CS02); |
99 | TCCR0B |= (1<<CS01); |
100 | TCCR0B &=~(1<<CS00); // CS 010 für Prescaler 8 |
101 | |
102 | OCR0A = 99; // Timer PER = 100us |
103 | OCR0B = 8; // magic number eigentlich müsste hier 24 stehen um die mitte zu treffen aber dank |
104 | //später einsetzendem ADC und C overhead wird schon früher ausgelöst
|
105 | |
106 | TIMSK0 |=(1<<OCIE0B); // Compare interupt a |
107 | |
108 | |
109 | }
|
110 | |
111 | void TIMER1_init() |
112 | {
|
113 | TCCR1A |= (1<<COM1A1); |
114 | TCCR1A &=~(1<<COM1A0); // 10 clear on match set @ bottom |
115 | TCCR1A |= (1<<COM1B1); |
116 | TCCR1A |= (1<<COM1B0); // 11 set on match clear @ bottom invert mode |
117 | |
118 | TCCR1B &=~(1<<CS22); |
119 | TCCR1B |=(1<<CS21); |
120 | TCCR1B &=~(1<<CS20); // CS 010 für Prescaler 8 |
121 | |
122 | TCCR1B |=(1<<WGM13); |
123 | TCCR1B |= (1<<WGM12); |
124 | TCCR1A |= (1<<WGM11); |
125 | TCCR1A &=~(1<<WGM10); // WGM 1110 fast pwm 0-ICR1 |
126 | |
127 | ICR1=99; |
128 | OCR1A = 49; |
129 | OCR1B = 49; |
130 | |
131 | TIMSK1 |=(1<<TOIE2); // Overflow interupt |
132 | |
133 | DDRB |=(1<<DDB6); // PB6 = OC1B |
134 | DDRB |=(1<<DDB5); // PB5 = OC1A |
135 | |
136 | |
137 | }
|
138 | |
139 | void ADC_init() |
140 | {
|
141 | |
142 | ADMUX = ADMUX &~ (1<<REFS0); //Referenzspannung 1,1 Volt interne Referenz |
143 | ADMUX = ADMUX | (1<<REFS1); |
144 | |
145 | ADMUX = ADMUX &~ (1<<ADLAR); //Ergebnis rechtsbündig |
146 | |
147 | ADMUX = ADMUX &~ (1<<MUX4); |
148 | ADMUX = ADMUX &~ (1<<MUX3); |
149 | ADMUX = ADMUX &~ (1<<MUX2); //Singleended mit Gain = 1 adc1 |
150 | ADMUX = ADMUX &~ (1<<MUX1); |
151 | ADMUX = ADMUX | (1<<MUX0); |
152 | |
153 | ADCSRA |=(1<<ADPS2); |
154 | ADCSRA &=~(1<<ADPS1); |
155 | ADCSRA &=~(1<<ADPS0); // 100 für preescaler 16 |
156 | |
157 | // ADCSRA|=(1<<ADATE); // auto Trigger enable
|
158 | |
159 | ADCSRA|=(1<<ADIE); // ADC Interrupt bei fertiger Wandlung |
160 | |
161 | // ADCSRB&=~(1<<ADTS2);
|
162 | // ADCSRB|=(1<<ADTS1); // auto start conversion bei Timer 0 Compare match a
|
163 | // ADCSRB|=(1<<ADTS0);
|
164 | |
165 | |
166 | ADCSRA = ADCSRA | (1<<ADEN); //ADC Enable |
167 | |
168 | |
169 | }
|
170 | |
171 | |
172 | uint16_t Takte_1000() // einfach nur wirrer kram um eine lange funktion in die main zu setzen |
173 | {
|
174 | uint16_t temp1=200; |
175 | for (uint8_t i=0;i<=30;i++) |
176 | {
|
177 | temp1+=(1<<(i & 0b00001111)); |
178 | }
|
179 | return temp1; |
180 | }
|
edit: Ich hoffe der Code ist nicht zu lang, um ihn direkt zu posten.
:
Bearbeitet durch User
oldmax schrieb: > Nimm einmal zur Kenntnis, niemals würde ich eine Programmlösung "Kacke" > finden, egal von wem Georg hatte recht, ich meinte auch den C-hater, der ist ja immer etwas aufbrausender^^. War einfach vom ganzen coden verwirrt,....SRY
Bernhard, erstma vielen Dank! Krasse Arbeit, die du dir gemacht hast. Ich werde das gleich mal ansehen nd versuchen zu verstehen...*Daumen hoch*
Nachtrag: den Abschnitt:
1 | ADC_summe_changed=false; // es wäre blöd wenn der interrupt während dieser Berechnung zuschlägt -> vermeiden |
2 | do
|
3 | {
|
4 | ADC_ergebnis=ADC_summe/ADC_zaehler; |
5 | |
6 | } while (ADC_summe_changed==true); |
müsste ich nochmal überdenken. Könnte damit ein Deadlock produziert haben, wenn die Division länger dauert als der Abstand zwischen 2 AD/Wandlungen... Denn Bock hab ich aber sowiso geschossen. Wenn die Werte ausgewertet werden müssen die Variablen ja auf 0 zurückgesetzt werden, sonst gibt es nen Überlauf. Das hatte ich vergessen... Besser wäre es die Variablen zwischenzupuffern. Ich hab jetzt die Variablendeklaration der Puffer nicht mit in den Codeschnipsel gehauen...
1 | // es wäre blöd wenn der interrupt während dieser Berechnung zuschlägt -> vermeiden
|
2 | do
|
3 | {
|
4 | ADC_summe_changed=false; |
5 | ADC_summe_puffer=ADC_summe; |
6 | ADC_puffer_zaehler = ADC_zaehler; |
7 | ADC_summe=0; |
8 | ADC_zaehler=0; |
9 | } while (ADC_summe_changed==true); |
10 | ADC_ergebnis=ADC_summe_puffer/ADC_zaehler_puffer; |
Aber bevor ich mich so auf die Mittelung einschieße: Wie willst du es handhaben wenn die Main noch beschäftigt ist aber ein Messwert auftritt? 1) Willst du den Messwert fallen lassen? 2) Willst du nur jeden X. Wert aufnehmen ? 3) Willst du die Messwerte solange mitteln bis die main wieder zeit hat? 4) Willst du die Main so gestalten, dass sie nicht beschäftigt sein kann? 5) Hast du ne besere Idee ?
:
Bearbeitet durch User
FLorian Unbekannto schrieb: > Naja das habe ich mir schon durchgelesen. Aber 1.5 ADC Cycles ist jetzt > nicht sooo die Welt...Für Einschwingvorgänge eher suboptimal. Du hast es vielleicht gelesen, aber definitiv nicht begriffen. 1.5 ADC-Cycles sind was völlig anderes als 1.5 Takte. Im Minimum sind es drei Takte, denn der kleinstmögliche ADC-Prescaler ist 2. Nun hatte ich aber geschrieben, daß ein geeigneter ADC-Prescaler gewählt werden sollte. 2 ist für die konkrete Anwendung aber nicht optimal geeignet. Besser ausgedrückt: absolut ungeeignet. Schon wieder so eine schwierige Division: 8.000.000/(2*13)=307692,3 Viel zu viel, du brauchst nur etwas mehr als 10kHz Samplerate. Mal ganz abgesehen davon, daß bei 8.000.000/2=4MHz ADC-Clock die ADC im Wesentlichen nur noch Müll liefern würde. Du suchst also erstmal nach den bekannten Methoden einen wirklich geeigneten ADC-Prescaler. Der ist 32. Damit erreichst du nämlich eine Samplerate von ca. 19kHz, der nächstgrößere Prescaler würde nur noch ca. 9,5 kHz erlauben, was definitiv weniger ist als die 10kHz deiner PWM und damit sicher ungeeignet, um in jedem PWM-Zyklus eine Messung zu machen. Was bedeutet nun ein ADC-Prescaler von 32? Zum einen bedeutet es, daß du 250kHz ADC-Clock benutzt und damit wenigstens nicht allzuweit jenseits dessen bist, wofür Atmel 10Bit Auflösung garantiert. Mit effektiv neuneinhalb Bit oder so kann man meist auch noch sehr gut leben. Weiterhin bedeutet es, daß ein ADC-Cycle 32 MCU-Takte umfaßt, 1.5ADC-Cycles also 48 MCU-Takten entsprechen. Der Meßzweitpunkt liegt also bei 12% des zeitlichen Meßfensters zwischen zwei Pegelwechseln.
c-hater schrieb: > Weiterhin bedeutet es, daß ein ADC-Cycle 32 MCU-Takte umfaßt, > 1.5ADC-Cycles also 48 MCU-Takten entsprechen. Der Meßzweitpunkt liegt > also bei 12% des zeitlichen Meßfensters zwischen zwei Pegelwechseln. Oops, die Interpretation vergessen: 12% ist im Allgemeinen schon ein recht günstiger Zeitpunkt. Kurzzeitige Überschwinger nach den Pegelwechseln sollten hier weitestgehend abgeklungen sein. Wenn nicht, stimmt was mit der betriebenen Schaltung oder dem Layout der Leiterplatte nicht. Das hat dann typisch weit schlimmere Folgen als nur einen versauten Meßwert...
FLorian Unbekannto schrieb: > Georg hatte recht, ich meinte auch den C-hater, der ist ja immer etwas > aufbrausender^^. War einfach vom ganzen coden verwirrt,....SRY Nur hatte c-hater zu deinem Code noch überhaupt garnichts geschrieben. Der arme Mann muß nämlich tagsüber i.d.R. malochen und hat dann oft keine Zeit, sich mit solchen Trivialitäten zu beschäftigen... Hätte er es allerdings getan, wäre die Kritik allerdings in der Tat absolut vernichtend gewesen, das hast du sicher geahnt und deshalb c-hater in der Art einer selbsterfüllenden Prophezeiung eine solche Äußerung schonmal unterstellt, bevor sie überhaupt erfolgt ist...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.