Analog Sensors and the MSP430 Launchpad


This tutorial is an introduction to interfacing analog sensors to MSP430 series microcontrollers. A sample circuit and program will be developed using a light dependant resistor (LDR) and the MSP430 Launchpad. The circuit functions as a simple light meter. When the light hitting the LDR exceeds a set threshold, the LaunchPad's built in red LED is on. If the light hitting the LDR is below that threshold, the LED is off.

The sample circuit will be based on the Texas Instruments MSP430G2231 microcontroller. The G2231 comes preinstalled on the Launchpad and features a 10-bit analog to digital converter (ADC). Code is developed here using TI's Code Composer Studio (CCS). It is not necessary to completely replicate this setup. The principles presented will be applicable to many MSP430 processors and can be implemented using a variety programming environments.

Analog to Digital Conversion

It is often necessary in robotics and other embedded applications to process real world analog data. Examples of analog inputs would include sensors that measure light, temperature, touch, and sound. Unlike a switch or other digital input which is either on or off, an analog input will vary along a continuous range of possible values.

The basic premise of microcontroller analog to digital conversion is to compare a variable voltage to a reference voltage and return a number which reflects the proportion between the two. For the 10-bit ADC on the G2231, that number will be between 0 and 1023. For example, if an analog input is 2 volts and the LaunchPad's 3.6 Vcc is used as the reference voltage, then the ADC operation would return 568 because 2/3.6 is the same proportion as 568/1023.

The Cadmium Sulfide LDR

The cadmium sulfide (CdS) LDR is a hobbyist mainstay. These sensors are readily available, inexpensive, and easy to use. The electrical resistance of an LDR varies with light - the more light striking the CdS track on the face of the sensor, the lower the resistance. There is considerable variation in the characteristics of available LDRs. A typical part might have a resistance of 300k ohms in complete darkness and drop to 3k ohms in full light. However on some parts dark resistance might be in the megaohm range or at the other extreme a part may have its resistance drop to just a few hundred ohms in full light.

Although we are using the LDR here somewhat generically as an example of an analog sensor, these items are pretty useful and you will see them used in a number of interesting commercial and hobby applications. As simple light or dark detectors they are found in everything from solar garden lights to photovore robots. More elaborate robotics applications include line following, motion detection, color sensing, and proximity sensing. CdS LDRs have even been used for transmit and receive circuits using modulated visible light.

Analog Sensors and the Voltage Divider

In order to use the MSP430 to measure light hitting the LDR, the variable resistance must be channeled in such a way that it produces a variable voltage - this will be accomplished using a voltage divider circuit. A standard two resistor voltage divider is illustrated schematically on the left side of the below graphic.

In these circuits an output is tapped between two resistors and the voltage of that output is proportional to the value of the resistors. If the two resistors are of equal value, then voltage out will be half of the voltage in. The greater the resistance of resistor 1 in relation to resistor 2, the lower the voltage out in relation to voltage in. When an LDR is used as resistor 1 in the circuit, then voltage out will increase as more light shines on the LDR. The voltage output of resistive divider circuits can be calculated using the following formula:

There is some flexibility in selecting a proper resistor for R2 in this project - partly because there is no precise level of light at which we know we want the circuit to trigger and partly because we will calibrate the circuit in software later in the tutorial. If you are so inclined, you might test the LDR with a multimeter at various light levels and select a resistor value close to the meter reading at the light level where you want to toggle the LaunchPad LED. Otherwise, you can take a rough order of magnitude "guess" and say that typical resistor values of 5k to 50k ohms are generally functional in such a circuit. If you have a 10k or 22k ohm resistor handy in your parts box that would be an ideal starting point.

The Assembled Circuit

As promised, the complete circuit for this tutorial is very simple. Pictured below is an illustration of a LaunchPad connected to an LDR resistor divider circuit assembled on a breadboard. The output from the divider circuit goes to pin 1.7 on the LaunchPad, a convenient (if somewhat arbitrary) free pin. Power is provided at 3.6 volts drawn from the USB connection to a computer.

ADC and MSP430

