adc4simul_dma.c - Simultaneous sampling of 4 channels (only for PIC24 CPUs with DMA)ΒΆ

Simultaneously samples four channels and uses a timer used to trigger conversions. Four channels are Ping-pong buffering is used. Conversion results are printed to screen. (HEX values and voltages are printed.) This is only for PIC24 CPUs without DMA.

 
#include "pic24_all.h"
#include "stdio.h"


#if !defined(_DMA0IF) || defined(__PIC24E__) || defined(__dsPIC33E__)
# warning "This processor selection does not have a DMA module; this code example is incompatible with a PIC24 CPU that does not have DMA."
# warning "OR this is a E family processor, which this example does not support."
int main(void) {
  return 0;
}
#else
 

setup an output to help us measure ADC IRQ responses

#define CONFIG_LED2()       CONFIG_RB5_AS_DIG_OUTPUT()
#define LED2                _LATB5

#define   ADC_LEN           10
#define   ADC_NSTEPS        1024
#define   ADC_12BIT_FLAG    0
 

set this to one of the values of 1, 2, 4, 8, 16, 32, 64, 128

#define CONVERSIONS_PER_INPUT  1 //for this example, assumed always to be '1'
#define MAX_CHANNELS   16
//DMA transfer size is in words.
#define MAX_TRANSFER (CONVERSIONS_PER_INPUT*MAX_CHANNELS)   //make power of two for alignment to work

//DMA buffers, alignment is based on number of bytes
/// \cond nodoxygen

These attributes confuse Doxygen.

uint16_t au16_bufferA[MAX_TRANSFER] __attribute__((space(dma),aligned(MAX_TRANSFER*2)));
uint16_t au16_bufferB[MAX_TRANSFER] __attribute__((space(dma),aligned(MAX_TRANSFER*2)));
/// \endcond


//For this example, we will just use ordered conversion mode,
//and one conversion per ADC input
void configDMA_ADC(uint8_t    u8_ch0Select, \
                   uint16_t   u16_ch123SelectMask, \
                   uint16_t   u16_numTcyMask) {


  AD1CON1bits.ADON = 0;   // turn off ADC (changing setting while ADON is not allowed)
  /** Configure the internal ADC **/
  AD1CON1 = ADC_CLK_TMR | ADC_SAMPLE_SIMULTANEOUS | ADC_ADDMABM_ORDER;
  AD1CON3 = (u16_numTcyMask & 0x00FF);
  AD1CON2 = ADC_VREF_AVDD_AVSS | ADC_CONVERT_CH0123;
#if (defined(__PIC24H__)|| defined(__dsPIC33F__))
  AD1CHS0 = ADC_CH0_NEG_SAMPLEA_VREFN | (u8_ch0Select & 0x1F);
  AD1CHS123 = u16_ch123SelectMask;
#else
  AD1CHS = ADC_CH0_NEG_SAMPLEA_VREFN | (u8_ch0Select & 0x1F);
#endif
  AD1CON4 = ADC_1_WORD_PER_INPUT;
  AD1CSSL = 0;

  //configure the DMA channel 0 interrupt
  DMA0PAD = (unsigned int) &ADC1BUF0;
  DMA0REQ = DMA_IRQ_ADC1;
  DMA0STA = __builtin_dmaoffset(au16_bufferA);
  DMA0STB = __builtin_dmaoffset(au16_bufferB);
  DMA0CNT = 4 - 1; //converting four inputs, so DMA0CNT = 3
  DMA0CON =   //configure and enable the module Module
    (DMA_MODULE_ON |
     DMA_SIZE_WORD |
     DMA_DIR_READ_PERIPHERAL |
     DMA_INTERRUPT_FULL |
     DMA_NULLW_OFF |
     DMA_AMODE_REGISTER_POSTINC |
     DMA_MODE_CONTINUOUS_PING_PONG);

  _DMA0IF = 0;
  _DMA0IP = 6;
  _DMA0IE = 1;


  AD1CON1bits.ADON = 1;   // turn on the ADC
}

uint16_t              au16_buffer[MAX_TRANSFER];
volatile  uint16_t    au16_sum[MAX_TRANSFER];
volatile  uint8_t     u8_gotData;
volatile  uint8_t     u8_activeBuffer;

void _ISRFAST _DMA0Interrupt(void) {
  static uint8_t      u8_adcCount=64;
  uint8_t       u8_i;
  uint16_t*     au16_adcHWBuff = (uint16_t*) &au16_bufferA;
  _DMA0IF = 0;

  if (u8_activeBuffer) {
    au16_adcHWBuff = (uint16_t*) &au16_bufferB;
    u8_activeBuffer = 0;
  } else {
    au16_adcHWBuff = (uint16_t*) &au16_bufferA;
    u8_activeBuffer = 1;
  }

  //accumulate the sum
  for ( u8_i=0; u8_i<MAX_TRANSFER; u8_i++) {
    au16_buffer[u8_i] += au16_adcHWBuff[u8_i];
  } //end for()
 

we got the data, so start the sampling process again

  SET_SAMP_BIT_ADC1();
  u8_adcCount--;
  if (u8_adcCount==0) {
    u8_adcCount = 64;
    u8_gotData = 1;
    for ( u8_i=0; u8_i<MAX_TRANSFER; u8_i++) {
      au16_sum[u8_i] = au16_buffer[u8_i];
      au16_buffer[u8_i] = 0;
    } //end for()
  }

toggle a port pin so that we can measure how often DMA IRQs are coming in

  LED2 = !LED2;
}


int main (void) {
  uint8_t   u8_i;
  uint16_t  u16_pot;
  uint32_t  u32_ticks;
  float   f_pot;

  configBasic(HELLO_MSG);
 

make RA0/AN0/VREF+ a digital input to kill the pullup and set the TRISA bit, then make it ANALOG so the ADC will work

  CONFIG_RA0_AS_ANALOG();
  CONFIG_RA1_AS_ANALOG();
  CONFIG_RB0_AS_ANALOG();
  CONFIG_RB12_AS_ANALOG();

  CONFIG_LED2();

  u8_gotData = 0;
 

configure T2/T3 as 32-bit timer to trigger every 1/64 second

  T3CONbits.TON = 0;
  T2CONbits.TON = 0;
  T2CON = T2_32BIT_MODE_ON | T2_PS_1_1 | T2_SOURCE_INT;
  TMR3 = 0;
  TMR2 = 0;
  u32_ticks = usToU32Ticks(15625, getTimerPrescale(T2CONbits)) - 1;     // # of ticks for 1/64 seconds
  PR3 = u32_ticks>>16;
  PR2 = u32_ticks & 0xFFFF;
  T2CONbits.TON = 1;

  configDMA_ADC(12, ADC_CH123_POS_SAMPLEA_AN0AN1AN2, ADC_CONV_CLK_10Tcy );
  SET_SAMP_BIT_ADC1();

  while (1) {
    while (!u8_gotData) {
      doHeartbeat();
    }
    u8_gotData = 0;
    for ( u8_i=0; u8_i<4; u8_i++) {
      u16_pot = au16_sum[u8_i];
      f_pot = (3.3 / 1023 / 64 ) * u16_pot;
      printf("r");
      outChar( '0'+u8_i );
      printf(":0x%04X=%1.3fV  ",  u16_pot, (double) f_pot );
    } //end for()
    printf("\n");
  } //endof while()
} // endof main()
#endif