Merge branch 'master' of gitlab:wintermute/hackover2013-badge-firmware
[hackover2013-badge-firmware.git] / core / adc / adc.c
1 /**************************************************************************/
2 /*!
3 @file adc.c
4 @author K. Townsend (microBuilder.eu)
5 @date 22 March 2010
6 @version 0.10
7
8 @section Description
9
10 SW-based single-channel A/D conversion. If you wish to convert
11 multiple ADC channels simultaneously, this code will need to be
12 modified to work in BURST mode.
13
14 @section Example
15
16 @code
17 #include "core/cpu/cpu.h"
18 #include "core/adc/adc.h"
19
20 void main (void)
21 {
22 cpuInit();
23 adcInit();
24
25 uint32_t results = 0;
26 while(1)
27 {
28 // Get A/D conversion results from A/D channel 0
29 results = adcRead(0);
30 }
31 }
32 @endcode
33
34 @section LICENSE
35
36 Software License Agreement (BSD License)
37
38 Copyright (c) 2010, microBuilder SARL
39 All rights reserved.
40
41 Redistribution and use in source and binary forms, with or without
42 modification, are permitted provided that the following conditions are met:
43 1. Redistributions of source code must retain the above copyright
44 notice, this list of conditions and the following disclaimer.
45 2. Redistributions in binary form must reproduce the above copyright
46 notice, this list of conditions and the following disclaimer in the
47 documentation and/or other materials provided with the distribution.
48 3. Neither the name of the copyright holders nor the
49 names of its contributors may be used to endorse or promote products
50 derived from this software without specific prior written permission.
51
52 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
53 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
54 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
56 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
57 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
58 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
59 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
60 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
61 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62
63 */
64 /**************************************************************************/
65
66 #include "adc.h"
67
68 static bool _adcInitialised = false;
69 static uint8_t _adcLastChannel = 0;
70
71 /**************************************************************************/
72 /*!
73 @brief Returns the conversion results on the specified ADC channel.
74
75 This function will manually start an A/D conversion on a single
76 channel and return the results.
77
78 @param[in] channelNum
79 The A/D channel [0..7] that will be used during the A/D
80 conversion. (Note that only A/D channel's 0..3 are
81 configured by default in adcInit.)
82
83 @return 0 if an overrun error occured, otherwise a 10-bit value
84 containing the A/D conversion results.
85 @warning Only AD channels 0..3 are configured for A/D in adcInit.
86 If you wish to use A/D pins 4..7 they will also need to
87 be added to the adcInit function.
88 */
89 /**************************************************************************/
90 uint32_t adcReadSingle (uint8_t channelNum)
91 {
92 if (!_adcInitialised) adcInit();
93
94 uint32_t regVal, adcData;
95
96 /* make sure that channel number is 0..7 */
97 if ( channelNum >= 8 )
98 {
99 // ToDo: Change this to throw an exception back
100 channelNum = 0;
101 }
102
103 /* Deselect all channels */
104 ADC_AD0CR &= ~ADC_AD0CR_SEL_MASK;
105
106 /* Start converting now on the appropriate channel */
107 ADC_AD0CR |= ADC_AD0CR_START_STARTNOW | (1 << channelNum);
108
109 /* wait until end of A/D convert */
110 while ( 1 )
111 {
112 // Get data register results for the requested channel
113 switch (channelNum)
114 {
115 case 0:
116 regVal = (*(pREG32(ADC_AD0DR0)));
117 break;
118 case 1:
119 regVal = (*(pREG32(ADC_AD0DR1)));
120 break;
121 case 2:
122 regVal = (*(pREG32(ADC_AD0DR2)));
123 break;
124 case 3:
125 regVal = (*(pREG32(ADC_AD0DR3)));
126 break;
127 case 4:
128 regVal = (*(pREG32(ADC_AD0DR4)));
129 break;
130 case 5:
131 regVal = (*(pREG32(ADC_AD0DR5)));
132 break;
133 case 6:
134 regVal = (*(pREG32(ADC_AD0DR6)));
135 break;
136 case 7:
137 regVal = (*(pREG32(ADC_AD0DR7)));
138 break;
139 default:
140 regVal = (*(pREG32(ADC_AD0DR0)));
141 break;
142 }
143
144 /* read result of A/D conversion */
145 if (regVal & ADC_DR_DONE)
146 {
147 break;
148 }
149 }
150
151 /* stop ADC */
152 ADC_AD0CR &= ~ADC_AD0CR_START_MASK;
153
154 /* return 0 if an overrun occurred */
155 if ( regVal & ADC_DR_OVERRUN )
156 {
157 return (0);
158 }
159
160 /* return conversion results */
161 adcData = (regVal >> 6) & 0x3FF;
162 return (adcData);
163 }
164
165 /**************************************************************************/
166 /*!
167 @brief Returns the oversampled conversion results on the specified ADC channel.
168
169 This function will manually start A/D conversions on a single
170 channel sum and divide results to get a value of increased resolution.
171
172 Read more: AVR121: Enhancing ADC resolution by oversampling [http://www.atmel.com/Images/doc8003.pdf]
173
174 @param[in] channelNum
175 The A/D channel [0..7] that will be used during the A/D
176 conversion. (Note that only A/D channel's 0..3 are
177 configured by default in adcInit.)
178
179 @param[in] extraBits
180 Additional bits you want to add to resolution. Hardware resolution is
181 10bits, if 6 is psecified as extraBits, the resolution will be increated to
182 16 bit.
183
184 @return 0 if an overrun error occured, otherwise a 10-bit value
185 containing the A/D conversion results.
186
187 @warning Only AD channels 0..3 are configured for A/D in adcInit.
188 If you wish to use A/D pins 4..7 they will also need to
189 be added to the adcInit function.
190 */
191 /**************************************************************************/
192
193 uint32_t adcReadOversampled (uint8_t channelNum, uint8_t extraBits) {
194 uint32_t sampleCount = 1 << (extraBits * 2);
195 uint16_t i;
196 uint32_t adcOversampled = 0;
197
198 for(i = 0; i < sampleCount; i++) {
199 uint16_t adcValue = adcReadSingle(channelNum);
200 adcOversampled += adcValue;
201 }
202
203 adcOversampled = adcOversampled >> extraBits;
204 return adcOversampled;
205 }
206
207 /**************************************************************************/
208 /*!
209 @brief Returns the conversion results on the specified ADC channel.
210
211 This function will manually start an A/D conversion on a single
212 channel and return the results. If ADC Averaging is enabled
213 (via ADC_AVERAGING_ENABLE) the specified number of values will be
214 samples from the ADC and the average value will be returned.
215
216 @param[in] channelNum
217 The A/D channel [0..7] that will be used during the A/D
218 conversion. (Note that only A/D channel's 0..3 are
219 configured by default in adcInit.)
220
221 @return 0 if an overrun error occured, otherwise a 10-bit value
222 containing the A/D conversion results.
223
224 @warning Only AD channels 0..3 are configured for A/D in adcInit.
225 If you wish to use A/D pins 4..7 they will also need to
226 be added to the adcInit function.
227 */
228 /**************************************************************************/
229 uint32_t adcRead (uint8_t channelNum)
230 {
231 if (!_adcInitialised) adcInit();
232
233 #if ADC_AVERAGING_ENABLE
234 uint32_t adcTotal, i;
235 adcTotal = 0;
236 for (i=0; i<ADC_AVERAGING_SAMPLES;i++)
237 {
238 adcTotal += adcReadSingle(channelNum);
239 }
240 return adcTotal/ADC_AVERAGING_SAMPLES;
241 #else
242 return adcReadSingle(channelNum);
243 #endif
244 }
245
246 /**************************************************************************/
247 /*!
248 @brief Initialises the A/D converter and configures channels 0..3
249 for 10-bit, SW-controlled A/D conversion.
250
251 @return Nothing
252 */
253 /**************************************************************************/
254 void adcInit (void)
255 {
256 /* Disable Power down bit to the ADC block. */
257 SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_ADC);
258
259 /* Enable AHB clock to the ADC. */
260 SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_ADC);
261
262 /* Digital pins need to have the 'analog' bit set in addition
263 to changing their pin function */
264
265 /* Set AD0 to analog input */
266 IOCON_JTAG_TDI_PIO0_11 &= ~(IOCON_JTAG_TDI_PIO0_11_ADMODE_MASK |
267 IOCON_JTAG_TDI_PIO0_11_FUNC_MASK |
268 IOCON_JTAG_TDI_PIO0_11_MODE_MASK);
269 IOCON_JTAG_TDI_PIO0_11 |= (IOCON_JTAG_TDI_PIO0_11_FUNC_AD0 &
270 IOCON_JTAG_TDI_PIO0_11_ADMODE_ANALOG);
271
272 /* Set AD1 to analog input */
273 IOCON_JTAG_TMS_PIO1_0 &= ~(IOCON_JTAG_TMS_PIO1_0_ADMODE_MASK |
274 IOCON_JTAG_TMS_PIO1_0_FUNC_MASK |
275 IOCON_JTAG_TMS_PIO1_0_MODE_MASK);
276 IOCON_JTAG_TMS_PIO1_0 |= (IOCON_JTAG_TMS_PIO1_0_FUNC_AD1 &
277 IOCON_JTAG_TMS_PIO1_0_ADMODE_ANALOG);
278
279 /* Set AD2 to analog input */
280 IOCON_JTAG_TDO_PIO1_1 &= ~(IOCON_JTAG_TDO_PIO1_1_ADMODE_MASK |
281 IOCON_JTAG_TDO_PIO1_1_FUNC_MASK |
282 IOCON_JTAG_TDO_PIO1_1_MODE_MASK);
283 IOCON_JTAG_TDO_PIO1_1 |= (IOCON_JTAG_TDO_PIO1_1_FUNC_AD2 &
284 IOCON_JTAG_TDO_PIO1_1_ADMODE_ANALOG);
285
286 /* Set AD3 to analog input */
287 IOCON_JTAG_nTRST_PIO1_2 &= ~(IOCON_JTAG_nTRST_PIO1_2_ADMODE_MASK |
288 IOCON_JTAG_nTRST_PIO1_2_FUNC_MASK |
289 IOCON_JTAG_nTRST_PIO1_2_MODE_MASK);
290 IOCON_JTAG_nTRST_PIO1_2 |= (IOCON_JTAG_nTRST_PIO1_2_FUNC_AD3 &
291 IOCON_JTAG_nTRST_PIO1_2_ADMODE_ANALOG);
292
293 /* Note that in SW mode only one channel can be selected at a time (AD0 in this case)
294 To select multiple channels, ADC_AD0CR_BURST_HWSCANMODE must be used */
295 ADC_AD0CR = (ADC_AD0CR_SEL_AD0 | /* SEL=1,select channel 0 on ADC0 */
296 (((CFG_CPU_CCLK / SCB_SYSAHBCLKDIV) / 1000000 - 1 ) << 8) | /* CLKDIV = Fpclk / 1000000 - 1 */
297 ADC_AD0CR_BURST_SWMODE | /* BURST = 0, no BURST, software controlled */
298 ADC_AD0CR_CLKS_10BITS | /* CLKS = 0, 11 clocks/10 bits */
299 ADC_AD0CR_START_NOSTART | /* START = 0 A/D conversion stops */
300 ADC_AD0CR_EDGE_RISING); /* EDGE = 0 (CAP/MAT signal falling, trigger A/D conversion) */
301
302 /* Set initialisation flag */
303 _adcInitialised = true;
304
305 /* Set last channel flag to 0 (initialised above) */
306 _adcLastChannel = 0;
307
308 return;
309 }
This page took 0.073518 seconds and 5 git commands to generate.