Tuesday, September 14, 2010

A Simple ADC Example on the LaunchPad

I finally have had the time to write up this post. Hopefully in a few more weeks I will have more time and can keep up a steady stream of posts like I was doing before.

As promised in the last post, this post will discuss the basics of the ADC10 peripheral in the G2231 which comes with the LaunchPad. The code will allow the microcomputer to accept simple ASCII commands sent over the LaunchPad's UART from a computer, and act on those commands. Four commands are implemented for this post; there will be two more in the next post which will add streaming functionality to the code.

The Functionality

The first command tests the overall data throughput we can achieve with the microcomputer. I think this is very important since it allows us to see how well our device is performing, and allows us to determine how fast the ADC should sample in real time. The second command uses a built in voltage divider to measure VCC in relation to an internal reference. The third command measures the internal temperature sensor in relation to a different internal reference. The final command measures the voltage on an external pin, in this case A3 in relation to VCC. Note that this code does not continually sample the ADC channels but will only sample once per command. The next post will discuss continuous sampling of a single channel, which will allow us to build a simple (slow) oscilloscope using only the LaunchPad.

Computer Application

To accompany the code on the MSP430 I have written a simple
computer application using C# which will allow us to easily interface with the device so we do not have to play around with terminal programs. If any of your projects require a computer interface I highly recommend writing a custom application, I really hate fiddling around with terminal programs.

Note: It seems that there are some problems with the installer. I have not figured out what causes the application to work on some computers and not on others. Hopefully it is just my laptop that is having trouble. I recommend having the .NET 4.0 framework installed, even though I wrote the program for 3.5.

The software allows you to connect to any COM port at any baud rate. The baud rate for this post will be 9600, considering that is the maximum of the LaunchPad, but can be changed if you want to use something like an FTDI chip. Make sure that your LaunchPad is plugged in and the VCP is ready before you run the software, the program does not constantly check which ports are available.

The GUI is pretty self explanatory; if you have any questions, bug reports, or suggestions feel free to post. There is also some additional functionality built into the program which is currently disabled; I will provide a second version of the code for the next post when we start streaming data.

The Code

As always the code is available as a gist at http://gist.github.com/593606. If anyone knows how to force the embedded code to show a vertical scroll bar please let me know.


Going Through the Code

Most of this code builds off of previous posts so I will not be going over it in great detail. To read more about the UART please see the previous post. The code is broken down into multiple functions to increase readability. Receive(); is called when the main loop receives a new value from the software UART. This is not necessary, and might actually reduce the efficiency slightly since every function call requires an extra few assembly commands, but in my opinion it’s worth it since the code is more readable and much easier to modify.

After initializing all the registers the device goes into its main loop. So far the ADC is not initialized, only the software UART is set up. The main loop will determine which command was sent and then calls the necessary functions; the loop will also send any converted ADC values out using the software UART. As is true with most of the code I post, the chip goes into LPM0 when there is nothing to do. Before the CPU goes into low power mode it makes sure there are no flags waiting; this keeps the CPU from missing any events. If this was not done it is possible that a second event would be missed, since the CPU was enabled while it was currently in a loop.

The Receive() function will initiate different tasks depending on which command was sent to the computer. The first command which is handled is used to test the speed at which the UART can send data. It will send 256 values, from 0x0000 to 0x00FF, as fast as it can; this data will be received by the computer application and is tested for errors; if there are no errors the speed of the transmission is displayed. It is important to note that 512 bytes are sent total because each value is a 16 bit word. This was chosen for two reasons. First, the MSP430 is a 16 bit processor; second the ADC measurement can not fit in one byte. The next three commands measure an external analog channel (A3), the temperature, and VCC.

About the ADC

I will not be going into how ADCs work, but I will provide some basic theory pertaining to the one in the G2231. The G2231 has a 10 bit ADC and a multitude of channels available to be measured. A few of these channels are internal. They provide access to an integrated temperature sensor, a zero level calibration channel, and a VCC measurement channel.

First things first, the ADC is not some magical device that instantly determines the amplitude of a signal, the ADC conversion takes a certain number of clock cycles and has a limited sample rate determined by this number. Since we are using the LaunchPad's UART for this project, we are limited by the speed at which we can send the values to the computer and not the maximum sample rate. This post will not deal with consecutive measurements from the ADC, so speed is not a problem yet.