The MSP430's ADC10 module is a powerful and flexible peripheral that provides fast and accurate analog readings. There are numerous options associated with the module's eight registers which enable the process to support a great variety of data collecting needs and function correctly in low voltage scenarios. Choices include multiple clock sources, internal and external voltage references, single-channel, repeated single-channel, sequence, and repeated sequence conversion modes.

While all these options can make the ADC10 appear complex, in basic applications such as our light meter the MSP430's analog to digital functionality is actually straightforward. Here we will present a configuration that will be useful for interfacing many simple sensors and you will hopefully find it easy to understand and integrate into your own projects.

The code sample below is developed using Code Composer Studio's Assembler. If you are unfamiliar with MSP430 Assembly Language syntax, you may wish to review the LaunchPad MSP430 Assembly Language Tutorial. Much of the discussion here will focus on the register settings and operation of the ADC module, so the information provided should be equally useful when developing in C.


;------------------------------------------------------------------------------
;   A software demo of analog to digital conversion to support a sample circuit 
;   using a light dependant resistor (LDR) and the MSP430 Launchpad.  
;   The circuit functions as a simple light meter.  When the light hitting the 
;   LDR exceeds a pre-defined threshold, the LaunchPad's built in red LED is on.  
;   If the light hitting the LDR is below that threshold, the LED is off.
;
;   Built with Code Composer Studio
;------------------------------------------------------------------------------

            .cdecls C,LIST,"msp430g2231.h"    ; cdecls tells assembler to allow
                                              ; the c header file

;------------------------------------------------------------------------------
;   Initialize MSP430
;------------------------------------------------------------------------------

            .text                              ; program start
            .global _main		       ; define entry point

_main       mov.w   #0280h,SP                  ; initialize stack pointer
            mov.w   #WDTPW+WDTHOLD,&WDTCTL     ; stop watchdog timer

;------------------------------------------------------------------------------
;   Configure the ADC10 Control Registers ADC10CTL0 and ADC10CTL1
;        ADC10ON  = enable ADC10
;        ADC10SHT_2 = select clock speed of 16x
;        ADC10IE = interrupt enable
;------------------------------------------------------------------------------

            mov.w   #ADC10SHT_2+ADC10ON+ADC10IE,&ADC10CTL0  

            mov.w   #INCH_7,&ADC10CTL1          ; input channel 7

;------------------------------------------------------------------------------
;   Select and configure inputs and outputs 
;------------------------------------------------------------------------------

            bis.b   #BIT7,&ADC10AE0             ; P1.7 as analog input A7
            bis.b   #BIT0,&P1DIR                ; P1.0 output

;------------------------------------------------------------------------------
;   Read the LDR
;   Compare the result to a threshold value
;   Set the LaunchPad LED on or off
;   Repeat forever
;------------------------------------------------------------------------------

Mainloop    bis.w   #ENC+ADC10SC,&ADC10CTL0      ; initiate conversion
            bis.w   #CPUOFF+GIE,SR               ; turn off CPU (enter low power mode 0)
                                                 ; and enable interrupts - wait in low 
                                                 ; power mode until ADC sample is complete

            bic.b   #BIT0,&P1OUT                 ; start with LED off (P1.0 = 0)
            cmp.w   #01FFh,&ADC10MEM             ; test ADC result stored in ADC10MEM
                                                 ; against a threshold value

            jnc     Mainloop                     ; if the result is less than threshold
                                                 ; loop back with LED still off

            bis.b   #BIT0,&P1OUT                 ; if result is greater than threshold
                                                 ; then turn LED on

            jmp     Mainloop                     ; start the sample process again
                                            
;------------------------------------------------------------------------------
;  The interrupt service routine for the ADC10 interrupt
;------------------------------------------------------------------------------


ADC10_ISR   bic.w   #CPUOFF,0(SP)                 ; when the ADC conversion is complete
            reti                                  ; exit low power mode and resume
                                                  ; processing 
                                            
;------------------------------------------------------------------------------
;   Interrupt Vectors
;------------------------------------------------------------------------------

            .sect   ".reset"                      ; MSP430 RESET Vector
            .short  _main                  
            .sect   ".int05"                      ; ADC10 Vector
            .short  ADC10_ISR               
            .end

