Transcript Slide 1

Lecture 18: ADC Implementation
http://www.analog.com/library/analogDialogue/archives/39-06/data_conversion_handbook.html
Lecturers:
Professor John Devlin
Mr Robert Ross
Overview
• Using a MAX1111
• Using the internal ADC
2
MAX1111
• If a microprocessor (or a microcontroller
without an ADC) is used, an external ADC
will be required (like the MAX1111)
• Features of the MAX1111
– 8 Bit
– Successive Approximation Conversion
– 8 Channel
– SPI Interface
– Max Conversion time: 20μs
3
MAX1111 Architecture
4
MAX1111 – Analog Input MUX
• MAX1111 has 8 input
channels which are
multiplexed to a hold
circuit
• The user can
alternately select any
channel for sampling
5
MAX1111 – REF Voltage
• A +2.048V Reference
voltage is internally
generated and can be
connected to REFIN
to be used as the
reference voltage
• Alternately a different
reference voltage
may be used
6
MAX1111 – T/H
• Track/Hold Circuitry
• When not converting,
tracks the analog
input voltage
• While converting
holds and stores the
value of the analog
input voltage
7
Track and Hold Circuits
• Holds input voltage constant for the
conversion period.
8
MAX1111 – SAR ADC
• Successive
Approximation Block
• Performs Binary
search
• Uses REFIN as the
reference voltage
• Supplies result to
output shift register
9
MAX1111 - Circuit
10
MSP430 Internal ADC
• The MSP430F2013 has a 16 Bit sigmadelta ADC
• 8 Channel
• Internal temperature sensor
• Internal reference (1.2V)
• Input range = 0-600mV (Gain = 1)
• Internal Clock divider
11
MSP430 ADC – Internal View
12
MSP430 ADC – Code
LED Temp increase display
#include <msp430x20x3.h>
#define ADCDeltaOn 31
static unsigned int LastADCVal;
void main(void)
{
BCSCTL2 |= DIVS_3;
WDTCTL = WDT_MDLY_32;
IE1 |= WDTIE;
P1DIR |= 0x01;
SD16CTL = SD16REFON +SD16SSEL_1;
SD16INCTL0 = SD16INCH_6;
SD16CCTL0 = SD16SNGL + SD16IE ;
_BIS_SR(LPM0_bits + GIE);
}
MSP430 ADC – Code
LED Temp increase display
Interrupt routines
#pragma vector=SD16_VECTOR
__interrupt void SD16ISR(void)
{
if (SD16MEM0 <= LastADCVal +
ADCDeltaOn)
P1OUT &= ~0x01;
else
P1OUT |= 0x01;
LastADCVal = SD16MEM0;
}
// Watchdog Timer interrupt service routine
#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer(void)
{
SD16CCTL0 |= SD16SC;
}
MSP430 ADC – Circuit Diagram
3.3V
4.7K
MSP430F2013
A4: P1.1
1K
15
MSP430 ADC – Code
RESET
MOV.W #0280h,SP
; Set stackpointer (128B RAM device)
stopWDT
MOV.W #WDTPW+WDTHOLD,&WDTCTL ; Stop watchdog timer
setupP1
MOV.B #11111101b,&P1DIR
; Set P1.0 -> P1.7 as outputs, P1.1 as input
BIS.B #00010010b,&P1SEL
; P1.1 and P1.4 TA/SMCLK options
setupP2
BIS.B #0C0h,&P2DIR
; Set P2.6 -> P2.7 as outputs
Set_clock ; Set to calibrated 1MHz Clock
MOV.B &CALBC1_1MHZ,&BCSCTL1
; Set range; DCO = 1 MHz
MOV.B &CALDCO_1MHZ,&DCOCTL
; Set DCO step + modulation
setupADC
MOV.W #0000000011010100b,&SD16CTL
; SMCLK, Ref On
MOV.W #0001000000000010b,&SD16CCTL0
; Unipolar, Start conversion
MOV.B #00000100b,&SD16INCTL0
; A4 (connected to P1.1)
16
MSP430 ADC – Code
MOV.W #0FFFFh, R7
init
MOV.W #04h, R6
AND SD16CCTL0, R6
JNZ read_adc
init2
MOV.W R7, R4
XOR.B #01h, &P1OUT
main
DEC R4
JNZ main
JMP init
read_adc
MOV.W &SD16MEM0, R7
JMP init2
;
; Mask out other bits to get SD16IFG
; ADC conversion completed
; Toggle Pin
; Read ADC value from MEM0
17
Summary
• The MAX1111 is an example of an
external 8 bit serial ADC
• Most microcontrollers have an internal
ADC, which is simple to setup and use.
• The MSP430F2013 has a 16 bit, 8
channel ADC
18
19
// eZ430led2.c - self-dimming LED on P1.0 in eZ430 using SD16A
// PWM controlled by software, about 100Hz from ACLK = VLO,
// SD16A measures light during off phase of PWM
// Calibrated 8MHz DCO, no crystal, ACLK from VLO, power from JTAG (SBW)
// J H Davies, 2007-09-30; IAR Kickstart version 3.42A PAGE 469 => LED TOUCH MOVIE , SLAC136
//---------------------------------------------------------------------#include <io430x20x3.h>
// Header file for this device
#include <intrinsics.h>
// Intrinsic functions
#include <stdint.h>
// Standard integer types
#define
LED_OUT
#define
LED_ANALOG
#define
RANGE
#define PWM_MAX
#define
DIVISOR
#define
SENSE_TIME
uint16_t dutyCycle = PWM_MAX;
P1OUT_bit.P1OUT_0
// Output to LED on P1.0
SD16AE_bit.SD16AE0
// Enable analog input from LED
4096
// Dynamic range of values from SD16
128
// Maximum value of duty cycle
(RANGE/PWM_MAX)
// For converting SD16 -> PWM
2
// Cycles of TACLK needed for SD16
// Duty cycle computed from SD16
// Start at maximum (but LED off)
void main (void)
{
WDTCTL = WDTPW | WDTHOLD;
// Stop watchdog
BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;
BCSCTL2 = DIVS_3;
BCSCTL3 = LFXT1S_2;
P2SEL = 0;
P2REN = BIT6|BIT7;
P1REN = ~BIT0;
P1DIR = BIT0;
LED_OUT = 0;
// Configure SD16A: clock from SMCLK, no division, internal reference on
SD16CTL = SD16XDIV_0 | SD16DIV_0 | SD16SSEL_1 | SD16REFON;
// Unipolar, single convs, OSR = 32, interrupts on finish
SD16CCTL0 = SD16UNI | SD16SNGL | SD16OSR_32 | SD16IE;
// PGA gain = 16, input channel A0+/-, result after 4th conversion
SD16INCTL0 = SD16GAIN_16 | SD16INCH_0 | SD16INTDLY_0;
// Timer_A for software-assisted PWM using channel 1, up to TACCR0 mode
TACCR0 = PWM_MAX + SENSE_TIME;
// Overall period
TACCR1 = dutyCycle;
TACCTL1 = CCIE;
// Start Timer_A from ACLK, undivided, up mode, clear, interrupts
TACTL = TASSEL_1 | ID_0 | MC_1 | TACLR | TAIE;
for (;;) {
__low_power_mode_3();
}
}
// Calibrated range for DCO
// Calibrated tap and modulation
// SMCLK = DCO / 8 = 1MHz
// Select ACLK from VLO (no crystal)
// Digital i/o rather than crystal
// Pull Rs on unused pins (6 and 7)
// Pull Rs on all pins except 0
// To drive LED on P1.0
// LED initially off (active high)
// Initial duty cycle
// Interrupts on compare
// Loop forever
// All action in interrupts
20
//---------------------------------------------------------------------// Interrupt service routine for CCIFG1 and TAIFG; share vector
//---------------------------------------------------------------------#pragma vector = TIMERA1_VECTOR
__interrupt void TIMERA1_ISR (void) // Shared ISR for CCIFG1 and TAIFG
{
switch (__even_in_range(TAIV, TAIV_TAIFG)) {
// Acknowledges int
case 0:
// No interrupt pending
break;
// No action
case TAIV_TAIFG:
// TAIFG vector
// Start PWM duty cycle by turning LED on and updating duty cycle
LED_OUT = 1;
// Turn LED on; duty cycle always > 0
TACCR1 = dutyCycle;
// Update duty cycle from SD16 reading
break;
case TAIV_CCIFG1:
// CCIFG1 vector
// Finish PWM duty cycle by turning off LED, then measuring light level
LED_OUT = 0;
// End of duty cycle: Turn off LED
LED_ANALOG = 1;
// Switch LED to SD16A input A0+
SD16CCTL0_bit.SD16SC = 1;
// Start SD16A conversion
// Change mode from LPM3 to LPM0 on exit to provide SMCLK for SD16
__bic_SR_register_on_exit(LPM3_bits);
__bis_SR_register_on_exit(LPM0_bits);
break;
default:
// Should not be possible: ignore
break;
}
}
//---------------------------------------------------------------------// ISR for SD16A: compute new duty cycle in range [1, PWM_MAX]
//---------------------------------------------------------------------#pragma vector = SD16_VECTOR
__interrupt void SD16_ISR (void)
// Acknowledged when SD16MEM0 read
{
static uint16_t floor = 0xFFFF - RANGE; // Dark reading from SD16
LED_ANALOG = 0;
// Switch LED back to digital output
if (SD16MEM0 < floor) {
// Update floor if new reading is lower
floor = SD16MEM0;
dutyCycle = 1;
// Minimum value; never go down to 0
} else if (SD16MEM0 >= (floor + RANGE - DIVISOR)) {
dutyCycle = PWM_MAX;
// Maximum value (saturates)
} else {
dutyCycle = (SD16MEM0 - floor) / DIVISOR + 1;
}
// Change mode from LPM0 to LPM3 on exit: SMCLK no longer needed
__bic_SR_register_on_exit(LPM0_bits); // (not really necessary)
__bis_SR_register_on_exit(LPM3_bits);
}
21
//******************************************************************************
// MSP430F20x3 Demo - SD16A, Obtain LED-generated voltage, set LED
//
brightness accordingly
//
// Description: The voltage generated by the LED is measured using the
// SD16_A. Based on the result, the LED brightness is adjusted using PWM.
// The LED PWM frequency is 50Hz. An LED voltage reading is obtained
// every 200ms. Based on the defined "Min" and "Max" reference values,
// the LED active duty cycle is adjusted according to the current light
// conditions. The darker the ambient light is, the brigther the LED will
// get illuminated. After starting the code, the board must be exposed
// to darkness for a short moment in order to calibrate the LED's offset
// voltage. The VLO is used to clock Timer_A, which is used for both
// PWM generation but also to derive the timings. A calibration process
// is implemented to accomodate for the variations in VLO frequency.
// Normal operating mode is LPM3.
//
// ACLK = VLO ~ 12kHz, MCLK = SMCLK = SD16CLK = Calibrated 1MHz
////
MSP430F20x3
//
-----------------//
/|\|
XIN|//
||
|
//
--|RST
XOUT|\ | / Light
//
|
|
\|/ Source
//
|
|
----O---//
|
P1.0/A0+|<-->LED
/|\
//
|
|
/|\
//
22
#include "msp430x20x3.h"
// RESULT_MIN determines the SD16 result that is equivalent to maximum
// LED brightness. With increasing ambient light intensity, RESULT_DELTA
// determines after how many SD16 LSB counts based on RESULT_MIN the LED
// is switched off completely.
#define RESULT_MIN
10000
#define RESULT_DELTA
230
extern unsigned int Measure_VLO_SW(void); // External function to measure
// speed of the VLO
// (implemented in Measure_VLO_SW.s43)
int Log[16];
unsigned int LogPtr = 0;
// Debug buffer
void main(void)
{
unsigned int VLO_Period_Length;
int Temp;
int Min = RESULT_MIN;
// Assign initial limits
int Max = RESULT_MIN + RESULT_DELTA;
WDTCTL = WDTPW + WDTHOLD;
// Setup GPIO
P1DIR = 0xFF;
P1OUT = 0;
P2DIR = 0xFF;
P2OUT = 0;
// Stop watchdog timer
// All P1.x outputs
// All P1.x reset
// All P2.x outputs
// All P2.x reset
23
// Setup Clock System
VLO_Period_Length = Measure_VLO_SW(); // Determine VLO period in
us
BCSCTL1 = CALBC1_1MHZ;
// Set DCO = 1MHz
DCOCTL = CALDCO_1MHZ;
BCSCTL3 |= LFXT1S_2;
// ACLK = VLO
// Setup Timer_A
TACCR0 = 20000 / VLO_Period_Length - 1; // Period length = 20ms, f =
50Hz
TACCTL1 = CCIE;
// TACCR1 interrupt enabled
TACTL = TASSEL_1 + MC_1 + TAIE;
// ACLK, Up mode, Overflow int
// Setup SD16_A
SD16CTL = SD16SSEL_1;
// Use SMCLK
SD16INCTL0 = SD16GAIN_4 + SD16INCH_0; // Use channel A0, gain
4x
SD16CCTL0 = SD16SNGL + SD16DF + SD16IE; // Single conversion, 2s
compl.,
// 256OSR, enable interrupts
24
while (1)
{
__bis_SR_register(LPM3_bits + GIE); // Wait for conversion result
Temp = SD16MEM0;
// Get result
Log[LogPtr++] = Temp;
LogPtr &= 0x0f;
// Log data for test purposes
// Wrap buffer pointer
// Re-adjust boundaries in case of a new low-light condition
if (Temp < Min)
// Lower minimum found?
{
Min = Temp;
// Re-adjust boundaries
Max = Temp + RESULT_DELTA;
}
// Limit measured value to Max boundary
if (Temp > Max) Temp = Max;
// Calculate PWM duty cycle based on the relative brightness compared
// to the Min / Max limits and assign result to TACCR1.
//
// TACCR1
Max - SD16_Result
// -------- = ------------------// TACCR0
Max - Min
TACCR1 = (long)TACCR0 * ((long)Max - Temp) / ((long)Max - Min);
}
}
25
// Timer_A interrupt service routine
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A1_ISR(void)
{
static unsigned int TA_PrdCtr = 0;
switch (__even_in_range(TAIV, 0x0e))
{
case 0x02 :
// TACCR1 CCIFG
P1OUT &= ~0x01;
// Disable LED on P1.0
TA_PrdCtr++;
// Increment Timer_A period counter
if (TA_PrdCtr == 10)
// 200ms wrap (50Hz / 10 = 5Hz)
{
TA_PrdCtr = 0;
// Reset Timer_A period counter
SD16AE |= SD16AE0;
// Enable analog function for P1.0
SD16CTL |= SD16REFON;
// Enable SD16_A 1.2V Vref
SD16CCTL0 |= SD16SC;
// Start 1st SD16 conversion
// Switch over to LPM0 on exit, as the DCO is sourcing the SD16_A
__bic_SR_register_on_exit(LPM3_bits);
__bis_SR_register_on_exit(LPM0_bits);
}
break;
case 0x0a :
if (TACCR1)
{
P1OUT |= 0x01;
}
break;
}
}
// TAIFG
// LED duty cycle > 0?
// Enable LED on P1.0
26
// SD16_A interrupt service routine
#pragma vector = SD16_VECTOR
__interrupt void SD16_ISR(void)
{
static unsigned char Flag = 0;
// Flag is used to distinguish
// between 1st and 2nd conversion
if (Flag)
// Flag set? -> "Real Conversion"
{
SD16AE &= ~SD16AE0;
// Disable analog function for P1.0
SD16CTL &= ~SD16REFON;
// Disable voltage reference
__bic_SR_register_on_exit(LPM0_bits); // Wake-up MCU
}
else
// Flag clear? -> "Settling Timer"
{
SD16CCTL0 |= SD16SC;
// Start 2nd SD16 conversion
}
// (this is our "real" conversion)
Flag ^= 0xff;
SD16CCTL0 &= ~SD16IFG;
// Clear interrupt flag
}
27