Forum: Mikrocontroller und Digitale Elektronik LPC1114 - ADC mit Interrupt


von Skyper (Gast)


Angehängte Dateien:

Lesenswert?

Moin,

ich habe angefangen mich mit den ARM LPCs von NXP zu beschäftigen. Für 
erste Versuche habe ich ein LPCXpresso Board mit dem LPC 1114 hier auf 
dem Schreibtisch liegen. (IDE MCUXpresso mit CMSIS_CORE)

Das Board hat eine LED (PIO0_7), ich habe eine weitere LED an PIO2_4 
angeschlossen und an PIO0_11 (AD0) ein 10k Poti als Spannungsteiler zum 
testen.

Ein kurzes Testprogramm funktioniert, es fragt den ADC über Polling ab, 
in der While Schleife wird die ADC Messung gestartet, dann wird gewartet 
bis das "done" Bit gesetzt ist und das ADC Register ausgelesen, maskiert 
und geschiftet und dann geprüft ob der Wert kleiner als 512 ist und 
davon abhängig die LED an PIO0_7 an- oder ausgeschaltet.

Als Basis diente mir die "Getting Started Seite von Digikey"
https://www.digikey.com/eewiki/display/microcontroller/Getting+Started+with+NXP%27s+LPC11XX+Cortex-M0+ARM+Microcontrollers

Mein zweiter Versuch den ADC über den Interrupt abzufragen schlägt 
leider fehl. Das untere Programm wird fehlerfrei kompiliert, führt den 
Interrupt aber nicht aus...
Setze ich das auskommentierte Bit "Start Conversion" wird mein 
"Hauptprogramm - die Schleife" nicht mehr ausgeführt....


Wer hat Erfahrung mit den ARM LPCs und kann mir weiterhelfen?

Gruß
Boris

1
//ADC Polling
2
3
#include "LPC11xx.h"
4
#include <cr_section_macros.h>
5
6
int main(void)
7
{
8
9
  SystemCoreClockUpdate();
10
  LPC_SYSCON->SYSAHBCLKCTRL   |= (1<<6);     //enable clock GPIO (sec 3.5.14)
11
12
  LPC_IOCON->PIO0_7           &= ~(0x10);    //NOT REQUIRED, turn off pull up (sec 7.4.19)
13
  LPC_GPIO0->DIR              |= (1<<7);     //set pin direction to output (sec 12.3.2)
14
15
    //SET UP THE ADC
16
    LPC_SYSCON->PDRUNCFG        &= ~(0x1<<4);  //power the ADC (sec. 3.5.35)
17
    LPC_SYSCON->SYSAHBCLKCTRL   |= (1<<13);    //enable clock for ADC (sec. 3.5.14)
18
    LPC_IOCON->R_PIO0_11        &= 0xFFFFFF78; //clear FUNC field for pin 32, set to analog input (sec. 7.4.28)
19
    LPC_IOCON->R_PIO0_11        |= (1<<1);     //set to ADC mode for pin 32 (sec. 7.4.28)
20
    LPC_ADC->CR                 = 0x0B01;      //select ADC channel AD0 (pin 32), set up clock (sec 25.5.1)
21
22
    volatile unsigned int i = 0;
23
    while(1)
24
    {
25
      LPC_ADC->CR     |= (1<<24);      //start conversion by setting "Start Conversion Now" bit (sec. 25.5.1)
26
      while((LPC_ADC->DR[0] < 0x7FFFFFFF));                  //wait for "done" bit to be set (sec. 25.5.4)
27
28
      uint32_t ADCValue = (LPC_ADC->DR[0] & 0xFFC0) >> 6;
29
30
        if(ADCValue < 512)     //check state of pin PIO3_2  (sec 12.3.1)
31
          LPC_GPIO0->DATA  &= ~(1<<7);   //set pin low (sec 12.3.1)
32
        else
33
          LPC_GPIO0->DATA  |= (1<<7);    //set pin high (sec 12.3.1)
34
35
        for(i=0; i < 0xFFFFF; ++i);                            //simple delay to make scope shots easier to view
36
    }
37
    return 0 ;
38
}
1
//ADC Interrupt
2
#include "LPC11xx.h"
3
#include <cr_section_macros.h>
4
void ADC_IRQHandler(void);
5
6
7
int main(void)
8
{
9
10
  SystemCoreClockUpdate();
11
  LPC_SYSCON->SYSAHBCLKCTRL   |= (1<<6);     //enable clock GPIO (sec 3.5.14)
12
13
  LPC_IOCON->PIO0_7           &= ~(0x10);    //NOT REQUIRED, turn off pull up (sec 7.4.19)
14
  LPC_IOCON->PIO2_4           &= ~(0x10);    //NOT REQUIRED, turn off pull up (sec 7.4.19)
15
  LPC_GPIO0->DIR              |= (1<<7);     //set pin direction to output (sec 12.3.2)
16
  LPC_GPIO2->DIR              |= (1<<4);     //set pin direction to output (sec 12.3.2)
17
18
    //SET UP THE ADC
19
    LPC_SYSCON->PDRUNCFG        &= ~(0x1<<4);  //power the ADC (sec. 3.5.35)
20
    LPC_SYSCON->SYSAHBCLKCTRL   |= (1<<13);    //enable clock for ADC (sec. 3.5.14)
21
    LPC_IOCON->R_PIO0_11        &= 0xFFFFFF78; //clear FUNC field for pin 32, set to analog input (sec. 7.4.28)
22
    LPC_IOCON->R_PIO0_11        |= (1<<1);     //set to ADC mode for pin 32 (sec. 7.4.28)
23
    LPC_ADC->CR                 = 0x0B01;      //select ADC channel AD0 (pin 32), set up clock (sec 25.5.1)
24
25
26
    LPC_ADC->INTEN = 0x1FF;        /* Enable all interrupts */
27
    NVIC_EnableIRQ(ADC_IRQn);
28
    //LPC_ADC->CR     |= (1<<24);      //start conversion by setting "Start Conversion Now" bit (sec. 25.5.1)
29
30
31
    volatile unsigned int i = 0;
32
    while(1)
33
    {
34
      LPC_GPIO2->DATA  &= ~(1<<4);   //set pin low (sec 12.3.1)
35
      for(i=0; i < 0xFFFFF; ++i);                            //simple delay to make scope shots easier to view
36
37
      LPC_GPIO2->DATA  |= (1<<4);    //set pin high (sec 12.3.1)
38
      for(i=0; i < 0xFFFFF; ++i);                            //simple delay to make scope shots easier to view
39
    }
40
    return 0 ;
41
}
42
43
44
45
void ADC_IRQHandler (void)
46
{
47
  uint32_t ADCValue = (LPC_ADC->DR[0] & 0xFFC0) >> 6;
48
49
    if(ADCValue < 512)     //check state of pin PIO3_2  (sec 12.3.1)
50
      LPC_GPIO0->DATA  &= ~(1<<7);   //set pin low (sec 12.3.1)
51
    else
52
      LPC_GPIO0->DATA  |= (1<<7);    //set pin high (sec 12.3.1)
53
54
  //LPC_ADC->CR &= 0xF8FFFFFF;   /* stop ADC now */
55
}

