PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
audio.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 #include "pic24_all.h"
30 #include <stdio.h>
31 
32 /** \file
33 Capstone project that digitizes audio, saves to EEPROM,
34 and plays it back. Uses the internal ADC, 24LC515 serial EEPROM,
35 8-bit SPI DAC (MAXIM 548A), Timer3.
36 */
37 
38 extern void ADPCMEncoderInit(void);
39 extern uint8_t ADPCMEncoder( int16_t i16_sample);
40 extern int16_t ADPCMDecoder(uint8_t u8_code);
41 extern void ADPCMDecoderInit(void);
42 
43 
44 #define EEPROM 0xA0 //LC515 address assuming both address pins tied low.
45 #define BLKSIZE 64
46 
47 //Assumes WDT is configured for longer than EEPROM write time
48 void waitForWriteCompletion(uint8_t u8_i2cAddr) {
49  uint8_t u8_ack, u8_savedSWDTEN;
50  u8_savedSWDTEN = _SWDTEN;
51  _SWDTEN = 1; //enable WDT so that do not get stuck in infinite loop!
52  u8_i2cAddr = I2C_WADDR(u8_i2cAddr); //write operation, R/W# = 0;
53  do {
54  startI2C1();
55  u8_ack = putNoAckCheckI2C1(u8_i2cAddr);
56  stopI2C1();
57  } while (u8_ack == I2C_NAK);
58  _SWDTEN = u8_savedSWDTEN; //restore WDT to original state
59 }
60 
61 //this version just expects a block of 64 data bytes
62 void memWriteLC515(uint8_t u8_i2cAddr, uint16_t u16_MemAddr, uint8_t *pu8_buf) {
63  uint8_t u8_AddrLo, u8_AddrHi;
64  uint16_t u16_i;
65  u8_AddrLo = u16_MemAddr & 0x00FF;
66  u8_AddrHi = (u16_MemAddr >> 8);
67  if (u16_MemAddr & 0x8000) {
68  // if MSB set , set block select bit
69  u8_i2cAddr = u8_i2cAddr | 0x08;
70  }
71  waitForWriteCompletion(u8_i2cAddr);
72  //write the data to the EEPROM
73  startI2C1();
74  putI2C1(I2C_WADDR(u8_i2cAddr));
75  putI2C1(u8_AddrHi);
76  putI2C1(u8_AddrLo);
77  for (u16_i=0; u16_i < BLKSIZE; u16_i++) {
78  putI2C1(*pu8_buf);
79  pu8_buf++;
80  }
81  stopI2C1();
82 }
83 
84 void memReadLC515(uint8_t u8_i2cAddr, uint16_t u16_MemAddr, uint8_t *pu8_buf) {
85 
86  uint8_t u8_AddrLo, u8_AddrHi;
87 
88  u8_AddrLo = u16_MemAddr & 0x00FF;
89  u8_AddrHi = (u16_MemAddr >> 8);
90 
91  if (u16_MemAddr & 0x8000) {
92  // if MSB set , set block select bit
93  u8_i2cAddr = u8_i2cAddr | 0x08;
94  }
95  waitForWriteCompletion(u8_i2cAddr);
96  //set address counter
97  write2I2C1(u8_i2cAddr,u8_AddrHi, u8_AddrLo);
98  //read data
99  readNI2C1(u8_i2cAddr,pu8_buf, BLKSIZE);
100 }
101 
102 
103 #define CONFIG_SLAVE_ENABLE() CONFIG_RB3_AS_DIG_OUTPUT()
104 #define SLAVE_ENABLE() _LATB3 = 0 //low true assertion
105 #define SLAVE_DISABLE() _LATB3 = 1
106 
107 
108 void configSPI1(void) {
109  //spi clock = 40MHz/1*4 = 40MHz/4 = 10MHz
110  SPI1CON1 = SEC_PRESCAL_1_1 | //1:1 secondary prescale
111  PRI_PRESCAL_4_1 | //4:1 primary prescale
112  CLK_POL_ACTIVE_HIGH | //clock active high (CKP = 0)
113  SPI_CKE_ON | //out changes active to inactive (CKE=1)
114  SPI_MODE8_ON | //8-bit mode
115  MASTER_ENABLE_ON; //master mode
116  //configure pins. Only need SDO, SCLK since POT is output only
117  CONFIG_SDO1_TO_RP(6); //use RP6 for SDO
118  CONFIG_SCK1OUT_TO_RP(7); //use RP7 for SCLK
119 
120  SPI1STATbits.SPIEN = 1; //enable SPI mode
121 }
122 void configDAC() {
123  CONFIG_SLAVE_ENABLE(); //chip select for DAC
124  SLAVE_DISABLE(); //disable the chip select
125 }
126 
127 void writeDAC (uint8_t dacval) {
128  SLAVE_ENABLE(); //assert Chipselect line to DAC
129  ioMasterSPI1(0b00001001); //control byte that enables DAC A
130  ioMasterSPI1(dacval); //write DAC value
131  SLAVE_DISABLE();
132 }
133 
134 #define ISR_PERIOD 125 // in microseconds
135 
136 void configTimer3(void) {
137  //ensure that Timer2,3 configured as separate timers.
138  T2CONbits.T32 = 0; // 32-bit mode off
139  //T3CON set like this for documentation purposes.
140  //could be replaced by T3CON = 0x0020
141  T3CON = T3_OFF |T3_IDLE_CON | T3_GATE_OFF
142  | T3_SOURCE_INT
143  | T3_PS_1_1 ;
144  PR3 = usToU16Ticks (ISR_PERIOD, getTimerPrescale(T3CONbits)) - 1;
145  TMR3 = 0; //clear timer3 value
146  _T3IF = 0; //clear interrupt flag
147  _T3IP = 1; //choose a priority
148  _T3IE = 1; //enable the interrupt
149  T3CONbits.TON = 1; //turn on the timer
150 }
151 
152 
153 uint8_t au8_bufferA[BLKSIZE];
154 uint8_t au8_bufferB[BLKSIZE];
155 //some one-bit flags
156 typedef struct tagFLAGBITS {
157 unsigned u1_activeBuffer:
158  1;
159 unsigned u1_writeFlag:
160  1;
161 unsigned u1_recordFlag:
162  1;
163 unsigned u1_playbackFlag:
164  1;
165 unsigned u1_readFlag:
166  1;
167 unsigned u1_passThruFlag:
168  1;
169 unsigned u1_compressionFlag:
170  1;
171 } FLAGBITS;
172 volatile FLAGBITS flags;
173 
174 uint8_t u8_sampleCount;
175 uint8_t u8_bufferCount;
176 uint16_t u16_adcVal;
177 uint8_t u8_dacVal;
178 int16_t i16_adcval;
179 
180 void isrRecord() {
181  uint8_t u8_tmp;
182  if (flags.u1_compressionFlag) {
183  //convert to signed value
184  i16_adcval = u16_adcVal;
185  i16_adcval -= 2048;
186  u8_tmp = ADPCMEncoder( i16_adcval);
187  if (u8_sampleCount & 0x01) {
188  u8_dacVal = (u8_tmp << 4)| u8_dacVal;
189  u8_sampleCount++;
190  if (flags.u1_activeBuffer) au8_bufferB[u8_bufferCount++] = u8_dacVal;
191  else au8_bufferA[u8_bufferCount++] = u8_dacVal;
192  } else {
193  u8_dacVal = u8_tmp;
194  u8_sampleCount++;
195  }
196  } else {
197  u8_dacVal = (u16_adcVal>>4) & 0x00FF;
198  if (flags.u1_activeBuffer) au8_bufferB[u8_bufferCount++] = u8_dacVal;
199  else au8_bufferA[u8_bufferCount++] = u8_dacVal;
200  }
201  if (u8_bufferCount == BLKSIZE) {
202  flags.u1_activeBuffer = !flags.u1_activeBuffer;
203  flags.u1_writeFlag = 1;
204  u8_bufferCount = 0;
205  u8_sampleCount = 0;
206  }
207 }
208 
209 void isrPlayback() {
210  uint8_t u8_tmp;
211  if (flags.u1_activeBuffer) u8_tmp = au8_bufferB[u8_bufferCount];
212  else u8_tmp = au8_bufferA[u8_bufferCount];
213  if (flags.u1_compressionFlag) {
214  if (u8_sampleCount & 0x01) {
215  i16_adcval = ADPCMDecoder(u8_tmp >> 4);
216  u8_bufferCount++;
217  } else {
218  i16_adcval = ADPCMDecoder(u8_tmp & 0x0F);
219  }
220  i16_adcval += 2048;
221  u16_adcVal = i16_adcval;
222  u8_dacVal = (u16_adcVal>>4) & 0x00FF;
223  writeDAC(u8_dacVal);
224  u8_sampleCount++;
225  } else {
226  writeDAC(u8_tmp);
227  u8_bufferCount++;
228  }
229  if (u8_bufferCount == BLKSIZE) {
230  flags.u1_activeBuffer = !flags.u1_activeBuffer;
231  flags.u1_readFlag = 1;
232  u8_bufferCount = 0;
233  u8_sampleCount = 0;
234  }
235 }
236 
237 void _ISR _T3Interrupt (void) {
238 
239  _T3IF = 0;
240  u16_adcVal = ADC1BUF0;
241  //start next sample
242  SET_SAMP_BIT_ADC1();
243  if (flags.u1_recordFlag) isrRecord();
244  if (flags.u1_playbackFlag) isrPlayback();
245  if (flags.u1_passThruFlag) {
246  u8_dacVal = (u16_adcVal>>4) & 0x00FF;
247  writeDAC(u8_dacVal);
248  }
249 }
250 
251 #define VREF 3.3 //assume Vref = 3.3 volts
252 
253 void doRecord(uint8_t u8_compression) {
254  uint16_t u16_MemAddr;
255  flags.u1_compressionFlag = u8_compression;
256  if (u8_compression) ADPCMEncoderInit();
257  flags.u1_activeBuffer = 0;
258  u8_sampleCount = 0;
259  u8_bufferCount = 0;
260  u16_MemAddr = 0; //start at location 0 in memory
261  flags.u1_writeFlag = 0;
262  flags.u1_recordFlag = 1;
263  do {
264  if (flags.u1_writeFlag) reportError("Record overflow");
265  while (!flags.u1_writeFlag) doHeartbeat();
266  flags.u1_writeFlag = 0;
267  if (flags.u1_activeBuffer)
268  memWriteLC515(EEPROM,u16_MemAddr,au8_bufferA);
269  else
270  memWriteLC515(EEPROM,u16_MemAddr,au8_bufferB);
271  u16_MemAddr = u16_MemAddr +64;
272  outString("*");
273  } while (u16_MemAddr != 0);//end record loop
274  flags.u1_recordFlag = 0;
275  flags.u1_compressionFlag = 0;
276  outString("Finished recording\n");
277 }
278 
279 void doPlayback(uint8_t u8_compression) {
280  uint16_t u16_MemAddr;
281 
282  flags.u1_compressionFlag = u8_compression;
283  if (u8_compression) ADPCMDecoderInit();
284  flags.u1_activeBuffer = 0;
285  flags.u1_readFlag = 0;
286  u8_sampleCount = 0;
287  u8_bufferCount = 0;
288  u16_MemAddr = 0; //start at location 0 in memory
289  //playback mode
290  memReadLC515(EEPROM,u16_MemAddr,au8_bufferA); //first buffer read
291  u16_MemAddr = u16_MemAddr +64;
292  flags.u1_playbackFlag = 1;
293  while (!isCharReady()) {
294  if (flags.u1_activeBuffer)
295  memReadLC515(EEPROM,u16_MemAddr,au8_bufferA); // do read
296  else
297  memReadLC515(EEPROM,u16_MemAddr,au8_bufferB); // do read
298  u16_MemAddr = u16_MemAddr +64;
299  if (flags.u1_writeFlag) reportError("Playback underflow");
300  while (!flags.u1_readFlag) doHeartbeat();
301  flags.u1_readFlag = 0;
302  outChar('*');
303  }//end while
304  inChar();
305  outString("\nPlayback terminated.\n");
306  flags.u1_playbackFlag = 0;
307  flags.u1_compressionFlag = 0;
308 }
309 
310 void doSampledPassthru(void) {
311  flags.u1_passThruFlag = 1;
312  while (!isCharReady()) doHeartbeat();
313  inChar();
314  flags.u1_passThruFlag = 0;
315  outString("\nFeedthru terminated.\n");
316 }
317 
318 //just examine raw bytes
319 void doTest(void) {
320  _T3IE = 0;
321  while (!isCharReady()) {
322  doHeartbeat();
323  while (!_T3IF);
324  _T3IF = 0;
325  u16_adcVal = ADC1BUF0;
326  //start next sample
327  SET_SAMP_BIT_ADC1();
328  outUint16(u16_adcVal);
329  outString("\n");
330  }
331  _T3IE = 1;
332 }
333 
334 int main (void) {
335 
336  uint8_t u8_mode;
337 
338  configBasic(HELLO_MSG);
339  CONFIG_AN0_AS_ANALOG();
340  configADC1_ManualCH0( ADC_CH0_POS_SAMPLEA_AN0, 31, 1 );
341  SET_SAMP_BIT_ADC1(); //start first conversion
342  configSPI1();
343  configDAC();
344  configTimer3();
345  configI2C1(400); //configure I2C for 400 KHz
346  while (1) {
347  outString("\nEnter 'r' (record), 'p' (playback), 'f' (sampled feedthru). 'R', 'P' use compression. Any key terminates: ");
348  u8_mode = inCharEcho();
349  outString("\n");
350  switch (u8_mode) {
351  case 'r':
352  doRecord(0);
353  break;
354  case 'R':
355  doRecord(1);
356  break;
357  case 'p':
358  doPlayback(0);
359  break;
360  case 'P':
361  doPlayback(1);
362  break;
363  case 'f':
364  doSampledPassthru();
365  break;
366  case 't':
367  doTest();
368  break;
369 
370  }
371  } //end while(1)
372 }