Brady's first commit
authorbaobrien <baobrien@01035d8c-6547-0410-b346-abe4f91aad63>
Sat, 7 Mar 2015 06:45:43 +0000 (06:45 +0000)
committerbaobrien <baobrien@01035d8c-6547-0410-b346-abe4f91aad63>
Sat, 7 Mar 2015 06:45:43 +0000 (06:45 +0000)
-IIR tner is fixed point
-IIR tuner center changed to 700Khz
-IIR Upconverter simulated in octave
-STM32F4 DAC code copied and modified to support high sample rate on DAC1 (2Mhz)
-Got a start on the C IIR Upconverter. Not tested yet.

git-svn-id: https://svn.code.sf.net/p/freetel/code@2059 01035d8c-6547-0410-b346-abe4f91aad63

13 files changed:
codec2-dev/octave/dacres.m [new file with mode: 0644]
codec2-dev/octave/octave-workspace [new file with mode: 0644]
codec2-dev/src/codec2.c
codec2-dev/src/postfilter.c
codec2-dev/src/quantise.c
codec2-dev/stm32/Makefile
codec2-dev/stm32/inc/iir_duc.h [new file with mode: 0644]
codec2-dev/stm32/inc/stm32f4_adc_tuner.h
codec2-dev/stm32/inc/stm32f4_dacduc.h [new file with mode: 0644]
codec2-dev/stm32/src/fast_dac_ut.c [new file with mode: 0644]
codec2-dev/stm32/src/iir_duc.c [new file with mode: 0644]
codec2-dev/stm32/src/iir_tuner.c
codec2-dev/stm32/src/stm32f4_dacduc.c [new file with mode: 0644]

