Author Topic: Some ADC notes on M522xx  (Read 6340 times)

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3237
    • View Profile
    • uTasker
Some ADC notes on M522xx
« on: May 06, 2012, 02:55:10 PM »
Hi All

In addition to the following two threads
http://www.utasker.com/forum/index.php?topic=280.0
http://www.utasker.com/forum/index.php?topic=437.0
the following discusses how to configure for a scan of all channels and a single interrupt on completion.

To set up for a sequential scan of all inputs and then an interrupt (just once) the following can be used:

    ADC_SETUP adc_setup;                                                 // interrupt configuration parameters
    adc_setup.int_type = ADC_INTERRUPT;                                  // identifier when configuring
    adc_setup.int_handler = adc_result_ready;                            // handling function
    adc_setup.int_priority = ADC_ERR_PRIORITY;                           // ADC interrupt priority
    adc_setup.int_adc_int_type = 0;
    adc_setup.int_adc_mode = (ADC_CONFIGURE_CHANNEL | ADC_CONFIGURE_ADC | ADC_CONFIGURE_CHANNEL | ADC_SEQUENTIAL_MODE | ADC_SINGLE_ENDED);
    adc_setup.int_adc_speed = (unsigned char)(ADC_SAMPLING_SPEED(5000000));// 5MHz sampling (must be between 100kHz and 5MHz)
    adc_setup.int_adc_result = 0;                                        // no result is requested

    adc_setup.int_adc_bit = 0;
    fnConfigureInterrupt((void *)&adc_setup);                            // configure ADC and channel 0
    adc_setup.int_adc_mode = (ADC_CONFIGURE_CHANNEL);
    adc_setup.int_adc_bit = 1;
    fnConfigureInterrupt((void *)&adc_setup);                            // configure channel 1
    adc_setup.int_adc_bit = 2;
    fnConfigureInterrupt((void *)&adc_setup);                            // configure channel 2
    adc_setup.int_adc_bit = 3;
    fnConfigureInterrupt((void *)&adc_setup);                            // configure channel 3
    adc_setup.int_adc_bit = 4;
    fnConfigureInterrupt((void *)&adc_setup);                            // configure channel 4
    adc_setup.int_adc_bit = 5;
    fnConfigureInterrupt((void *)&adc_setup);                            // configure channel 5
    adc_setup.int_adc_bit = 6;
    fnConfigureInterrupt((void *)&adc_setup);                            // configure channel 6
    adc_setup.int_adc_bit = 7;
    adc_setup.int_adc_int_type = (ADC_END_OF_SCAN_INT | ADC_SINGLE_SHOT_TRIGGER_INT); // interrupt types
    adc_setup.int_adc_mode = (ADC_START_OPERATION | ADC_CONFIGURE_CHANNEL);
    fnConfigureInterrupt((void *)&adc_setup);                            // configure channel 7 and start conversion


This configures the ADC (its speed and basic mode) and each channel.
Notice that at the end the interrupt is also added – a single shot one at the end of the scan. The operation is also started.

The interrupt call back adc_result_ready() is called by the interrupt. Often this is used to schedule a task that will then read and handle the values
 
// This interrupt is called when the ADC level changes below programmed threshold (on one of the enabled channels)
//
static void adc_result_ready(ADC_INTERRUPT_RESULT *adc_result)
{
    fnInterruptMessage(OWN_TASK, ADC_LOW_0);
}



The following shows the handling reading all values to the ucResults[] array and displaying just one of them – it could of course display more. Then it starts a timer which is used to repeat the exercise at regular intervals.

    ADC_SETUP adc_setup;                                 // interrupt configuration parameters
    ADC_RESULTS results;
    adc_setup.int_type = ADC_INTERRUPT;                  // identifier
    adc_setup.int_priority = ADC_ERR_PRIORITY;           // port interrupt priority
    adc_setup.int_adc_result = &results;
    adc_setup.int_adc_bit = 7;                           // ADC bit 7
    fnDebugMsg("Temperature value = ");
    adc_setup.int_adc_mode = (ADC_READ_ONLY | ADC_ALL_RESULTS);// start operation now and return the present result
    fnConfigureInterrupt((void *)&adc_setup);            // enter interrupt for low level trigger
    fnDebugHex(results.sADC_value[7], (WITH_SPACE | WITH_LEADIN | WITH_CR_LF | 2));
    uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(10.0 * SEC), E_NEXT_SAMPLE );



The simulator can be used to task the basic operation (it is quite accurate and so if it works there is will very probably work on the HW – if it doesn’t it will probably also not work on the HW and can be debugged before making the next step.

The ADC interface supports most (or all) modes of ADC operation. It can be either used directly or, if the code is to be optimally efficient, the registers that it sets up (and values) can be checked and then copied to user code in the same sequence with the same values.
The single-shot operation powers down the ADCs once the scan is completed, so the ADCs need to be powered and initialised each time.


The mode names should match with the modes supported by the ADC. They won’t necessarily match with bits in registers since they are used to inform the initialisation part of the ADC interface to control appropriate parts.
Some important mode flags:
ADC_CONFIGURE_ADC      - the main ADC mode is set (this needs to be set only one after the ADC is initialised/powered up)
ADC_CONFIGURE_CHANNEL  - the specified ADC channel will be initialised (also port pins configure appropriately)
ADC_SEQUENTIAL_MODE    - the main mode is sequential sampling
ADC_SINGLE_ENDED       - inputs are single-ended rather than differential
ADC_START_OPERATION    - the conversion should be started


Interrupts are not always used (it is possible to let the ADC free-run and just read at appropriate times) but the following interrupt types can be configured
ADC_END_OF_SCAN_INT             - interrupt when a scan has completed (one interrupt for ADC)
ADC_ZERO_CROSSING_INT_POSITIVE  - interrupt when the ADC input crosses zero in the positive direction (there is an interrupt per channel)
ADC_ZERO_CROSSING_INT_NEGATIVE  - interrupt when the ADC input crosses zero in the negative direction (there is an interrupt per channel)
ADC_LOW_LIMIT_INT               - interrupt when the ADC input falls below the low level trigger (there is an interrupt per channel)
ADC_SINGLE_SHOT_CROSSING_INT    - single shot mode for zero crossing channel interrupt (otherwise there are interrupt on every crossing)
ADC_HIGH_LIMIT_INT              - interrupt when the ADC input rises above the low level trigger (there is an interrupt per channel)
ADC_SINGLE_SHOT_TRIGGER_INT     - single shot mode for channel threshold interrupts (otherwise there are interrupt on every threshold crossing)
ADC_DISABLE_INTS                - disable interrupt(s)


Regards

Mark