Viewing the code at a high level reveals an ADC process broken down into several major steps:

  • Configure the ADC10 control registers
  • Initiate the conversion and set an interrupt to wait for the results
  • Read the results from the ADC10MEM register

The ADC10 module has two associated 16-bit control registers, ADC10CTL0 and ADC10CTL1, where the bulk of the configuration settings are made. The initial setup for those registers comes from the following two lines of code:


            mov.w   #ADC10SHT_2+ADC10ON+ADC10IE,&ADC10CTL0  

            mov.w   #INCH_7,&ADC10CTL1          ; input channel 7

The first line moves the values of ADC10SHT_2, ADC10ON, and ADC10IE into the ADC10CTL0 register. The diagram below shows the status of the register after that move operation.

The Select Reference (SREFx) bits are used to choose the reference voltage. These bits remain in the default state of "000" which means the LaunchPad's 3.6 volt source voltage will be used for the reference.

The ADC10 sample-and-hold time (ADC10SHTx) bits have been set to "10" with the ADC10SHT_2 value, which sets the sample time to 16 clock cycles. The process of analog to digital conversion is not instant, but takes a number of clock cycles. The time required for an accurate conversion varies based upon a number of factors, but put simply: the higher the impedance (resistance) of the input, the longer time is needed for the sampling phase. In this case we've configured a middle-of-the-road delay which should work well for our LDR and similar resistive sensors.

The ADC10 on (ADC10ON) bit turns the module on and the ADC10 interrupt enable (ADC10IE) bit activates the ADC10 interrupt system.

Two additional bits in this register - the enable conversion (ENC) bit and the start conversion (ADC10SC) bit - will come into play later in the program.

The second line of code moves the value of INCH_7 into the ADC10CTL1 register. The diagram below shows the status of the register after that move operation.

The input channel select (INCHx) bits select the channel for a single-conversion or the highest channel for a sequence of conversions. In this case INCH_7 has selected "0111" or "7" which corresponds to the pin we will be using for the input.

The sample-and-hold source select (SHSx) bits have been left to their default "00" which means that the module will use the status of the ADC10SC bit (rather than a timer output) to trigger the sample process.

The ADC10 clock source select (ADC10SSELx) bits have been left to their default "00" which means that the module will use the ADC10's internal oscillator which runs at about 5Mhz.

The conversion sequence mode select (CONSEQx) bits have been left to their default "00" which selects single-channel-single-conversion, the simplest of the conversion modes. In this mode a single channel selected by INCHx is sampled and converted once and the result is written to the ADC10MEM register. To sample and read the input pin again, the start conversion bit must be reset in software.

The remaining bits of the ADC10CTL0 and ADC10CTL1 registers (those not described above) are primarily concerned with more advanced features - understanding their function and use is not necessary for this basic analog sensor project.

As a final step before entering the main body of the code, the following two lines setup the pins required for input and output:


            bis.b   #BIT7,&ADC10AE0             ; P1.7 as analog input A7
            bis.b   #BIT0,&P1DIR                ; P1.0 output

The bits of the ADC10AE0 register enable the corresponding pin for analog input. Bit 0 corresponds to A0, Bit 1 corresponds to A1, etc.

With the "hard part" out of the way, all that remains is to take a reading from the analog input we've configured and apply some program flow logic to the result:


Mainloop    bis.w   #ENC+ADC10SC,&ADC10CTL0      ; initiate conversion
            bis.w   #CPUOFF+GIE,SR               ; turn off CPU (enter low power mode 0)
                                                 ; and enable interrupts - wait in low 
                                                 ; power mode until ADC sample is complete

            bic.b   #BIT0,&P1OUT                 ; start with LED off (P1.0 = 0)
            cmp.w   #01FFh,&ADC10MEM             ; test ADC result stored in ADC10MEM
                                                 ; against a threshold value

            jnc     Mainloop                     ; if the result is less than threshold
                                                 ; loop back with LED still off

            bis.b   #BIT0,&P1OUT                 ; if result is greater than threshold
                                                 ; then turn LED on

            jmp     Mainloop                     ; start the sample process again
                                            
;------------------------------------------------------------------------------
;  The interrupt service routine for the ADC10 interrupt
;------------------------------------------------------------------------------


