www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ARM7: ADC Wandler 16 Bit ?


Autor: Christian J. (elektroniker1968)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

bei der Spielerei mit dem AD Wandler des LPC2138 fiel mir was 
Sonderbares auf: Das 32 Bit Register AD0DR0 (=Kanal von Pin AD0.0) 
enthält ganz oben 11 und muss daher maskiert werden und weiterhin spuckt 
er 16 Bit Wert raus! Dabei ist die Auflösung doch nur 10 Bit, also bis 
1023.

Was weiterhin auffiel ist, dass die Taktfrequenz von 4,5 Mhz bei 11 
Cycles Sampling viel zu hoch ist, die Werte stimmen nicht. Erst wenn ich 
wesentlich weiter runter gehe, also den APB Takt / 5 teile (~ 2,7 Mhz) 
stimmen die Zahle und vor allem "flattern" sie nicht mehr. Dreht man 
höher hat man immer wieder Ausreisser und der maximale Wert wird nicht 
erreicht.

Hat da schonmal jemand Erfahrungen gesammelt?

Hier die Configuration:

unsigned int ADC_Init(unsigned int channel_mask)
{
    // Pin konfigurieren
    PINSEL0 |= (0x01<<22);
    PINSEL1 |= (0x01<<22);

    // Kanal 0 v AD0, CLKDIV = 1:5; Burst Mode, CLKS = 11, PDN = ON
    AD0CR = (channel_mask) | (4<<AD0CR_CLKDIV_BIT) | (0<<AD0CR_CLKS_BIT) | (1<<AD0CR_BURST_BIT)
            | (1<<AD0CR_PDN_BIT);
   

}

Autor: Michael G. (linuxgeek) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was haeltst Du davon, mal ins Datenblatt zu schauen, da steht das mit 
Sicherheit alles drinnen.

Autor: Christian J. (elektroniker1968)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das habe ich natürlich vorher studiert, BEVOR ich diese Frage hier 
stellte. Und da steht nichts drin, da stehen auch keine Sample Zeiten 
drin, da steht eigentlich nichts drin was da rein gehört, denn ein AD 
Wandler ist etwas komplizierter, der braucht Sample & Hold Zeiten. Das 
Datenblatt des ARM7 von Phlips ist wirklich "arm".

Und wenn man den GCC verwendet, dann wundert man sich auch, warum eine
 for (i=0;i<10000000;i++)

Schleife als Zeitsteuerung plötzlich rasend schnell durchlaufen wird, 
egal welcher Wert, wenn man den Optimization Level auf 3 setzt.  Ein 
Blick in den Assembercode: Die Schleife ist weg, existiert nicht mehr. 
Der optimiert dann nämlich einfach die ganze Schleife weg und setzt i 
nur noch auf den grössten Wert, was ich schlichtweg krank finde.


Gruss,
Christian

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

was erwartest Du?

Du sagst dem Compiler, er soll optimieren auf minimale Laufzeit oder 
Codegröße, er macht genau das und Du maulst...
Ein solche Schleife macht nichts, außer Platz im Code kosten und unnütz 
Rechenzeit zu verbraten, das Ergebnis ist eindeutig absehbar, also setzt 
er es gleich.

Solche Busy-Loops waren schon zu meinen Z80 oder 6502-ASm-Zeiten sehr 
unbeliebt, weil in dieser Zeit nichts anderes gemacht werden konnte und 
vor allem keine User-Aktionen beachtet werden konnten.

Du kannst natürlich auch dort Deinen Willen durchsetzen und den Compiler 
austricksen, das wird mindestens 1x die Woche hier erklärt.

Gruß aus Berlin
Michael

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Nachtrag:

Ich hab mal interessehalber in das User-Manual geschaut,
logisch da evtl. Bits gesetzt, Bit31 DONE, Bit30 OVERRUN, 26:24 CHN 
können/müssen natürlich gesetzt sein.

Außerdem sind bei mir Bit15:6 sehr wohl 10 Bit und keine 16 für das 
Ergebnis V/Vref.

