erlerobot / smart_motor

4 stars 2 forks source link

ADC module #12

Closed jlamperez closed 8 years ago

jlamperez commented 8 years ago

ADC module

adc The adc.h file has an adc_init() function for initialize ADC conversion, 5 variables declared externally so inline functions work and 7 inline functions for fast access to power flags and values:

The adc initialization function

void adc_init(void);

Externally declared variables, and volatile because their value can change in no close code.

extern volatile uint8_t adc_power_ready;
extern volatile uint16_t adc_power_value;
extern volatile uint8_t adc_position_ready;
extern volatile uint16_t adc_position_value;
extern volatile uint8_t adc_voltage_needed;

Inline functions for fast access to power flags and values:


inline static uint16_t adc_get_power_value(void)
// Return the signed 16-bit ADC power value.
{
    // Clear the ready ADC value ready flag.
    adc_power_ready = 0;

    // Return the value.
    return adc_power_value;
}

inline static uint8_t adc_power_value_is_ready(void)
// Return the ADC power value ready flag.
{
    // Return the value ready flag.
    return adc_power_ready;
}

inline static void adc_power_value_clear_ready(void)
// Clear the ready ADC power value ready flag.
{
    adc_power_ready = 0;
}
inline static uint16_t adc_get_position_value(void)
// Return the 16-bit ADC position value.
{
    // Clear the ready ADC value ready flag.
    adc_position_ready = 0;

    // Return the value.
    return adc_position_value;
}

inline static uint8_t adc_position_value_is_ready(void)
// Return the ADC position value ready flag.
{
    // Return the value ready flag.
    return adc_position_ready;
}

inline static void adc_position_value_clear_ready(void)
// Clear the ready ADC power value ready flag.
{
    adc_position_ready = 0;
}

inline static void adc_read_voltage(void)
// Set a flag to start a adc on supply voltage channel.
{
    adc_voltage_needed = 1;
}

The 10-bit Analog to Digital Converter (ADC) on the ATmega MCU is used to provide power and position feedback from the servo circuitry. The analog inputs are assigned as follows:

We can see that at the beginning of the adc.c

#define ADC_CHANNEL_CURRENT         0
#define ADC_CHANNEL_BATTERY         1
#define ADC_CHANNEL_POSITION        2
#define ADC_CHANNEL_TEMPERATURE     3
#define ADC_CHANNEL_BACKEMF         7

Then in the file a variable is defined for the last 3 bits of the ADCSRA (ADC Status Register A) the ADPS (ADC clock prescaler). The variable value is '111' and looking in the ADPS table this configuration is to divide the system clock of 20 Mhz by 128 having a 156.25 KHz ADC Clock. This is a timeslice used when analog sample is read.

#define ADPS        ((1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)) // a bit shifted to the ADPSX position.

Then the timer clock prescaler of 1024 is selected to yield a 19.531 KHz ADC clock from a 20 MHz system clock.

#define CSPS        ((1<<CS02) | (0<<CS01) | (1<<CS00))

A variable to compare register value to generate a timer interrup and initiate a ADC sample every 9.984 ms.

#define CRVALUE     195

After this defines there are global used variables to maintain ADC state and values.

volatile uint8_t adc_power_ready;//flag
volatile uint16_t adc_power_value;//value
volatile uint8_t adc_position_ready;//flag
volatile uint16_t adc_position_value;//value
volatile uint8_t adc_voltage_needed;//flag

Once we have the global variables and defines we are going to explain the initialization adc function and the two ISR functions defined in the adc.c file:

adc_init() Initialize ADC conversion for reading current monitoring and position

    adc_power_ready = 0;
    adc_power_value = 0;
    adc_position_ready = 0;
    adc_position_value = 0;
    adc_voltage_needed = 1;
PORTC &= ~((1<<PC0) | (1<<PC1) | (1<<PC2));

Overview of the 6 different registers used by the ATMega168 when performing analog to digital conversion.

Register Description Utilization
ADMUX ADC Multiplexer Selection Register Set reference Voltage, Left adjustment of results, Selection of input Channel
ADCSRA ADC Control Status Register A Control AD conversion process
ADCSRB ADC Control Status Register B Control AD conversion process
DIDR0 Digital Input Disable Register 0 Used to disable the digital input buffers on PC0 to PC5
ADCL ADC Data Register-Low The AD Converter returns a 10 bit value which is stored in these 2 registers
ADCH ADC Data Register-High The AD Converter returns a 10 bit value which is stored in these 2 registers
#if ENCODER_ENABLED
    DIDR0 |= (1<<ADC0D) | (1<<ADC1D);
#else
    DIDR0 |= (1<<ADC0D) | (1<<ADC1D) |(1<<ADC2D);
#endif
// Set the ADC multiplexer selection register.
    ADMUX = (0<<REFS1) | (1<<REFS0) |                       // Select AVCC as voltage reference.
            (0<<ADLAR) |                                    // Keep high bits right adjusted.
            ADC_CHANNEL_POSITION;                           // Position as the first channel to sample.
