Tinkering TI MSP430F5529

REF Module and ADC12 in Polling Mode

Good ADC performance and accurate readings depend on good reference sources. The stable these reference sources are with respect to changes in temperature and input voltage fluctuations, the better the ADC readings.  MSP430F5529 is equipped with a built-in reference source generator module called REF module that is able to generate 1.5V, 2.0V and 2.5V with good precision. REF module has good temperature coefficients and power-saving features. REF module doesn’t restrict us from using external reference sources but since they are available internally and factory trimmed, it is better to use them to reduce external parts count and avoid unnecessary hassle. The highlighted region in ADC12’s block diagram shown below shows the components of MSP430F5529’s REF module. A quick view reveals how they are connected with the ADC.

ADC12 is perhaps simply understood without the use of DMA and interrupts. ADC12 is configured and started. Readings of ADC channels are stored in ADC memory locations after conversions. Reading ADC memories give out last conversion results. In this example, we will see exactly that and we will be reading MSP430’s internal temperature sensor with the ADC.

Code Example

#include "driverlib.h"
#include "delay.h"
#include "lcd.h"
#include "lcd_print.h"

void clock_init(void);
void GPIO_init(void);
void ADC12_init(void);
void REF_init(void);

void main(void)
{
    signed int ADC_Count = 0;
    signed long temp = 0;

    WDT_A_hold(WDT_A_BASE);

    clock_init();
    GPIO_init();
    REF_init();
    ADC12_init();

    LCD_init();
    LCD_clear_home();
    load_custom_symbol();

    LCD_goto(0, 0);
    LCD_putstr("ADC :");

    LCD_goto(0, 1);
    LCD_putstr("T/ C:");
    print_symbol(2, 1, 0);

    while(1)
    {
        ADC_Count = ADC12_A_getResults(ADC12_A_BASE,
                                       ADC12_A_MEMORY_0);

        temp = ((((signed long)ADC_Count - 1855) * 667) / 4096);

        print_I(11, 0, ADC_Count);
        print_I(13, 1, temp);

        delay_ms(200);
    };
}

void clock_init(void)
{
    PMM_setVCore(PMM_CORE_LEVEL_3);

    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,
                                               (GPIO_PIN4 | GPIO_PIN2));

    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P5,
                                                (GPIO_PIN5 | GPIO_PIN3));

    UCS_setExternalClockSource(XT1_FREQ,
                               XT2_FREQ);

    UCS_turnOnXT2(UCS_XT2_DRIVE_4MHZ_8MHZ);

    UCS_turnOnLFXT1(UCS_XT1_DRIVE_0,
                    UCS_XCAP_3);

    UCS_initClockSignal(UCS_FLLREF,
                        UCS_XT2CLK_SELECT,
                        UCS_CLOCK_DIVIDER_4);

    UCS_initFLLSettle(MCLK_KHZ,
                      MCLK_FLLREF_RATIO);

    UCS_initClockSignal(UCS_SMCLK,
                        UCS_XT2CLK_SELECT,
                        UCS_CLOCK_DIVIDER_2);

    UCS_initClockSignal(UCS_ACLK,
                        UCS_XT1CLK_SELECT,
                        UCS_CLOCK_DIVIDER_1);
}

void GPIO_init(void)
{
    GPIO_setAsOutputPin(GPIO_PORT_P4,
                        GPIO_PIN7);
}


void ADC12_init(void)
{
    ADC12_A_configureMemoryParam configureMemoryParam = {0};

    ADC12_A_init(ADC12_A_BASE,
                 ADC12_A_SAMPLEHOLDSOURCE_SC,
                 ADC12_A_CLOCKSOURCE_ACLK,
                 ADC12_A_CLOCKDIVIDER_1);

    ADC12_A_setupSamplingTimer(ADC12_A_BASE,
                               ADC12_A_CYCLEHOLD_768_CYCLES,
                               ADC12_A_CYCLEHOLD_4_CYCLES,
                               ADC12_A_MULTIPLESAMPLESENABLE);

    ADC12_A_setResolution(ADC12_A_BASE,
                          ADC12_A_RESOLUTION_12BIT);

    configureMemoryParam.memoryBufferControlIndex = ADC12_A_MEMORY_0;
    configureMemoryParam.inputSourceSelect = ADC12_A_INPUT_TEMPSENSOR;
    configureMemoryParam.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_INT;
    configureMemoryParam.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
    configureMemoryParam.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;

    ADC12_A_configureMemory(ADC12_A_BASE,
                            &configureMemoryParam);

    ADC12_A_enable(ADC12_A_BASE);

    ADC12_A_startConversion(ADC12_A_BASE,
                            ADC12_A_MEMORY_0,
                            ADC12_A_REPEATED_SINGLECHANNEL);
}

