PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
adc7scan1_dma_scatter_gather_1.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 
34 #ifndef _DMA0IF
35 #error "This processor selection does not have a DMA module; this code example is incompatible with a PIC24 CPU that does not have DMA."
36 
37 #endif
38 
39 /** \file
40  * Performs a basic config of the ADC and samples seven channels sequentially
41  * with automatic channel scanning. ADC values are 10-bit results.
42  * Samples are obtained continuously, and DMA is used to write a memory buffer.
43  * The DMA is done using Scatter/Gather mode, with a block size determined by CONVERSIONS_PER_INPUT.
44  * For block size = 1 and scatter/gather mode, this
45  * means that AN0 result is written to DMA buffer[0], AN1 result to the DMA buffer[1],
46  * AN15 to DMA buffer[15], etc.
47  * Uses DMA completion interrupts to get values from the DMA memory buffer.
48  * The DMA mode for Scatter/Gather mode should be DMA_AMODE_PERIPHERAL_INDIRECT (peripheral indirect)
49  * Main routine fetches the "latest" values from memory.
50  * This code assumes a maximum of 16 ANx inputs.
51  * For block size > 1, the main code averages the values for a given channel.
52  * Compare the main averaging code with 'adc7scan1_dma2.c' to see the difference in
53  * how results are stored in the buffer for 'ordered' mode versus 'scatter/gather'.
54  *
55  * Conversion results are printed to screen to match adc2pots1.c project
56  * (HEX values and voltages are printed.)
57  * This is only for PIC24 CPUs with DMA.
58 */
59 
60 #define CONFIG_LED2() CONFIG_RB5_AS_DIG_OUTPUT()
61 #define LED2 _LATB5
62 
63 
64 // uncomment the next line to setup this project for a 12-bit ADC
65 //#define USE_12BIT_ADC
66 
67 #ifdef USE_12BIT_ADC
68 #define ADC_LEN 12
69 #define ADC_NSTEPS 4096
70 #define ADC_12BIT_FLAG 1
71 #else
72 #define ADC_LEN 10
73 #define ADC_NSTEPS 1024
74 #define ADC_12BIT_FLAG 0
75 #endif
76 
77 
78 
79 /***
80  *** HERE IS THE CODE!
81  ***
82  ***/
83 
84 // set this to one of the values of
85 // 1, 2, 4, 8, 16, 32, 64, 128
86 #define CONVERSIONS_PER_INPUT 4
87 #define MAX_CHANNELS 16
88 //DMA transfer size is in words.
89 #define MAX_TRANSFER (CONVERSIONS_PER_INPUT*MAX_CHANNELS) //make power of two for alignment to work
90 
91 //DMA buffers, alignment is based on number of bytes
92 uint16_t au16_bufferA[MAX_TRANSFER] __attribute__((space(dma),aligned(MAX_TRANSFER*2)));
93 
94 //generic DMA/ADC configuration function, enables scanning, uses DMA channel 0
95 //returns the number of channels that are scanned as specified by the mask.
96 uint8_t configDMA_ADC(uint16_t u16_ch0ScanMask, \
97  uint8_t u8_autoSampleTime, \
98  uint8_t u8_use12bit,
99  uint8_t u8_useScatterGather,
100  uint8_t u8_dmaLocsPerInput) {
101  uint8_t u8_i, u8_nChannels=0;
102  uint16_t u16_mask = 0x0001;
103  uint16_t u16_dmaMode;
104 
105 
106  // compute the number of Channels the user wants to scan over
107  for (u8_i=0; u8_i<16; u8_i++) {
108  if (u16_ch0ScanMask & u16_mask)
109  u8_nChannels++;
110  u16_mask<<=1;
111  } //end for
112 
113  if (u8_autoSampleTime > 31) u8_autoSampleTime=31;
114 
115  AD1CON1bits.ADON = 0; // turn off ADC (changing setting while ADON is not allowed)
116  /** Configure the internal ADC **/
117  AD1CON1 = ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON;
118 #ifdef _AD12B
119  if (u8_use12bit)
120  AD1CON1bits.AD12B = 1;
121  else
122  AD1CON1bits.AD12B = 0;
123 #endif
124  if (u8_useScatterGather) {
125  AD1CON1bits.ADDMABM = 0;
126  u16_dmaMode = DMA_AMODE_PERIPHERAL_INDIRECT;
127  } else {
128  //order mode
129  AD1CON1bits.ADDMABM = 1;
130  u16_dmaMode = DMA_AMODE_REGISTER_POSTINC;
131  }
132  //AD1CON3 = ADC_CONV_CLK_INTERNAL_RC | (u8_autoSampleTime<<8);
133  //at FCY = 40 MHz, Tcy = 25 ns, and use ADC clock = 10* Tcy = 10 * 25 ns = 250 ns
134  //use clock based on Tcy so that we can accurately measure ADC clock period
135  AD1CON3 = ADC_CONV_CLK_SYSTEM | (u8_autoSampleTime<<8) |ADC_CONV_CLK_10Tcy;
136  //Note: PIC24H family reference manual (16.13.2) says that for 'ordered' mode, the
137  //SMPI bits should be cleared. However, when scanning, this seems to be incorrect as the
138  //settings that work are the same ones used for 'scatter/gather' mode.
139  AD1CON2 = ADC_VREF_AVDD_AVSS | ADC_CONVERT_CH0 | ADC_SCAN_ON | ((u8_nChannels-1)<<2);
140 
141 #if (defined(__PIC24H__)|| defined(__dsPIC33F__))
142  AD1CHS0 = ADC_CH0_NEG_SAMPLEA_VREFN;
143 #else
144  AD1CHS = ADC_CH0_NEG_SAMPLEA_VREFN;
145 #endif
146  AD1CSSL = u16_ch0ScanMask;
147 
148  switch (u8_dmaLocsPerInput) {
149  case 1 :
150  AD1CON4 = ADC_1_WORD_PER_INPUT;
151  break;
152  case 2 :
153  AD1CON4 = ADC_2_WORD_PER_INPUT;
154  break;
155  case 4 :
156  AD1CON4 = ADC_4_WORD_PER_INPUT;
157  break;
158  case 8 :
159  AD1CON4 = ADC_8_WORD_PER_INPUT;
160  break;
161  case 16 :
162  AD1CON4 = ADC_16_WORD_PER_INPUT;
163  break;
164  case 32 :
165  AD1CON4 = ADC_32_WORD_PER_INPUT;
166  break;
167  case 64 :
168  AD1CON4 = ADC_64_WORD_PER_INPUT;
169  break;
170  case 128 :
171  AD1CON4 = ADC_128_WORD_PER_INPUT;
172  break;
173  default:
174  AD1CON4 = ADC_1_WORD_PER_INPUT;
175  break;
176  }
177 
178 //configure the DMA channel 0 interrupt
179  DMA0PAD = (unsigned int) &ADC1BUF0;
180  DMA0REQ = DMA_IRQ_ADC1;
181  DMA0STA = __builtin_dmaoffset(au16_bufferA);
182  DMA0CNT = (u8_nChannels * u8_dmaLocsPerInput)-1;
183  DMA0CON = //configure and enable the module Module
184  (DMA_MODULE_ON |
185  DMA_SIZE_WORD |
186  DMA_DIR_READ_PERIPHERAL |
187  DMA_INTERRUPT_FULL |
188  DMA_NULLW_OFF |
189  u16_dmaMode |
190  DMA_MODE_CONTINUOUS);
191 
192  _DMA0IF = 0;
193  _DMA0IP = 6;
194  _DMA0IE = 1;
195 
196  AD1CON1bits.ADON = 1; // turn on the ADC
197  return(u8_nChannels);
198 }
199 
200 volatile uint16_t au16_buffer[MAX_TRANSFER];
201 volatile uint8_t u8_waiting;
202 
203 
204 void _ISRFAST _DMA0Interrupt(void) {
205  uint8_t u8_i;
206  uint16_t* au16_adcHWBuff = (uint16_t*) &au16_bufferA;
207  _DMA0IF = 0;
208  if (u8_waiting ) {
209  for ( u8_i=0; u8_i<MAX_TRANSFER; u8_i++) {
210  au16_buffer[u8_i] = au16_adcHWBuff[u8_i];
211  } //end for()
212  u8_waiting = 0; // signal main() that data is ready
213  }
214  // toggle a port pin so that we can measure how often DMA IRQs are coming in
215  LED2 = !LED2;
216 }
217 
218 
219 
220 int main (void) {
221  uint8_t u8_i, u8_j, u8_k;
222  uint16_t u16_sum;
223  uint16_t u16_pot;
224  float f_pot;
225 
226  configBasic(HELLO_MSG);
227 
228  // make RA0/AN0/VREF+ a digital input to kill the pullup and
229  // set the TRISA bit, then make it ANALOG so the ADC will work
230  CONFIG_AN0_AS_ANALOG();
231  CONFIG_AN1_AS_ANALOG();
232  CONFIG_AN4_AS_ANALOG();
233  CONFIG_AN5_AS_ANALOG();
234  CONFIG_AN10_AS_ANALOG();
235  CONFIG_AN11_AS_ANALOG();
236  CONFIG_AN12_AS_ANALOG();
237 
238  CONFIG_LED2();
239 
240  configDMA_ADC( ADC_SCAN_AN0 | ADC_SCAN_AN1 | ADC_SCAN_AN4 | \
241  ADC_SCAN_AN5 | ADC_SCAN_AN10 | ADC_SCAN_AN11 | ADC_SCAN_AN12,
242  31, ADC_12BIT_FLAG, 1, CONVERSIONS_PER_INPUT);
243 
244  u8_waiting = 1;
245  while (1) {
246  while (u8_waiting) {}; // wait for valid data in ISR
247  //data is updated in array by DMA ISR when u8_waiting flag is cleared
248  //iterate over channels, and average results for each channel
249  //data in array will not be updated again by DMA ISR until u8_waiting flag is set.
250  u8_k = 0; //buffer index
251  for ( u8_i=0; u8_i<16; u8_i++) { //each channel
252  for (u8_j=0; u8_j<CONVERSIONS_PER_INPUT; u8_j++) { //each result per channel
253  if (u8_j == 0) u16_sum = au16_buffer[u8_k];
254  else u16_sum += au16_buffer[u8_k];
255  u8_k++;
256  }
257  u16_pot = u16_sum/CONVERSIONS_PER_INPUT; //take the average
258  f_pot = 3.3 / ADC_NSTEPS * u16_pot;
259  printf("r");
260  if (u8_i < 10) outChar( '0'+u8_i );
261  else outChar( 'A'-10+u8_i );
262  printf(":0x%04X=%1.3fV ", u16_pot, (double) f_pot );
263  if ((u8_i % 4) == 3) printf("\n");
264  } //end for()
265  printf("\n");
266  u8_waiting = 1;
267  doHeartbeat();
268  DELAY_MS(1500);
269  } //endof while()
270 } // endof main()
271