von A. F. (artur-f) Benutzerseite


Lesenswert?

Skyper schrieb:
> Wer hat Erfahrung mit den ARM LPCs und kann mir weiterhelfen?

Ich habe die Erfahrung, aber eher mit dem Polling-Mode.
Was mir aber auffällt, in der ISR musst du ADC IR löschen und eine 
Prüfung für den Überlauf gehört vorsichtshalber auch rein. Und überprüfe 
auch noch in der Init die bits vom LPC_ADC->CR register, da stellt man 
ein ob der ADC im Polling oder IRQ gelesen wird.

von Skyper (Gast)


Lesenswert?

A. F. schrieb:
> Ich habe die Erfahrung, aber eher mit dem Polling-Mode.
> Was mir aber auffällt, in der ISR musst du ADC IR löschen und eine
> Prüfung für den Überlauf gehört vorsichtshalber auch rein. Und überprüfe
> auch noch in der Init die bits vom LPC_ADC->CR register, da stellt man
> ein ob der ADC im Polling oder IRQ gelesen wird.

Danke A.F. für die Hinweise:

Mein erster Fehler war, das ich den Burst-Modus nicht im CR Register 
gesetzt hatte.

Zweiter Fehler, die ISR nicht mit " extern "C" " zu Kennzeichnen.

Das IR Register (wie beim Timer) gibt es beim ADC scheinbar nicht, es 
reicht das auslesen aus: The result register for an A/D channel that is 
generating an interrupt must be read in order to clear the corresponding 
DONE flag.

Das mit dem "OVERRUN" Bit verstehe ich aber nicht so richtig:

This bit is 1 in burst mode if the results of one or more conversions
was (were) lost and overwritten before the conversion that produced
the result in the V_VREF bits.

Ich dachte, das Bit zeigt nur an, das die vorherigen Kovertierungen 
nicht gelesen / verworfen wurden?

von Skyper (Gast)


Lesenswert?

