Single Software Controlled ADC Conversion on LPC1769

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

Single Software Controlled ADC Conversion on LPC1769

1,702件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lsimons on Fri Feb 03 08:49:48 MST 2012
In order to sample a single ADC channel on the LPC1769, I used Timer0 to start a software conversion (set the START bits in ADCR to 001).  I have this Timer0 interrupt toggling a pin so I can verify that it is occurring at the desired 4kHz. 

In the ADC interrupt handler, I can see the DONE bit on ADGDR reset on reading the data (which I think is how that interrupt is cleared?).  The ADC interrupt fires, but it is toggling it's debug pin at almost 1MHz.  Since this is faster than than the ADC's fastest rate, it looks to me like I'm missing something when it comes to resetting the interrupt.

After reading the user manual some more, it looks like maybe I should just put the ADC into START on MATch mode and save the function calls.  However, I'd like to understand what's going on before I rewrite the architecture.

Thanks,
Louis
0 件の賞賛
返信
6 返答(返信)

1,648件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elef on Fri Aug 03 22:24:13 MST 2012

Quote: Zero

  regVal = LPC_ADC->ADSTAT;                //clear interrupt



I might be wrong, but i think reading ADSTAT doesn't clear the interrupt as far as i can tell... I think you actually have to read the data registers to clear the DONE flags...
0 件の賞賛
返信

1,648件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lsimons on Wed Feb 08 07:08:11 MST 2012
That looks very similar to the timer triggered version I had impemented, though I hadn't checked for OVERRUN or tried reading from any of the registers except the global ADDR.

Reading the individual registers rather than the global ADDR seems to fix it for me.

Thanks,
Louis
0 件の賞賛
返信

1,648件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Fri Feb 03 14:30:42 MST 2012
Sorry, can't follow you without timer setup :rolleyes:

This is a working timer triggered sample:

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

#define LED     21                        //define GPIO0.21 LED
#define LED_TOG LPC_GPIO0->FIOPIN^=(1<<LED);

volatile unsigned int adc0_val=0;
volatile unsigned char adc0_ready=0;

void adc_init(void)                        //init adc
{
 LPC_SC->PCONP |= (1 << 12);            //set adc power
 LPC_PINCON->PINSEL1 &= ~(3<<14);
 LPC_PINCON->PINSEL1 |=  (1<<14);        //set AD0.0 P0.23

 LPC_ADC->ADCR = ( 0x01 << 0 ) |         //channel 0
        ( 4 <<  8 ) |                     //CLKDIV 4 => 25MHz /5 = 5 MHz
        ( 1 << 21 ) |                      //normal operation
//        ( 0 << 24 ) |                      //start conversion
        ( 4 << 24 ) |                     //start conversion with MAT0.1
        ( 0 << 27 ) ;                    //start conversion on rising edge of match
 LPC_ADC->ADINTEN = 0x01;                //int channel0 enable
 NVIC_EnableIRQ(ADC_IRQn);                //adc int enable
}                                        //end adc init

void ADC_IRQHandler(void)
{
 unsigned int regVal,regOver;
 regVal = LPC_ADC->ADSTAT;                //clear interrupt
 if (regVal & 0x0000FF00)                //check OVERRUN error first
 {
  regOver = (regVal & 0x0000FF00) >> 0x08;
  switch (regOver)
  {
   case 0x01:regOver = LPC_ADC->ADDR0;
             break;
   case 0x02:regOver = LPC_ADC->ADDR1;
             break;
   case 0x04:regOver = LPC_ADC->ADDR2;
             break;
   case 0x08:regOver = LPC_ADC->ADDR3;
             break;
   case 0x10:regOver = LPC_ADC->ADDR4;
             break;
   case 0x20:regOver = LPC_ADC->ADDR5;
             break;
   case 0x40:regOver = LPC_ADC->ADDR6;
             break;
   case 0x80:regOver = LPC_ADC->ADDR7;
             break;
   default:
            break;
  }
 }
 else                                    //no overrun
 {
  regVal = (regVal & 0x000000FF);        //check done bit
  switch (regVal)
  {
   case 0x01:regVal = LPC_ADC->ADDR0;    //read channel 0
             adc0_val = (regVal >> 4) & 0xFFF;//calc 12 bit value
             adc0_ready =1;                //set adc ready
             LED_TOG;                                //toggle LED
               break;
   case 0x02:regVal = LPC_ADC->ADDR1;
               break;
   case 0x04:regVal = LPC_ADC->ADDR2;
               break;
   case 0x08:regVal = LPC_ADC->ADDR3;
              break;
   case 0x10:regVal = LPC_ADC->ADDR4;
               break;
   case 0x20:regVal = LPC_ADC->ADDR5;
               break;
   case 0x40:regVal = LPC_ADC->ADDR6;
               break;
   case 0x80:regVal = LPC_ADC->ADDR7;
               break;
   default:
              break;
  }
 }
}