diff --git a/codec2-dev/octave/dacres.m b/codec2-dev/octave/dacres.m
new file mode 100644 (file)
index 0000000..155a65a
--- /dev/null
@@ -0,0 +1,56 @@
+% dacres.m
+% David Rowe 18 Feb 2015
+% Brady O'Brien 5 Mar 2015
+% DAC upconversion simulation
+
+graphics_toolkit ("gnuplot");
+
+
+M = 25;                                %interpolation
+fs = 2E6/M;                    %Input sampling frequency
+fb = 7E5;                      %Bandpass frequency
+
+f1 = fs/4;
+f2 = f1 + 4E3;
+f3 = f1 - 4E3;
+t = (0:(fs-1));
+
+beta1 = 0.999;
+b1x = -2*sqrt(beta1)*cos(2*pi*fb/(fs*M))
+beta2 = 1 - (1-0.999)*M;
+
+s1 = [fs zeros(1,fs-1)];       % noise floor, continuous interferers 
+s2 = 100*4*cos(t*2*pi*f2/fs);  % wanted signal 40dB above interferers
+s3 = 100*3*cos(t*2*pi*f3/fs);  % another wanted frequency
+s4 = 100*4*cos(t*2*pi*f1/fs);  % wanted signal at center frequency
+s = s1 + s2 + s3 + s4;
+
+
+s2 = filter([1 0 beta2],1,s);  %pre interpolation notch filter to equalize bandpass after interpolation
+s3 = zeros(1,length(s2)*M);    %interpolate by zero-stuffing
+s3(1:M:length(s2)*M) = s2;
+s4 = filter(1,[1 b1x beta1],s3); %select wanted signal
+
+figure(1)
+subplot(211)
+plot(20*log10(abs(fft(s)/fs)))
+grid
+axis([0 fs/2 -10 50])
+title('Output from modem');
+subplot(212)
+plot(20*log10(abs(fft(s2/fs))))
+grid
+axis([0 fs/2 -10 70])
+title('After Pre-eq');
+
+figure(2)
+subplot(211)
+plot(20*log10(abs(fft(s3)/fs)))
+grid
+axis([0 (fs/2)*M -10 80])
+title('After interpolation');
+subplot(212)
+plot(20*log10(abs(fft(s4)/fs)))
+grid
+title('After bandpass');
+axis([0 (fs/2)*M -10 80])
diff --git a/codec2-dev/octave/octave-workspace b/codec2-dev/octave/octave-workspace
new file mode 100644 (file)
index 0000000..e69de29
index 2f63d88f459dbfb2024d8808b5b9b790f33222cf..08604a55553ee054fd69e7badfdf1899fff5e8f7 100644 (file)
@@ -418,12 +418,11 @@ void codec2_decode_3200(struct CODEC2 *c2, short speech[], const unsigned char *
 
     interp_Wo(&model[0], &c2->prev_model_dec, &model[1]);
     e[0] = interp_energy(c2->prev_e_dec, e[1]);
-
     /* LSPs are sampled every 20ms so we interpolate the frame in
        between, then recover spectral amplitudes */
 
     interpolate_lsp_ver2(&lsps[0][0], c2->prev_lsps_dec, &lsps[1][0], 0.5);
-
     for(i=0; i<2; i++) {
        lsp_to_lpc(&lsps[i][0], &ak[i][0], LPC_ORD);
        aks_to_M2(c2->fft_fwd_cfg, &ak[i][0], LPC_ORD, &model[i], e[i], &snr, 0, 0, 
index fc88c0eaa50d5521abcd4c42513dd1811fc7153b..f347658cf7269f6d27d1de2474392215f7551af4 100644 (file)
@@ -111,7 +111,7 @@ void postfilter(
   e = 1E-12;
   for(m=1; m<=model->L; m++)
       e += model->A[m]*model->A[m];
-  
+
   assert(e > 0.0);
   e = 10.0*log10f(e/model->L);
 
index 105380410d3207fa6b5486f354ce5a50142ade2e..03b9812ed19b8e548d9d039bf35a5884004015a7 100644 (file)
@@ -941,9 +941,8 @@ void aks_to_M2(
 
   /* Determine power spectrum P(w) = E/(A(exp(jw))^2 ------------------------*/
 
-  for(i=0; i<FFT_ENC/2; i++) {
-    Pw[i].real = 1.0/(Aw[i].real*Aw[i].real + Aw[i].imag*Aw[i].imag + 1E-6);
-  }
+  for(i=0; i<FFT_ENC/2; i++)
+    Pw[i].real = 1.0/(Aw[i].real*Aw[i].real + Aw[i].imag*Aw[i].imag);
 
   PROFILE_SAMPLE_AND_LOG(tpw, tfft, "      Pw"); 
 
index ae8a90d58060a5f4f7cad194880e4d4528da6127..7a4ca3994fad2439c8e4a19df29825bccc781d34 100644 (file)
@@ -6,7 +6,7 @@ FLOAT_TYPE=hard
 
 ###################################################
 
-BINPATH=~/gcc-arm-none-eabi-4_7-2013q1/bin
+BINPATH=/opt/gcc-arm-none-eabi-4_7-2013q1/bin
 CC=$(BINPATH)/arm-none-eabi-gcc
 OBJCOPY=$(BINPATH)/arm-none-eabi-objcopy
 SIZE=$(BINPATH)/arm-none-eabi-size
@@ -30,7 +30,7 @@ endif
 
 PERIPHLIBURL    = http://www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/firmware/
 PERIPHLIBZIP    = stm32f4_dsp_stdperiph_lib.zip
-PERIPHLIBVER   = V1.1.0
+PERIPHLIBVER   = V1.4.0
 PERIPHLIBNAME  = STM32F4xx_DSP_StdPeriph_Lib
 PERIPHLIBDIR   = $(PERIPHLIBNAME)_$(PERIPHLIBVER)
 CMSIS          = $(PERIPHLIBDIR)/Libraries/CMSIS
@@ -109,7 +109,7 @@ OBJS = $(SRCS:.c=.o)
 
 ###################################################
 
-all: libstm32f4.a codec2_profile.elf fft_test.elf dac_ut.elf dac_play.elf adc_rec.elf pwm_ut.elf fdmdv_profile.elf sm1000_leds_switches_ut.elf sm1000.elf adcdac_ut.elf freedv_tx_profile.elf freedv_rx_profile.elf adc_sd.elf usb_vcp_ut.elf fdmdv_dump_rt.elf tuner_ut.elf
+all: libstm32f4.a codec2_profile.elf fft_test.elf dac_ut.elf dac_play.elf adc_rec.elf pwm_ut.elf fdmdv_profile.elf sm1000_leds_switches_ut.elf sm1000.elf adcdac_ut.elf freedv_tx_profile.elf freedv_rx_profile.elf adc_sd.elf usb_vcp_ut.elf fdmdv_dump_rt.elf tuner_ut.elf fast_dac_ut.elf
 
 dl/$(PERIPHLIBZIP):
        mkdir -p dl
@@ -155,6 +155,19 @@ dac_ut.elf: $(DAC_UT_SRCS)
        $(CC) $(CFLAGS) -O0 $^ -o $@ $(LIBPATHS) $(LIBS)
        $(OBJCOPY) -O binary dac_ut.elf dac_ut.bin
 
+FAST_DAC_UT_SRCS=\
+src/fast_dac_ut.c \
+../src/fifo.c \
+src/stm32f4_dacduc.c \
+src/debugblinky.c \
+src/system_stm32f4xx.c \
+src/startup_stm32f4xx.s \
+src/init.c
+
+fast_dac_ut.elf: $(FAST_DAC_UT_SRCS)
+       $(CC) $(CFLAGS) -O0 $^ -o $@ $(LIBPATHS) $(LIBS)
+       $(OBJCOPY) -O binary fast_dac_ut.elf fast_dac_ut.bin
+
 ADCDAC_UT_SRCS=\
 src/adcdac_ut.c \
 ../src/fifo.c \
diff --git a/codec2-dev/stm32/inc/iir_duc.h b/codec2-dev/stm32/inc/iir_duc.h
new file mode 100644 (file)
index 0000000..42f9d15
--- /dev/null
@@ -0,0 +1,33 @@
+ /*---------------------------------------------------------------------------*\
+
+  FILE........: iir_duc.h
+  AUTHOR......: Brady O'Brien
+  DATE CREATED: 6 Mar 2015
+
+  Interapolator/Filter for IF upconversion
+
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2015 Brady O'Brien
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  This program is
+  distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __IIR_DUC_H
+#define __IIR_DUC_H
+
+void iir_upconv(float modin[],unsigned short dac_out[]);
+
+#endif
index 2db99fef71a583da96db6398a2d707c3c946cca6..88f16a14f8f80d03d41f0a312c6ced52e5b28c75 100644 (file)
@@ -29,7 +29,7 @@
 #ifndef __STM32F4_ADC_TUNER__
 #define __STM32F4_ADC_TUNER__
 
-#define ADC_TUNER_M  45   /* decimation rate */
+#define ADC_TUNER_M  55   /* decimation rate */
 #define ADC_TUNER_N  160
 #define ADC_TUNER_BUF_SZ  (ADC_TUNER_M*ADC_TUNER_N)
 
diff --git a/codec2-dev/stm32/inc/stm32f4_dacduc.h b/codec2-dev/stm32/inc/stm32f4_dacduc.h
new file mode 100644 (file)
index 0000000..580bfcc
--- /dev/null
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------------*\
+
+  FILE........: stm32f4_dac.h
+  AUTHOR......: David Rowe
+  DATE CREATED: 1 June 2013
+
+  Two channel FIFO buffered DAC driver module for STM32F4. DAC1 is fixed at
+  Fs=2Mhz
+
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2013 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  This program is
+  distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __STM32F4_DAC__
+#define __STM32F4_DAC__
+
+#define DUC_N 100
+#define DUC_M  25
+#define DAC_DUC_BUF_SZ DUC_M*DUC_N
+#define DAC_BUF_SZ 2048
+
+void dac_open(int fifo_sz);
+int dac1_write(short buf[], int n); /* DAC1 pin PA4 */
+int dac2_write(short buf[], int n); /* DAC2 pin PA5 */
+
+#endif
diff --git a/codec2-dev/stm32/src/fast_dac_ut.c b/codec2-dev/stm32/src/fast_dac_ut.c
new file mode 100644 (file)
index 0000000..bc19547
--- /dev/null
@@ -0,0 +1,56 @@
+/*---------------------------------------------------------------------------*\\r
+\r
+  FILE........: dac_ut.c\r
+  AUTHOR......: David Rowe\r
+  DATE CREATED: May 31 2013\r
+\r
+  Plays a 500 Hz sine wave sampled at 16 kHz out of PA5 on a Discovery board,\r
+  or the speaker output of the SM1000.\r
+\r
+\*---------------------------------------------------------------------------*/\r
+\r
+/*\r
+  Copyright (C) 2013 David Rowe\r
+\r
+  All rights reserved.\r
+\r
+  This program is free software; you can redistribute it and/or modify\r
+  it under the terms of the GNU Lesser General Public License version 2.1, as\r
+  published by the Free Software Foundation.  This program is\r
+  distributed in the hope that it will be useful, but WITHOUT ANY\r
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public\r
+  License for more details.\r
+\r
+  You should have received a copy of the GNU Lesser General Public License\r
+  along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+*/\r
+\r
+#include <assert.h>\r
+#include "stm32f4_dacduc.h"\r
+\r
+#define SINE_SAMPLES   60\r
+\r
+\r
+/* 32 sample sine wave which at Fs=16kHz will be 500Hz.  Note samples\r
+   are 16 bit 2's complement, the DAC driver convertsto 12 bit\r
+   unsigned. */\r
+\r
+short aWave[] = {4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,\r
+       4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,};\r
+\r
+short aSine[] = {390, 3981, 1410, 841, 4080, 841, 1410, 3981, 390, 2040, 3691, 100, 2671, 3240, 0, 3240, 2671, 100, 3691, 2040, 390, 3981, 1410, 841, 4080, 841, 1410, 3981, 390, 2040, 3691, 100, 2671, 3240, 0, 3240, 2671, 100, 3691, 2040,390, 3981, 1410, 841, 4080, 841, 1410, 3981, 390, 2040, 3691, 100, 2671, 3240, 0, 3240, 2671, 100, 3691, 2040\r
+};\r
+\r
+int main(void) {\r
+\r
+    dac_open(4*DAC_DUC_BUF_SZ);\r
+    while (1) {\r
+\r
+        /* keep DAC FIFOs topped up */\r
+\r
+        dac1_write((short*)aWave, SINE_SAMPLES);\r
+    }\r
+   \r
+}\r
+\r
diff --git a/codec2-dev/stm32/src/iir_duc.c b/codec2-dev/stm32/src/iir_duc.c
new file mode 100644 (file)
index 0000000..d419171
--- /dev/null
@@ -0,0 +1,80 @@
+ /*---------------------------------------------------------------------------*\
+
+  FILE........: iir_duc.c
+  AUTHOR......: Brady O'Brien
+  DATE CREATED: 6 Mar 2015
+
+  Interapolator/Filter for IF upconversion
+
+  Unit testing:
+  
+    ~/codec2-dev/stm32$ gcc -D__UNITTEST__ -Iinc src/iir_tuner.c -o iir_tuner -lm -Wall
+    ~/codec2-dev/stm32$ ./iir_tuner
+
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2015 Brady O'Brien
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  This program is
+  distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define BETA1                    .9990234375                   // B1MUL/(2**B1SHFT)
+#define B1MUL                   1023                           
+#define B1SMUL                  1204
+#define B1SHFT                  10                             // 10 bits gives us plenty of headroom between 31 bits of int and 14 bits of ADC
+#define B2MUL                   979                            // This actually matches BETA2 exactly with the supplied BETA1
+#define B2SHFT                  10                             // 10 is also the lowest we can go without beta1=1
+#define BETA2                    (1.0 - (1.0-BETA1)*DUC_M)// B2MUL/(2**B2SHFT)
+#define IN_SCALE                 1.0                            //Input scaling factor
+#define DAC_SCALE                4096                           //Maximum output to DAC
+
+
+/*
+   Upconvert and bandpass filter a chunk of spectrum from Fs/M to Fs. We're going for 700khz here.
+   modin needs to be DUC_N long and dac_out needs to be DUC_N*DUC_M long. This 
+*/
+
+float f_1,f_2,f;
+int   n_1,n_2,n;
+int   dac_temp[DUC_N*DUC_M];                                //Temporary storage for samples before IIR.
+
+void iir_upconv(float modin[],unsigned short dac_out[]){
+   memset((void*)dac_temp,0,sizeof(int)*DUC_N*DUC_M);      //Preset output array for interpolation
+   int i,j,k;
+   int m;
+   k=0;
+   //Iterate through input samples and apply pre-eq FIR, interpolate, and apply BPF IIR
+   for(i=0;i<DUC_N;i++){
+       f = modin[i]+f_2*BETA2;
+       f_1 = modin[i];
+       f_2 = f_1;
+       //m = (int)(f/(IN_SCALE*2))*DAC_SCALE;  //Scale and convert to int. I probably should add more bits here and truncate before DAC.
+       m = (int)((f/(IN_SCALE*2))*DAC_SCALE);  
+       dac_temp[k]= m;
+       for(j=0;j<DUC_M;j++,k++){
+           n = dac_temp[k] - ((B1SMUL*n_1)>>B1SHFT) - ((B1MUL*n_2)>>B1SHFT);
+            n_2 = n_1;
+            n_1 = n;
+           dac_out[k]=(unsigned short)(n+DAC_SCALE/2);
+       }
+   }
+
+}
+
+
+
+
+
+
index 334ff778f00b05234a5c7001215b6c537f688cbd..87d0ad2a360d0deece7638fec35dfef0ee47aaef 100644 (file)
@@ -1,4 +1,4 @@
-/*---------------------------------------------------------------------------*\
+ /*---------------------------------------------------------------------------*\
 
   FILE........: iir_tuner.c
   AUTHOR......: David Rowe
 /* Filter coefficients of IIR tuner (BETA1) and FIR equaliser (BETA2).
    Note neat trick to relate BETA2 to BETA1 by the decimation rate */
 
-#define BETA1                    0.999
-#define BETA2                    (1.0 - (1.0-BETA1)*ADC_TUNER_M)
+#define BETA1                    .9990234375                   // B1MUL/(2**B1SHFT)
+#define B1MUL                   1023                           
+#define B1SMUL                  1204
+#define B1SHFT                  10                             // 10 bits gives us plenty of headroom between 31 bits of int and 14 bits of ADC
+#define B2MUL                   979                            // This actually matches BETA2 exactly with the supplied BETA1
+#define B2SHFT                  10                             // 10 is also the lowest we can go without beta1=1
+#define BETA2                    (1.0 - (1.0-BETA1)*ADC_TUNER_M)// B2MUL/(2**B2SHFT)
+
+#define FIXED_IIR                                               //Define this to compile a fixed point IIR filter
 
 /* filter states - we keep them global due to the need for speed */
 
+#ifdef FIXED_IIR
+int n_2, n_1, o_2, o_1;
+#else
 float y_2, y_1, z_2, z_1;
+#endif
 
 /*
    ADC -> signed conversion - IIR BPF - Decimate - FIR Equaliser -> FIFO
@@ -59,26 +70,40 @@ float y_2, y_1, z_2, z_1;
 void iir_tuner(float dec_50[], unsigned short adc_buf[]) {
     int i, j, k;
     float x, y, z;
+    int n, m, o;
 
     for(i=0, j=0; i<ADC_TUNER_BUF_SZ/2; j++) {
 
         /* IIR BPF centred at Fs/4.  All your MIPs are belong to this
            loop. */
-
         for(k=0; k<ADC_TUNER_M; k++,i++) {
-            x = (int)adc_buf[i] - 32768;
-            y = x - BETA1*y_2;
-            y_2 = y_1;
+            #ifdef FIXED_IIR
+            m = (int)adc_buf[i];
+            n = m - ((B1SMUL*n_1)>>B1SHFT) - ((B1MUL*n_2)>>B1SHFT);
+            n_2 = n_1;
+            n_1 = n;
+            #else
+           x = (int)adc_buf[i] - 32768;
+           y = x - (BETA1*y_2);
+           y_2 = y_1;
             y_1 = y;
+            #endif
         }
 
         /* Equaliser FIR filter, notch at Fs/(4*ADC_TUNER_M) to smooth out 
            IIR BPF passband response */
-
-        z = y + BETA2*z_2;
-        dec_50[j] = z;
+        #ifdef FIXED_IIR
+       o = n + ((B2MUL*o_2)>>B2SHFT);
+       dec_50[j] = (float)o;
+       o_2 = o_1;
+       o_1 = n;
+        #else
+       z=y+BETA2*z_2;        
+       dec_50[j] = z;
         z_2 = z_1;
         z_1 = y;
+        #endif
+       
     }
 }
 
@@ -133,7 +158,7 @@ int main(void) {
 
     /* test Fs=2E6 unsigned short to Fs=50E3 float tuner/resampler -----------------------*/
 
-    f1 = 500E3;
+    f1 = 700E3;
     f2 = f1 + 8E3;       /* wanted */
     f3 = f1 - 7E3;       /* wanted */
     f4 = f1 - 207E3;     /* out of band, should be greatly attenuated */
diff --git a/codec2-dev/stm32/src/stm32f4_dacduc.c b/codec2-dev/stm32/src/stm32f4_dacduc.c
new file mode 100644 (file)
index 0000000..e1b231a
--- /dev/null
@@ -0,0 +1,422 @@
+/*---------------------------------------------------------------------------*\\r
+\r
+  FILE........: stm32f4_dac.c\r
+  AUTHOR......: David Rowe\r
+  DATE CREATED: 1 June 2013\r
+\r
+  DAC driver module for STM32F4. DAC1 if fixed at Fs of 2Mhz.\r
+\r
+\*---------------------------------------------------------------------------*/\r
+\r
+/*\r
+  Copyright (C) 2013 David Rowe\r
+\r
+  All rights reserved.\r
+\r
+  This program is free software; you can redistribute it and/or modify\r
+  it under the terms of the GNU Lesser General Public License version 2.1, as\r
+  published by the Free Software Foundation.  This program is\r
+  distributed in the hope that it will be useful, but WITHOUT ANY\r
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public\r
+  License for more details.\r
+\r
+  You should have received a copy of the GNU Lesser General Public License\r
+  along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+*/\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include "stm32f4xx.h"\r
+#include "codec2_fifo.h"\r
+#include "stm32f4_dacduc.h"\r
+#include "debugblinky.h"\r
+\r
+/* write to these registers for 12 bit left aligned data, as per data sheet \r
+   make sure 4 least sig bits set to 0 */\r
+\r
+#define DAC_DHR12R1_ADDRESS    0x40007408\r
+#define DAC_DHR12R2_ADDRESS    0x40007414\r
+\r
+#define DAC_MAX      4096            /* maximum amplitude */\r
+\r
+/* y=mx+c mapping of samples16 bit shorts to DAC samples.  Table: 74\r
+   of data sheet indicates With DAC buffer on, DAC range is limited to\r
+   0x0E0 to 0xF1C at VREF+ = 3.6 V, we have Vref=3.3V which is close.\r
+ */\r
+\r
+#define M ((3868.0-224.0)/65536.0)\r
+#define C 2047.0\r
+\r
+\r
+static struct FIFO *dac1_fifo;\r
+static struct FIFO *dac2_fifo;\r
+\r
+static unsigned short dac1_buf[DAC_DUC_BUF_SZ];\r
+static unsigned short dac2_buf[DAC_BUF_SZ];\r
+\r
+static void tim6_config(void);\r
+static void tim7_config(void);\r
+static void dac1_config(void);\r
+static void dac2_config(void);\r
+\r
+int dac_underflow;\r
+\r
+void dac_open(int fifo_size) {\r
+\r
+    memset(dac1_buf, 32768, sizeof(short)*DAC_DUC_BUF_SZ);\r
+    memset(dac2_buf, 32768, sizeof(short)*DAC_BUF_SZ);\r
+\r
+    /* Create fifos */\r
+\r
+    dac1_fifo = fifo_create(fifo_size);\r
+    dac2_fifo = fifo_create(fifo_size);\r
+    assert(dac1_fifo != NULL);\r
+    assert(dac2_fifo != NULL);\r
+\r
+    /* Turn on the clocks we need -----------------------------------------------*/\r
+\r
+    /* DMA1 clock enable */\r
+    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);\r
+    /* GPIOA clock enable (to be used with DAC) */\r
+    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);                         \r
+    /* DAC Periph clock enable */\r
+    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);\r
+\r
+    /* GPIO Pin configuration DAC1->PA.4, DAC2->PA.5 configuration --------------*/\r
+\r
+    GPIO_InitTypeDef GPIO_InitStructure;\r
+    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;\r
+    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;\r
+    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;\r
+    GPIO_Init(GPIOA, &GPIO_InitStructure);\r
+\r
+    /* Timer and DAC 1 & 2 Configuration ----------------------------------------*/\r
+    tim7_config();\r
+    tim6_config();  \r
+    dac1_config();\r
+    dac2_config();\r
+\r
+    init_debug_blinky();\r
+}\r
+\r
+\r
+/* Call these puppies to send samples to the DACs.  For your\r
+   convenience they accept signed 16 bit samples. */\r
+\r
+int dac1_write(short buf[], int n) {   \r
+    return fifo_write(dac1_fifo, buf, n);\r
+}\r
+\r
+int dac2_write(short buf[], int n) {   \r
+    return fifo_write(dac2_fifo, buf, n);\r
+}\r
+\r
+static void tim6_config(void)\r
+{\r
+  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;\r
+\r
+  /* TIM6 Periph clock enable */\r
+  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);\r
+  \r
+  /* --------------------------------------------------------\r
+  \r
+  TIM6 input clock (TIM6CLK) is set to 2 * APB1 clock (PCLK1), since\r
+  APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig\r
+  13 clock tree figure in DM0031020.pdf).\r
+\r
+     Sample rate Fs = 2*PCLK1/TIM_ClockDivision \r
+                    = (HCLK/2)/TIM_ClockDivision\r
+                    \r
+  ----------------------------------------------------------- */\r
+\r
+  /* Time base configuration */\r
+\r
+  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); \r
+  TIM_TimeBaseStructure.TIM_Period = 80;          \r
+  TIM_TimeBaseStructure.TIM_Prescaler = 0;       \r
+  TIM_TimeBaseStructure.TIM_ClockDivision = 0;    \r
+  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  \r
+  TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);\r
+\r
+  /* TIM6 TRGO selection */\r
+\r
+  TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);\r
+  \r
+  /* TIM6 enable counter */\r
+\r
+  TIM_Cmd(TIM6, ENABLE);\r
+}\r
+\r
+/* Sets up tim7 to run at a high sample rate */\r
+void tim7_config(void)\r
+{\r
+  /* Set up tim7 */\r
+\r
+\r
+  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;\r
+\r
+  /* TIM7 Periph clock enable */\r
+  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);\r
+  \r
+  /* --------------------------------------------------------\r
+\r
+  \r
+  TIM7 input clock (TIM7CLK) is set to 2 * APB1 clock (PCLK1), since\r
+  APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig\r
+  13 clock tree figure in DM0031020.pdf).\r
+\r
+     Sample rate Fs = 2*PCLK1/TIM_ClockDivision \r
+                    = (HCLK/2)/TIM_ClockDivision\r
+                    \r
+  ----------------------------------------------------------- */\r
+\r
+  /* Time base configuration */\r
+\r
+  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); \r
+  TIM_TimeBaseStructure.TIM_Period = 41;          \r
+  TIM_TimeBaseStructure.TIM_Prescaler = 0;       \r
+  TIM_TimeBaseStructure.TIM_ClockDivision = 0;    \r
+  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  \r
+  TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure);\r
+\r
+  /* TIM7 TRGO selection */\r
+\r
+  TIM_SelectOutputTrigger(TIM7, TIM_TRGOSource_Update);\r
+  \r
+  /* TIM7 enable counter */\r
+\r
+  TIM_Cmd(TIM7, ENABLE);\r
+}\r
+\r
+static void dac1_config(void)\r
+{\r
+  DAC_InitTypeDef  DAC_InitStructure;\r
+  DMA_InitTypeDef  DMA_InitStructure;\r
+  NVIC_InitTypeDef NVIC_InitStructure;\r
+  \r
+  /* DAC channel 1 Configuration */\r
+\r
+  /* \r
+     This line fixed a bug that cost me 5 days, bad wave amplitude\r
+     value, and some STM32F4 periph library bugs caused triangle wave\r
+     geneartion to be enable resulting in a low level tone on the\r
+     SM1000, that we thought was caused by analog issues like layour\r
+     or power supply biasing\r
+  */\r
+  DAC_StructInit(&DAC_InitStructure); \r
+\r
+  DAC_InitStructure.DAC_Trigger = DAC_Trigger_T7_TRGO; \r
+  DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;\r
+  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;\r
+  DAC_Init(DAC_Channel_1, &DAC_InitStructure);\r
+\r
+  /* DMA1_Stream5 channel7 configuration **************************************/\r
+  /* Table 35 page 219 of the monster data sheet */\r
+\r
+  DMA_DeInit(DMA1_Stream5);\r
+  DMA_InitStructure.DMA_Channel = DMA_Channel_7;  \r
+  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R1_ADDRESS;\r
+  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac1_buf;\r
+  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;\r
+  DMA_InitStructure.DMA_BufferSize = DAC_DUC_BUF_SZ;\r
+  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;\r
+  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;\r
+  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;\r
+  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;\r
+  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;\r
+  DMA_InitStructure.DMA_Priority = DMA_Priority_High;\r
+  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         \r
+  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;\r
+  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;\r
+  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;\r
+  DMA_Init(DMA1_Stream5, &DMA_InitStructure);\r
+\r
+  /* Enable DMA Half & Complete interrupts */\r
+\r
+  DMA_ITConfig(DMA1_Stream5, DMA_IT_TC | DMA_IT_HT, ENABLE);\r
+\r
+  /* Enable the DMA Stream IRQ Channel */\r
+\r
+  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn;\r
+  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;\r
+  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;\r
+  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;\r
+  NVIC_Init(&NVIC_InitStructure);     \r
+\r
+  /* Enable DMA1_Stream5 */\r
+\r
+  DMA_Cmd(DMA1_Stream5, ENABLE);\r
+\r
+  /* Enable DAC Channel 1 */\r
+\r
+  DAC_Cmd(DAC_Channel_1, ENABLE);\r
+\r
+  /* Enable DMA for DAC Channel 1 */\r
+\r
+  DAC_DMACmd(DAC_Channel_1, ENABLE);\r
+}\r
+\r
+static void dac2_config(void)\r
+{\r
+  DAC_InitTypeDef  DAC_InitStructure;\r
+  DMA_InitTypeDef DMA_InitStructure;\r
+  NVIC_InitTypeDef NVIC_InitStructure;\r
+  \r
+  /* DAC channel 2 Configuration (see notes in dac1_config() above) */\r
+\r
+  DAC_StructInit(&DAC_InitStructure);\r
+  DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;\r
+  DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;\r
+  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;\r
+  DAC_Init(DAC_Channel_2, &DAC_InitStructure);\r
+\r
+  /* DMA1_Stream6 channel7 configuration **************************************/\r
+\r
+  DMA_DeInit(DMA1_Stream6);\r
+  DMA_InitStructure.DMA_Channel = DMA_Channel_7;  \r
+  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R2_ADDRESS;\r
+  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac2_buf;\r
+  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;\r
+  DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ;\r
+  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;\r
+  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;\r
+  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;\r
+  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;\r
+  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;\r
+  DMA_InitStructure.DMA_Priority = DMA_Priority_High;\r
+  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         \r
+  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;\r
+  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;\r
+  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;\r
+  DMA_Init(DMA1_Stream6, &DMA_InitStructure);\r
+\r
+  /* Enable DMA Half & Complete interrupts */\r
+\r
+  DMA_ITConfig(DMA1_Stream6, DMA_IT_TC | DMA_IT_HT, ENABLE);\r
+\r
+  /* Enable the DMA Stream IRQ Channel */\r
+\r
+  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn;\r
+  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;\r
+  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;\r
+  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;\r
+  NVIC_Init(&NVIC_InitStructure);     \r
+\r
+  /* Enable DMA1_Stream6 */\r
+\r
+  DMA_Cmd(DMA1_Stream6, ENABLE);\r
+\r
+  /* Enable DAC Channel 2 */\r
+\r
+  DAC_Cmd(DAC_Channel_2, ENABLE);\r
+\r
+  /* Enable DMA for DAC Channel 2 */\r
+\r
+  DAC_DMACmd(DAC_Channel_2, ENABLE);\r
+\r
+}\r
+\r
+/******************************************************************************/\r
+/*                 STM32F4xx Peripherals Interrupt Handlers                   */\r
+/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */\r
+/*  available peripheral interrupt handler's name please refer to the startup */\r
+/*  file (startup_stm32f40xx.s/startup_stm32f427x.s).                         */\r
+/******************************************************************************/\r
+\r
+/*\r
+  This function handles DMA1 Stream 5 interrupt request for DAC1.\r
+*/\r
+\r
+void DMA1_Stream5_IRQHandler(void) {\r
+    GPIOE->ODR |= (1 << 1);\r
+\r
+    /* Transfer half empty interrupt - refill first half */\r
+\r
+    if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_HTIF5) != RESET) {\r
+        /* fill first half from fifo */\r
+       \r
+        if (fifo_read(dac1_fifo, (short*)dac1_buf, DAC_DUC_BUF_SZ/2) == -1) {\r
+            memset(dac1_buf, 0, sizeof(short)*DAC_DUC_BUF_SZ/2);\r
+            dac_underflow++;\r
+        }\r
+\r
+        /* Clear DMA Stream Transfer Complete interrupt pending bit */\r
+\r
+        DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_HTIF5);  \r
+    }\r
+\r
+    /* Transfer complete interrupt - refill 2nd half */\r
+\r
+    if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_TCIF5) != RESET) {\r
+        /* fill second half from fifo */\r
+\r
+        if (fifo_read(dac1_fifo, (short*)(dac1_buf+DAC_DUC_BUF_SZ/2), DAC_DUC_BUF_SZ/2) == -1) {\r
+            memset(dac1_buf, 0, sizeof(short)*DAC_DUC_BUF_SZ/2);\r
+            dac_underflow++;\r
+        }    /* Clear DMA Stream Transfer Complete interrupt pending bit */\r
+\r
+        DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_TCIF5);  \r
+    }\r
+\r
+    GPIOE->ODR &= ~(1 << 1);\r
+}\r
+\r
+/*\r
+  This function handles DMA1 Stream 6 interrupt request for DAC2.\r
+*/\r
+\r
+void DMA1_Stream6_IRQHandler(void) {\r
+    int i, j, sam;\r
+    short signed_buf[DAC_BUF_SZ/2];\r
+\r
+    GPIOE->ODR |= (1 << 2);\r
+\r
+    /* Transfer half empty interrupt - refill first half */\r
+\r
+    if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET) {\r
+        /* fill first half from fifo */\r
+\r
+        if (fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) {\r
+            memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2);\r
+            dac_underflow++;\r
+        }\r
+\r
+        /* convert to unsigned */\r
+\r
+        for(i=0; i<DAC_BUF_SZ/2; i++) {\r
+            sam = (int)(M*(float)signed_buf[i] + C);\r
+            dac2_buf[i] = (unsigned short)sam;\r
+        }\r
+\r
+        /* Clear DMA Stream Transfer Complete interrupt pending bit */\r
+\r
+        DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_HTIF6);  \r
+    }\r
+\r
+    /* Transfer complete interrupt - refill 2nd half */\r
+\r
+    if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6) != RESET) {\r
+        /* fill second half from fifo */\r
+\r
+        if (fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) {\r
+            memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2);\r
+            dac_underflow++;\r
+        }\r
+\r
+        /* convert to unsigned  */\r
+\r
+        for(i=0, j=DAC_BUF_SZ/2; i<DAC_BUF_SZ/2; i++,j++) {\r
+            sam = (int)(M*(float)signed_buf[i] + C);\r
+            dac2_buf[j] = (unsigned short)sam;\r
+        }\r
+\r
+        /* Clear DMA Stream Transfer Complete interrupt pending bit */\r
+\r
+        DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6);  \r
+    }\r
+\r
+    GPIOE->ODR &= ~(1 << 2);\r
+}\r
+\r