ADC10_ISR   bic.w   #CPUOFF,0(SP)                 ; when the ADC conversion is complete
            reti                                  ; exit low power mode and resume
                                                  ; processing
                                            
;------------------------------------------------------------------------------
;   Interrupt Vectors
;------------------------------------------------------------------------------

            .sect   ".reset"                      ; MSP430 RESET Vector
            .short  _main                  
            .sect   ".int05"                      ; ADC10 Vector
            .short  ADC10_ISR               
            .end

The enable conversion (ENC) bit and start conversion (ADC10SC) bits in the first line of this code snippet initiate the conversion. The next line sets two bits in the status register (SR): the CPU off (CPUOFF) bit causes the microcontroller to stop processing and sets the MSP430 into one of its five low power modes. While low power modes are indeed useful for conserving power in battery applications, in this case the primary benefit is to pause processing while waiting for the analog sampling process to complete. The general interrupt enable (GIE) bit is also set. When the analog conversion is complete the ADC10 interrupt that was configured earlier is triggered and program flow branches to the interrupt service routine. (Note at the bottom of the code in the section labeled "Interrupt Vectors" that the label ADC10_ISR is defined as the destination for .int05 which is the hard coded interrupt vector for the ADC10 module.) The interrupt service routine turns the CPU on and returns to the main loop where processing was suspended. With the conversion complete the results are available in the ADC10MEM register.

Calibration

Once your circuit is programmed and assembled, you can test the sensitivity by allowing more and less light to strike the surface of the LDR and observing the status of the LED. If we've been lucky, the LaunchPad's LED will turn on and off at a reasonable enough threshold to call the demo a success. Unfortunately, it is more likely that the circuit will either be too sensitive and the LED will always be on, or the circuit will not be sensitive enough and the LED will always be off. The sensitivity can be adjusted by changing the value of the source operand in the following line:


            cmp.w   #01FFh,&ADC10MEM             ; test ADC result stored in ADC10MEM
                                                 ; against a threshold value

For version 1.0 of the demo we've used the hexadecimal value 01FFh which is approximately half of the reference voltage. Increasing the value of this threshold will make the circuit less sensitive, decreasing the value will make it more sensitive.

It is of course possible to adjust the threshold value by trial and error until a satisfactory performance is achieved. However Code Composer Studio's debug tool provides a more precise mechanism to calibrate, enabling us to view the value of the ADC10MEM register in different conditions and select a more appropriate threshold value.

To use the debugger, start CCS as you normally would and select menu item: Target --> Debug Active Project. Debug will open up with five default windows - Debug, Variables, source code, Dissasembly, and Console.

The Registers window does not open by default, it must be loaded via menu item: View --> Registers. As you can see in the image below the registers are organized in a tree structure group by module. In order to view the ADC10 registers, you will need to expand the branch.

The Debug window itself provides various iconic controls for basic functions such as run, halt, step in, step out, and reset.

Additional functions are available via the Target menu. Note that when using the CCS debugger, values are not updated as the application is running. In order to observe multiple readings you will need to either start and stop the application repeatedly via the run and halt icons, or use the step into feature to move through the code line by line.

Next Steps

Now that you are armed with a basic understanding of the ADC10 module, you should take a fresh look at the ADC10 chapter in the MSP430x2xx Family User's Guide (SLAU144). Familiarize yourself with the details of the many register settings and how they can be used to facilitate different sensor and instrumentation applications. We've intentionally stuck to the basics in this tutorial, but there are many good ADC examples published online that run the gamut from student projects to robust medical devices. The speed and accuracy of the MSP430's analog to digital functions are one of the platform's great strengths. With a little investment of time you should find yourself designing and interfacing highly effective sensor solutions for your own projects.



Other Articles You Might Find Enjoyable

LaunchPad MSP430 Assembly Language Tutorial

An Arduino Neural Network

Flexinol and other Nitinol Muscle Wires

Flexinol Control Circuit Using PIC 16F690 and ULN2003A

Design and Build Your Own Robot

Robot Obstacle Detection and Avoidance with the Devantech SRF05 Ultrasonic Range Finder

Setting Up A Differential Drive For Your PICAXE Project

Basic PICAXE Servo Interfacing


Things to Do Here

Home
Links