Getting started with Atmel SAM4S-EK2

ADC

We will be using potentiometer and temperature sensor for this project. First we have to enable the clock for ADC hardware.

pmc_enable_periph_clk(ID_ADC);

Initialization of ADC requires some formula.

  1. ADCClock = MCK / ( (PRESCAL+1) * 2 )
  2. Startup Time = startup value / ADCClock
adc_init(ADC, sysclk_get_cpu_hz(), 6400000, ADC_STARTUP_TIME_4);

The second parameter is the main clock, third is ADC clock and the last is start up time which is 64 periods of ADCClock.

To configure the timing for ADC, we need

  1. Transfer Time = (TRANSFER * 2 + 3) / ADCClock
  2. Tracking Time = (TRACKTIM + 1) / ADCClock
  3. Settling Time = settling value / ADCClock
adc_configure_timing(ADC, TRACKING_TIME, ADC_SETTLING_TIME_3, TRANSFER_PERIOD);

Enable TAG option so that the number of the last converted channel can be indicated. p_adc is a pointer to an ADC instance.

adc_enable_tag(ADC);

Set user defined channel sequence using

adc_configure_sequence(ADC, ch_list, 2);

Now we have to start the sequencer.

adc_start_sequencer(ADC);

Enable the specified ADC channel.

adc_enable_channel(ADC, (enum adc_channel_num_t)i);

Now we have to update the channel numbers.

g_adc_sample_data.uc_ch_num[0] = ch_list[0];
g_adc_sample_data.uc_ch_num[1] = ch_list[1];

Now to enable the temperature sensor.

adc_enable_ts(ADC);

Now we have to set the gain and offset value.

adc_disable_anch(ADC);
adc_enable_anch(ADC);
adc_set_channel_input_gain(ADC, ADC_CHANNEL_POTENTIOMETER, ADC_GAINVALUE_2);
adc_enable_anch(ADC);
adc_enable_channel_input_offset(ADC, ADC_CHANNEL_POTENTIOMETER);

We can also set auto calibration mode.

adc_set_calibmode(ADC);
while (1) {
    if ((adc_get_status(ADC) & ADC_ISR_EOCAL) ==
            ADC_ISR_EOCAL)
        break;
}

To set power save mode.

adc_configure_power_save(ADC, 1, 0);

To transfer data with PDC

adc_read_buffer(ADC, g_adc_sample_data.us_value, BUFFER_SIZE);
adc_enable_interrupt(ADC, ADC_IER_RXBUFF);

and without PDC

adc_enable_interrupt(ADC, ADC_IER_DRDY);

Now we have to enable ADC interrupt.

NVIC_EnableIRQ(ADC_IRQn);

Now to configure trigger mode

case TRIGGER_MODE_SOFTWARE:
    adc_configure_trigger(ADC, ADC_TRIG_SW, 0);    /* Disable hardware trigger. */
    break;
case TRIGGER_MODE_ADTRG:
    gpio_configure_pin(PINS_ADC_TRIG, PINS_ADC_TRIG_FLAG);
    adc_configure_trigger(ADC, ADC_TRIG_EXT, 0);
    break;
case TRIGGER_MODE_TIMER:
    configure_time_trigger();
    break;
case TRIGGER_MODE_PWM:
    configure_pwm_trigger();
    break;
case TRIGGER_MODE_FREERUN:
    adc_configure_trigger(ADC, ADC_TRIG_SW, 1);
    break;

The interrupt handler for the ADC is

if (g_adc_test_mode.uc_pdc_en) {
    if ((adc_get_status(ADC) & ADC_ISR_RXBUFF) ==
            ADC_ISR_RXBUFF) {
        g_adc_sample_data.us_done = ADC_DONE_MASK;
        adc_read_buffer(ADC, g_adc_sample_data.us_value, BUFFER_SIZE);
        /* Only keep sample value, and discard channel number. */
        for (i = 0; i < NUM_CHANNELS; i++) {
            g_adc_sample_data.us_value[i] &= ADC_LCDR_LDATA_Msk;
        }
    }
} else {    /* Without PDC transfer */
    if ((adc_get_status(ADC) & ADC_ISR_DRDY) ==
            ADC_ISR_DRDY) {
        ul_temp = adc_get_latest_value(ADC);
        for (i = 0; i < NUM_CHANNELS; i++) {
            uc_ch_num = (ul_temp & ADC_LCDR_CHNB_Msk) >>
                    ADC_LCDR_CHNB_Pos;
            if (g_adc_sample_data.uc_ch_num[i] == uc_ch_num) {
                g_adc_sample_data.us_value[i] =
                        ul_temp &
                        ADC_LCDR_LDATA_Msk;
                g_adc_sample_data.us_done |= 1 << i;
            }
        }
    }
}

Application note on ADC