Tutorial: ADC with the Freedom Board

Unlike other boards from Freescale, the FRDM-KL25Z has no potentiometer or analog components on it. But in many applications an ADC conversion is needed, so here we go with a tutorial reading in an external potentiometer with Eclipse, CodeWarrior and Processor Expert. For this tutorial I have a 10k Ohm linear potentiometer connected to the Freedom board:

Linear potentiometer with the FRDM-KL25Z

Linear potentiometer with the FRDM-KL25Z

💡 While this tutorial is for the Freedom KL25Z project, things are generic for any microcontroller supported by Processor Expert. Only the port/pins might be different.

The black (GND) and red (3.3V) wires are connected to the ‘full range’ connectors for the potentiometer, while the white wire is connected to the current position pin. The white wire is connected to the ‘Arduino’ A0 pin of the Freedom board:

Schematic with 10k Ohm Potentiometer

Schematic with 10k Ohm Potentiometer

Creating the Project

With CodeWarrior for MCU10.3, I create a project with the ‘New Project Wizard’ (menu File > New > Bareboard Project). I give a project name, select my microcontroller (KL25Z), connection (OpenSDA), Toolchain (gcc) and Rapid Application Development (Processor Expert).

Adding ADC Component

From the Components Library I add the ADC component to the project:

Adding ADC Component

Adding ADC Component

💡 If the Component Library view is not open, use the menu Processor Expert > Show Views.

Configuring ADC Component

With the component added, it shows with an error marker as I need to configure it first:

Added ADC Component

Added ADC Component

First, to configure the pin to be used. According to the schematics, the A0 pin is connected to ADC0_SE8/PTB0:

Selecting ADC Pin

Selecting ADC Pin

Next to configure the conversion time. For this I click the ‘…’ button:

❗ I need to click *into* the field to show the ‘…’ button.

Conversion Time Setup

Conversion Time Setup

This opens a dialog where I can specify the conversion time. I select one of the available values from the right side (e.g. double clicking on it):

Conversion Time Selection

Conversion Time Selection

💡 The Conversion Time specifies how fast the AD conversion shall be performed. This has of course an impact on the timing and quality of the conversion. For this tutorial, we simply select a timing.

Generating Code

With everything configured, I use the ‘Generate Code’ button:

Code Generation

Code Generation

The generated code ends up in the Generated_Code folder of the project:

Generated Code Folder

Generated Code Folder

In the next step, I’m going to use it in the main() inside the file ProcessorExpert.c.

Doing the Conversion

Instead of typing the code, I can drag&drop the method names to my source:

Drag and Drop of Method

Drag and Drop of Method

The method Measure() needs an extra boolean argument if it shall wait for the finish of the conversion (yes, I want to wait). And I’m not interested in the error return code, so I cast it to void:

  /* Write your code here */
(void)AD1_Measure(TRUE);
/* For example: for(;;) { } */

To get the conversion value, the method GetValue16() is used which returns the 16bit result. For the result I define a 16bit global variable:

static uint16_t value;

And do the AD conversion in an endless loop:

for(;;) {
  (void)AD1_Measure(TRUE); /* do conversion and wait for the result */
  (void)AD1_GetValue16(&value); /* get the result into value variable */
}

With this, it could look like this:

ProcessorExpert.c

ProcessorExpert.c

Build and Debug

With the menu Project > Build Project I compile my project. I press the ‘Debug’ button I download the program to the board and let it run.

💡 To watch the variable changing in the debugger while it is running, see Live View for Variables and Memory.

In the Variables View, I can see how the value changes:

Value in Variables View

Value in Variables View

Using Interrupts

So far I was waiting for the conversion to be finished with the TRUE parameter to the Measure() method. By default, the ADC component already has interrupts enabled:

Interrupts Enabled

Interrupts Enabled

If I use interrupts and do not wait for the conversion to be finished, I get notified by the OnEnd() event:

OnEnd Event

OnEnd Event

Double-Clicking on the OnEnd() method gets me to the Events.c file, where I can fill in the code:

AD1_OnEnd Hook

AD1_OnEnd Hook

To keep things simple, I set a boolean flag:

void AD1_OnEnd(void)
{
  extern volatile bool AD_finished;
  AD_finished = TRUE;
}

💡 I have marked this variable with volatile as the variable is changed in an interrupt service routine, and used the same time from the main program.  The volatile prevents the compiler to keep that variable in a register.

I add this boolean variable to my global variables in Processor Expert.c:

static uint16_t value;
volatile bool AD_finished;

💡 Once things are working, consider to put that AD_finished variable declaration into a header file.

Then I change my loop to start the conversion without waiting, so I can do something else while the conversion is going on:

for(;;) {
  AD_finished = FALSE; /* reset flag */
(void)AD1_Measure(FALSE); /* AD_finished will be set to TRUE once */
while(!AD_finished) {
/* do something else here... */
}
/* AD_finished set to TRUE by the interrupt to indicate the result is ready */
(void)AD1_GetValue16(&value); /* get the result into value variable */
}

Multiple Channels

So far I was using a single AD value. But the component (and hardware) can be configured to convert multiple channels. To read in as well the A1 pin which is mapped to PTB1 I use the ‘+’ icon to add another channel:

💡 I need to click into the field to have the ‘+’ and ‘-‘ icons.

Added another channel

Added another channel

