Forum: Mikrocontroller und Digitale Elektronik 10khz rechteckspannung mit uC


von FLorian U. (florian_u)


Lesenswert?

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

von Thomas E. (thomase)


Lesenswert?

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.

von Georg (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Georg (Gast)


Lesenswert?

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

von Jim M. (turboj)


Lesenswert?

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.

von Bernhard F. (bernhard_fr)


Lesenswert?

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
von Georg (Gast)


Lesenswert?

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

FLorian Unbekannto schrieb:
> Teilweise dauern auch manche Interruptroutinen länger als 1000 Takte.
Warum? Das hört sich für mich nach "Programmfehler" an...

von Detlev Neumann (Gast)


Lesenswert?

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.

von Felix (Gast)


Lesenswert?

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.

von spess53 (Gast)


Lesenswert?

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

von Detlev Neumann (Gast)


Lesenswert?

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

von oldmax (Gast)


Angehängte Dateien:

Lesenswert?

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

von Bernhard F. (bernhard_fr)


Lesenswert?

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
von Georg (Gast)


Lesenswert?

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

von FLorian U. (florian_u)


Lesenswert?

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

von Bernhard F. (bernhard_fr)


Lesenswert?

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
von Wolfgang A. (Gast)


Lesenswert?

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

von FLorian U. (florian_u)


Lesenswert?

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.

von Klaus (Gast)


Lesenswert?

FLorian Unbekannto schrieb:
> einen Leitwertmesser handelt

Ein Aquarianer, die messen 1/R immer mit Wechselstrom und am liebsten 
alle paar µs

MfG Klaus

von FLorian U. (florian_u)


Lesenswert?

soll heißen?

von c-hater (Gast)


Lesenswert?

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.

von Paul Baumann (Gast)


Lesenswert?

c-Hater murmelte:
>Meine Fresse.

Meine auch...
(Isch war beim Schaahnarzscht, deshalb)

Nichts desto Rotz: Ein gute Erklärung.

MfG Paul

von Detlev Neumann (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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
von FLorian U. (florian_u)


Lesenswert?

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

von Mike (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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?

von FLorian U. (florian_u)


Lesenswert?

Naja das habe ich mir schon durchgelesen. Aber 1.5 ADC Cycles ist jetzt 
nicht sooo die Welt...Für Einschwingvorgänge eher suboptimal.

von oldmax (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Borsti (Gast)


Lesenswert?

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

von oldmax (Gast)


Lesenswert?

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

von FLorian U. (florian_u)


Lesenswert?

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?

von Georg (Gast)


Lesenswert?

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

von oldmax (Gast)


Lesenswert?

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

von Bernhard F. (bernhard_fr)


Angehängte Dateien:

Lesenswert?

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
von Bernhard F. (bernhard_fr)


Lesenswert?

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
von FLorian U. (florian_u)


Lesenswert?

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

von FLorian U. (florian_u)


Lesenswert?

Bernhard, erstma vielen Dank! Krasse Arbeit, die du dir gemacht hast.
Ich werde das gleich mal ansehen nd versuchen zu verstehen...*Daumen 
hoch*

von Bernhard F. (bernhard_fr)


Lesenswert?

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
von c-hater (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.