LPC15xx sample multiple channels with software control

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC15xx sample multiple channels with software control

1,131 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by joepbrown on Sun Jan 24 22:20:09 MST 2016
I'm trying to sample ADC0_0 through ADC0_5 via software trigger but for some reason I can only read valid data from ADC0_0.

My chip is the LPC1517. Before I init any of the ADC code I enable the fixed pins:

Chip_IOCON_PinMuxSet(LPC_IOCON,0,8,IOCON_MODE_INACT);
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC0_0);
Chip_IOCON_PinMuxSet(LPC_IOCON,0,7,IOCON_MODE_INACT);
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC0_1);
Chip_IOCON_PinMuxSet(LPC_IOCON,0,6,IOCON_MODE_INACT);
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC0_2);
Chip_IOCON_PinMuxSet(LPC_IOCON,0,5,IOCON_MODE_INACT);
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC0_3);
Chip_IOCON_PinMuxSet(LPC_IOCON,0,4,IOCON_MODE_INACT);
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC0_4);
Chip_IOCON_PinMuxSet(LPC_IOCON,0,3,IOCON_MODE_INACT);
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC0_5);



I init the ADC:

Chip_ADC_Init(LPC_ADC0, 0);
Chip_ADC_StartCalibration(LPC_ADC0);
while (!(Chip_ADC_IsCalibrationDone(LPC_ADC0)));
Chip_ADC_SetClockRate(LPC_ADC0, ADC_MAX_SAMPLE_RATE / 4);
Chip_ADC_DisableSequencer(LPC_ADC0,ADC_SEQA_IDX);
Chip_ADC_SetSequencerBits(LPC_ADC0,ADC_SEQA_IDX,(0x3F | ADC_SEQ_CTRL_MODE_EOS));
Chip_ADC_ClearFlags(LPC_ADC0, Chip_ADC_GetFlags(LPC_ADC0));
Chip_ADC_EnableSequencer(LPC_ADC0, ADC_SEQA_IDX);


And in my ADC read function I try to return the single channel I'm trying to read:

Chip_ADC_StartSequencer(LPC_ADC0, ADC_SEQA_IDX);
uint32_t sample = 0;
do
{
    sample = Chip_ADC_GetDataReg(LPC_ADC0,channel);
} while (!(sample & ADC_SEQ_GDAT_DATAVALID));
return ADC_DR_RESULT(sample);


This works fine for channel ADC0_0 but all of the other channels are always 0.

I have eliminated the hardware as the problem (can trance every pin on the board and verify the voltage). I've tried different ADC modes (BURST, SINGLESTEP) and it doesn't make a difference.

Is there some sample code somewhere that shows how to sample multiple channels on the ADC sequencer?
Labels (1)
0 Kudos
Reply
3 Replies

830 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cgroen on Tue Jan 26 11:18:48 MST 2016
Thanks Joe,
you are absolutely right about the call to Chip_ADC_init!
Why I had that in the first place I don't know, guess it must have been a copy/paste brain short here
Thanks for pointing it out (as you wrote, the value was 0 anyway, but still it was an error to mix the ADC_SEQA_IDX into that call!)



Chip_ADC_Init(LPC_ADC1, 0);
  Chip_ADC_SetClockRate(LPC_ADC1, ADC_MAX_SAMPLE_RATE);
Chip_ADC_SetupSequencer(LPC_ADC1, ADC_SEQA_IDX, 
(ADC_SEQ_CTRL_CHANSEL(0) | // AD1[0] P1.1 = Batt 1 current
 ADC_SEQ_CTRL_CHANSEL(9) | // AD1[9] P0.16 = Batt 2 current
 ADC_SEQ_CTRL_CHANSEL(8) | // AD1[8] P0.15 = VBUS sense
 ADC_SEQ_CTRL_CHANSEL(4) | // AD1[4] P1.2  = Batt 1 voltage
 ADC_SEQ_CTRL_CHANSEL(5) | // AD1[5] P1.3  = Batt 2 voltage
 ADC_SEQ_CTRL_MODE_EOS));  // We want interrupt on end of sequence
Chip_ADC_SetTrim(LPC_ADC1, ADC_TRIM_VRANGE_HIGHV);

// Disables pullups/pulldowns and disable digital mode
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 1,  IOCON_MODE_INACT);
Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 16, IOCON_MODE_INACT);
Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 15, IOCON_MODE_INACT);
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 2,  IOCON_MODE_INACT);
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 3,  IOCON_MODE_INACT);

0 Kudos
Reply

830 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by joepbrown on Tue Jan 26 10:31:12 MST 2016
Thanks for the reply. With your example I was able to get my code fixed by swapping the order of initialization around and adding the Chip_ADC_SetTrim() function.

One note: I think the second parameter in your init line Chip_ADC_Init(LPC_ADC1, ADC_SEQA_IDX); is incorrect. The init function takes some flags and ADC_SEQA_IDX is just an enum. I don't think it matters since SEQA_IDX is just 0, but it's something you may want to correct.

Here is my code if anyone is interested. I configure the pins for IOCON_MODE_INACT and enabled the fixed pin before the init is called.