void REF_init(void)
{
    while(REF_ACTIVE == Ref_isRefGenBusy(REF_BASE));

    Ref_setReferenceVoltage(REF_BASE,
                            REF_VREF1_5V);

    Ref_enableReferenceVoltage(REF_BASE);

    __delay_cycles(100);
}

Hardware Setup

Explanation

Let’s see first how the REF module is configured. REF module is not clock dependent as like other hardware and doesn’t require use of any external pin.

void REF_init(void)
{
    while(REF_ACTIVE == Ref_isRefGenBusy(REF_BASE));
    Ref_setReferenceVoltage(REF_BASE, REF_VREF1_5V);
    Ref_enableReferenceVoltage(REF_BASE);
    __delay_cycles(100);
}

Using REF module is very easy. We first check its state. We set our desired reference voltage. Here we need the 1.5V reference. After selection, we enable it and wait for some time to get it settled. 

void ADC12_init(void)
{
    ADC12_A_configureMemoryParam configureMemoryParam = {0};

    ADC12_A_init(ADC12_A_BASE,
                 ADC12_A_SAMPLEHOLDSOURCE_SC,
                 ADC12_A_CLOCKSOURCE_ACLK,
                 ADC12_A_CLOCKDIVIDER_1);

    ADC12_A_setupSamplingTimer(ADC12_A_BASE,
                               ADC12_A_CYCLEHOLD_768_CYCLES,
                               ADC12_A_CYCLEHOLD_4_CYCLES,
                               ADC12_A_MULTIPLESAMPLESENABLE);

    ADC12_A_setResolution(ADC12_A_BASE, ADC12_A_RESOLUTION_12BIT);

    configureMemoryParam.memoryBufferControlIndex = ADC12_A_MEMORY_0;
    configureMemoryParam.inputSourceSelect = ADC12_A_INPUT_TEMPSENSOR;
    configureMemoryParam.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_INT;
    configureMemoryParam.negativeRefVoltageSourceSelect =                                      ADC12_A_VREFNEG_AVSS;
    configureMemoryParam.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;

    ADC12_A_configureMemory(ADC12_A_BASE, &configureMemoryParam);

    ADC12_A_enable(ADC12_A_BASE);

    ADC12_A_startConversion(ADC12_A_BASE,
                            ADC12_A_MEMORY_0,
                            ADC12_A_REPEATED_SINGLECHANNEL);
}

We are not using any external channel and so there is no special pin setup for ADC12. As with any hardware, the source of ADC12’s clock is set first. Here the clock source is unscaled ACLK.

UCS_initClockSignal(UCS_ACLK, UCS_XT1CLK_SELECT, UCS_CLOCK_DIVIDER_1);

ACLK is fed with an external 32.768 kHz XT1 crystal running at 32.768 kHz.

Next, we set the sampling timer by defining the sample-hold times. We also set the ADC’s resolution to 12 bits. We can also set 8 and 10 resolution if required.

We also have to let the ADC know which ADC channel to read, where to store the readings and what the reference voltages are for the ADC block. Note that the reference voltage source is the 1.5V source.

We, finally, start the ADC and leave it in repeated single channel conversion mode. In this way, the ADC will continuously measure our desired channel and store readings in an ADC memory location (here Memory 0).

From the graph shown above, we can see that the voltage (ultimately ADC count) vs temperature relationship of the internal temperature sensor is a perfect straight-line and so a linear equation will be needed to describe this relationship. Now if we know the ADC count of the internal temperature sensor, we can determine MSP430’s core temperature.

In the main loop, as I stated earlier, ADC memory 0 location is read and the reading is converted to temperature. The ADC count and the temperature are then shown on an LCD display.

ADC_Count = ADC12_A_getResults(ADC12_A_BASE, ADC12_A_MEMORY_0);

temp = ((((signed long)ADC_Count - 1855) * 667) / 4096);

Demo

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

Related Posts

10 comments

Leave a Reply

Your email address will not be published. Required fields are marked *