void timer0_init(void)
{
 LPC_TIM0->MR1 = 25000;                    //MR1 counter @ 25MHz = 1KHz
 LPC_TIM0->MCR = (3<<3);                //interrupt & reset MR1
 LPC_TIM0->EMR = (1<<1)|(3<<6);            //toggle external match bit, ADC read = 1/2 of 1kHz
//set match bit to MAT0.1 output is not necessary !!!
 LPC_PINCON->PINSEL3 =(0x03<<26);        //set MAT0.1 @ GPIO 1.29
 LPC_TIM0->TCR =1;                        //run timer 0
}

int main(void)                            //main
{
 LPC_GPIO0->FIODIR |= (1<<LED);            //set LED output
 adc_init();                            //init ADC
 timer0_init();                            //init timer0
 while(1)                                //start loop
 {
  if(adc0_ready)                        //adc ready
  {
   adc0_ready=0;                        //reset adc ready
   //LED_TOG;                                //toggle LED
  }                                        //end adc ready
 }                                        //end loop
 return 0 ;
}                                        //end main
0 件の賞賛
返信

1,648件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lsimons on Fri Feb 03 14:04:31 MST 2012
For those playing along at home, I think it's solved.  It's not pretty, and I don't think it should be necessary (I may be reading the manual wrong), but if I read from all of the channel registers, the interrupt clears on returning from the handler.

    uint32_t dummy;
    uint32_t i;
    for ( i = 0; i < 8; i++ )
    {
        dummy = ((uint32_t *)0x40034010);
        dummy = dummy >> 4;
        dummy = ( dummy >> 4 ) & 0xFFF;
    }


I'm hoping to hear back from NXP on why this is and will provide an update if it's useful.  It's also possible I've messed up some magic bit.
0 件の賞賛
返信

1,648件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lsimons on Fri Feb 03 12:23:45 MST 2012
So, I modified the code to only initiate an ADC read once.  The ADC Interrupt handler keeps firing in the hundreds of hertz rather than just once as it should.  It seems like it's definitely because the ADC interrupt bit isn't clearing in IABR0.  Bit 22 remains high even after reading from the ADC data registers.  The funny thing is the ADC data register done bit succesfully clears.

Am I missing something stupid?  I've checked the errata sheet and it doesn't mention a problem with ADC interrupts.

Here's my ADC handler.  I've tried to make it somewhat self-documenting:
void ADC_IRQHandler(void)
{
    uint16_t result;
    uint32_t adc_status;

    debug_set_timing_pin_high();

    set_reg_masked_bits(&LPC_ADC->ADCR, 7<<24, 0<<24);

    /// @note Interrupt SHOULD BE cleared when ADC read
    adc_status = LPC_ADC->ADSTAT;
    result = LPC_ADC->ADGDR & 0xFFFF;

    /*if (on_complete_handler)
    {
        on_complete_handler(result);
    }*/

    debug_set_timing_pin_low();

    return;
}


Edit:
It's starting to look like a timing bug.  If I put a breakpoint into the handler before anything happens, and then start running again, the interrupt is cleared properly.  I can't find any type of timing requirement in the user manual, though.
0 件の賞賛
返信

1,648件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lsimons on Fri Feb 03 09:36:38 MST 2012
So, after changing the ADCR register START bits to begin capture on toggle of MAT0.1 (external match from Timer 0 at MR1 match), the ADC interrupt is occuring at 960 kHz.  I don't know if this is a coincidence (24 MHz ADC peripheral clock / 960 kHz = 25.0), but it seems like I'm not understanding how to prevent immediate re-entry into the ADC interrupt.
0 件の賞賛
返信