Forum: Compiler & IDEs ADCMUX Verständnisfrage


von Neuer (Gast)


Lesenswert?

Hallo,
ich möchte auf einem Tiny13 alle vier ADC Eingänge kontinuierlich 
nacheinander abfragen.
Dazu habe ich mir gedacht den ADC im free running Modus laufen zu lassen 
und dann folgende ISR:
1
//Analog inputs
2
ISR(ADC_vect)
3
{
4
  if(uADCChange < 4)
5
  {
6
    //vier mal einlesen
7
    uADCChange++;
8
   //jeweiliger Wert des ADC-Pins ins Input-Array
9
   uInputs[ADMUX]=ADCL;
10
  }
11
  else
12
  { 
13
    uADCChange = 0;
14
    //ADC Pins weiterschalten
15
    ADMUX++%3;
16
  }
17
}
Kann ich mit den Registern so wie mit Variablen arbeiten ?
Oder muß ich jeweils händisch die Werte in eine Zwischenvariable holen ?
Weil so wär's mir am liebsten ;-)
Danke.

von Jan M. (mueschel)


Lesenswert?

Ja das sollte gehen. Aber:

- ADCMUX++%3 macht nicht ganz das, was du wahrscheinlich denkst: Die 
Erhoehung findet erst nach dem modulo statt.
- %3 ? Du willst vier mal erhoehen, also %4 => moegliche Werte (0,1,2,3)
- Du schaltest den MUX ja waehrend einer Messung um, also kannst du das 
naechste Ergebnis nach jedem Umschalten nicht gebrauchen.
- Du brauchst die zusaetzliche Zaehlvariable gar nicht: Du kannst direkt 
den Wert aus ADCMUX verwenden.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jan M. wrote:

> - Du schaltest den MUX ja waehrend einer Messung um, also kannst du das
> naechste Ergebnis nach jedem Umschalten nicht gebrauchen.

Stimmt so nicht.  Die Umschaltung erfolgt halt nur nach dem Ende der
laufenden Messung.

von Jan M. (mueschel)


Lesenswert?

@Joerg:
Dann ist gut - ich war davon ausgegangen, dass die Aenderung sofort 
wirksam wird und nicht erst am Ende der Messung.

von Neuer (Gast)


Lesenswert?

Danke für die schnelle Antwort.
Da ich sowieso vier mal hintereinander den selben Pin Abfrage (daher die 
Zählvariable), sollte das mit dem Umschalten während der Messung nicht 
ins Gewicht fallen.
Das Modulo vor dem Inkrement ausgeführt wird wußte ich gar nicht.
Und typischer Denkfehler 3%3 ist 0, Danke X-D
Also müsste das so OK sein:
1
//Analog inputs
2
ISR(ADC_vect)
3
{
4
  if(uADCChange < 4)
5
  {
6
    //vier mal selben Pin einlesen
7
    uADCChange++;
8
   //jeweiliger Wert des ADC-Pins ins Input-Array
9
   uInputs[ADMUX]=ADCL;
10
  }
11
  else
12
  { 
13
    uADCChange = 0;
14
    //ADC Pins weiterschalten
15
    (ADMUX++)%4;
16
  }
17
}

Weitere Frage:
Da ich ja den RESET Pin nehmen muß, um vier analoge Eingänge zu haben, 
wie kann ich den Tiny13 dann neu programmieren ?
ISP funktioniert dann ja nicht ?
Im Datenblatt steht was über High-Voltage Serial Programming.
Kann ich einfach 12V an RESET geben und dann mit einem üblichen ISP 
programmieren, oder brauche ich da zusätzliche Hard-/Software ?
Ich verwende den aktuellen WinAVR und den billisgten Parport-ISP mit 
avrdude.
Danke nochmal.

von Johannes M. (johnny-m)


Lesenswert?

> uInputs[ADMUX]=ADCL;
So wird das nichts. Es muss auf jeden Fall auch (oder nur) ADCH gelesen 
werden. Das alleinige Lesen von ADCL ist nicht nur ziemlich sinnfrei, es 
sorgt auch dafür, dass sich der Wert in den Ergebnisregistern nicht mehr 
ändern kann. Wenn Du nur 8 Bit Auflösung brauchst, dann setze das 
ADLAR-Bit im ADMUX und lies nur das ADCH aus.

von Karl H. (kbuchegg)


Lesenswert?

Neuer wrote:

> Also müsste das so OK sein:
>
1
>     (ADMUX++)%4;
2
>

