-
Notifications
You must be signed in to change notification settings - Fork 54
/
DAC.h
841 lines (659 loc) · 29.9 KB
/
DAC.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
// DAC.h
#ifndef DAC_h // include guard
#define DAC_h
#include <math.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "FreeRTOS.h"
#include "spi.h"
#include "mult16x16.h"
#include "mult16x8.h"
#include "mult32x16.h"
#ifdef __cplusplus
extern "C" {
#endif
/*--------------------------------------------------*/
/*------------------Definitions---------------------*/
/*--------------------------------------------------*/
/*
* XXX Define for using the normal SPI bus as in Prototype 1 and Prototype 2.
* Un-define for using MSPI on USART1 in Prototype 3 and Production Goldilocks Analogue.
*/
// #define LEGACY_GA
/*
* XXX Ping PD7 to check timing on the audio processing.
* PD7 will be set high when the DAC interrupt is entered, and set low when exited.
* This allows you to check that there is sufficient time for the rest of the system to operate,
* and to ensure that the sample interrupts are not being missed or are overlapping.
*/
// #define DEBUG_PING
/*
* Bytes used to control the MCP4822 DAC
*/
#define CH_A_OUT 0x10
#define CH_B_OUT 0x90
#define CH_A_OFF 0x00
#define CH_B_OFF 0x80
/*
* The LDAC pin on the MCP4822 synchronises the two channels to produce a simultaneous sample output
* (even though the samples are clocked in at slightly different times).
* Added for support of LDAC pin on Goldilocks Analogue MCP4822
*/
#define DAC_PORT_LCAC PORTC
#define DAC_PORT_DIR_LDAC DDRC
#define DAC_PORT_PIN_LDAC PINC
#define DAC_BIT_LDAC _BV(PC3)
//==================================================
//****************** IIR Filter ******************//
//==================================================
#ifndef SAMPLE_RATE
#define SAMPLE_RATE 12000 // set up the sampling Timer1 to 48000Hz, 44100Hz (or lower), runs at audio sampling rate in samples per Second.
#endif // 384 = 3 x 2^7 divisors 2k, 3k, 4k, 6k, 8k, 12k, 16k, 24k, 48k
// IIR filter coefficient scaling
// multiply the coefficients by 32, assume a(1) is 32 * (1 + alpha), to get better accuracy in fixed format.
#define IIRSCALEFACTOR 32
#define IIRSCALEFACTORSHIFT 5
// IIR Resonance (maximum) at the corner frequency.
#define Q_MAXIMUM (6.0f)
#define Q_LINEAR (M_SQRT1_2)
// float-fix conversion macros
// assuming we're using 8.8 for the coefficients.
#define float2int(a) ((int16_t)((a)*256.0))
#define int2float(a) ((double)(a)/256.0)
/*--------------------------------------------------*/
/*--------------------Typedefs----------------------*/
/*--------------------------------------------------*/
typedef union _DAC_value_t{
uint16_t u16;
int16_t i16;
uint8_t u8[2];
} DAC_value_t;
typedef struct __attribute__ ((packed)) _DAC_command_t {
uint8_t command;
DAC_value_t value;
} DAC_command_t;
/*
* Prototype for the audio DSP function to be implemented.
* Needs to at least provide *ch_A and *ch_B
* Runs within Timer0/1 interrupt routine - time critical I/O.
* Keep it very short and punchy!
* Use the DEBUG pings to ensure you're not using too much time.
*/
typedef void (audioCodec_DSP)( uint16_t* ch_A, uint16_t* ch_B);
//==================================================
//****************** IIR Filter ******************//
//==================================================
typedef struct __attribute__ ((packed)) _filter_t {
uint16_t sample_rate; // sample rate in Hz
uint16_t cutoff; // normalised cutoff frequency, 0-65536. maximum is sample_rate/2
uint16_t peak; // normalised Q factor, 0-65536. maximum is Q_MAXIMUM
int16_t b0,b1,b2,a1,a2; // Coefficients in 8.8 format
int16_t xn_1, xn_2; //IIR state variables
int16_t yn_1, yn_2; //IIR state variables
} filter_t;
/*--------------------------------------------------*/
/*--------------------Local Variables---------------*/
/*--------------------------------------------------*/
#if defined(portANALOGUE) || defined(portANALOGSHIELD)
// DSP function callback pointer.
static audioCodec_DSP * audioHandler;
// pointers to storage for the values to be written to MCP4822
uint16_t* ch_A_ptr;
uint16_t* ch_B_ptr;
#endif //defined(portANALOGUE) || defined(portANALOGSHIELD)
/*--------------------------------------------------*/
/*----------Public Function Definitions-------------*/
/*--------------------------------------------------*/
//========================================================
// Some info on the Timers.
// Timer 2 is a bit different to the other timers.
/*
* Timer_2 / Counter_2 Prescale
* CS22 CS21 CS20 Description
* 0 0 0 No clock source (Timer/Counter stopped).
* 0 0 1 clk T2S /1 (No prescaling).
* 0 1 0 clk T2S /8 (From prescaler).
* 0 1 1 clk T2S /32 (From prescaler).
* 1 0 0 clk T2S /64 (From prescaler).
* 1 0 1 clk T2S /128 (From prescaler).
* 1 1 0 clk T2S /256 (From prescaler).
*/
/*
* Timer_n / Counter_n Prescale
* CSn2 CSn1 CSn0 Description
* 0 0 0 No clock source (Timer/Counter stopped).
* 0 0 1 clk I/O /1 (No prescaling).
* 0 1 0 clk I/O /8 (From prescaler).
* 0 1 1 clk I/O /64 (From prescaler).
* 1 0 0 clk I/O /256 (From prescaler).
* 1 0 1 clk I/O /1024 (From prescaler).
* 1 1 0 External clock source on Tn pin. Clock on falling edge.
* 1 1 1 External clock source on Tn pin. Clock on rising edge.
*/
void AudioCodec_Timer0_init(uint16_t samplesSecond); // set up the sampling Timer0, runs at audio sampling rate in Hz.
// 8 bit timer useful for fractions of 48,000 Hz.
// 32,000 Hz, 16,000 Hz, 8,000 Hz, 4,000 Hz, 2,000 Hz.
/*** Initialise one OR the other Timer (but not both) ***/
void AudioCodec_Timer1_init(uint16_t samplesSecond); // set up the sampling Timer1, runs at audio sampling rate in Hz.
// 16 bit timer useful for any sampling rate.
// Including 44,100 Hz, 22,050 Hz, 11,025 Hz and any above rates too.
void AudioCodec_setHandler(audioCodec_DSP* handler, uint16_t* ch_A, uint16_t* ch_B); // set up the DSP processing to prepare the samples.
void DAC_init(void); // initialise the SPI or MSPIM bus specifically for DAC use.
// Assumes that the DAC SPI has already been selected, as the selection process is quite time consuming.
// if (spiSelect (Analogue))
// {
// uint16_t j = 0x1234;
// uint16_t k = 0x5678;
// DAC_out(NULL, NULL); // the ch_A and ch_B are turned off by NULL, or
// DAC_out(&j, &k); // output the j and k values by reference
// spiDeselect (Analogue);
// }
void DAC_out(const uint16_t * ch_A, const uint16_t * ch_B) __attribute__ ((hot, flatten));
// Assumes that the DAC SPU has already been selected, as the selection process is quite time consuming.
// if (spiSelect (Analogue))
// {
// DAC_out_P( (const uint16_t *)&sinewave[i], (const uint16_t*)&sinewave[i]); // for PROGMEM stored arrays of samples
// spiDeselect (Analogue);
// }
void DAC_out_P(const uint16_t * ch_A, const uint16_t * ch_B) __attribute__ ((hot, flatten));
//==================================================
//****************** IIR Filter ******************//
//==================================================
// second order IIR -- "Direct Form I Transposed"
// a(0)*y(n) = b(0)*x(n) + b(1)*x(n-1) + b(2)*x(n-2)
// - a(1)*y(n-1) - a(2)*y(n-2)
// assumes a(0) = IIRSCALEFACTOR = 32
// Compute filter function coefficients
// http://en.wikipedia.org/wiki/Digital_biquad_filter
// https://www.hackster.io/bruceland/dsp-on-8-bit-microcontroller
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
void setIIRFilterLPF( filter_t *filter ); // Low Pass
void setIIRFilterBPF( filter_t *filter ); // Band Pass
void setIIRFilterHPF( filter_t *filter ); // High Pass
// returns y(n) filtered by the biquad IIR process in place of x(n)
void IIRFilter( filter_t *filter, int16_t * xn ) __attribute__ ((hot, flatten));
/*--------------------------------------------------*/
/*---------------Public Functions-------------------*/
/*--------------------------------------------------*/
#if defined(portANALOGUE) || defined(portANALOGSHIELD)
void DAC_init(void)
{
#if defined(portANALOGSHIELD)
spiBegin(Analogue); // initialise the SPI bus for DAC use.
spiSetBitOrder(SPI_MSBFIRST);
spiSetClockDivider(SPI_CLOCK_DIV2);
spiSetDataMode(SPI_MODE1);
DDRD |= _BV(DDD6) | _BV(DDD5) | _BV(DDD3); // Set DAC _LDAC & DAC _SS & ADC _SS to be an output for the Analog Shield
PORTD |= _BV(PORTD5) | _BV(DDD3); // Pull high: the Analog Shield ADC _SS and DAC _SS.
PORTD &= ~_BV(PORTD6); // Pull low: the Analog Shield _LDAC.
#elif defined(portANALOGUE)
#if defined(LEGACY_GA)
SPI_PORT_DIR_SS_DAC |= SPI_BIT_SS_DAC; // Set _SS as output pin.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull _SS high to deselect the Goldilocks Analogue DAC.
spiBegin(Analogue); // initialise the SPI bus for DAC use.
spiSetBitOrder(SPI_MSBFIRST);
spiSetClockDivider(SPI_CLOCK_DIV2);
spiSetDataMode(SPI_MODE0);
#else
/* For Prototype 3 and onwards, we are using USART1 in MSPIM mode, rather than using the overloaded SPI interface, to drive the DAC.
* The SPI SCK is connected to XCK1, and the SPI MOSI is connected to TX1. SPI MISO is not connected, as it is not needed.
*/
SPI_PORT_DIR_SS_DAC |= SPI_BIT_SS_DAC; // Set _SS as output pin.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull _SS high to deselect the Goldilocks Analogue DAC.
DAC_PORT_DIR_LDAC |= DAC_BIT_LDAC; // Set MCP4822 _LDAC as output pin.
DAC_PORT_LCAC |= DAC_BIT_LDAC; // Pull MCP4822 _LDAC high to disable the DAC latch. No output will be latched.
UBRR1 = 0x0000; // Set Baud Rate to maximum, to speed XCK1 setting time.
DDRD |= _BV(PD4); // Setting the XCK1 port pin as output, enables USART master SPI mode.
UCSR1C = _BV(UMSEL11) | _BV(UMSEL10); // Set USART MSPI mode of operation using SPI data mode 0,0. UCPHA1 = UCSZ10
UCSR1B = _BV(TXEN1); // Enable transmitter. Enable the TX1 (don't enable the RX1, and the rest of the interrupt enable bits leave set to 0 too).
/* Set baud rate. IMPORTANT: The actual Baud Rate must be set after the transmitter is enabled */
UBRR1 = 0x0000; // FCPU/2 = 0x0000
#endif // #if defined(LEGACY_GA)
#endif // #if defined(portANALOGUE) || defined(portANALOGSHIELD)
}
void AudioCodec_Timer0_init(uint16_t samplesSecond) // set up the sampling reconstruction Timer0, runs at audio sampling rate in Hz.
{
if (TIMSK0 && _BV(OCIE0A)) // if the timer has already been set, then just adjust the sample rate.
{
OCR0A = (F_CPU / ((uint32_t)samplesSecond *64)) -1;
}
else // otherwise we have to set up the timer before enabling the interrupt routine.
{
#if defined(DEBUG_PING)
DDRD |= _BV(DDD7); // set the debugging ping
PORTD &= ~_BV(PORTD7);
#endif
// setup Timer0 for codec clock division
TCCR0A = _BV(WGM01); // set to CTC Mode 2. TOP = OCR0A. Normal port operation, OC0A disconnected.
TCCR0B = _BV(CS01) | _BV(CS00); // Clk I/O scaler 64(From prescaler) CTC Mode. TOP = OCR0A. Clock on rising edge.
TCNT0 = 0x00; // clear the counter.
OCR0A = (F_CPU / ((uint32_t)samplesSecond *64)) -1;
TIMSK0 = _BV(OCIE0A); // turn on compare match interrupt
}
}
void AudioCodec_Timer1_init(uint16_t samplesSecond) // set up the sampling reconstruction Timer1, runs at audio sampling rate in Hz.
{
if( TIMSK1 && _BV(OCIE1A)) // if the timer has already been set, then just adjust the sample rate.
{
portENTER_CRITICAL(); // turn off interrupts
OCR1A = (F_CPU / ((uint32_t)samplesSecond)) -1;
portEXIT_CRITICAL(); // turn on interrupts
}
else // otherwise we have to set up the timer before enabling the interrupt routine.
{
#if defined(DEBUG_PING)
DDRD |= _BV(DDD7); // set the debugging ping
PORTD &= ~_BV(PORTD7);
#endif
// setup Timer1 for codec clock division
TCCR1A = 0x00; // set to CTC Mode.
TCCR1B = _BV(WGM12) | _BV(CS10); // CTC Mode 4. TOP = OCR1A. No prescaling. Clock on rising edge.
TCCR1C = 0x00; // not used
portENTER_CRITICAL(); // turn off interrupts
TCNT1 = 0x0000; // clear the counter, high byte first for 16bit writes. gcc compiler knows how to handle this.
OCR1A = (F_CPU / ((uint32_t)samplesSecond)) -1;
portEXIT_CRITICAL(); // turn on interrupts
TIMSK1 = _BV(OCIE1A); // turn on compare match interrupt
}
}
/**
* Sets the audio handler function.
* This function will be called to process the audio at each sample period.
*
*/
void AudioCodec_setHandler(audioCodec_DSP * handler, uint16_t *ch_A, uint16_t *ch_B) // set up the DSP processing to prepare the samples.
{
audioHandler = handler;
ch_A_ptr = ch_A;
ch_B_ptr = ch_B;
}
#endif // #if defined(portANALOGUE) || defined(portANALOGSHIELD)
//========================================================
//********************* IIR Filter *********************//
//========================================================
// second order IIR -- "Direct Form I Transposed"
// a(0)*y(n) = b(0)*x(n) + b(1)*x(n-1) + b(2)*x(n-2)
// - a(1)*y(n-1) - a(2)*y(n-2)
// assumes a(0) = IIRSCALEFACTOR = 32
// Compute filter function coefficients
// http://en.wikipedia.org/wiki/Digital_biquad_filter
// https://www.hackster.io/bruceland/dsp-on-8-bit-microcontroller
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
void setIIRFilterLPF( filter_t *filter ) // Low Pass Filter Setting
{
if ( !(filter->sample_rate) )
filter->sample_rate = SAMPLE_RATE;
if ( !(filter->cutoff) )
filter->cutoff = UINT16_MAX >> 1; // 1/4 of sample rate = filter->sample_rate>>2
if ( !(filter->peak) )
filter->peak = (uint16_t)(M_SQRT1_2 * UINT16_MAX / Q_MAXIMUM); // 1/sqrt(2) effectively
double frequency = ((double)filter->cutoff * (filter->sample_rate>>1)) / UINT16_MAX;
double q = (double)filter->peak * Q_MAXIMUM / UINT16_MAX;
double w0 = (2.0 * M_PI * frequency) / filter->sample_rate;
double sinW0 = sin(w0);
double cosW0 = cos(w0);
double alpha = sinW0 / (q * 2.0f);
double scale = IIRSCALEFACTOR / (1 + alpha); // a0 = 1 + alpha
filter->b0 = \
filter->b2 = float2int( ((1.0 - cosW0) / 2.0) * scale );
filter->b1 = float2int( (1.0 - cosW0) * scale );
filter->a1 = float2int( (-2.0 * cosW0) * scale );
filter->a2 = float2int( (1.0 - alpha) * scale );
}
void setIIRFilterHPF( filter_t *filter ) // High Pass Filter Setting
{
if ( !(filter->sample_rate) )
filter->sample_rate = SAMPLE_RATE;
if ( !(filter->cutoff) )
filter->cutoff = UINT16_MAX >> 1; // 1/4 of sample rate = filter->sample_rate>>2
if ( !(filter->peak) )
filter->peak = (uint16_t)(M_SQRT1_2 * UINT16_MAX / Q_MAXIMUM); // 1/sqrt(2) effectively
double frequency = ((double)filter->cutoff * (filter->sample_rate>>1)) / UINT16_MAX;
double q = (double)filter->peak * Q_MAXIMUM / UINT16_MAX;
double w0 = (2.0 * M_PI * frequency) / filter->sample_rate;
double sinW0 = sin(w0);
double cosW0 = cos(w0);
double alpha = sinW0 / (q * 2.0f);
double scale = IIRSCALEFACTOR / (1 + alpha); // a0 = 1 + alpha
filter->b0 = float2int( ((1.0 + cosW0) / 2.0) * scale );
filter->b1 = float2int( -(1.0 + cosW0) * scale );
filter->b2 = float2int( ((1.0 + cosW0) / 2.0) * scale );
filter->a1 = float2int( (-2.0 * cosW0) * scale );
filter->a2 = float2int( (1.0 - alpha) * scale );
}
void setIIRFilterBPF( filter_t *filter ) // Band Pass Filter Setting
{
if ( !(filter->sample_rate) )
filter->sample_rate = SAMPLE_RATE;
if ( !(filter->cutoff) )
filter->cutoff = UINT16_MAX >> 1; // 1/4 of sample rate = filter->sample_rate>>2
if ( !(filter->peak) )
filter->peak = (uint16_t)(M_SQRT1_2 * UINT16_MAX / Q_MAXIMUM); // 1/sqrt(2) effectively
double frequency = ((double)filter->cutoff * (filter->sample_rate>>1)) / UINT16_MAX;
double q = (double)filter->peak * Q_MAXIMUM / UINT16_MAX;
double w0 = (2.0 * M_PI * frequency) / filter->sample_rate;
double sinW0 = sin(w0);
double cosW0 = cos(w0);
double alpha = sinW0 / (q * 2.0f);
double scale = IIRSCALEFACTOR / (1 + alpha); // a0 = 1 + alpha
filter->b0 = float2int( alpha * scale );
filter->b1 = 0;
filter->b2 = float2int( -alpha * scale );
filter->a1 = float2int( (-2.0 * cosW0) * scale );
filter->a2 = float2int( (1.0 - alpha) * scale );
}
// Coefficients in 8.8 format
// interim values in 24.8 format
// returns y(n) in place of x(n)
void IIRFilter( filter_t *filter, int16_t * xn )
{
int32_t yn; // current output
int32_t accum; // temporary accumulator
// sum the 5 terms of the biquad IIR filter
// and update the state variables
// as soon as possible
MultiS16X16to32(yn,filter->xn_2,filter->b2);
filter->xn_2 = filter->xn_1;
MultiS16X16to32(accum,filter->xn_1,filter->b1);
yn += accum;
filter->xn_1 = *xn;
MultiS16X16to32(accum,*xn,filter->b0);
yn += accum;
MultiS16X16to32(accum,filter->yn_2,filter->a2);
yn -= accum;
filter->yn_2 = filter->yn_1;
MultiS16X16to32(accum,filter->yn_1,filter->a1);
yn -= accum;
filter->yn_1 = yn >> (IIRSCALEFACTORSHIFT + 8); // divide by a(0) = 32 & shift to 16.0 bit outcome from 24.8 interim steps
*xn = filter->yn_1; // being 16 bit yn, so that's what we return.
}
/*--------------------------------------------------*/
/*-----------Time Critical Functions----------------*/
/*--------------------------------------------------*/
#if defined(portANALOGUE) || defined(portANALOGSHIELD)
void DAC_out(const uint16_t * ch_A, const uint16_t * ch_B)
{
DAC_command_t write;
#if defined(portANALOGSHIELD)
//control byte
//A1 - Always 0
//A0 - Always 0
//LD1 - Load DAC selected
//LD0 - Load DAC selected
//NIL - NOT USED
//DAC Sel 1 - Write DAC Buffer selected
//DAC Sel 0 - Write DAC Buffer selected
//PD0 - power down mode
// If the SPI module has not been enabled yet, then return with nothing.
if( !(SPCR & _BV(SPE)) ) return;
// The SPI module is enabled, but it is in slave mode, so we can not
// transmit the byte. This can happen if SSbar is an input and it went low.
// We will try to recover by setting the MSTR bit.
// Check this once only at the start. Assume that things don't change.
if( !(SPCR & _BV(MSTR)) )
{
SPCR |= _BV(MSTR);
if( !(SPCR & _BV(MSTR)) ) return;
}
write.command = 0x00; // set to channel 0 '00'
write.value.u16 = *ch_A;
PORTD &= ~_BV(PORTD5); // Pull _SS low to select the Analog Shield DAC
// (because this might not be the first time through).
SPDR = write.command; // Begin transmission ch_A.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[1]; // Begin transmission ch_A.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[0]; // Continue transmission ch_A.
write.value.u16 = *ch_B;
write.command = 0x22; // set to channel 1 '01' Write to buffer with data and then load all DACs simultaneously from their corresponding buffers.
while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_A.
PORTD |= _BV(PORTD5); // Pull _SS high to deselect the Analog Shield DAC.
PORTD &= ~_BV(PORTD5); // Pull _SS low to select the Analog Shield DAC.
SPDR = write.command; // Begin transmission ch_B, and latch value into DAC.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[1]; // Begin transmission ch_B.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[0]; // Continue transmission ch_B.
while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_B.
PORTD |= _BV(PORTD5); // Pull _SS high to deselect the Analog Shield DAC.
#elif defined(portANALOGUE)
#if defined(LEGACY_GA)
if (ch_A != NULL)
{
write.value.u16 = (*ch_A) >> 4;
write.value.u8[1] |= CH_A_OUT;
}
else // ch_A is NULL so we turn off the DAC
{
write.value.u8[1] = CH_A_OFF;
}
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull SS low to select the Goldilocks Analogue DAC
// (because this might not be the first time through).
SPDR = write.value.u8[1]; // Begin transmission ch_A.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[0]; // Continue transmission ch_A.
if (ch_B != NULL) // start processing ch_B while we're doing the ch_A transmission
{
write.value.u16 = (*ch_B) >> 4;
write.value.u8[1] |= CH_B_OUT;
}
else // ch_B is NULL so we turn off the DAC
{
write.value.u8[1] = CH_B_OFF;
}
while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_A.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull SS high to deselect the Goldilocks Analogue DAC, and latch value into DAC.
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull SS low to select the Goldilocks Analogue DAC.
SPDR = write.value.u8[1]; // Begin transmission ch_B.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[0]; // Continue transmission ch_B.
while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_B.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull SS high to deselect the Goldilocks Analogue DAC, and latch value into DAC.
#else // For Prototype 3 and onwards
DAC_PORT_LCAC &= ~DAC_BIT_LDAC; // Pull MCP4822 _LDAC low to enable the DAC output to latch the previous sample input buffer values.
// We do this first to minimise the sample jitter when latching output values.
// if (ch_A != NULL)
// {
write.value.u16 = (*ch_A) >> 4;
write.value.u8[1] |= CH_A_OUT;
// }
// else // ch_A is NULL so we turn off the DAC
// {
// write.value.u8[1] = CH_A_OFF;
// }
DAC_PORT_LCAC |= DAC_BIT_LDAC; // Pull MCP4822 _LDAC high to disable the DAC latch.
// Values we write into the DAC buffer this time, will be latched into the DAC output next interrupt.
UCSR1A = _BV(TXC1); // Clear the Transmit complete flag.
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull _SS low to select the Goldilocks Analogue DAC
UDR1 = write.value.u8[1]; // Begin transmission ch_A.
UDR1 = write.value.u8[0]; // Continue transmission ch_A. UDR1 is double buffered.
// if (ch_B != NULL) // start processing ch_B while we're doing the ch_A transmission
// {
write.value.u16 = (*ch_B) >> 4;
write.value.u8[1] |= CH_B_OUT;
// }
// else // ch_B is NULL so we turn off the DAC
// {
// write.value.u8[1] = CH_B_OFF;
// }
while ( !(UCSR1A & _BV(TXC1)) ); // Check we've finished ch_A.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull _SS high to deselect the Goldilocks Analogue DAC.
UCSR1A = _BV(TXC1); // Clear the Transmit complete flag.
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull _SS low to select the Goldilocks Analogue DAC.
UDR1 = write.value.u8[1]; // Begin transmission ch_B.
UDR1 = write.value.u8[0]; // Continue transmission ch_B.
while ( !(UCSR1A & _BV(TXC1)) ); // Check we've finished ch_B.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull _SS high to deselect the Goldilocks Analogue DAC.
#endif
#endif
}
void DAC_out_P(const uint16_t * ch_A, const uint16_t * ch_B)
{
DAC_command_t write;
#if defined(portANALOGSHIELD)
//control byte
//A1 - Always 0
//A0 - Always 0
//LD1 - Load DAC selected
//LD0 - Load DAC selected
//NIL - NOT USED
//DAC Sel 1 - Write DAC Buffer selected
//DAC Sel 0 - Write DAC Buffer selected
//PD0 - power down mode
// If the SPI module has not been enabled yet, then return with nothing.
if( !(SPCR & _BV(SPE)) ) return;
// The SPI module is enabled, but it is in slave mode, so we can not
// transmit the byte. This can happen if SSbar is an input and it went low.
// We will try to recover by setting the MSTR bit.
// Check this once only at the start. Assume that things don't change.
if( !(SPCR & _BV(MSTR)) )
{
SPCR |= _BV(MSTR);
if( !(SPCR & _BV(MSTR)) ) return;
}
write.command = 0x00; // Set to channel 0 '00'
write.value.u16 = pgm_read_word(ch_A);
PORTD &= ~_BV(PORTD5); // Pull _SS low to select the Analog Shield DAC
// (because this might not be the first time through).
SPDR = write.command; // Begin transmission ch_A.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[1]; // Begin transmission ch_A.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[0]; // Continue transmission ch_A.
write.value.u16 = pgm_read_word(ch_B);
write.command = 0x22; // Set to channel 1 '01' Write to buffer with data and then load all DACs simultaneously from their corresponding buffers.
while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_A.
PORTD |= _BV(PORTD5); // Pull _SS high to deselect the Analog Shield DAC.
PORTD &= ~_BV(PORTD5); // Pull _SS low to select the Analog Shield DAC.
SPDR = write.command; // Begin transmission ch_B.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[1]; // Begin transmission ch_B.
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[0]; // Continue transmission ch_B.
while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_B.
PORTD |= _BV(PORTD5); // Pull _SS high to deselect the Analog Shield DAC, and latch value into DAC.
#elif defined(portANALOGUE)
#if defined(LEGACY_GA)
if (ch_A != NULL)
{
write.value.u16 = pgm_read_word(ch_A) >> 4;
write.value.u8[1] |= CH_A_OUT;
}
else // ch_A is NULL so we turn off the DAC
{
write.value.u8[1] = CH_A_OFF;
}
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull SS low to select the Goldilocks Analogue DAC
// (because this might not be the first time through).
SPDR = write.value.u8[1]; // Begin transmission
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[0]; // Continue transmission
if (ch_B != NULL) // start ch_B while we're doing the transmission
{
write.value.u16 = pgm_read_word(ch_B) >> 4;
write.value.u8[1] |= CH_B_OUT;
}
else // ch_B is NULL so we turn off the DAC
{
write.value.u8[1] = CH_B_OFF;
}
while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_A.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull SS high to deselect the Goldilocks Analogue DAC, and latch value into DAC.
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull SS low to select the Goldilocks Analogue DAC.
SPDR = write.value.u8[1]; // Begin transmission
while ( !(SPSR & _BV(SPIF)) );
SPDR = write.value.u8[0]; // Continue transmission
while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_B.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull SS high to deselect the Goldilocks Analogue DAC, and latch value into DAC.
#else // For Prototype 3 and onwards.
DAC_PORT_LCAC &= ~DAC_BIT_LDAC; // Pull MCP4822 _LDAC low to enable the DAC output to latch the previous sample input buffer values.
// We do this first to minimise the sample jitter when latching output values.
// if (ch_A != NULL)
// {
write.value.u16 = pgm_read_word(ch_A) >> 4;
write.value.u8[1] |= CH_A_OUT;
// }
// else // ch_A is NULL so we turn off the DAC
// {
// write.value.u8[1] = CH_A_OFF;
// }
DAC_PORT_LCAC |= DAC_BIT_LDAC; // Pull MCP4822 _LDAC high to disable the DAC latch.
// Values we write into the DAC buffer this time, will be latched into the DAC output next interrupt.
UCSR1A = _BV(TXC1); // Clear the Transmit complete flag.
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull _SS low to select the Goldilocks Analogue DAC
UDR1 = write.value.u8[1]; // Begin transmission
UDR1 = write.value.u8[0]; // Continue transmission
if (ch_B != NULL) // start ch_B while we're doing the transmission
{
write.value.u16 = pgm_read_word(ch_B) >> 4;
write.value.u8[1] |= CH_B_OUT;
}
else // ch_B is NULL so we turn off the DAC
{
write.value.u8[1] = CH_B_OFF;
}
while ( !(UCSR1A & _BV(TXC1)) ); // Check we've finished ch_A.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull _SS high to deselect the Goldilocks Analogue DAC.
UCSR1A = _BV(TXC1); // Clear the Transmit complete flag.
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull _SS low to select the Goldilocks Analogue DAC.
UDR1 = write.value.u8[1]; // Begin transmission
UDR1 = write.value.u8[0]; // Continue transmission
while ( !(UCSR1A & _BV(TXC1)) ); // Check we've finished ch_B.
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull _SS high to deselect the Goldilocks Analogue DAC.
#endif
#endif
}
#endif //#if defined(portANALOGUE) || defined(portANALOGSHIELD)
/*--------------------------------------------------*/
/*-------Interrupt (Loop at Sample Rate)------------*/
/*--------------------------------------------------*/
// This is an example interrupt process that will output values at the sample rate defined.
// Other processes may use the DAC_out_P to increment across a set of samples stored in PROGMEM
#if defined(portANALOGUE) || defined(portANALOGSHIELD)
ISR(TIMER1_COMPA_vect) __attribute__ ((hot, flatten));
ISR(TIMER1_COMPA_vect)
{
#if defined(DEBUG_PING)
// start mark - check for start of interrupt - for debugging only
PORTD |= _BV(PORTD7); // Ping IO line.
#endif
// MCP4822 data transfer routine
// move data to the MCP4822 - done first for regularity (reduced jitter).
// &'s are necessary on data_in variables
DAC_out (ch_A_ptr, ch_B_ptr); // OR
// DAC_out_P(ch_A_ptr, ch_B_ptr); // Where ch_A_ptr and ch_B_ptr are pointers to values stored in PROGMEM
// audio processing routine - do whatever processing on input is required - prepare output for next sample.
// Fire the global audio handler.
if (audioHandler!=NULL)
audioHandler(ch_A_ptr, ch_B_ptr);
#if defined(DEBUG_PING)
// end mark - check for end of interrupt - for debugging only
PORTD &= ~_BV(PORTD7);
#endif
}
ISR(TIMER0_COMPA_vect) __attribute__ ((hot, flatten));
ISR(TIMER0_COMPA_vect)
{
#if defined(DEBUG_PING)
// start mark - check for start of interrupt - for debugging only
PORTD |= _BV(PORTD7); // Ping IO line.
#endif
// MCP4822 data transfer routine
// move data to the MCP4822 - done first for regularity (reduced jitter).
// &'s are necessary on data_in variables
DAC_out (ch_A_ptr, ch_B_ptr); // OR
// DAC_out_P(ch_A_ptr, ch_B_ptr); // Where ch_A_ptr and ch_B_ptr are pointers to values stored in PROGMEM
// audio processing routine - do whatever processing on input is required - prepare output for next sample.
// Fire the global audio handler, if set.
if (audioHandler!=NULL)
audioHandler(ch_A_ptr, ch_B_ptr);
#if defined(DEBUG_PING)
// end mark - check for end of interrupt - for debugging only
PORTD &= ~_BV(PORTD7);
#endif
}
#endif //#if defined(portANALOGUE) || defined(portANALOGSHIELD)
#ifdef __cplusplus
}
#endif
#endif // DAC_h end include guard