The last important theoretical topic I think is important to understand when using an ADC, is the reference voltage. In the ADC world, reference voltages are done differently for different types of converters. For example, what I am about to say does not pertain to the SD16 is the ADC in the F2013. I will probably make a future post about the SD16.

All ADCs need to have a voltage reference which the input voltage can be compared to. There not only is an upper voltage level which the signal is reference to, but also lower level voltage. For the ADC10, you can use the following as references.

  • VCC (upper)
  • VSS (lower)
  • VEREF- (lower)
  • VEREF+ (upper)
  • VREF- (lower)
  • VREF+ (upper)

There are two voltages which can be generated in the G2231, the 1.5V and the 2.5V reference. For this example we will use both the 1.5V and the 2.5V references, in addition to VCC, for all cases VSS will be used as the lower voltage reference.

The Two ADC Functions

The most basic of the ADC measurement functions, is the Single_Measure(chan) function. This does not use any internal references and will measure the channel 'chan' only once.

ADC10CTL0 &= ~ENC; // Disable ADC
ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10IE; // 16 clock ticks, ADC On, enable ADC interrupt
ADC10CTL1 = ADC10SSEL_3 + chan; // Set 'chan', SMCLK
ADC10CTL0 |= ENC + ADC10SC; // Enable and start conversion

The first line is important since this might not be the first time the ADC is setup. Most values in CTL0 cannot be changed until the ENC bit is cleared; this is done to prevent unintentional parameter changes. Since this starts a single conversion we do not need to worry about setting up the timing correctly; I did not want to go into too much detail about this until the next post.

The next line determines how many clock cycles there are per conversion, turns on the ADC, and enables the ADC interrupt. Turning on and off the ADC10 is very important since the ADC will draw power as long as it is turned on. It is good programming practice to turn the device off when you are not using it.

The last two lines set the clock source, the ADC channel, and then start the ADC. This example uses the SMCLK and sets the channel to what was defined by ‘chan’. In order for the ADC to be started both the ENC and ADC10SC bits need to be set. See the Receive() function for how to call this ADC function.

Single_Measure_REF(chan, ref) is the second ADC function in this code. Like the first function this will only read one ADC value; the difference is that this function allows us to use one of the two internal references. See the Receive() function for how this function is used.
Other than the delay, there is only one different line between the first and the second ADC functions. TI recommends using a small delay after turning on an internal voltage reference to allow time for the voltage to settle; thus the small delay. Note: This only needs to be done when the reference is turned on.

ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ref + ADC10IE;

This line of code does 3 things which the last function did not do. In addition to enabling the internal reference and setting whether the 2.5V or 1.5V reference is used, it specifies how the ADC uses these references. This line of code sets the ADC to use SREF_1, which uses VREF+ and VSS for upper and lower references respectively.

The Rest of the Code

The only other part of the code which you might not recognize is the ADC interrupt function. This function will let the main loop know that an ADC value is ready to be transmitted. The transmission is not done directly in the interrupt because one should minimize the amount of code which is present in interrupt routines. This is important since problems can arise when a new interrupt is thrown and the last interrupt has not finished executing yet.

Conclusion

I have decided to not include the source code for the application in this post, but I will be providing it for the next. For now the application is available as an installer only. See the readme provided with the application for a bit more information on how to use it and uninstall the program. If anyone knows how to release a truly standalone .exe using Visual Studio 2010, please let me know since I don’t like installers.

I hope that you find this code useful and are not too disappointed that I have not provided the code for continuous measurement just yet. The code is done, and I just need to test a few more things and write up the next post.

So if you have any questions, or feedback, please comment away. I feel as if this post is not as nicely written as my others, so if there is anything that is not clear please let me know.

Hope you enjoy the code and mini application!