7 6 5 4 3 2 1 0
ADCSRA ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0
Enable ADC converter '1' when start conversion '1' enable autotrigering conversion complete, data written to registers(ADCL/ADCH) activate interrupt timeslice used when analog sample is read
7 6 5 4 3 2 1 0
ADCSRB - ACME - - - ADTS2 ADTS1 ADTS0

In the ADCSRB the value set is '011' that matches to Timer/Counter0 Compare Match A option

    // Set the ADC control and status register B.
    ADCSRB = (0<<ADTS2) | (1<<ADTS1) | (1<<ADTS0);          // Timer/Counter0 Compare Match A.

    // Set the ADC control and status register A.
    ADCSRA = (1<<ADEN) |                                    // Enable ADC.
             (0<<ADSC) |                                    // Don't start yet, will be triggered manually.
             (0<<ADATE) |                                   // Don't start auto triggering.
             (1<<ADIE) |                                    // Activate ADC conversion complete interrupt.
             ADPS;                                          // Prescale -- see above.

Overview of the two 8 bit timer registers

Counter0 Counter2 Description
TCCR0A TCCR2A Timer/Counter Control Register A. WGM0 control counter's mode(table)
TCCR0B TCCR2B Timer/Counter Control Register B. CS0 to slow down the rate.
TCNT0 TCNT2 Timer/Counter Register. Compared with OCR0A and increment.
OCR0A OCR2A Output Compare Register A. Threshold
OCR0B OCR2B Output Compare Register B. Threshold
TIMSK0 TIMSK2 Timer/Counter Interrupt Mask Register
TIFR0 TIFR2 Timer/Counter Interrupt Flag Register
TIFR0 = (1<OCF0B) | (1<<OCF0A) | (1<TOV0);
TIMSK0 = (1<<OCIE0A) |                                  // Interrupt on compare match A.
             (0<<OCIE0B) |                                  // No interrupt on compare match B.
             (0<<TOIE0);                                    // No interrupt on overflow.
 TCNT0 = 0;                         //Timer/Counter register 
 OCR0A = CRVALUE;               //Threshold. When reached 'something' happens
 OCR0B = 0;                                             
    // Set timer/counter0 control register A.
    TCCR0A = (0<<COM0A1) | (0<<COM0A0) |                    // Disconnect OCOA.
             (0<<COM0B1) | (0<<COM0B0) |                    // Disconnect OCOB.
             (1<<WGM01) | (0<<WGM00);                       // Mode 2 - clear timer on compare match.

    // Set timer/counter0 control register B.
    TCCR0B = (0<<FOC0A) | (0<<FOC0B) |                      // No force output compare A or B.
             (0<<WGM02) |                                   // Mode 2 - clear timer on compare match.
             CSPS;                                          // Timer clock prescale -- see above.

ISR

_SIGNAL(SIG_OUTPUTCOMPARE0A) : When the timer is equal to the compare value this kind of interruption happens. Handles timer/counter0 compare match A.

timer_increment();
ADMUX = (0<<REFS1) | (1<<REFS0) |            // Select AVCC as voltage reference.
(0<<ADLAR) |                                                   // Keep high bits right adjusted.
ADC_CHANNEL_POSITION;                           // Position as the first channel to sample.
ADCSRA |= (1<<ADSC);

_SIGNAL(SIGADC) :This interruption handles the AD conversion.

 // Read the 10-bit ADC value.
    uint16_t new_value = ADCW;
    // Which channel is being read?
    switch (ADMUX & 0x0f)
- case _ADC_CHANNEL_POSITION_: new position value is save, flag the position value as ready and set Power as the next channel to sample.
            // Save the new position value.
            adc_position_value = new_value;

            // Flag the position value as ready.
            adc_position_ready = 1;

            // Set the ADC multiplexer selection register.
            ADMUX = (0<<REFS1) | (1<<REFS0) |                       // Select AVCC as voltage reference.
                    (0<<ADLAR) |                                    // Keep high bits right adjusted.
                    ADC_CHANNEL_CURRENT;                            // Power as the next channel to sample.

            // Start the ADC of the power channel now
            ADCSRA |= (1<<ADSC);

            break;
            // Save the new power value.
            adc_power_value = new_value;

            // Flag the power value as ready.
            adc_power_ready = 1;

            // Check the flag to see if a battery voltage measurement is required.
            if (adc_voltage_needed)
            {
                // Set the ADC multiplexer selection register.
                ADMUX = (0<<REFS1) | (1<<REFS0) |                       // Select AVCC as voltage reference.
                        (0<<ADLAR) |                                    // Keep high bits right adjusted.
                        ADC_CHANNEL_BATTERY;                            // Battery voltage as the next channel to sample.

                // Start the ADC of the voltage channel now
                ADCSRA |= (1<<ADSC);
            }

            break;
            // Remove flag
            adc_voltage_needed = 0;

            // Save voltage value to registers
            registers_write_word(REG_VOLTAGE_HI, REG_VOLTAGE_LO, new_value);

            break;

Links for a better understanding of timers and adc in a microcontroller: