Add ADC10 application for sampling analog values.
[goodfet] / firmware / apps / adc / adc.c
1 /*! \file adc.c
2
3         \author Scott Livingston
4
5         \brief Simple A/D sampling of a GoodFET pin.  Currently assumes
6                x2274 chip, on a GoodFET31 board.
7
8         \date September 2010
9 */
10
11 #include "apps.h"
12 #include "platform.h"
13 #include "command.h"
14
15 #include "adc.h"
16
17
18 void init_adc10()
19 {
20         ADC10CTL0 = ADC10DISABLE;
21         ADC10CTL0 = REFON | REF2_5V | ADC10ON; // internal reference, at 2.5V
22         ADC10CTL1 = INCH_5 | ADC10SSEL_2; // input A5; using MCLK as clock source.
23         ADC10AE0 = 1<<5; // enable channel A5
24         ADC10SA = 0x200; // Start at bottom of RAM (i.e. lowest address).
25 }
26
27 void uninit_adc10()
28 {
29         ADC10CTL0 = ADC10DISABLE;
30         ADC10CTL0 = ADC10CTL1 = 0x0000;
31 }
32
33
34 //! Handle an ADC10 command; currently assumes x2274, on a GoodFET31 board.
35 void adchandle( unsigned char app, unsigned char verb, unsigned long len )
36 {
37         u16 sample;
38         u16 actual_N;
39
40         switch (verb) {
41
42         case ADC10_INIT:
43                 init_adc10();
44                 txdata(app,verb,0);
45                 break;
46
47         case ADC10_UNINIT:
48                 uninit_adc10();
49                 txdata(app,verb,0);
50                 break;
51
52         case ADC10_1SAMPLE: //! Capture and return a single sample.
53                 sample = sample_adc10();
54                 *cmddata = sample & 0xff;
55                 *(cmddata+1) = sample >> 8;
56                 txdata(app,verb,2);
57                 break;
58
59         case ADC10_NSAMPLE: //! Capture and return a sequence, with constanst Sps.
60                 if (*cmddata == 0)
61                         *cmddata = 0xff; // Max length
62                 /*if (len == 0) {
63                         actual_N = nsamples_adc10( 0xff, 3, 8 ); 
64                 } else if (len == 1) {
65                         actual_N = nsamples_adc10( *cmddata, 3, 8 );
66                         } else {*/
67                         actual_N = nsamples_adc10( *cmddata, *(cmddata+1), *(cmddata+2) );
68 //              }
69                 txdata( app, verb, actual_N*2 );
70                 break;
71
72         default:
73                 debugstr( "Verb unimplemented in ADC10 application." );
74                 txdata(app,NOK,0);
75                 break;
76
77         }
78 }
79
80
81 u16 sample_adc10()
82 {
83         // We assume the ADC10 module has already been initialized.
84         u16 result;
85         u16 ctl0_start = ADC10CTL0;
86
87         ADC10CTL0 |= ADC10SHT_3; /* Switch to longest sample-and-hold time
88                                                                                                                   (i.e. 64 ticks), to increase likelihood
89                                                                                                                   of success given we are only taking a
90                                                                                                                   single sample here. */
91         ADC10CTL0 |= 0x0003; // ENC | ADC10SC
92         while (!(ADC10CTL0 & ADC10IFG)) ;
93         result = ADC10MEM;
94         
95         ADC10CTL0 = ctl0_start; /* Return ADC10 control register to original
96                                                                                                            state, at calling of this function. */
97         return result;
98 }
99
100
101 u16 nsamples_adc10( u8 N_count, u8 t_sample, u8 clock_div )
102 {
103         u16 ctl0_start;
104         u16 ctl1_start;
105
106         //while (ADC10BUSY) ; // Wait till any pending operation completes.
107
108         ctl0_start = ADC10CTL0;
109         ctl1_start = ADC10CTL1; // Save control registers states.
110         
111         if (N_count > (CMDDATALEN-4)/2) { // Bound number of samples to be obtained here.
112                 N_count = (CMDDATALEN-4)/2;
113         }
114         clock_div--; // Place in form appropriate for control register
115
116         // Additional preparations of ADC10
117         ADC10CTL0 |= (t_sample&0x3)<<11 | MSC;
118         ADC10CTL1 |= (clock_div&0x7)<<5
119                 | ADC10SSEL_2 // source from MCLK (should be 16 MHz),
120                 | CONSEQ_2; // repeat-single-channel mode.
121
122         // Setup DTC (to make acquisition of block of samples easy/reliable)
123         ADC10DTC0 = 0x0000;
124         ADC10DTC1 = N_count;
125         ADC10SA = cmddata;
126
127         ADC10CTL0 |= 0x0003; // ENC | ADC10SC
128
129         while (!(ADC10CTL0 & ADC10IFG)) ;
130         
131         ADC10CTL0 = ctl0_start; // Restore control registers
132         ADC10CTL1 = ctl1_start;
133         ADC10DTC1 = 0x0000; // ...and disable DTC
134
135         return N_count;
136 }