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