Korrigierte und lauffähige Version:
1
#include "LPC11xx.h"
2
#include <cr_section_macros.h>
3
4
int main(void)
5
{
6
7
  SystemCoreClockUpdate();
8
  LPC_SYSCON->SYSAHBCLKCTRL   |= (1<<6);     //enable clock GPIO (sec 3.5.14)
9
10
  LPC_IOCON->PIO0_7           &= ~(0x10);    //NOT REQUIRED, turn off pull up (sec 7.4.19)
11
  LPC_IOCON->PIO2_4           &= ~(0x10);    //NOT REQUIRED, turn off pull up (sec 7.4.19)
12
  LPC_GPIO0->DIR              |= (1<<7);     //set pin direction to output (sec 12.3.2)
13
  LPC_GPIO2->DIR              |= (1<<4);     //set pin direction to output (sec 12.3.2)
14
15
    //SET UP THE ADC
16
    LPC_SYSCON->PDRUNCFG        &= ~(0x1<<4);  //power the ADC (sec. 3.5.35)
17
    LPC_SYSCON->SYSAHBCLKCTRL   |= (1<<13);    //enable clock for ADC (sec. 3.5.14)
18
    LPC_IOCON->R_PIO0_11        &= 0xFFFFFF78; //set to analog in(sec. 7.4.28)
19
    LPC_IOCON->R_PIO0_11        |= (1<<1);     //set to ADC mode for pin 32 (sec. 7.4.28)
20
21
    LPC_ADC->CR |= (0x01);      // Select Channel AD0
22
    LPC_ADC->CR |= (0x01 << 8);    // Set CLK Divider
23
    LPC_ADC->CR |= (0x01 << 9);    // Set CLK Divider
24
    LPC_ADC->CR |= (0x01 << 11);  // Set CLK Divider
25
    LPC_ADC->CR |= (0x01 << 16);  // Set burst mode and start A/D convert
26
27
    LPC_ADC->INTEN = 0x001;           //Enable interrupts for Channel AD0
28
    NVIC_EnableIRQ(ADC_IRQn);
29
30
31
    volatile unsigned int i = 0;
32
    while(1)
33
    {
34
      LPC_GPIO2->DATA  &= ~(1<<4);   //set pin low (sec 12.3.1)
35
      for(i=0; i < 0xFFFFF; ++i);     //simple delay
36
37
      LPC_GPIO2->DATA  |= (1<<4);    //set pin high (sec 12.3.1)
38
      for(i=0; i < 0xFFFFF; ++i);     //simple delay
39
    }
40
    return 0 ;
41
}
42
43
44
45
extern "C" void ADC_IRQHandler (void)
46
{
47
  uint32_t ADCValue = (LPC_ADC->DR[0] & 0xFFC0) >> 6;
48
49
    if(ADCValue < 512)     //check state of pin PIO3_2  (sec 12.3.1)
50
      LPC_GPIO0->DATA  &= ~(1<<7);   //set pin low (sec 12.3.1)
51
    else
52
      LPC_GPIO0->DATA  |= (1<<7);    //set pin high (sec 12.3.1)
53
}

von W.S. (Gast)


Lesenswert?

Skyper schrieb:
> Zweiter Fehler, die ISR nicht mit " extern "C" " zu Kennzeichnen.

Nun, sowas mag ja beim GCC mittlerweile angesagt sein.
Beim Keil schreibt man stattdessen

void __irq ADC_IRQHandler (void)

denn schlußendlich muß ja der Linker erkennen, daß es da für den 
besagten Interrupt außer dem [WEAK] Handler einen tatsächlichen 
nicht-weak-Handler gibt.

Wenn ich mich recht erinnere, dann muß man für den GCC folgendes 
schreiben:

#if defined (GCC)
#define _irq  __attribute_ ((interrupt("IRQ")))
#endif

Ist ja so schön einfach und übersichtlich.

W.S.

von Jürgen S. (starblue) Benutzerseite


Lesenswert?

Skyper schrieb:

> Zweiter Fehler, die ISR nicht mit " extern "C" " zu Kennzeichnen.

Das sollte nur nötig sein, wenn mit einem C++-Compiler übersetzt wird.
Kann natürlich sein, dass MCUXpresso das macht.

von Skyper (Gast)


Lesenswert?

Jürgen S. schrieb:
> Das sollte nur nötig sein, wenn mit einem C++-Compiler übersetzt wird.
> Kann natürlich sein, dass MCUXpresso das macht.

Ja, war als C++ Projekt eingerichtet...

Kann jemand vielleicht noch was zum Overrun sagen... Wert auslesen und 
wenn das Bit gesetzt ist verwerfen und warten bis zum nächsten mal?

von Jim M. (turboj)


Lesenswert?

Skyper schrieb:
> Kann jemand vielleicht noch was zum Overrun sagen... Wert auslesen und
> wenn das Bit gesetzt ist verwerfen und warten bis zum nächsten mal?

Ein Flag setzen das in der Hauptschleife eine entsprechende 
Fehlermeldung auswirft.

ADC Overrun ist ein Programmierfehler, den möchte man als Programmierer 
zeitnah mitbekommen.

Beim Kunden ist der i.d.R. ohne Bedeutung (lies: zu spät) - es sei denn 
man schreibt ein Logfile o.ä. mit.

von Skyper (Gast)


Lesenswert?

Jim M. schrieb:
> Ein Flag setzen das in der Hauptschleife eine entsprechende
> Fehlermeldung auswirft.
>
> ADC Overrun ist ein Programmierfehler, den möchte man als Programmierer
> zeitnah mitbekommen.
>
> Beim Kunden ist der i.d.R. ohne Bedeutung (lies: zu spät) - es sei denn
> man schreibt ein Logfile o.ä. mit.

Danke für die Erläuterung Jim,

so ähnlich hatte ich das in Beispielen auch gesehen, wenn das 
Overrun-Bit gesetzt war, Wert verwerfen und ein Bit in der globalen 
Fehler Variable für "Overrun" setzen...

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.