Nein. Ist nach C Standard immer noch undefiniert. Ganz abgesehen
davon, dass das Ergebnis der Modulo Division nirgends benutzt wird :-)

Was ist so schlimm an
1
    ADMUX = ( ADMUX + 1 ) % 4;

PS: Dir ist schon klar, dass dadurch alle anderen Bits in ADMUX,
so denn welche gesetzt sein sollten, auf 0 gesetzt werden?

von Neuer (Gast)


Lesenswert?

@ Johannes M.
Dann habe ich das mit dem ADLAR Bit falsch verstanden.
Stimmt, hätte mal auf der nächsten Seite weiterlesen sollen X-D

@ Karl heinz Buchegger
Schreibfaulheit X-D
Ja das alle anderen auf 0 gesetzt werden ist klar, deswegen ja ADCL 
statt ADCH.

OK kann ich dann sowas tun:
1
//Analog inputs
2
ISR(ADC_vect)
3
{
4
  if(uADCChange < 4)
5
  {
6
    //vier mal selben Pin einlesen
7
    uADCChange++;
8
   //jeweiliger Wert des ADC-Pins ins Input-Array
9
   uInputs[ADMUX]=ADCL;
10
   ADCL=ADCH;
11
  }
12
  else
13
  { 
14
    uADCChange = 0;
15
    //ADC Pins weiterschalten
16
    ADMUX = (ADMUX + 1)%4;
17
  }
18
}
Oder habe ich da jetzt wegen dem ADLAR unbrauchbare Werte ?
Sah vorhin noch so einfach aus ...
Danke.

von Johannes M. (johnny-m)


Lesenswert?

>  ADCL=ADCH;
ADCL und ADCH sind read-only... Macht aber auch ansonsten nicht viel 
Sinn... Was Du da genau machen willst, verstehe ich immer noch nicht 
ganz. Warum nur die letzten 8 Bit? Das macht keinen Sinn. Die laufen 
im Wertebereich insgesamt dreimal über.

von Neuer (Gast)


Lesenswert?