I can use the same methods as above (Measure() and GetValue16(), but as GetValue16() is using a pointer to an array of values, I need to make sure I pass the right number of items to it:

byte AD1_GetValue16(word *Values)

The component has created a #define for the number of channels for me (CHANNEL_COUNT) which I can use for my value variable:

static uint16_t value[AD1_CHANNEL_COUNT];
volatile bool AD_finished;

To the GetValue16() method, I pass the address of the first item:

(void)AD1_GetValue16(&value[0]);

With this, my code works with multiple channels.

Summary

With Processor Expert, it is not very difficult to use ADC conversion. Once familiar with the concept, it is easy to add multiple channels and to use interrupt synchronization. I do not cover here advanced features like using DMA (as not supported out oft the box with Processor Expert yet), or using different conversion modes. I think this is enough for now to get you started :razz:.

Happy ADCing 🙂

Advertisements

168 thoughts on “Tutorial: ADC with the Freedom Board

  1. Good day erich, I have a problem. I declare my variables as well
    Static uint16_t potnivel [ADC_CHANNEL_COUNT]

    Static uint16_t sensorlm35 [ADC_CHANNEL_COUNT];
    And I use them like that
    ADC_Measure (TRUE);
    ADC_GetValue16 (& potential [0]);
    Level = ((90/65535) * pot level [0]) + 10;

    This does not send me error but it does not work, what am I doing wrong? 😦

    Like

    • Hi Juan,
      I don’t see anything wrong with your code. You say it does not send you any error, but I don’t see any error handling in your code (you are not checking the return error codes?).
      But what means ‘does not work’? Your are always getting zero back from GetValue16()? And if you measure the voltage with a multimeter, what do you get?
      The other thing is: 90/65535 will be zero, dos that does not make any sense?
      I hope this helps,
      Erich

      Like

  2. Hello,

    I am using bandgap voltage as a reference for my a2d channels and measuring the temperature.Some hoe in repeated measurements I see the ADCreading of my bandgap voltage to be half of what is expected.

    As in,

    ADC Resolution / System Voltage = ADCreading BandGap/ BandGap voltage
    //4095/5(VoltsAnalog) = ADCreading BandGap/1.16(VoltsAnalog)
    //ADCreading BandGap = 950 (approx)

    What I am looking at the ADCResult register is around 350 to 500 value in decimal.
    I would like to know what steps should be taken to debug this. Thanks.

    I am using a 5 Volt system MKE02Z-FRDM (MKE02Z64VQH4) device with Channel 0 as bandgap and Channel 1 as internal temperature sensor. Processor Expert is being used to setup the software for this.

    Like

    • I’m affraid that I don’t have a MKE02, so won’t be able to help out here. The KE devices are so very different from the other Kinetis devices, so I have avoided that extra complexity in my designs and using the usual Kinetis and LPC devices instead.

      Like

    • Hi Priya,
      I’m also trying to to configure KE02 ADC, I’m bit new to ADCs, infact 1st experience. Can you please provide me some help, how should I get started.
      Taimur

      Like

  3. Great tutorial, exactly what I was looking for.
    Although I ran into an issue: I can’t seem to get the multiple channel read working. I got the single channel read going. But when I switch to more than one channel the generated code doesn’t seem to change.
    I have tried to generate the code multiple times with slightly different settings. I tried generating different functions to call the channels specifically but that doesn’t work either – when I do try and read multiple channels it just reads the same adc channel over and over again.
    Has anyone else experiences this issue? Does anyone have any direction they lend me?
    thanks

    Like

      • I have tried numerous different calls to get multiple ADC values.
        Here is the code that is generated to retrieve multiple values with a pointer – to me it doesn’t appear to be correct.

        byte AD1_GetValue16(word *Values)
        {
        if (!OutFlg) { /* Is measured value(s) available? */
        return ERR_NOTAVAIL; /* If no then error */
        }
        *Values = (word)(RVL[0] << 0x04U); /* Store conversion data to user buffer */
        return ERR_OK; /* OK */
        } //Can someone confirm this is what should be generated when for retrieving multiple values?

        And yes, I created an array large enough to hold all the values.
        The call looks like this:

        static uint16_t theAdcVals[2];

        AD1_GetValue1((uint16_t*)theAdcVals);
        Also tried:
        AD1_GetValue1(&theAdcVals[0]);

        thanks for the help.

        Like

  4. Hi Enrich,
    Have you used KE02 ADC???
    I’m trying to configure it for a couple of days now for my application without success, and there is not much information regarding KE02 ADC on web.

    Like

    • I quickly tried it with Kinetis Design Studio (KDS) for a KE02 and added the ADC Processor Expert component, and that worked fine. So this might be a good starting point for you too?

      Like

      • Hi Erich,
        Thanks for your reply. Yeah I’m using code warriors. I already added ADC_LDD component, currently i’m trying to configure ADC for single channel, but I’m not able to call the functions properly to get the measured valued.
        I tried the steps you mentioned in this tutorial, but without success. Can you please provide the source code you used to get the measured values?

        Like

        • Hi Erich,
          Unfortunately still I’m stuck. I’m getting nothing,the buffer variable isn’t updating. I want to measure a voltage level from a GPIO. I have to measure four channels, but initially i’m trying to get measurment for a single channel. I hv no idea what I’m doing wrong.

          Like

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.