Search code examples
cmicrocontrollerlpcadc

Has anyone written working code for the NXP LPC17xx ADC?


I'm working with the NXP LPC1788 microcontroller, and I'm trying to write code that will let me perform ADC measurements on analog channels 0-7. The code I have right now is:

uint16_t getADCChannelValue(uint8_t adcChannel)
{
  uint16_t adc_value;

  ADC_ChannelCmd(LPC_ADC, adcChannel, ENABLE);
  ADC_StartCmd(LPC_ADC, ADC_START_NOW);

  // Wait for measurement to complete.
  while (!(ADC_ChannelGetStatus(LPC_ADC, adcChannel, ADC_DATA_DONE)));

  adc_value = ADC_ChannelGetData(LPC_ADC, adcChannel);
  ADC_ChannelCmd(LPC_ADC, adcChannel, DISABLE);

  // With delay - code works. Without delay - channel 0 is correct,
  // channels 1-7 have values close to channel 0 (~2150) (incorrect).
  //OS_Delay(1);

  return adc_value;
}

With the delay, the code seems to work, but I don't want an arbitrary delay in there. I've been playing around with the code for hours, and for whatever reason, the overrun flag is set when the delay is there (that is, it complains about an overrun when the function is outputting the correct value).

I'm applying voltage only to analog channel 0. This is the output I get when the delay is included:

Channel 0 = 2151
Channel 1 = 35
Channel 2 = 33
Channel 3 = 34
Channel 4 = 32
Channel 5 = 34
Channel 6 = 32
Channel 7 = 31

And when it is not included:

Channel 0 = 2150
Channel 1 = 2151
Channel 2 = 2151
Channel 3 = 2150
Channel 4 = 2150
Channel 5 = 2150
Channel 6 = 2149
Channel 7 = 2150

Has anyone written any code that will let me run through all the ADC channels and record their values as quickly as possible without arbitrary delays?