58 comments:

  1. That is an extremely well written post. Good work!
    Waiting on the application code.

    ReplyDelete
  2. Looks awesome, too bad the control app is written for windows lol

    ReplyDelete
  3. Nice post! Did you actually put this up on the 14th and I haven't noticed until now, or was it more recent?

    Anyways, great work on demonstrating the ADC. I'll take a closer look at it now and see what I might have done wrong with my ADC implementation, if anything. Thanks a ton, man!

    ReplyDelete
  4. Thanks guys :-)

    And hhmmmm. Weird, I posted it a few days ago and definitely not as early as the 14th. That must have been when I started writing the post. The code was written almost a month ago, but only posted recently. O well, definitely weird.

    ReplyDelete
  5. Super post! is there an easy way to determine temperature from the serial message? Looking forward to your c# app code;-) great help for beginners like me!

    ReplyDelete
  6. Here is a semi-pseudo code excerpt from the C# program where I calculate temperature. ADCValue is the 10 bit value read by the ADC. The 1.5 used to calculate the voltage is because we are using the 1.5V internal reference on the uC.

    Tconstant = .00355
    V_Toffset = .986
    ADCBitSize = 10^10

    voltage = ADCValue * 1.5 / ADCBitSize;

    temperature = ((voltage - V_Toffset) / Tconstant) * 9 / 5 + 32;

    Note: The constants are different for different microcomputers, so if you are not using the G2231 you will have to look at the datasheets to find correct values.

    Hope that answered your question. :-)

    ReplyDelete
    Replies
    1. @NJC I guess you meant ADCBitSize = 2^10

      O, I implemented a python script to read these measurements. You're code was super helpful. Thanks.

      https://sites.google.com/site/tasifkhan/hardware/msp430

      Python script: https://gist.github.com/4411290

      Delete
    2. Very cool! Thank you for sharing that. I actually have been using Python scripts to capture data recently instead of C#. It is just so much easier for those quick projects. Thank you again!

      Delete
  7. Thanks a lot! Keep up the good work!

    ReplyDelete
  8. Wow....very good article. Regarding about the scrollbox, here is a link:

    http://www.quackit.com/html/codes/html_scroll_box.cfm

    I hope it can help you in creating the vertical scrollbar.

    ReplyDelete
  9. Hey, I have a project which contains a Piezo Vibration Sensor and an XBee. I need the LaunchPad to send the values from the Piezo Vibration Sensor to a receiver XBee. That is all it has to do. Will this code help me to do it?

    This is the sensor: http://www.sparkfun.com/commerce/product_info.php?products_id=9197

    ReplyDelete
  10. Don't stop writing, these articles are wonderful.. very useful stuff.

    ReplyDelete
  11. Your efforts are much appreciated.

    Cheers,
    Tony

    ReplyDelete
  12. Is this C# code or C code? You called it C# code at start.

    ReplyDelete
  13. @Peter
    Thanks for the link! When I have some more time I will look into it. Looks quite promising.

    @Anonymous - Feel free to post that question on 43oh.com/forum if you haven't done so already. There are a ton of knowledgeable people there who can help with your specific question.

    @SB
    There are two parts to this project. The part I mention in the beginning of the post is the computer application. This run's on the computer (it's an .exe). This was written in C#. The second part of the project is the code which the MSP430 runs, this is C code. Hope that clears things up.

    @Everyone
    I'll be doing my best to get the next post done ASAP. The code has been done for quite some time, I just haven't had the time to get it commented and a post written. Please be patient :-) I still plan on making many more posts when I can.

    ReplyDelete
  14. how big is this code
    i was wondering because on the project im working on i need adc but a small code size will this code work for that
    btw i love your blog it is well writen and very helpful

    ReplyDelete
  15. This is a very useful blog. It opens the door for the beginner to understand more complex concepts. I hope you continue on with this series to open up the host computer application and the oscilloscope project. I am looking forward to your next post. THANKS!

    ReplyDelete
  16. Hello NJC, thanks for the great post! I am a beginner with a question on external pin selection. This is probably blindingly obvious, but what exactly is pin A3? I've searched for the reference in the MSP430 and QuickLaunch user guides, but to no avail. Am I correct in assuming that A3 would just be pin 1.3?

    Eric

    ReplyDelete
  17. Thank you everyone :-)

    @Eric, the latest post answers your question (at least I hope so).

    ReplyDelete
  18. Nice, but using the plus operator is somewhat confusing when you're really doing bitwise operations. I'd recommend using | or ^

    ReplyDelete
  19. hello can you explain what is ADC10SHT_3 bit used for,please? i want to sample an analogue signal with 11 kHz. i would use timerA interrupt and enabling conversation in there for getting exact sampling period. But in all examples sampling time is set by these bits i guess? can you tell me how i can set sampling time as i want?

    ReplyDelete
  20. Can you plz. write the codes in detail.
    and draw circuit diagram for temperature sensor.

    ReplyDelete
  21. I'm having a little bit of trouble with your code, particularly the RXByte value. You're bit counter includes 8 bits and the stop bit, but you later remove the start bit as well? The value I transmit to the device and the RXByte value never match up.

    ReplyDelete
  22. @Anonymous - the ADC10SHT_X bits set the "sample-and-hold time". In other words, this is the number of clock cycles you want the conversion to take. This value must be taken into account when determining the sample rate.

    @Bhim - I am not sure what you mean by the writing the codes in detail? And for a circuit diagram of the temperature sensor, please see the 2xx Family User Guide from TI. Look at chapter 20, and more specifically Figure 20-1.

    @Anonymous - If you need a bit more information and haven't seen it yet, I recommend taking a look at my blog posts where I discuss the RX code in detail. A quick answer to your question though, is that the start bit is not removed because it is never represented in the RXByte. The start bit causes an interrupt which starts the whole processes, so we already know that it came in, so there is no need to store this value just to remove it later on.

    I hope that answered all of your questions!

    ReplyDelete
  23. Thank you for the great work done! I'm a great fan :)

    ReplyDelete
  24. Hi, This is really a very useful blog for beginners like me! I am a biomedical engg student and had started working recently with MSP430 Launchpad for one of my projects. I had read through all your previous blogs and learnt many things about this uC. The coding part is clear and understandable. Could you please tell me how to connect the analog signal input to the Launchpad. Its actually from the output of an instrumentation amplifier and filter on the bread board. Which pin should I connect it to? The input pulse signal taken from the wrist is now amplified to around 2V and should I need to make any changes in the code for this application?
    Very eager to hear from you asap:)

    ReplyDelete
  25. @Dhivya - Thank you! I am guessing that you are making an ECG? It doesn't really make a difference in how you hook it up to the MSP430, but I am just curious.

    First, the code does not need to be changed (unless you need to use an external voltage reference, which I am guessing you will not). Next, I must ask you a question. How is your INA being powered? With a single ended supply? What is your reference voltage for the analog circuitry? Let me know what voltages power what and I can help you hook it up to the MSP430. I need to know in detail what is powering your analog circuitry and what is powering your MSP430, and how the power supplies are connected to one another.

    Sorry I can't help right away, but I need a bit more information.

    ReplyDelete
  26. Hi NJC, First of all thanks for your immediate response. As you said, I am not using any external voltage reference. The INA and the active filter are powered by single ended supply of 5V DC (Since I dint want to have any loading issues due to double ended supply to INA and single ended to the filter). Could you please tell me how to connect the output to the Launchpad?in the sense to which pin and do I need to connect any pins other than the output from the filter?

    ReplyDelete
  27. Hi NJC,
    I got the doubt cleared by myself. Sorry for disturbing you and the launchscope is working great. THANKS A LOT!!!!!!!!

    ReplyDelete
  28. Hi NJC,

    I'm planning to use a msp430g2231 to make a basic guitar sound processor.
    It's a little bit related to your project.

    Basically the circuit takes in a guitar signal, multiplies and offsets it to make it louder and to make the "zero" point 1.65v (halfway between the ADC reference voltages of 0 to 3.3v), digitizes it in an msp430g2231 using it's 10 bit ADC, runs it through some software, throws the digtal signal out to an SPI 12 bit DAC (Microchip MCP4921), buffers it through an OPAMP, and sends it out to a guitar amp.

    What kind of algorithm should I use?

    It's my fist time with digital chip.

    Thank you for your help.

    Alex

    ReplyDelete
  29. @Dhivya - Glad you got everything figured out!

    @Alex - I am not sure what you mean by what kind of algorithm should you use. The algorithm you choose will depend on what kind of effect you want from the guitar. The algorithm you choose can be implemented on an MSP430 as it would be on any system (for the most part).

    Hope that helps!

    ReplyDelete
  30. dear NJC, i don't know where i can find "ADC internal", please help me. thanks!

    ReplyDelete
  31. Please post the code of the C# software.It will really help me out.Other wise this is such a great post.Thank you.

    ReplyDelete
  32. @anhduy - I am not sure what you mean by the ADC internal? Do you mean the internal channels of the ADC? Such as the one for the temperature sensor?

    @Fraidycat - I posted the C# code in the following post which completes this series on the ADC. The post on the LaunchScope can be found here:

    http://www.msp430launchpad.com/2010/12/njcs-launchscope-launchpad-oscilloscope.html

    ReplyDelete
  33. hi sir ,am using MSP430FR5739 board and PAN1323 for wireless Transmission of ECG . guide me how i write code for this project.

    ReplyDelete
  34. Sorry, but that's not very "simple" to me..
    You could've juste put the function returning the ADC value, and said how this works and how to configure all that..
    If your're talking ADC, then talk ADC, and not also UART and cie..

    ReplyDelete
  35. Will the above listing overwrite the existing Blinky and Internal Temperature Measurement programs on the MSP430G2231?
    Thanks for the requested info.
    Sira

    ReplyDelete
  36. I keep gettig this error when I compile has anyone had an issue?

    Description Resource Path Location Type
    #20 identifier "TIMERA0_VECTOR" is undefined main.c /ADC_test_Code line 207 C/C++ Problem

    gabe

    ReplyDelete
    Replies
    1. Gabe, are you using CSS? or some other environment? If you are not using CSS, the interrupt vector names will be different.

      Delete
    2. While this post is a bit old, it still can help you make sure your project is setup correctly.

      http://www.msp430launchpad.com/2010/07/my-version-of-getting-started.html

      If you still are having trouble, I would recommend heading over to forum.43oh.com, they are very friendly, helpful, and knowledgeable there.

      Delete
    3. @ Gabriel:
      I think you're using the Rev.1.5 of the lauchpad. If that's the case you need to change the header file:
      #include

      Second, change the timer identifier
      #pragma vector=TIMER0_A0_VECTOR

      It worked fine for me. Thanks NJC.

      Delete
    4. Yasser, thank you so much for clearing that up! I only have the Rev. 1.1 boards, I'll have to get one of the new ones for future blog posts. Thanks again!

      Delete
  37. I loaded the code onto LaunchPad and opened up the ADC interface. I selected the port and 9600 baud rate, pressed validate and it says running, but I can't get any values from the 3 options. Did I miss something?

    ReplyDelete
  38. I have this problem

    MSP430: Error initializing emulator: Could not find MSP-FET430UIF on specified COM port

    why???

    Simon A.

    ReplyDelete
    Replies
    1. It depends on a lot of things. The most important of which, is that you must have the drivers installed to initialize the emulator. Check your device manager (if you are on Windows) to see if the LaunchPad is showing up. Take a look at my getting started post and also Texas Instrument's getting started pages. They can be quite helpful.

      Delete
  39. i have implemented a super neat and and a fast oscilloscope here.
    https://www.youtube.com/watch?v=gp3-xhoz4jk

    ReplyDelete
  40. can we make code to detect voltage at certain level let say 3V and if the battery goes to 2.5V, it will send 0 signal

    ReplyDelete
    Replies
    1. While the MSP430 definitely could be used for this application, I would recommend using analog circuitry. A simple comparator combined with a reference voltage could be used to test if a battery goes below a certain voltage.

      Delete
  41. how should run the program? because I do not get anything

    ReplyDelete
  42. Is it not necessary to enable the channel in ADC10AE0?

    ReplyDelete
  43. Great article! I come back time to time to your blog :-)
    "It is good programming practice to turn the device off when you are not using it." and I am completely agree. However, trying
    ADC10CTL0 &=~ENC;
    ADC10CTL0 &=~ADC10ON;
    ADC10CTL0^=ADC10CTL0;
    in different combinations does not turn ADC10 off.
    Measuring power consumption, I got ~ 1.5uA but after ADC10 is activated it keeps consuming ~ 600uA (LPM3 that is required in my case to use Timer_A).

    ReplyDelete
    Replies
    1. Thank you!

      Note that the third line of code will toggle every bit in the register. I would avoid using this option.

      You should have no issue with using the first two lines to first disable the ADC and then turn it off.

      It can be very challenging debugging what is causing power consumption. Even though it may appear to be the ADC that is causing the power draw, it may not be. I would recommend creating a post on the e2e TI forums or on 43oh.com forums. Both are great resources.

      Delete
  44. What kinds of changes would I have to make for this to run on a MSP430G2553?

    ReplyDelete
    Replies
    1. I figured it out. "TIMERA0_VECTOR" should be changed to "TIMER0_A0_VECTOR". They changed the name of the interrupt vector on the G2553 due to it having more than 1.

      Delete

Note: Only a member of this blog may post a comment.