@  Johannes M.
Schade, hab' gerade gepeilt das ich das mit dem ADLAR total falsch 
verstanden hatte :-(
Also nochmal:
1
//Analog inputs
2
ISR(ADC_vect)
3
{
4
  if(uADCChange < 4)
5
  {
6
    //vier mal selben Pin einlesen
7
    uADCChange++;
8
   //jeweiliger Wert des ADC-Pins ins Input-Array
9
   uInputs[ADMUX]=ADCH;
10
  }
11
  else
12
  { 
13
    uADCChange = 0;
14
    //ADC Pins weiterschalten
15
    ADMUX = (ADMUX + 1)%4;
16
    ADMUX |= (1<<ADLAR);
17
  }
18
}
Stimmt's jetzt ?

Um nochmal auf meine Frage wegen dem RESET Pin zurück zu kommen:
Geht das wie ich meine mit einem normalen ISP, nur das dann RESET statt 
auf GND auf +12V gesetzt wird ?
Danke.

von Johannes M. (johnny-m)


Lesenswert?

Neuer wrote:
> @  Johannes M.
> Schade, hab' gerade gepeilt das ich das mit dem ADLAR total falsch
> verstanden hatte :-(
> Also nochmal:
> [...]
> Stimmt's jetzt ?
Sieht gut aus...

> Um nochmal auf meine Frage wegen dem RESET Pin zurück zu kommen:
> Geht das wie ich meine mit einem normalen ISP, nur das dann RESET statt
> auf GND auf +12V gesetzt wird ?
Nein, das geht nicht. Da muss eine bestimmte Sequenz eingehalten werden.

von Johannes M. (johnny-m)


Lesenswert?

> ADMUX = (ADMUX + 1)%4;
> ADMUX |= (1<<ADLAR);
Das kannste auch in einem Abwasch machen
1
ADMUX = ((ADMUX + 1)%4) | (1 << ADLAR);
Dann ist es nur ein Schreibzugriff auf ADMUX. Ich weiß nämlich nicht, 
was passiert, wenn das ADLAR während einer laufenden Wandlung gelöscht 
und dann wieder gesetzt wird...

von Neuer (Gast)


Lesenswert?

OK,
schade das ich nicht meinen ISP nehmen kann zum HV-Programmieren :-(
Nun zum nächsten Problem:
Mit den ausgelesenen Potis soll eine in Frequenz (0-250 Hz) und 
Tastverhältnis (0-100%) PWM ausgegeben werden.
Soweit ich das im Datenblatt verstanden habe wird die Frequenz durch die 
CPU-Frequenz/Prescaler vorgegeben und ich kann im PWM Modus über das 
OCRx Register das Tastverhältnis setzen.
Nur wie bekomme ich nun eine Frequenz von weniger als CLK/256 hin ?
Sorry wenn ich nerve aber ich stehe gerade ziemlich auf dem Schlauch.
Danke.

von Johannes M. (johnny-m)


Lesenswert?

Neuer wrote:
> Mit den ausgelesenen Potis soll eine in Frequenz (0-250 Hz) und
> Tastverhältnis (0-100%) PWM ausgegeben werden.
Also 0 Hz ist meist ein bisschen schwierig, v.a. bei einer PWM...

Du willst also ein PWM-Signal erzeugen und sowohl die Frequenz 
(Periodendauer) als auch das Tastverhältnis ändern können?

> Soweit ich das im Datenblatt verstanden habe wird die Frequenz durch die
> CPU-Frequenz/Prescaler vorgegeben und ich kann im PWM Modus über das
> OCRx Register das Tastverhältnis setzen.
CPU-Frequenz/Prescaler ist die Frequenz, mit der der Timer getaktet 
wird. Der wiederum läuft im Normalfall nach 256 Takten über, man kann im 
CTC- bzw. Fast-PWM-Modus aber über eines der beiden Compare-Register 
auch andere Werte vorgeben ==> Datenblatt...

> Nur wie bekomme ich nun eine Frequenz von weniger als CLK/256 hin ?
Im PWM-Modus 7 kannst Du über OCR0A die Periodendauer und über OCR0B das 
Tastverhältnis ändern. Allerdings muss das Tastverhältnis natürlich 
jeweils an die Periodendauer angepasst werden. Bei höheren Frequenzen 
geht die Auflösung in den Keller. Abgesehen davon kann man aber über den 
Prescaler auch andere Bereiche einstellen (der kann ja nicht nur durch 
256, sondern auch durch 64, durch 8 und durch 1 teilen)...

von Peter D. (peda)


Lesenswert?

Neuer wrote:
> Da ich ja den RESET Pin nehmen muß, um vier analoge Eingänge zu haben,
> wie kann ich den Tiny13 dann neu programmieren ?
> ISP funktioniert dann ja nicht ?

Stimmt.

Das ist dann ein Fall für den Bootloader-Man.


Peter

von Neuer (Gast)


Lesenswert?

@  Johannes M.
Sorry da ist die 1 nicht mitgekommen 10-250 Hz :-)
Und 0 geht auch, Timer abschalten fällt mir so spontan ein (gut ist dann 
keine PWM mehr).

Also ich lese jetzt schon länger die Timer Beschreibungen durch, OCRB 
wird da irgendwie nie so richtig erklärt.
Ich denke das ist identisch zu OCRA ?

Wenn ich Dich jetzt richtig verstanden habe kann ich durch die beiden 
Register den Timer so beeinflussen, das er einmal wenn OCRA erreicht 
wird einen Output mit dem eingestellten Wert macht (Tastverhältnis) und 
wenn OCRB erreicht wird der Timer von vorne anfängt und ich dadurch die 
Frequenz ändern kann und umgekehrt ?
OK, dann muß ich je nach dem wie groß der Wert von OCRB ist OCRA 
proportional daran anpassen, damit das Tastverhältnis identisch bleibt 
wenn sich die PWM Frequenz ändert.

Ich denke mal das ich jetzt erstmal ein Bier trinke und mein Projekt 
Projekt sein lasse.
Mal sehen ob ich's morgen kapiere ;-)
Danke für die Hilfestellung !

von Johannes M. (johnny-m)


Lesenswert?

Neuer wrote:
> @  Johannes M.
> Sorry da ist die 1 nicht mitgekommen 10-250 Hz :-)
> Und 0 geht auch, Timer abschalten fällt mir so spontan ein (gut ist dann
> keine PWM mehr).
Genau das ist das Problem...

> Also ich lese jetzt schon länger die Timer Beschreibungen durch, OCRB
> wird da irgendwie nie so richtig erklärt.
> Ich denke das ist identisch zu OCRA ?
Der µC hat zwei Compare-Einheiten, die in ihrer Grundfunktion identisch 
sind und deshalb nicht gesondert erwähnt werden (in den 
Registerbeschreibungen im Datenblatt steht dann OCR0x, wobei x für A 
oder B stehen kann). Bei einer PWM mit fester Frequenz (8-Bit-PWM) kann 
man mit beiden Compare-Einheiten zwei PWM-Kanäle mit der selben 
Frequenz, aber unabhängig voneinander einstellbaren Tastverhältnissen 
implementieren.

Wenn man die Frequenz verändern will, wird eins der beiden 
Compare-Register "zweckentfremdet" und nimmt den Überlaufwert auf 
(ähnlich wie bei CTC, nur dass im PWM-Betrieb noch ein paar zusätzliche 
Dinge automatisch gemacht werden, wie z.B. Synchronisation der 
Register). Das andere Compare-Register kann dann den Wert für das 
Tastverhältnis aufnehmen und am dazugehörigen Ausgangspin steht dann das 
PWM-Signal zur Verfügung.

> Wenn ich Dich jetzt richtig verstanden habe kann ich durch die beiden
> Register den Timer so beeinflussen, das er einmal wenn OCRA erreicht
> wird einen Output mit dem eingestellten Wert macht (Tastverhältnis) und
> wenn OCRB erreicht wird der Timer von vorne anfängt und ich dadurch die
> Frequenz ändern kann und umgekehrt ?
Naja, so ungefähr...

> OK, dann muß ich je nach dem wie groß der Wert von OCRB ist OCRA
> proportional daran anpassen, damit das Tastverhältnis identisch bleibt
> wenn sich die PWM Frequenz ändert.
Ja. Und das geht nicht stufenlos (OK, so richtig stufenlos geht es 
sowieso nicht, weil digital und sowieso nur 8 Bit).

> Ich denke mal das ich jetzt erstmal ein Bier trinke und mein Projekt
> Projekt sein lasse.
Bier ist meist ne gute Idee...

> Mal sehen ob ich's morgen kapiere ;-)
Wird schon...

von Matthias L. (Gast)


Lesenswert?

Leute...

Ihr wollt von 0 bis 3 zählen!

Zu dessen Begrenzung nimmt man doch kein Modulo!
1
ADMUX = (ADMUX + 1) & 0x03;

von Johannes M. (johnny-m)


Lesenswert?

Matthias Lipinsky wrote:
> Leute...
>
> Ihr wollt von 0 bis 3 zählen!
>
> Zu dessen Begrenzung nimmt man doch kein Modulo!
>
>
1
> ADMUX = (ADMUX + 1) & 0x03;
2
>
Was, lieber Matthias, glaubst Du, was der Compiler wohl aus dem "%4" 
macht? OK, über die Schreibweise an sich kann man streiten, aber der 
generierte Code dürfte (müsste) identisch sein. Einen Compiler, der es 
nicht packt, aus "x % 2^n" ein "x & 2^n-1" zu machen, musste mir noch 
zeigen...

von Neuer (Gast)


Lesenswert?

OK,
also jetzt denke ich das mit dem Modus 7 verstanden zu haben.
Ich setze Timer0 mit Prescaler 256, dann schreibe ich 16 ins OCR0A und 
bekomme statt den 4,6 kHz 292 Hz als PWM Grundfrequenz und bei 256 ca. 
18 Hz.
Der Tiny läuft mit 1,2 MHz internem Takt.
Dann trage ich in OCR1A das Tastverhältnis ein, welches ich vorher durch 
den OCR0A Wert teile und behalte so bei einer Frequenzänderung das 
Tastverhältnis.
Wäre dann so etwas:
1
void setFreq(unsigned char Freq)
2
{
3
 OCR0A=Freq;
4
}
5
6
void setTast(unsigned char Tast)
7
{
8
 OCR1A = Tast >> OCR0A;
9
}
Welcher PWM Pin wird dann genutzt, OCR1A ?
Oder hab ich's wieder nicht richtig verstanden ?

@ Peter Dannegger:
Was ist denn der "Bootloader-Man" ?

Danke.

von Johannes M. (johnny-m)


Lesenswert?

Neuer wrote:
> OK,
> also jetzt denke ich das mit dem Modus 7 verstanden zu haben.
> Ich setze Timer0 mit Prescaler 256, dann schreibe ich 16 ins OCR0A und
> bekomme statt den 4,6 kHz 292 Hz als PWM Grundfrequenz und bei 256 ca.
> 18 Hz.
Fast

> Der Tiny läuft mit 1,2 MHz internem Takt.
> Dann trage ich in OCR1A das Tastverhältnis ein, welches ich vorher durch
> den OCR0A Wert teile und behalte so bei einer Frequenzänderung das
> Tastverhältnis.
Der Tiny13 hat nur einen Timer, und das ist Timer 0. Dementsprechend 
gibt es kein OCR1A. Die beiden Compare-Register heißen OCR0A und OCR0B, 
wie oben schon mal erwähnt. OCR0A definiert die Frequenz, OCR0B das 
Tastverhältnis, das PWM-Signal wird an OC0B ausgegeben.

> Wäre dann so etwas:
> [...]
> Welcher PWM Pin wird dann genutzt, OCR1A ?
> Oder hab ich's wieder nicht richtig verstanden ?
Siehe oben...

Problem ist bei der genannten Konfiguration mit OCR0A = 16, dass das 
Tastverhältnis auch nur noch in 16 Stufen einstellbar ist.

Ich sehe grad, dass in der Timer-Betriebsarten-Tabelle (Tabelle 32 im 
Datenblatt) das Compare-Register irreführend mit OCRA angegeben ist. Es 
muss OCR0A heißen.

EDIT:
Übrigens, damit das Signal auch ausgegeben wird, müssen natürlich die 
COM-Bits im TCCR0A entsprechend gesetzt werden.

Was mir auch noch aufgefallen ist: Im Datenblatt wird für Fast-PWM mit 
OCR0A als TOP keine explizite Formel für die Berechnung der Frequenz 
anhand des Compare-Wertes angegeben. Die lautet

von Neuer (Gast)


Lesenswert?

OK,
nun hab ich's mal fertig geschrieben und das makefile_template händisch 
geändert.
Nur kommt dann:
1
> "make.exe" all
2
makefile:419: *** missing separator.  Stop.
3
4
> Process Exit Code: 2
5
> Time Taken: 00:00
Geändert habe ich nur das Target und die Frequenz:
1
# MCU name
2
MCU = attiny13
3
4
5
# Processor frequency.
6
#     This will define a symbol, F_CPU, in all source code files equal to the
7
#     processor frequency. You can then use this symbol in your source code to
8
#     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
9
#     automatically to create a 32-bit value in your source code.
10
#     Typical values are:
11
#         F_CPU =  1000000
12
#         F_CPU =  1843200
13
#         F_CPU =  2000000
14
#         F_CPU =  3686400
15
#         F_CPU =  4000000
16
#         F_CPU =  7372800
17
#         F_CPU =  8000000
18
#         F_CPU = 11059200
19
#         F_CPU = 14745600
20
#         F_CPU = 16000000
21
#         F_CPU = 18432000
22
#         F_CPU = 20000000
23
F_CPU = 1200000
Angelegt ist das makefile im gleichen Verzeichnis wie der Source Code.
Zeile 419 ist "Eye Candy" ????
1
# Eye candy.
2
# AVR Studio 3.x does not check make's exit code but relies on
3
# the following magic strings to be generated by the compile job.
4
begin:
5
   @echo
6
   @echo $(MSG_BEGIN)
7
8
end:
9
   @echo $(MSG_END)
10
   @echo
Oder muß ich noch TCL/TK installieren und das ganze mit dem Script im 
mfile Verzeichnis machen ?
Danke.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Die Kommandos im Makefile müssen mit einem <TAB> (hard tab!) eingerückt
werden, nicht irgendwie mit Leerzeichen.

Historischer Ballast...

von Neuer (Gast)


Lesenswert?

Danke das war's ;)
Noch eine Frage, ist das normal das das .hex größer ist als was gcc 
anzeigt ?
1
Size after:
2
AVR Memory Usage
3
----------------
4
Device: attiny13
5
6
Program:     608 bytes (59.4% Full)
7
(.text + .data + .bootloader)
8
9
Data:          9 bytes (14.1% Full)
10
(.data + .bss + .noinit)
11
12
13
14
-------- end --------
Das .hex:
1
17.04.2008  17:32             1.723 main.hex
2
               1 File(s)          1.723 bytes
3
               0 Dir(s)   5.460.754.432 bytes free

von Karl H. (kbuchegg)


Lesenswert?

Neuer wrote:
> Danke das war's ;)
> Noch eine Frage, ist das normal das das .hex größer ist als was gcc
> anzeigt ?

Ja das ist normal.
gcc rechnet die Programmgröße in Bytes. Im HexFile wird aber
jedes Byte durch 2 Zeichen dargestellt (Ein Hex File ist ja
auch nur ein Textfile. Wenn du willst kannst du ja mal reinschauen).
Dazu dann noch ein paar Steuerinformationen, wie Startadresse,
Checksumme und dgl. und schon ist das HexFile rund 3 mal so groß
wie die eigentliche 'Nutzinformation' gemessen in Byte.

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.