Den Rest habe ich mir nicht angeschaut, wird aber wohl auch 
drinstehen...

Gruß aus Berlin
Michael

Autor: Michael G. (linuxgeek) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian J. wrote:
> Und wenn man den GCC verwendet, dann wundert man sich auch, warum eine
>
>
 for (i=0;i<10000000;i++)
>
> Schleife als Zeitsteuerung plötzlich rasend schnell durchlaufen wird,
> egal welcher Wert, wenn man den Optimization Level auf 3 setzt.

Vollkommen klar, da es ein nutzloses Konstrukt ist wenn der 
Schleifenkoerper leer ist.

Autor: let (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hihi, womit wir wieder beim ominösen 'volatile' wären ;)

Aber den Punkt mit dem ADC kann ich nicht nachvollziehen. Wie kommst
du auf 16Bit?

Autor: Christian J. (elektroniker1968)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

der User oben hat das falsch verstanden, er meint das AD0GDR Register, 
wo unter 15:6 das RESULT steht. Das Ergebnis einer Operation wird in 
zwei Registern abgelegt, in jenem ist es zwischen anderen Bits 
eingemauert.

Es gibt aber noch die ADxRx Register, die sind übrigens nicht im LPC 
Headerfile mit drin (warum weiss ich nicht). Dort stehen die Ergebnisse 
jedes ADC einzeln drin. Im Manual von Philips ist das auf der Seite 212, 
ADDR0 bis ADDR7, jeweils für beide Wandler.

Den ADC einstellen reduziert sich auf das Bitweise Setzen des ADCFG 
Registers im Burst Mode, dann rennt das Viech los und scannt fortlaufend 
in einer Schleife alle gesetzten Pins, die im PINSEL als AD deklariert 
wurden. Man muss nur noch aus den besagten Registern auslesen und 
braucht sich um nichts weiter zu kümmern. Der Burst Mode kostet ca 2mA 
mehr an Strom, die die Platine zieht.

Hier der Code für einen Pin am AD0.0
// Das AD Poti an AD0.0 auslesen
void AD0_0_Init(void)
{
    unsigned int apb,teiler;

    // Pin AD0.0 konfigurieren
    PINSEL0 |= (0x01<<22);
    PINSEL1 |= (0x01<<22);
    
    // richtige APB Frequenz ermitteln < als 4.5 Mhz
    apb = PLL_GetAPBClock();
    teiler = 1;
    while ((apb/teiler > 4500000) && (teiler<10))
      teiler++;

    // Kanal 0 v AD0, CLKDIV = 4; Burst Mode, CLKS = 11, PDN = ON
    AD0CR = 1 | ((teiler-1)<<AD0CR_CLKDIV_BIT) | (0<<AD0CR_CLKS_BIT) | (1<<AD0CR_BURST_BIT)
            | (1<<AD0CR_PDN_BIT);
}

wird ausgelesen mit
debug_printf("Kanal 0 = %u \n",(AD0DR0 & 0xffff)>>6);

Dort, im AD0DR0 (32 Bit) stehen 16 Bit Werte drin (obere 2 Bits sind 
auch gesetzt). Nachdem ich damit etwas gespielt habe, habe ich bemerkt, 
dass die letzten 4 Bit für die Tonne sind, die floaten nur herum. 
Maximal 12 Bit aber auch nur so eben. Das Floaten wird weniger, wenn man 
den uC ohne PLL laufen lässt und die APB Taktrate weit runter dreht, 
klar, EMV lässt grüssen, 3.3V / 4096 sind nur noch 800uV.

Zum GCC Compiler: Plötzlich läuft mein "Flash Release", vorher kamen 
immer seltsame Fehlermeldungen des Assemblers, die undeutbar waren. Und 
mit Optimization Level 3 reduziert sich der Code von 3400 Bytes auf 1600 
Bytes runter. Das Wörtchen volatile vorsichtshalber mal etwas öfter 
verwendet und die Funktionen auf static gesetzt.