Solution

  • Tag, I think that you should be using "burst mode". Here's what I have as the ADC initialisation code:

    void adcHandlerInit()
    {
      // Clear all bits of the analog pin registers except for the filter disable
      // bit.
      *((uint32_t *)(LPC_IOCON_BASE + ((BRD_ADC_CH_PORT_0 * 32
                                 + BRD_ADC_CH_PIN_23)*sizeof(uint32_t)))) = 1 << 8;
      *((uint32_t *)(LPC_IOCON_BASE + ((BRD_ADC_CH_PORT_0 * 32
                                 + BRD_ADC_CH_PIN_24)*sizeof(uint32_t)))) = 1 << 8;
      *((uint32_t *)(LPC_IOCON_BASE + ((BRD_ADC_CH_PORT_0 * 32
                                 + BRD_ADC_CH_PIN_25)*sizeof(uint32_t)))) = 1 << 8;
      *((uint32_t *)(LPC_IOCON_BASE + ((BRD_ADC_CH_PORT_0 * 32
                                 + BRD_ADC_CH_PIN_26)*sizeof(uint32_t)))) = 1 << 8;
      *((uint32_t *)(LPC_IOCON_BASE + ((BRD_ADC_CH_PORT_1 * 32
                                 + BRD_ADC_CH_PIN_30)*sizeof(uint32_t)))) = 1 << 8;
      *((uint32_t *)(LPC_IOCON_BASE + ((BRD_ADC_CH_PORT_1 * 32
                                 + BRD_ADC_CH_PIN_31)*sizeof(uint32_t)))) = 1 << 8;
      *((uint32_t *)(LPC_IOCON_BASE + ((BRD_ADC_CH_PORT_0 * 32
                                 + BRD_ADC_CH_PIN_12)*sizeof(uint32_t)))) = 1 << 8;
      *((uint32_t *)(LPC_IOCON_BASE + ((BRD_ADC_CH_PORT_0 * 32
                                 + BRD_ADC_CH_PIN_13)*sizeof(uint32_t)))) = 1 << 8;
    
      PINSEL_ConfigPin(BRD_ADC_CH_PORT_0, BRD_ADC_CH_PIN_23, BRD_ADC_CH_FUNC_NO_1);
      PINSEL_ConfigPin(BRD_ADC_CH_PORT_0, BRD_ADC_CH_PIN_24, BRD_ADC_CH_FUNC_NO_1);
      PINSEL_ConfigPin(BRD_ADC_CH_PORT_0, BRD_ADC_CH_PIN_25, BRD_ADC_CH_FUNC_NO_1);
      PINSEL_ConfigPin(BRD_ADC_CH_PORT_0, BRD_ADC_CH_PIN_26, BRD_ADC_CH_FUNC_NO_1);
      PINSEL_ConfigPin(BRD_ADC_CH_PORT_1, BRD_ADC_CH_PIN_30, BRD_ADC_CH_FUNC_NO_3);
      PINSEL_ConfigPin(BRD_ADC_CH_PORT_1, BRD_ADC_CH_PIN_31, BRD_ADC_CH_FUNC_NO_3);
      PINSEL_ConfigPin(BRD_ADC_CH_PORT_0, BRD_ADC_CH_PIN_12, BRD_ADC_CH_FUNC_NO_3);
      PINSEL_ConfigPin(BRD_ADC_CH_PORT_0, BRD_ADC_CH_PIN_13, BRD_ADC_CH_FUNC_NO_3);
    
      /* Configuration for ADC :
      *  ADC conversion rate = 400Khz
      */
      ADC_Init(LPC_ADC, 400000);
    
      ADC_IntConfig(LPC_ADC, BRD_ADC_INTR_0, DISABLE);
      ADC_IntConfig(LPC_ADC, BRD_ADC_INTR_1, DISABLE);
      ADC_IntConfig(LPC_ADC, BRD_ADC_INTR_2, DISABLE);
      ADC_IntConfig(LPC_ADC, BRD_ADC_INTR_3, DISABLE);
      ADC_IntConfig(LPC_ADC, BRD_ADC_INTR_4, DISABLE);
      ADC_IntConfig(LPC_ADC, BRD_ADC_INTR_5, DISABLE);
      ADC_IntConfig(LPC_ADC, BRD_ADC_INTR_6, DISABLE);
      ADC_IntConfig(LPC_ADC, BRD_ADC_INTR_7, DISABLE);
    
      // Start burst mode.
      ADC_ChannelCmd(LPC_ADC, ADC_CHANNEL_0, ENABLE);
      ADC_ChannelCmd(LPC_ADC, ADC_CHANNEL_1, ENABLE);
      ADC_ChannelCmd(LPC_ADC, ADC_CHANNEL_2, ENABLE);
      ADC_ChannelCmd(LPC_ADC, ADC_CHANNEL_3, ENABLE);
      ADC_ChannelCmd(LPC_ADC, ADC_CHANNEL_4, ENABLE);
      ADC_ChannelCmd(LPC_ADC, ADC_CHANNEL_5, ENABLE);
      ADC_ChannelCmd(LPC_ADC, ADC_CHANNEL_6, ENABLE);
      ADC_ChannelCmd(LPC_ADC, ADC_CHANNEL_7, ENABLE);
      ADC_StartCmd(LPC_ADC, ADC_START_CONTINUOUS);
      ADC_BurstCmd(LPC_ADC, ENABLE);
    }
    

    The part at the bottom is important. It will make the microcontroller perform repeated measurements on all the ADC channels. After that, you can just get the channel voltage value with:

    uint16_t getADCChannelValue(uint8_t adcChannel)
    {
      return (uint16_t)ADC_ChannelGetData(LPC_ADC, adcChannel);
    }
    

    This should help you out. However, it would be nice to see a way to do this without using burst mode, so anyone else with a correct answer that doesn't rely on burst mode should be awarded the accepted answer in place of me.