void Adc::Init(Hw::Pin p)
{
    channel = p.adc;
    if (!setup)
    {
        Chip_ADC_Init(LPC_ADC0, 0);
        Chip_ADC_SetClockRate(LPC_ADC0, ADC_MAX_SAMPLE_RATE / 4);
        Chip_ADC_SetupSequencer(LPC_ADC0, ADC_SEQA_IDX,ADC_SEQ_CTRL_CHANSEL(channel));
        Chip_ADC_SetTrim(LPC_ADC0,ADC_TRIM_VRANGE_LOWV);
        Chip_ADC_StartCalibration(LPC_ADC0);
        while (!(Chip_ADC_IsCalibrationDone(LPC_ADC0)));
        Chip_ADC_ClearFlags(LPC_ADC0, Chip_ADC_GetFlags(LPC_ADC0));
        Chip_ADC_EnableSequencer(LPC_ADC0, ADC_SEQA_IDX);
        setup = true;
    }
    else
    {
        Chip_ADC_DisableSequencer(LPC_ADC0,ADC_SEQA_IDX);
        Chip_ADC_SetSequencerBits(LPC_ADC0,ADC_SEQA_IDX,ADC_SEQ_CTRL_CHANSEL(channel));
        Chip_ADC_EnableSequencer(LPC_ADC0, ADC_SEQA_IDX);
    }
}

uint16_t Adc::Read(void)
{
    Chip_ADC_StartSequencer(LPC_ADC0, ADC_SEQA_IDX);
    uint32_t sample = 0;
    uint32_t timeout = scheduler.Millis() + 10;
    do
    {
        sample = Chip_ADC_GetDataReg(LPC_ADC0,channel);
    } while (!(sample & ADC_DR_DATAVALID) && timeout > scheduler.Millis());
    sample = (ADC_DR_RESULT(sample) * 3300) / 4095;
    return sample;
}
0 Kudos
Reply

830 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cgroen on Mon Jan 25 02:03:33 MST 2016
I run the ADC in interrupt, my setup code is like this:


Chip_ADC_Init(LPC_ADC1, ADC_SEQA_IDX);
  Chip_ADC_SetClockRate(LPC_ADC1, ADC_MAX_SAMPLE_RATE);
Chip_ADC_SetupSequencer(LPC_ADC1, ADC_SEQA_IDX, 
(ADC_SEQ_CTRL_CHANSEL(0) | // AD1[0] P1.1 = Batt 1 current
 ADC_SEQ_CTRL_CHANSEL(9) | // AD1[9] P0.16 = Batt 2 current
 ADC_SEQ_CTRL_CHANSEL(8) | // AD1[8] P0.15 = VBUS sense
 ADC_SEQ_CTRL_CHANSEL(4) | // AD1[4] P1.2  = Batt 1 voltage
 ADC_SEQ_CTRL_CHANSEL(5) | // AD1[5] P1.3  = Batt 2 voltage
 ADC_SEQ_CTRL_MODE_EOS));  // We want interrupt on end of sequence
Chip_ADC_SetTrim(LPC_ADC1, ADC_TRIM_VRANGE_HIGHV);

// Disables pullups/pulldowns and disable digital mode
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 1,  IOCON_MODE_INACT);
Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 16, IOCON_MODE_INACT);
Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 15, IOCON_MODE_INACT);
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 2,  IOCON_MODE_INACT);
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 3,  IOCON_MODE_INACT);

// Assign AD channels to their pins via SWM (fixed pins)
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC1_0); // P1.1
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC1_9); // P0.16
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC1_8); // P0.15
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC1_4); // P1.2
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC1_5); // P1.3

// Start calibration
  Chip_ADC_StartCalibration(LPC_ADC1);
while (!(Chip_ADC_IsCalibrationDone(LPC_ADC1))) OS_WAIT(10);

// Clear any pending interrupts
Chip_ADC_ClearFlags(LPC_ADC1, Chip_ADC_GetFlags(LPC_ADC1));

// Enable sequence A completion
Chip_ADC_EnableInt(LPC_ADC1, ADC_INTEN_SEQA_ENABLE);

NVIC_EnableIRQ(ADC1_SEQA_IRQn);

// Enable sequence A, will be started by thAD thread
Chip_ADC_EnableSequencer(LPC_ADC1, ADC_SEQA_IDX);




...and the interrupt routine:

//-----------------------------------------------------------------------------
// IRQ handler for sequence A from AD converter
// Called when the sequence is completed
//-----------------------------------------------------------------------------
void ADC1_SEQA_IRQHandler(void) {
unsigned int pending;
// Get pending interrupts
pending = Chip_ADC_GetFlags(LPC_ADC1);

// Sequence A completion interrupt
if (pending & ADC_FLAGS_SEQA_INT_MASK) {
sequence1Complete = TRUE;
}
// Clear Sequence A completion interrupt
Chip_ADC_ClearFlags(LPC_ADC1, ADC_FLAGS_SEQA_INT_MASK);
}



I then in a seperate thread read the results:


// Start the process....         
Chip_ADC_StartSequencer(LPC_ADC1, ADC_SEQA_IDX);

while (1) {
  // If ADC is complete, go start another conversion
if (sequence1Complete) {
sequence1Complete=FALSE;
// Read all channels (and filter)
adc_value[0]=filter(0, ADC_DR_RESULT(Chip_ADC_GetDataReg(LPC_ADC1, 0)));
adc_value[1]=filter(1, ADC_DR_RESULT(Chip_ADC_GetDataReg(LPC_ADC1, 9)));
adc_value[2]=filter(2, ADC_DR_RESULT(Chip_ADC_GetDataReg(LPC_ADC1, 8)));
adc_value[3]=filter(3, ADC_DR_RESULT(Chip_ADC_GetDataReg(LPC_ADC1, 4)));
adc_value[4]=filter(4, ADC_DR_RESULT(Chip_ADC_GetDataReg(LPC_ADC1, 5)));
// Start another conversion
Chip_ADC_StartSequencer(LPC_ADC1, ADC_SEQA_IDX);
}
  .. do other stuff etc
  ..

}





Hope this helps ?
0 Kudos
Reply