Insgesamt muss ich eines sagen: Der ARM mit Crossworks hat einen 
gewissen Suchtfaktor entwickelt :-) Mit dem bunten Editor macht es 
einfach Spass sauberen Code zu schreiben.

Gruss,
Christian

Autor: let (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nun, ich habe gerade kein aktuelles Datenblatt für den 2138.
Beim 2368 stehen die Daten an Position 15:6 in den Datenregistern.

Die separaten Register von die ADC Kanäle sind soweit ich weiß
erst mit dem 2138/01 eingeführt worden. Wenn du also so einen
hast, kannst du auch die FIO-Register der GPIO benutzen.
(F)IOPIN kann dann auch beschrieben werden.

Ich nehme an das du ein Headerfile vom 2138 verwendest. Die
zusätzlichen ADC- und FIO-Register fehlen dort.
FIO muß vor Verwendung aber aktiviert werden. Beim 2368 geht das
mit 'SCS |= GPIOM;'

>Mit dem bunten Editor macht es
>einfach Spass sauberen Code zu schreiben.
Du kennst also Eclipse noch nicht? Dann aber ran ;)

Autor: let (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: FIO enable beim 2138/01 mit
SCS = (1<<0) | (1<<1);

Bin gerade zu faul um nachzusehen warum das anders ist.

Autor: Christian J. (elektroniker1968)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm....

Was seltsam ist, dass ich einen Fehler "Warning: Writeback of base 
register ist UNPREDICTABLE" bekomme, wenn ich die LED Funktion aus dem 
Timer IRQ heraus aufrufe... komisch. Muss man das was beachten, wenn man 
aus einem ISR eine andere Routine aufruft? Anm: Die LED Routine wird 
nirgendwo sonst aufgerufen, damit es keine Rekursion gibt.
// LED schalten (1=ein, 0=aus)
void LED_ctrl(char led_rot, char led_grn)
{
  // Anm: FIOSET ist R/W, FIOCLR ist RO 

  // Rote LED
  if (led_rot) // Ein
    FIO0CLR = LED_ROT_MASK; 
  else         // Aus
    FIO0SET = LED_ROT_MASK;

  // Grüne LED...
  if (led_grn) // Ein
    FIO0CLR = LED_GRN_MASK; 
  else        // Aus
    FIO0SET = LED_GRN_MASK;
}

Der IRQ
void __attribute__ ((interrupt("IRQ"))) timer0ISR(void)
{
  static unsigned char counter;
  
  if (++counter %2 ==0)
      LED_ctrl(1,0);
  else
    LED_ctrl(0,1);

   // IR Flag des M0 Match Registers löschen (=setzen)
   T0IR |= T0IR_MR0_MASK;
 
  VICVectAddr = 0;       // Acknowledge Interrupt
}

Klicke ich auf die Fehlermeldung zeigt er mir die Stelle im Asm Code an:
timer0ISR:
.LFB12:
  .loc 1 234 0
  @ Interrupt Service Routine.
  @ args = 0, pretend = 0, frame = 0
  @ frame_needed = 0, uses_anonymous_args = 0
  sub  lr, lr, #4
  stmfd  sp!, {r0, r1, r2, r3, ip, lr}
.LCFI4:
  .loc 1 237 0
  ldr  r3, .L64
  ldrb  r2, [r3, #0]  @ zero_extendqisi2
  .loc 1 238 0
  mov  r0, #1
  .loc 1 237 0
  add  r2, r2, r0
  ands  r1, r2, r0
  strb  r2, [r3, #0]
  .loc 1 240 0
  movne  r0, #0
  movne  r1, #1
  bl  LED_ctrl
  .loc 1 243 0
  ldr  r2, .L64+4
  ldr  r3, [r2, #0]
  orr  r3, r3, #1
  str  r3, [r2, #0]
  .loc 1 245 0
  mov  r2, #0
  mvn  r3, #0
  str  r2, [r3, #-4047]
  .loc 1 246 0
  ldmfd  sp!, {r0, r1, r2, r3, ip, lr}^
.L65:
  .align  2

ldmfd  sp!, {r0, r1, r2, r3, ip, lr}^

ist rot unterschlängelt.

Autor: Robert Teufel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein paar Worte zum ADC.

Natuerlich ist es ein 10-bit Wandler und man kann den verschieden 
"anlehnen", also das LSB auf die Stelle "0" oder das MSB auf die 
hoechste Stelle des 16-bit (halb)Wortes.
Es sollen ja auch noch 12-bit Wandler nachkommen, da laesst sich 
Kompatibilitaet nur erreichen mit MSB aligned. Deshalb wurde das so 
gemacht wie es ist. Natuerlich kannst Du die WErte auch als 16-bit Werte 
verwenden, allerdings werden sich die 6 LSB nie veraendern.

Zum Thema Geschwindigket des ADC, das haengt natuerlich auch von der 
Impedanz Deiner Quelle ab. Bei einer niederohmigen Quelle laesst sich 
der ADC sehr wohl bei der angegebenen Geschwindigkeit betrieben.

Robert

Autor: Christian J. (elektroniker1968)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

nur als Info: Es ändern sich alle 16 Bits aber die letzten sind nicht 
brauchbar.

Nochmal die Frage: Muss man etwas beachten wenn man aus einer ISR eine 
andere Routine aufruft? Ich krieg da nämlich eine Fehlermeldung, erst 
wenn ich die Inhalte der aufgerufenen Routine in die ISR kopiere klappt 
es.

Autor: let (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wüßte nicht das es da etwas zu beachten gäbe. Aber der GCC hat
ein Problem mit der Codeerzeugung für ISR-Routinen. Zumindest im
Thumb-Mode funktioniert das nicht und Dateien die einen
ISR-Handler enthalten müssen im ARM-Mode übersetzt werden.

Es gibt zwei Alternativen dazu:
1. Man deklariert die Funktion als 'naked' und kümmert sich
selbst per ISR_BEGIN/ISR_END Makros (inline-ASM) um die Register
und Rücksprungadressen (Holzhammer-Methode).

2. Man baut einen ISR-Handler in den Startup-Code ein der den
Handler in C als normale Funktion aufruft. So machen es einige
Beispiele in der Sammlung von Martin Thomas (die ich übernommen
habe).

Soweit ich weiß hat Rowley auch Makros für ISR-Handler.

Autor: Christian J. (elektroniker1968)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

also die Holzhammer Methode vergessen wir mal lieber :-) Bei einem PIC 
ja aber nicht bei einer derart komplexen CPU. Ich habe mal gesucht nach 
dem Umsschalter für ARM und THUMB Code aber leider nichts gefunden. 
Weiss nicht ob das ein #pragma ist oder sowas.

Vielleicht wäre eine Seite mit Codebeispielen ganz gut, bisher wurde ich 
da nicht fündig. Nicht die grossen Projekte sonder die kleinen, die 
bestimmte Funktionen demonstrieren.

Was sicherlich noch interessant sein dürfte wären Möglichkeiten Daten im 
Flash abzulegen, etwa Messwerte. libmem bietet sich hier an. Aber ich 
verlasse das Thema des Threads. Die Wissensneugier ist gross aber es 
fehlen einfach die Beispiele und die Doku kann man insgesamt vergessen.

Ach ja... ein gutes Beispiel, wie man eine Library erzeugt wäre noch 
toll, damit ich meine mühsam Universalfunktionen später einmal mit 
einbinden kann, so wie unter Visual Basic fertige .dll eingebunden 
werden können, wenn man deren Funktionssatz kennt. Bisher habe im im 
Linker von Rowley dazu noch keine Möglichkeit gefunden den Object Code 
einzubinden.

Gruss,
Christian

Autor: Robert Teufel (robertteufel)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Codebeispiele (nicht fuer GCC aber portierbar)
Direkt von der NXP Webseite:
http://www.standardics.nxp.com/support/documents/m...
Das ist fuer Keil geschrieben aber immerhin Beispielcode.

Robert

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.