PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
adc7scan1_dma_conv_order.c
Go to the documentation of this file.
1 /*
2  * "Copyright (c) 2008 Robert B. Reese, Bryan A. Jones, J. W. Bruce ("AUTHORS")"
3  * All rights reserved.
4  * (R. Reese, reese_AT_ece.msstate.edu, Mississippi State University)
5  * (B. A. Jones, bjones_AT_ece.msstate.edu, Mississippi State University)
6  * (J. W. Bruce, jwbruce_AT_ece.msstate.edu, Mississippi State University)
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation for any purpose, without fee, and without written agreement is
10  * hereby granted, provided that the above copyright notice, the following
11  * two paragraphs and the authors appear in all copies of this software.
12  *
13  * IN NO EVENT SHALL THE "AUTHORS" BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
15  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE "AUTHORS"
16  * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17  *
18  * THE "AUTHORS" SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE "AUTHORS" HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
23  *
24  * Please maintain this header in its entirety when copying/modifying
25  * these files.
26  *
27  *
28  */
29 
30 #include "pic24_all.h"
31 #include "stdio.h"
32 
33 #ifndef _DMA0IF
34 #error "This processor selection does not have a DMA module; this code example is incompatible with a PIC24 CPU that does not have DMA."
35 
36 #endif
37 
38 /** \file
39  * Performs a basic config of the ADC and samples seven channels sequentially
40  * with automatic channel scanning. ADC values are 10-bit results.
41  * Samples are obtained continuously, and DMA is used to write a memory buffer.
42  * The DMA is done using Ordered mode, with a block size determined by CONVERSIONS_PER_INPUT.
43  * For a block size = 1 and ordered mode, this means that results are written to the DMA buffer
44  * in the order in which the
45  * conversion is done, in this case:
46  * AN0 result is written to DMA buffer[0]
47  * AN1 result to the DMA buffer[1],
48  * AN4 result to the DMA buffer[2],
49  * AN5 result to the DMA buffer[3], etc.
50  * Uses DMA completion interrupts to get values from the DMA memory buffer.
51  * The DMA mode for Ordered mode should be DMA_AMODE_REGISTER_POSTINC (register mode, post increment)
52  * Main routine fetches the "latest" values from memory.
53  * This code assumes a maximum of 16 ANx inputs.
54  * For block size > 1, the main code averages the values for a given channel.
55  * Compare the main averaging code with 'adc7scan1_dma1.c' to see the difference in
56  * how results are stored in the buffer for 'ordered' mode versus 'scatter/gather'.
57  *
58  * Conversion results are printed to screen to match adc2pots1.c project
59  * (HEX values and voltages are printed.)
60  * This is only for PIC24 CPUs with DMA.
61 */
62 
63 #define CONFIG_LED2() CONFIG_RB5_AS_DIG_OUTPUT()
64 #define LED2 _LATB5
65 
66 
67 // uncomment the next line to setup this project for a 12-bit ADC
68 //#define USE_12BIT_ADC
69 
70 #ifdef USE_12BIT_ADC
71 #define ADC_LEN 12
72 #define ADC_NSTEPS 4096
73 #define ADC_12BIT_FLAG 1
74 #else
75 #define ADC_LEN 10
76 #define ADC_NSTEPS 1024
77 #define ADC_12BIT_FLAG 0
78 #endif
79 
80 
81 
82 /***
83  *** HERE IS THE CODE!
84  ***
85  ***/
86 
87 // set this to one of the values of
88 // 1, 2, 4, 8, 16, 32, 64, 128
89 #define CONVERSIONS_PER_INPUT 1
90 #define MAX_CHANNELS 16
91 //DMA transfer size is in words.
92 #define MAX_TRANSFER (CONVERSIONS_PER_INPUT*MAX_CHANNELS) //make power of two for alignment to work
93 
94 //DMA buffers, alignment is based on number of bytes
95 uint16_t au16_bufferA[MAX_TRANSFER] __attribute__((space(dma),aligned(MAX_TRANSFER*2)));
96 uint8_t u8_NumChannelsScanned; //need this global for the main averaging code
97 
98 //generic DMA/ADC configuration function, enables scanning, uses DMA channel 0
99 //returns the number of channels that are scanned as specified by the mask.
100 uint8_t configDMA_ADC(uint16_t u16_ch0ScanMask, \
101  uint8_t u8_autoSampleTime, \
102  uint8_t u8_use12bit,
103  uint8_t u8_useScatterGather,
104  uint8_t u8_dmaLocsPerInput) {
105  uint8_t u8_i, u8_nChannels=0;
106  uint16_t u16_mask = 0x0001;
107  uint16_t u16_dmaMode;
108 
109  // compute the number of Channels the user wants to scan over
110  for (u8_i=0; u8_i<16; u8_i++) {
111  if (u16_ch0ScanMask & u16_mask)
112  u8_nChannels++;
113  u16_mask<<=1;
114  } //end for
115 
116  if (u8_autoSampleTime > 31) u8_autoSampleTime=31;
117 
118  AD1CON1bits.ADON = 0; // turn off ADC (changing setting while ADON is not allowed)
119  /** Configure the internal ADC **/
120  AD1CON1 = ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON;
121 #ifdef _AD12B
122  if (u8_use12bit)
123  AD1CON1bits.AD12B = 1;
124  else
125  AD1CON1bits.AD12B = 0;
126 #endif
127  if (u8_useScatterGather) {
128  AD1CON1bits.ADDMABM = 0;
129  u16_dmaMode = DMA_AMODE_PERIPHERAL_INDIRECT;
130  } else {
131  //order mode
132  AD1CON1bits.ADDMABM = 1;
133  u16_dmaMode = DMA_AMODE_REGISTER_POSTINC;
134  }
135  //AD1CON3 = ADC_CONV_CLK_INTERNAL_RC | (u8_autoSampleTime<<8);
136  //at FCY = 40 MHz, Tcy = 25 ns, and use ADC clock = 10* Tcy = 10 * 25 ns = 250 ns
137  //use clock based on Tcy so that we can accurately measure ADC clock period
138  AD1CON3 = ADC_CONV_CLK_SYSTEM | (u8_autoSampleTime<<8) |ADC_CONV_CLK_10Tcy;
139  //Note: PIC24H family reference manual (16.13.2) says that for 'ordered' mode, the
140  //SMPI bits should be cleared. However, when scanning, this seems to be incorrect as the
141  //settings that work are the same ones used for 'scatter/gather' mode.
142  AD1CON2 = ADC_VREF_AVDD_AVSS | ADC_CONVERT_CH0 | ADC_SCAN_ON | ((u8_nChannels-1)<<2);
143 
144 
145 #if (defined(__PIC24H__)|| defined(__dsPIC33F__))
146  AD1CHS0 = ADC_CH0_NEG_SAMPLEA_VREFN;
147 #else
148  AD1CHS = ADC_CH0_NEG_SAMPLEA_VREFN;
149 #endif
150  AD1CSSL = u16_ch0ScanMask;
151 
152  switch (u8_dmaLocsPerInput) {
153  case 1 :
154  AD1CON4 = ADC_1_WORD_PER_INPUT;
155  break;
156  case 2 :
157  AD1CON4 = ADC_2_WORD_PER_INPUT;
158  break;
159  case 4 :
160  AD1CON4 = ADC_4_WORD_PER_INPUT;
161  break;
162  case 8 :
163  AD1CON4 = ADC_8_WORD_PER_INPUT;
164  break;
165  case 16 :
166  AD1CON4 = ADC_16_WORD_PER_INPUT;
167  break;
168  case 32 :
169  AD1CON4 = ADC_32_WORD_PER_INPUT;
170  break;
171  case 64 :
172  AD1CON4 = ADC_64_WORD_PER_INPUT;
173  break;
174  case 128 :
175  AD1CON4 = ADC_128_WORD_PER_INPUT;
176  break;
177  default:
178  AD1CON4 = ADC_1_WORD_PER_INPUT;
179  break;
180  }
181 
182 //configure the DMA channel 0 interrupt
183  DMA0PAD = (unsigned int) &ADC1BUF0;
184  DMA0REQ = DMA_IRQ_ADC1;
185  DMA0STA = __builtin_dmaoffset(au16_bufferA);
186  DMA0CNT = (u8_nChannels * u8_dmaLocsPerInput)-1;
187  DMA0CON = //configure and enable the module Module
188  (DMA_MODULE_ON |
189  DMA_SIZE_WORD |
190  DMA_DIR_READ_PERIPHERAL |
191  DMA_INTERRUPT_FULL |
192  DMA_NULLW_OFF |
193  u16_dmaMode |
194  DMA_MODE_CONTINUOUS);
195 
196  _DMA0IF = 0;
197  _DMA0IP = 6;
198  _DMA0IE = 1;
199 
200  AD1CON1bits.ADON = 1; // turn on the ADC
201  return(u8_nChannels);
202 }
203 
204 
205 volatile uint16_t au16_buffer[MAX_TRANSFER];
206 volatile uint16_t au16_bufferSum[MAX_CHANNELS];
207 volatile uint8_t u8_waiting;
208 
209 
210 void _ISRFAST _DMA0Interrupt(void) {
211  uint8_t u8_i;
212  uint16_t* au16_adcHWBuff = (uint16_t*) &au16_bufferA;
213  _DMA0IF = 0;
214  if (u8_waiting ) {
215  for ( u8_i=0; u8_i<MAX_TRANSFER; u8_i++) {
216  au16_buffer[u8_i] = au16_adcHWBuff[u8_i];
217  } //end for()
218  u8_waiting = 0; // signal main() that data is ready
219  }
220  // toggle a port pin so that we can measure how often DMA IRQs are coming in
221  LED2 = !LED2;
222 }
223 
224 
225 
226 int main (void) {
227  uint8_t u8_i, u8_j, u8_k;
228  uint16_t u16_pot;
229  float f_pot;
230 
231  configBasic(HELLO_MSG);
232 
233  // make RA0/AN0/VREF+ a digital input to kill the pullup and
234  // set the TRISA bit, then make it ANALOG so the ADC will work
235  CONFIG_AN0_AS_ANALOG();
236  CONFIG_AN1_AS_ANALOG();
237  CONFIG_AN4_AS_ANALOG();
238  CONFIG_AN5_AS_ANALOG();
239  CONFIG_AN10_AS_ANALOG();
240  CONFIG_AN11_AS_ANALOG();
241  CONFIG_AN12_AS_ANALOG();
242 
243  CONFIG_LED2();
244 
245  u8_NumChannelsScanned = configDMA_ADC( ADC_SCAN_AN0 | ADC_SCAN_AN1 | ADC_SCAN_AN4 | \
246  ADC_SCAN_AN5 | ADC_SCAN_AN10 | ADC_SCAN_AN11 | ADC_SCAN_AN12,
247  31, ADC_12BIT_FLAG, 0, CONVERSIONS_PER_INPUT);
248 
249  u8_waiting = 1;
250  while (1) {
251  while (u8_waiting) {}; // wait for valid data in ISR
252  //data is updated in array by DMA ISR when u8_waiting flag is cleared
253  //iterate over channels, and average results for each channel
254  //data in array will not be updated again by DMA ISR until u8_waiting flag is set.
255  u8_k = 0; //buffer index
256  for (u8_j=0; u8_j<CONVERSIONS_PER_INPUT; u8_j++) {
257  for ( u8_i=0; u8_i<u8_NumChannelsScanned; u8_i++) { //each channel
258  //each result per channel
259  if (u8_j == 0) au16_bufferSum[u8_i] = au16_buffer[u8_k];
260  else au16_bufferSum[u8_i] += au16_buffer[u8_k];
261  u8_k++;
262  }
263  }
264  //zero out unused channels
265  for (u8_i=u8_NumChannelsScanned; u8_i<MAX_CHANNELS ; u8_i++) {
266  au16_bufferSum[u8_i] = 0;
267  }
268  //now average and print
269  for ( u8_i=0; u8_i<MAX_CHANNELS ; u8_i++) {
270  u16_pot = au16_bufferSum[u8_i]/CONVERSIONS_PER_INPUT; //take the average
271  f_pot = 3.3 / ADC_NSTEPS * u16_pot;
272  printf("r");
273  if (u8_i < 10) outChar( '0'+u8_i );
274  else outChar( 'A'-10+u8_i );
275  printf(":0x%04X=%1.3fV ", u16_pot, (double) f_pot );
276  if ((u8_i % 4) == 3) printf("\n");
277  } //end for()
278  printf("\n");
279  u8_waiting = 1;
280  doHeartbeat();
281  DELAY_MS(1500);
282  } //endof while()
283 } // endof main()
284