AD9857 IQ DDS Digital Up Converter Experiment using Nucleo F401

Dependencies:   mbed

Digital Signal Processing for IQ Quadradure Modulation DDS AD9857 using Nucleo F401.

see http://ttrftech.tumblr.com/post/114310226891/

Files at this revision

API Documentation at this revision

Comitter:
edy555
Date:
Sat Mar 21 08:31:20 2015 +0000
Child:
1:457ef59cce95
Commit message:
interpolation of fir x4 and cic x10

Changed in this revision

dsp.cpp Show annotated file Show diff for this revision Revisions of this file
dsp.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dsp.cpp	Sat Mar 21 08:31:20 2015 +0000
@@ -0,0 +1,522 @@
+#include "mbed.h"
+#include "dsp.h"
+
+extern Serial pc;
+
+
+int16_t htstat_buf[HILBERT_TRANSFORM_FIRSTATE_LENGTH];
+int16_t cap_buf[2][CAPTURE_LEN];
+
+int16_t fir_state[FIRSTATE_LENGTH];
+int16_t fir_buf[CAPTURE_LEN];
+
+int16_t cic_buf[CICBUF_LEN];
+int16_t dma_buf[2][DMA_DATALEN];
+
+CICState cic;
+
+void cic_interpolate_x10(CICState *cic, uint32_t *src, int src_len, uint32_t *dst)
+{
+    uint32_t p0 = cic->p0;
+    uint32_t s0 = cic->s0;
+    int i;
+    int j = 0;
+    for (i = 0; i < src_len; i++) {
+        uint32_t s = src[i];
+        uint32_t d0 = __SSUB16(s, p0);
+        s0 = __SADD16(s0, d0);        
+        uint32_t s1 = __SADD16(s0, d0);        
+        uint32_t s2 = __SADD16(s1, d0);        
+        uint32_t s3 = __SADD16(s2, d0);        
+        uint32_t s4 = __SADD16(s3, d0);        
+        dst[j  ] = s0;
+        dst[j+1] = s1;
+        dst[j+2] = s2;
+        dst[j+3] = s3;
+        dst[j+4] = s4;
+        j += 5;
+        s0 = s4;
+        s0 = __SADD16(s0, d0);        
+        s1 = __SADD16(s0, d0);
+        s2 = __SADD16(s1, d0);        
+        s3 = __SADD16(s2, d0);        
+        s4 = __SADD16(s3, d0);        
+        dst[j  ] = s0;
+        dst[j+1] = s1;
+        dst[j+2] = s2;
+        dst[j+3] = s3;
+        dst[j+4] = s4;
+        j += 5;
+        s0 = s4;
+        p0 = s;
+    }
+    cic->s0 = s0;
+    cic->p0 = p0;
+}
+
+const int16_t fir_coeff[8][16] = {
+       { -19,   -6,  102,  -22, -357,  264, 1798, 2064,  630, -337, -119,  111,   15,  -24,    0,    0},
+       { -13,  -20,   76,   51, -311,  -29, 1441, 2206, 1037, -231, -223,   96,   45,  -25,   -6,    0},
+       {  -6,  -25,   45,   96, -223, -231, 1037, 2206, 1441,  -29, -311,   51,   76,  -20,  -13,    0},
+       {   0,  -24,   15,  111, -119, -337,  630, 2064, 1798,  264, -357,  -22,  102,   -6,  -19,    0},
+       {   0,  -19,   -6,  102,  -22, -357,  264, 1798, 2064,  630, -337, -119,  111,   15,  -24,    0},
+       {   0,  -13,  -20,   76,   51, -311,  -29, 1441, 2206, 1037, -231, -223,   96,   45,  -25,   -6},
+       {   0,   -6,  -25,   45,   96, -223, -231, 1037, 2206, 1441,  -29, -311,   51,   76,  -20,  -13},
+       {   0,    0,  -24,   15,  111, -119, -337,  630, 2064, 1798,  264, -357,  -22,  102,   -6,  -19}
+};
+
+void fir_resample_x4(uint32_t *src_state, uint32_t *dst, int dst_len)
+{
+    uint32_t *src = src_state;
+    int index = 0;
+    int i;
+    for (i = 0; i < dst_len; i++) {
+        uint32_t *kp = (uint32_t*)fir_coeff[index];
+        uint32_t acc_i = 0;
+        uint32_t acc_q = 0;
+
+        // 8cycle x 8       
+#define CELL(n) do { \
+        uint32_t k = kp[n]; \
+        uint32_t p0 = src[n*2]; \
+        uint32_t p1 = src[n*2+1]; \
+        uint32_t i01 = __PKHTB(p1, p0, 16); \
+        uint32_t q01 = __PKHBT(p0, p1, 16); \
+        acc_i = __SMLAD(k, i01, acc_i); \
+        acc_q = __SMLAD(k, q01, acc_q); \
+} while(0)
+
+        CELL(0);  CELL(1);  CELL(2);  CELL(3);
+        CELL(4);  CELL(5);  CELL(6);  CELL(7);
+        *dst++ = __SSAT16(__PKHTB(acc_i, acc_q, 16), 11);
+
+        index++;
+        if (index >= 8) {
+            index = 0;
+            src += 2;
+        }
+    }
+    
+    for (i = 0; i < FIRSTATE_LENGTH/2; i++)
+        *src_state++ = *src++;
+}
+
+void
+interpolate_test()
+{
+    int freq = 1000;
+    int ampl = 3000;
+    int rate = 48000;
+    for (int i = 0; i < CAPTURE_LEN; i++){
+        cap_buf[0][i*2] = sin(2*3.141592 * i * freq / rate) * ampl; // Lch
+        cap_buf[0][i*2+1] = cos(2*3.141592 * i * freq / rate) * ampl; // Rch 
+    }
+#if 0
+    for (int i = 0; i < 48; i++){
+        pc.printf("%d, ", cap_buf[i*2]);
+    }
+    pc.printf("\n\r");
+#endif
+    fir_resample_x4((uint32_t *)&cap_buf[0], (uint32_t*)&cic_buf, CICBUF_LEN);
+    cic_interpolate_x10(&cic, (uint32_t*)&cic_buf, CICBUF_LEN, (uint32_t*)dma_buf[0]);
+    fir_resample_x4((uint32_t *)&cap_buf[0], (uint32_t*)&cic_buf, CICBUF_LEN);
+    cic_interpolate_x10(&cic, (uint32_t*)&cic_buf, CICBUF_LEN, (uint32_t*)dma_buf[0]);
+#if 0
+    for (int i = 0; i < 48*25/4; i++){
+        pc.printf("%d, ", cic_buf[i*2]);
+    }
+    pc.printf("\n\r");
+#endif
+#if 0
+    for (int i = DMA_DATALEN/2-400; i < DMA_DATALEN/2; i++){
+        pc.printf("%d, ", dma_buf[i*2]);
+    }
+    pc.printf("\n\r");
+    for (int i = 0; i < 400; i++){
+        pc.printf("%d, ", dma_buf[i*2]);
+    }
+    pc.printf("\n\r");
+#endif
+}
+
+// hilbert transform by 127 taps fir
+int16_t hilbert127_fir_coeff[32] = {
+        20848,  6917,  4112,  2897,  2212,  1768,  1454,  1219,  1036,
+          887,   764,   661,   572,   496,   429,   371,   319,   274,
+          234,   198,   167,   140,   117,    97,    79,    65,    53,
+           44,    36,    31,    28,    26
+};
+
+void
+hilbert_transform(uint32_t *src, uint32_t *dst, int dst_len, int sign)
+{
+    int j;
+    int len = sizeof htstat_buf / sizeof(uint32_t);
+    src -= len;
+    // 240 * (208 + 12) = 52800cycle = 0.62ms@84MHz
+    for (j = 0; j < dst_len; j++) {
+        int i;
+        int32_t acc = 0;
+#define OFFSET 64
+        // 16 * (10 + 3) = 208 cycle
+        for (i = 0; i < 32; i += 2) {
+            // 10 cycle
+            uint32_t c = *(uint32_t*)&hilbert127_fir_coeff[i];
+            uint32_t a0 = src[OFFSET - i*2 - 1];
+            uint32_t a1 = src[OFFSET - i*2 - 3];
+            uint32_t b0 = src[OFFSET + i*2 + 1];
+            uint32_t b1 = src[OFFSET + i*2 + 3];
+            // fetch only R-ch (top half of word) from 2 successive samples
+            uint32_t a = __PKHTB(a1, a0, 16);
+            // and also do at symmetry samples
+            uint32_t b = __PKHTB(b1, b0, 16);
+            uint32_t d = __QSUB16(b, a);
+            acc = __SMLAD(c, d, acc);
+        }
+        acc *= sign;
+        uint32_t real = src[OFFSET];
+        *dst++ = __PKHTB(real, acc, 15);
+        src++;
+    }
+}
+
+void
+hilbert_transform_save_fir_state(uint32_t *src_tail)
+{
+    int len = sizeof htstat_buf / sizeof(uint32_t);
+    uint32_t *dst = (uint32_t*)&htstat_buf[0];
+    uint32_t *src = src_tail - len;
+    int i;
+    for (i = 0; i < len; i++) {
+        *dst++ = *src++;
+    }
+}
+
+const int16_t cos_sin_table[257][4] = {
+    {     0,  402, -32767,    2 },
+    {   402,  402, -32765,    8 },
+    {   804,  402, -32757,   12 },
+    {  1206,  402, -32745,   17 },
+    {  1608,  401, -32728,   23 },
+    {  2009,  401, -32705,   27 },
+    {  2410,  401, -32678,   32 },
+    {  2811,  401, -32646,   37 },
+    {  3212,  400, -32609,   42 },
+    {  3612,  399, -32567,   46 },
+    {  4011,  399, -32521,   52 },
+    {  4410,  398, -32469,   57 },
+    {  4808,  397, -32412,   61 },
+    {  5205,  397, -32351,   66 },
+    {  5602,  396, -32285,   72 },
+    {  5998,  395, -32213,   76 },
+    {  6393,  393, -32137,   80 },
+    {  6786,  393, -32057,   86 },
+    {  7179,  392, -31971,   91 },
+    {  7571,  391, -31880,   95 },
+    {  7962,  389, -31785,  100 },
+    {  8351,  388, -31685,  105 },
+    {  8739,  387, -31580,  110 },
+    {  9126,  386, -31470,  114 },
+    {  9512,  384, -31356,  119 },
+    {  9896,  382, -31237,  124 },
+    { 10278,  381, -31113,  128 },
+    { 10659,  380, -30985,  133 },
+    { 11039,  378, -30852,  138 },
+    { 11417,  376, -30714,  143 },
+    { 11793,  374, -30571,  147 },
+    { 12167,  372, -30424,  151 },
+    { 12539,  371, -30273,  156 },
+    { 12910,  369, -30117,  161 },
+    { 13279,  366, -29956,  165 },
+    { 13645,  365, -29791,  170 },
+    { 14010,  362, -29621,  174 },
+    { 14372,  360, -29447,  179 },
+    { 14732,  358, -29268,  183 },
+    { 15090,  356, -29085,  187 },
+    { 15446,  354, -28898,  192 },
+    { 15800,  351, -28706,  196 },
+    { 16151,  348, -28510,  200 },
+    { 16499,  347, -28310,  205 },
+    { 16846,  343, -28105,  209 },
+    { 17189,  341, -27896,  213 },
+    { 17530,  339, -27683,  217 },
+    { 17869,  335, -27466,  221 },
+    { 18204,  333, -27245,  226 },
+    { 18537,  331, -27019,  229 },
+    { 18868,  327, -26790,  234 },
+    { 19195,  324, -26556,  237 },
+    { 19519,  322, -26319,  242 },
+    { 19841,  318, -26077,  245 },
+    { 20159,  316, -25832,  250 },
+    { 20475,  312, -25582,  253 },
+    { 20787,  309, -25329,  257 },
+    { 21096,  307, -25072,  261 },
+    { 21403,  302, -24811,  264 },
+    { 21705,  300, -24547,  268 },
+    { 22005,  296, -24279,  272 },
+    { 22301,  293, -24007,  276 },
+    { 22594,  290, -23731,  279 },
+    { 22884,  286, -23452,  282 },
+    { 23170,  282, -23170,  286 },
+    { 23452,  279, -22884,  290 },
+    { 23731,  276, -22594,  293 },
+    { 24007,  272, -22301,  296 },
+    { 24279,  268, -22005,  300 },
+    { 24547,  264, -21705,  302 },
+    { 24811,  261, -21403,  307 },
+    { 25072,  257, -21096,  309 },
+    { 25329,  253, -20787,  312 },
+    { 25582,  250, -20475,  316 },
+    { 25832,  245, -20159,  318 },
+    { 26077,  242, -19841,  322 },
+    { 26319,  237, -19519,  324 },
+    { 26556,  234, -19195,  327 },
+    { 26790,  229, -18868,  331 },
+    { 27019,  226, -18537,  333 },
+    { 27245,  221, -18204,  335 },
+    { 27466,  217, -17869,  339 },
+    { 27683,  213, -17530,  341 },
+    { 27896,  209, -17189,  343 },
+    { 28105,  205, -16846,  347 },
+    { 28310,  200, -16499,  348 },
+    { 28510,  196, -16151,  351 },
+    { 28706,  192, -15800,  354 },
+    { 28898,  187, -15446,  356 },
+    { 29085,  183, -15090,  358 },
+    { 29268,  179, -14732,  360 },
+    { 29447,  174, -14372,  362 },
+    { 29621,  170, -14010,  365 },
+    { 29791,  165, -13645,  366 },
+    { 29956,  161, -13279,  369 },
+    { 30117,  156, -12910,  371 },
+    { 30273,  151, -12539,  372 },
+    { 30424,  147, -12167,  374 },
+    { 30571,  143, -11793,  376 },
+    { 30714,  138, -11417,  378 },
+    { 30852,  133, -11039,  380 },
+    { 30985,  128, -10659,  381 },
+    { 31113,  124, -10278,  382 },
+    { 31237,  119,  -9896,  384 },
+    { 31356,  114,  -9512,  386 },
+    { 31470,  110,  -9126,  387 },
+    { 31580,  105,  -8739,  388 },
+    { 31685,  100,  -8351,  389 },
+    { 31785,   95,  -7962,  391 },
+    { 31880,   91,  -7571,  392 },
+    { 31971,   86,  -7179,  393 },
+    { 32057,   80,  -6786,  393 },
+    { 32137,   76,  -6393,  395 },
+    { 32213,   72,  -5998,  396 },
+    { 32285,   66,  -5602,  397 },
+    { 32351,   61,  -5205,  397 },
+    { 32412,   57,  -4808,  398 },
+    { 32469,   52,  -4410,  399 },
+    { 32521,   46,  -4011,  399 },
+    { 32567,   42,  -3612,  400 },
+    { 32609,   37,  -3212,  401 },
+    { 32646,   32,  -2811,  401 },
+    { 32678,   27,  -2410,  401 },
+    { 32705,   23,  -2009,  401 },
+    { 32728,   17,  -1608,  402 },
+    { 32745,   12,  -1206,  402 },
+    { 32757,    8,   -804,  402 },
+    { 32765,    2,   -402,  402 },
+    { 32767,   -2,      0,  402 },
+    { 32765,   -8,    402,  402 },
+    { 32757,  -12,    804,  402 },
+    { 32745,  -17,   1206,  402 },
+    { 32728,  -23,   1608,  401 },
+    { 32705,  -27,   2009,  401 },
+    { 32678,  -32,   2410,  401 },
+    { 32646,  -37,   2811,  401 },
+    { 32609,  -42,   3212,  400 },
+    { 32567,  -46,   3612,  399 },
+    { 32521,  -52,   4011,  399 },
+    { 32469,  -57,   4410,  398 },
+    { 32412,  -61,   4808,  397 },
+    { 32351,  -66,   5205,  397 },
+    { 32285,  -72,   5602,  396 },
+    { 32213,  -76,   5998,  395 },
+    { 32137,  -80,   6393,  393 },
+    { 32057,  -86,   6786,  393 },
+    { 31971,  -91,   7179,  392 },
+    { 31880,  -95,   7571,  391 },
+    { 31785, -100,   7962,  389 },
+    { 31685, -105,   8351,  388 },
+    { 31580, -110,   8739,  387 },
+    { 31470, -114,   9126,  386 },
+    { 31356, -119,   9512,  384 },
+    { 31237, -124,   9896,  382 },
+    { 31113, -128,  10278,  381 },
+    { 30985, -133,  10659,  380 },
+    { 30852, -138,  11039,  378 },
+    { 30714, -143,  11417,  376 },
+    { 30571, -147,  11793,  374 },
+    { 30424, -151,  12167,  372 },
+    { 30273, -156,  12539,  371 },
+    { 30117, -161,  12910,  369 },
+    { 29956, -165,  13279,  366 },
+    { 29791, -170,  13645,  365 },
+    { 29621, -174,  14010,  362 },
+    { 29447, -179,  14372,  360 },
+    { 29268, -183,  14732,  358 },
+    { 29085, -187,  15090,  356 },
+    { 28898, -192,  15446,  354 },
+    { 28706, -196,  15800,  351 },
+    { 28510, -200,  16151,  348 },
+    { 28310, -205,  16499,  347 },
+    { 28105, -209,  16846,  343 },
+    { 27896, -213,  17189,  341 },
+    { 27683, -217,  17530,  339 },
+    { 27466, -221,  17869,  335 },
+    { 27245, -226,  18204,  333 },
+    { 27019, -229,  18537,  331 },
+    { 26790, -234,  18868,  327 },
+    { 26556, -237,  19195,  324 },
+    { 26319, -242,  19519,  322 },
+    { 26077, -245,  19841,  318 },
+    { 25832, -250,  20159,  316 },
+    { 25582, -253,  20475,  312 },
+    { 25329, -257,  20787,  309 },
+    { 25072, -261,  21096,  307 },
+    { 24811, -264,  21403,  302 },
+    { 24547, -268,  21705,  300 },
+    { 24279, -272,  22005,  296 },
+    { 24007, -276,  22301,  293 },
+    { 23731, -279,  22594,  290 },
+    { 23452, -282,  22884,  286 },
+    { 23170, -286,  23170,  282 },
+    { 22884, -290,  23452,  279 },
+    { 22594, -293,  23731,  276 },
+    { 22301, -296,  24007,  272 },
+    { 22005, -300,  24279,  268 },
+    { 21705, -302,  24547,  264 },
+    { 21403, -307,  24811,  261 },
+    { 21096, -309,  25072,  257 },
+    { 20787, -312,  25329,  253 },
+    { 20475, -316,  25582,  250 },
+    { 20159, -318,  25832,  245 },
+    { 19841, -322,  26077,  242 },
+    { 19519, -324,  26319,  237 },
+    { 19195, -327,  26556,  234 },
+    { 18868, -331,  26790,  229 },
+    { 18537, -333,  27019,  226 },
+    { 18204, -335,  27245,  221 },
+    { 17869, -339,  27466,  217 },
+    { 17530, -341,  27683,  213 },
+    { 17189, -343,  27896,  209 },
+    { 16846, -347,  28105,  205 },
+    { 16499, -348,  28310,  200 },
+    { 16151, -351,  28510,  196 },
+    { 15800, -354,  28706,  192 },
+    { 15446, -356,  28898,  187 },
+    { 15090, -358,  29085,  183 },
+    { 14732, -360,  29268,  179 },
+    { 14372, -362,  29447,  174 },
+    { 14010, -365,  29621,  170 },
+    { 13645, -366,  29791,  165 },
+    { 13279, -369,  29956,  161 },
+    { 12910, -371,  30117,  156 },
+    { 12539, -372,  30273,  151 },
+    { 12167, -374,  30424,  147 },
+    { 11793, -376,  30571,  143 },
+    { 11417, -378,  30714,  138 },
+    { 11039, -380,  30852,  133 },
+    { 10659, -381,  30985,  128 },
+    { 10278, -382,  31113,  124 },
+    {  9896, -384,  31237,  119 },
+    {  9512, -386,  31356,  114 },
+    {  9126, -387,  31470,  110 },
+    {  8739, -388,  31580,  105 },
+    {  8351, -389,  31685,  100 },
+    {  7962, -391,  31785,   95 },
+    {  7571, -392,  31880,   91 },
+    {  7179, -393,  31971,   86 },
+    {  6786, -393,  32057,   80 },
+    {  6393, -395,  32137,   76 },
+    {  5998, -396,  32213,   72 },
+    {  5602, -397,  32285,   66 },
+    {  5205, -397,  32351,   61 },
+    {  4808, -398,  32412,   57 },
+    {  4410, -399,  32469,   52 },
+    {  4011, -399,  32521,   46 },
+    {  3612, -400,  32567,   42 },
+    {  3212, -401,  32609,   37 },
+    {  2811, -401,  32646,   32 },
+    {  2410, -401,  32678,   27 },
+    {  2009, -401,  32705,   23 },
+    {  1608, -402,  32728,   17 },
+    {  1206, -402,  32745,   12 },
+    {   804, -402,  32757,    8 },
+    {   402, -402,  32765,    2 },
+    {     0, -402,  32767,    0 }
+};
+
+static inline 
+uint32_t cos_sin(int16_t x)
+{
+    int idx = x >> 8;
+    int mod = x & 0xff;
+    int r = __PKHBT(0x00000100, mod, 16);
+    uint32_t *e = (uint32_t *)&cos_sin_table[idx+128];
+    uint32_t c = e[0];
+    uint32_t s = e[1];
+    c = __SMUAD(r, c);
+    s = __SMUAD(r, s);
+    c >>= 8;
+    s >>= 8;
+    return __PKHBT(s, c, 16);
+}
+
+FMModState fmmod;
+
+void
+fmmod_init(FMModState *fmmod)
+{
+    fmmod->vec = 0x7fff0000; 
+}
+    
+void
+frequency_modulation(FMModState *fmmod, uint32_t *src, uint32_t *dst, int dst_len)
+{
+    int j;
+    uint32_t vec = fmmod->vec;
+    for (j = 0; j < dst_len; j++) {
+        uint32_t s = *src++;
+        // fetch only R-ch (top half of word)
+        int16_t x = s >> 16;
+        uint32_t cs = cos_sin(x);
+        uint32_t real = __SMUSD(vec, cs);
+        uint32_t imag = __SMUADX(vec, cs);
+        real >>= 15;
+        imag >>= 15;
+        vec = __PKHBT(imag, real, 16);
+        *dst++ = vec;
+    }
+#if 1
+    uint32_t mag = __SMUAD(vec, vec);
+    if (mag < 0x10000) {
+        // force initialize
+        vec = 0x7fff0000;
+    } else if (mag < 0x3ff00000) {
+        uint32_t d = __PKHBT((int16_t)(vec&0xffff) >> 12, vec >> 12, 0);
+        vec = __QADD16(vec, d);
+    }
+#endif
+    fmmod->vec = vec;
+}
+
+void
+amplitude_modulation(uint32_t *src, uint32_t *dst, int dst_len)
+{
+    int j;
+    for (j = 0; j < dst_len; j++) {
+        uint32_t s = *src++;
+        // fetch only R-ch (top half of word) and halving it
+        int16_t x = s >> 16;
+        // add DC and set zero at quadrature
+        x /= 2;
+        x += 0x4000;
+        *dst++ = x & 0xffff;
+    }        
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dsp.h	Sat Mar 21 08:31:20 2015 +0000
@@ -0,0 +1,56 @@
+#ifndef _DSP_H_
+#define _DSP_H_
+
+#define HILBERT_TRANSFORM_FIRSTATE_LENGTH 128*2
+
+extern int16_t htstat_buf[HILBERT_TRANSFORM_FIRSTATE_LENGTH];
+// capture interval 10ms onto double buffer (5ms each)
+// 48kHz x 2ch * 5ms = 240*2
+#define CAPTURE_LEN 240*2
+extern int16_t cap_buf[2][CAPTURE_LEN];
+// 240*2 * 4 = 1920, single buffer
+#define FIRSTATE_LENGTH 16*2
+extern int16_t fir_state[FIRSTATE_LENGTH];
+extern int16_t fir_buf[CAPTURE_LEN];
+#define CICBUF_LEN 1920
+extern int16_t cic_buf[CICBUF_LEN];
+// 1920 * 10 = 19200, double buffer
+#define DMA_DATALEN 19200
+extern int16_t dma_buf[2][DMA_DATALEN];
+
+
+typedef struct {
+    uint32_t p0;        
+    uint32_t s0;
+} CICState;
+
+extern CICState cic;
+
+void cic_interpolate_x10(CICState *cic, uint32_t *src, int src_len, uint32_t *dst);
+
+
+void fir_resample_x4(uint32_t *src_state, uint32_t *dst, int dst_len);
+
+void interpolate_test();
+
+void hilbert_transform(uint32_t *src, uint32_t *dst, int dst_len, int sign);
+void hilbert_transform_save_fir_state(uint32_t *src_tail);
+
+void hilbert_transform_test();
+
+
+typedef struct {
+    uint32_t vec;
+} FMModState;
+
+extern FMModState fmmod;
+
+//uint32_t cos_sin(int16_t x);
+
+void fmmod_init(FMModState *fmmod);
+void frequency_modulation(FMModState *fmmod, uint32_t *src, uint32_t *dst, int dst_len);
+
+void amplitude_modulation(uint32_t *src, uint32_t *dst, int dst_len);
+
+
+#endif /* _DSP_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Mar 21 08:31:20 2015 +0000
@@ -0,0 +1,712 @@
+#include "mbed.h"
+#include "pinmap.h"
+
+#include "dsp.h"
+
+I2C i2c(I2C_SDA, I2C_SCL);
+DigitalOut myled(PA_10);
+Serial pc(SERIAL_TX, SERIAL_RX);
+
+SPI dds_spi(PA_7, NC, PA_5);
+DigitalOut dds_reset(PA_0);
+DigitalOut dds_dpd(PA_1);
+DigitalOut dds_cs(PA_4);
+DigitalOut dds_txen(PA_6);
+DigitalIn dds_plllockdetect(PA_13);
+DigitalIn dds_cicoverflow(PA_14);
+PortOut data(PortC, 0x3fff);
+
+
+void
+dds_write(uint8_t reg, uint8_t value)
+{
+    dds_spi.write(reg);
+    dds_spi.write(value);
+}
+
+void
+dds_write4(uint8_t reg, uint32_t value)
+{
+    dds_spi.write(reg | 0x60);    
+    dds_spi.write(value >> 24);
+    dds_spi.write(value >> 16);
+    dds_spi.write(value >> 8);
+    dds_spi.write(value >> 0);
+}
+
+void
+dds_init()
+{
+    dds_dpd = 0;
+    dds_txen = 0;
+    dds_cs = 1;
+    dds_reset = 1;
+    wait_us(10);
+    dds_reset = 0;
+    wait_us(200);
+    dds_cs = 0;
+    //dds_write(0x01, 0x01); // Single Tone Mode
+    dds_write(0x01, 0x00); // Quadrature Mode
+    //dds_write(0x01, 0x02); // DAC Mode
+    //dds_write(0x00, 0x20 | 0x08); // REFCLK MULTI 8
+    //dds_write(0x00, 0x20 | 0x14); // REFCLK MULTI 20
+    dds_write(0x00, 0x20 | 16); // REFCLK MULTI 16 (12MHz x 16 = 192MHz)
+    //dds_write(0x06, 0x20 << 2); // CIC interpolation ratio: 32
+    dds_write(0x06, 25 << 2); // CIC interpolation ratio: 25
+    dds_write(0x07, 0xff); // Scale Output: 0xff
+    dds_cs = 1;
+    while (dds_plllockdetect == 0)
+        ;
+}
+
+void
+dds_setup()
+{
+    dds_cs = 0;
+    dds_txen = 0;
+    //dds_write4(0x05, 11e6 * (65536.0*65536.0) / (12e6 * 16));
+    //dds_write4(0x05, 51.5e6 * (65536.0*65536.0) / (12e6 * 16));
+    dds_write4(0x05, 13.85e6 * (65536.0*65536.0) / (12e6 * 16));
+    //dds_write4(0x05, 82.5e6 * (65536.0*65536.0) / (12e6 * 16));
+    //dds_write4(0x05, 21.5e6 * (65536.0*65536.0) / (12e6 * 16));
+    //dds_write4(0x05, 82.5e6 * (65536.0*65536.0) / (12e6 * 20));
+    dds_cs = 1;
+}
+
+#if 0
+void dma_transfer_complete(DMA_HandleTypeDef *hdma)
+{
+    //dds_txen = 1;
+    myled = 0;
+}
+
+void dma_error(DMA_HandleTypeDef *hdma)
+{
+    //led = 0;
+}
+#endif
+
+DMA_HandleTypeDef DMA_HandleType ={
+    DMA2_Stream1,
+    {
+        DMA_CHANNEL_6,  // Request source is TIM_CH1
+        DMA_MEMORY_TO_PERIPH,
+        DMA_PINC_DISABLE,
+        DMA_MINC_ENABLE,             
+        DMA_PDATAALIGN_HALFWORD,
+        DMA_MDATAALIGN_WORD,
+        DMA_CIRCULAR,//DMA_PFCTRL,//
+        DMA_PRIORITY_HIGH,
+        DMA_FIFOMODE_DISABLE,//DMA_FIFOMODE_ENABLE,
+        DMA_FIFO_THRESHOLD_HALFFULL,
+        DMA_MBURST_SINGLE,
+        DMA_PBURST_SINGLE
+    },
+    HAL_UNLOCKED,
+    HAL_DMA_STATE_RESET,//HAL_DMA_STATE_READY
+    NULL, // parent
+    NULL, //dma_transfer_complete, // XferCpltCallback
+    NULL, // XferHalfCpltCallback
+    NULL, // XferM1CpltCallback
+    NULL, //dma_error, // XferErrorCallback
+    NULL  // ErrorCode
+};
+
+TIM_HandleTypeDef         htim;
+
+#if 0
+extern "C" static void
+DMA_TIM_IRQHandler()
+{    
+    HAL_DMA_IRQHandler(&DMA_HandleType);
+}
+#endif
+
+void dma_init(void)
+{
+    TIM_IC_InitTypeDef       icconf;
+    GPIO_InitTypeDef gpioconf;   
+    __DMA2_CLK_ENABLE();
+    __TIM1_CLK_ENABLE();
+    __GPIOA_CLK_ENABLE();
+
+    //NVIC_SetVector(DMA2_Stream1_IRQn, (uint32_t)DMA_TIM_IRQHandler);
+    //HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
+    
+    /* PA8 -> TIM1_CH1  */
+    gpioconf.Pin = GPIO_PIN_8;
+    gpioconf.Mode = GPIO_MODE_AF_OD;
+    gpioconf.Pull = GPIO_NOPULL;
+    gpioconf.Speed = GPIO_SPEED_HIGH;
+    gpioconf.Alternate = GPIO_AF1_TIM1;
+    HAL_GPIO_Init(GPIOA, &gpioconf);
+
+    HAL_DMA_Init(&DMA_HandleType);
+    
+    htim.Instance = TIM1;
+    htim.Init.Period        = 1;
+    htim.Init.Prescaler     = 0;
+    htim.Init.ClockDivision = 0;
+    htim.Init.CounterMode   = TIM_COUNTERMODE_UP;
+    HAL_TIM_IC_Init(&htim);
+    
+    //icconf.ICPolarity  = TIM_ICPOLARITY_FALLING;
+    icconf.ICPolarity  = TIM_ICPOLARITY_RISING;
+    icconf.ICSelection = TIM_ICSELECTION_DIRECTTI;
+    icconf.ICPrescaler = TIM_ICPSC_DIV1;
+    icconf.ICFilter    = 0;
+    HAL_TIM_IC_ConfigChannel(&htim, &icconf, TIM_CHANNEL_1);
+    __HAL_TIM_ENABLE_DMA(&htim, TIM_DMA_CC1);
+    
+    /* Start the TIM1 Channel1 */
+    HAL_TIM_IC_Start(&htim, TIM_CHANNEL_1);
+}
+
+void ddsdma_start()
+{
+    dds_txen = 1;
+    HAL_DMA_Start_IT(&DMA_HandleType, (uint32_t)dma_buf, (uint32_t)&GPIOC->ODR, DMA_DATALEN*2);
+}
+
+
+static void I2CWrite(int addr, char d0, char d1)
+{
+    char buf[] = { d0, d1 };
+    i2c.write(addr<<1, buf, 2);
+}
+
+static int I2CRead(int addr, char d0)
+{
+    char buf[] = { d0 };
+    i2c.write(addr<<1, buf, 1, true);
+    i2c.read((addr<<1)+1, buf, 1);
+    return (unsigned char)buf[0];
+}
+
+static void init_tlv320aic3204()
+{
+    I2CWrite(0x18, 0x00, 0x00); /* Initialize to Page 0 */
+    I2CWrite(0x18, 0x01, 0x01); /* Initialize the device through software reset */
+    I2CWrite(0x18, 0x04, 0x43); /* PLL Clock High, MCLK, PLL */
+    /* 12.000MHz*7.1680 = 86.016MHz, 86.016MHz/(2*7*128) = 48kHz */
+    I2CWrite(0x18, 0x05, 0x91); /* Power up PLL, P=1,R=1 */
+    I2CWrite(0x18, 0x06, 0x07); /* J=7 */
+    I2CWrite(0x18, 0x07, 6);    /* D=1680 = (6<<8) + 144 */
+    I2CWrite(0x18, 0x08, 144);
+    I2CWrite(0x18, 0x0b, 0x82); /* Power up the NDAC divider with value 2 */
+    I2CWrite(0x18, 0x0c, 0x87); /* Power up the MDAC divider with value 7 */
+    I2CWrite(0x18, 0x0d, 0x00); /* Program the OSR of DAC to 128 */
+    I2CWrite(0x18, 0x0e, 0x80);
+    I2CWrite(0x18, 0x3c, 0x08); /* Set the DAC Mode to PRB_P8 */
+    I2CWrite(0x18, 0x1b, 0x0c); /* Set the BCLK,WCLK as output */    
+    I2CWrite(0x18, 0x1e, 0x80 + 28); /* Enable the BCLKN divider with value 28 */
+    I2CWrite(0x18, 0x25, 0xee); /* DAC power up */
+    I2CWrite(0x18, 0x00, 0x01); /* Select Page 1 */
+    I2CWrite(0x18, 0x01, 0x08); /* Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO*/
+    I2CWrite(0x18, 0x02, 0x01); /* Enable Master Analog Power Control */
+    I2CWrite(0x18, 0x7b, 0x01); /* Set the REF charging time to 40ms */
+    I2CWrite(0x18, 0x14, 0x25); /* HP soft stepping settings for optimal pop performance at power up Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound. */
+//  I2CWrite(0x18, 0x0a, 0x00); /* Set the Input Common Mode to 0.9V and Output Common Mode for Headphone to Input Common Mode */
+    I2CWrite(0x18, 0x0a, 0x33); /* Set the Input Common Mode to 0.9V and Output Common Mode for Headphone to 1.65V */
+    I2CWrite(0x18, 0x0c, 0x08); /* Route Left DAC to HPL */
+    I2CWrite(0x18, 0x0d, 0x08); /* Route Right DAC to HPR */
+    I2CWrite(0x18, 0x03, 0x00); /* Set the DAC PTM mode to PTM_P3/4 */
+    I2CWrite(0x18, 0x04, 0x00);
+    I2CWrite(0x18, 0x10, 0x0a); /* Set the HPL gain to 0dB */
+    I2CWrite(0x18, 0x11, 0x0a); /* Set the HPR gain to 0dB */
+    I2CWrite(0x18, 0x09, 0x30); /* Power up HPL and HPR drivers */
+    
+    I2CWrite(0x18, 0x00, 0x00); /* Select Page 0 */
+    I2CWrite(0x18, 0x12, 0x87); /* Power up the NADC divider with value 7 */
+    I2CWrite(0x18, 0x13, 0x82); /* Power up the MADC divider with value 2 */
+    I2CWrite(0x18, 0x14, 0x80); /* Program the OSR of ADC to 128 */
+    I2CWrite(0x18, 0x3d, 0x01); /* Select ADC PRB_R1 */
+    I2CWrite(0x18, 0x00, 0x01); /* Select Page 1 */
+    I2CWrite(0x18, 0x3d, 0x00); /* Select ADC PTM_R4 */
+    I2CWrite(0x18, 0x47, 0x32); /* Set MicPGA startup delay to 3.1ms */
+    I2CWrite(0x18, 0x7b, 0x01); /* Set the REF charging time to 40ms */
+    I2CWrite(0x18, 0x34, 0x80); /* Route IN1L to LEFT_P with 20K input impedance */
+    I2CWrite(0x18, 0x36, 0x80); /* Route CM and IN3R to LEFT_N with 20K */
+    I2CWrite(0x18, 0x37, 0x80); /* Route IN1R and IN3R to RIGHT_P with input impedance of 20K */
+    I2CWrite(0x18, 0x39, 0x80); /* Route CM to RIGHT_N with impedance of 20K */
+    I2CWrite(0x18, 0x3b, 0x0); /* Unmute Left MICPGA, Gain selection of 32dB to make channel gain 0dB */
+    I2CWrite(0x18, 0x3c, 0x0); /* Unmute Right MICPGA, Gain selection of 32dB to make channel gain 0dB */
+    I2CWrite(0x18, 0x33, 0x60); /* Enable MIC bias, 2.5V */
+
+    wait_ms(40);
+    I2CWrite(0x18, 0x00, 0x00); /* Select Page 0 */
+    I2CWrite(0x18, 0x3f, 0xd6); /* Power up the Left and Right DAC Channels with route the Left Audio digital data to Left Channel DAC and Right Audio digital data to Right Channel DAC */
+    I2CWrite(0x18, 0x40, 0x00); /* Unmute the DAC digital volume control */
+    I2CWrite(0x18, 0x51, 0xc0); /* Power up Left and Right ADC Channels */
+    I2CWrite(0x18, 0x52, 0x00); /* Unmute Left and Right ADC Digital Volume Control */    
+    
+    I2CWrite(0x18, 0x43, 0x93); /* Enable Headphone detection, Debounce 256ms, Button Debounce 32ms */    
+}
+
+void route_dac_headset()
+{
+    I2CWrite(0x18, 0x00, 0x01); /* Select Page 1 */
+    I2CWrite(0x18, 0x34, 0x00); /* Route IN1L to LEFT_P with 20K input impedance */
+    I2CWrite(0x18, 0x36, 0x08); /* Route IN3R to LEFT_N with 20K */
+    I2CWrite(0x18, 0x37, 0x08); /* Route IN3R to RIGHT_P with input impedance of 20K */
+    I2CWrite(0x18, 0x39, 0x80); /* Route CM to RIGHT_N with impedance of 20K */
+    I2CWrite(0x18, 0x3b, 0x50); /* Unmute Left MICPGA, Gain selection of 40dB to make channel gain 0dB */
+    I2CWrite(0x18, 0x3c, 0x50); /* Unmute Right MICPGA, Gain selection of 40dB to make channel gain 0dB */
+    I2CWrite(0x18, 0x00, 0x00); /* Select Page 0 */
+}
+
+void route_dac_linein()
+{
+    I2CWrite(0x18, 0x00, 0x01); /* Select Page 1 */
+    I2CWrite(0x18, 0x34, 0x80); /* Route IN1L to LEFT_P with 20K input impedance */
+    I2CWrite(0x18, 0x36, 0x80); /* Route CM and IN3R to LEFT_N with 20K */
+    I2CWrite(0x18, 0x37, 0x80); /* Route IN1R and IN3R to RIGHT_P with input impedance of 20K */
+    I2CWrite(0x18, 0x39, 0x80); /* Route CM to RIGHT_N with impedance of 20K */
+    I2CWrite(0x18, 0x3b, 0x0); /* Unmute Left MICPGA, Gain selection of 32dB to make channel gain 0dB */
+    I2CWrite(0x18, 0x3c, 0x0); /* Unmute Right MICPGA, Gain selection of 32dB to make channel gain 0dB */
+    I2CWrite(0x18, 0x00, 0x00); /* Select Page 0 */
+}
+
+static const PinMap PinMap_I2S_CK[] = {
+    {PB_10,  SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
+    {PB_13,  SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
+    {NC,    NC,    0}};
+static const PinMap PinMap_I2S_WS[] = {
+    {PB_12,  SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
+    {PB_9,  SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
+    {NC,    NC,    0}};
+static const PinMap PinMap_I2S_SD[] = {
+    {PC_3,  SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
+    {PB_15,  SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
+    {NC,    NC,    0}};
+static const PinMap PinMap_I2Sext_SD[] = {
+    {PB_14,  SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_I2S2ext)},
+    {NC,    NC,    0}};
+
+extern I2S_HandleTypeDef hi2s;
+
+DMA_HandleTypeDef hdmatx ={
+    DMA1_Stream4,
+    { /*DMA_InitTypeDef*/
+        DMA_CHANNEL_0,
+        DMA_MEMORY_TO_PERIPH,
+        DMA_PINC_DISABLE,
+        DMA_MINC_ENABLE,             
+        DMA_PDATAALIGN_HALFWORD,
+        DMA_MDATAALIGN_WORD,
+        DMA_CIRCULAR,//DMA_PFCTRL,
+        DMA_PRIORITY_HIGH,
+        DMA_FIFOMODE_DISABLE,//DMA_FIFOMODE_ENABLE
+        DMA_FIFO_THRESHOLD_HALFFULL,
+        DMA_MBURST_SINGLE,
+        DMA_PBURST_SINGLE
+    },
+    HAL_UNLOCKED,
+    HAL_DMA_STATE_RESET,//HAL_DMA_STATE_READY
+    &hi2s, //Parent
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL    
+};
+
+DMA_HandleTypeDef hdmarx ={
+    DMA1_Stream3,
+    { /*DMA_InitTypeDef*/
+        DMA_CHANNEL_3,
+        DMA_PERIPH_TO_MEMORY,
+        DMA_PINC_DISABLE,
+        DMA_MINC_ENABLE,             
+        DMA_PDATAALIGN_HALFWORD,
+        DMA_MDATAALIGN_WORD,
+        DMA_CIRCULAR,//DMA_PFCTRL,
+        DMA_PRIORITY_HIGH,
+        DMA_FIFOMODE_ENABLE,//DMA_FIFOMODE_DISABLE,
+        DMA_FIFO_THRESHOLD_HALFFULL,
+        DMA_MBURST_SINGLE,
+        DMA_PBURST_SINGLE
+    },
+    HAL_UNLOCKED,
+    HAL_DMA_STATE_RESET,//HAL_DMA_STATE_READY
+    &hi2s, //Parent
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL    
+};
+
+I2S_HandleTypeDef hi2s = {
+     SPI2,
+     { /*I2S_InitTypeDef*/
+        I2S_MODE_SLAVE_TX,
+        I2S_STANDARD_PHILLIPS,
+        I2S_DATAFORMAT_16B,
+        I2S_MCLKOUTPUT_DISABLE,
+        I2S_AUDIOFREQ_48K,
+        I2S_CPOL_LOW,
+        I2S_CLOCK_EXTERNAL,
+        I2S_FULLDUPLEXMODE_ENABLE
+     },
+     NULL,  
+     0,
+     NULL,
+     NULL,
+     NULL,
+     NULL,
+     &hdmatx,
+     &hdmarx,
+     HAL_UNLOCKED,
+     HAL_I2S_STATE_RESET,
+     HAL_I2S_ERROR_NONE
+};
+
+extern "C" static void
+DMATX_IRQHandler()
+{    
+    HAL_DMA_IRQHandler(&hdmatx);
+}
+
+extern "C" static void
+DMARX_IRQHandler()
+{    
+    HAL_DMA_IRQHandler(&hdmarx);
+}
+
+void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s)
+{
+    NVIC_SetVector(DMA1_Stream4_IRQn, (uint32_t)DMATX_IRQHandler);
+    NVIC_SetVector(DMA1_Stream3_IRQn, (uint32_t)DMARX_IRQHandler);
+    HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
+    HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
+    //HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, );
+}
+
+void
+i2s_init()
+{
+    __SPI2_CLK_ENABLE();
+    __DMA1_CLK_ENABLE();
+    pinmap_pinout(PB_12, PinMap_I2S_WS);
+    pinmap_pinout(PB_13, PinMap_I2S_CK);
+    pinmap_pinout(PB_14, PinMap_I2Sext_SD);
+    pinmap_pinout(PB_15, PinMap_I2S_SD);
+    pin_mode(PB_12, PullUp);
+    pin_mode(PB_13, PullUp);
+    pin_mode(PB_14, PullUp);
+    pin_mode(PB_15, PullUp);
+    if (HAL_DMA_Init(&hdmatx) != HAL_OK)
+        pc.printf("Error:HAL_DMA_Init\n\r");
+    if (HAL_DMA_Init(&hdmarx) != HAL_OK)
+        pc.printf("Error:HAL_DMA_Init\n\r");
+    if (HAL_I2S_Init(&hi2s) != HAL_OK)
+        pc.printf("Error:HAL_I2S_Init\n\r");
+}
+
+void i2sdma_start() 
+{
+    if (HAL_I2SEx_TransmitReceive_DMA(&hi2s, (uint16_t*)cap_buf, (uint16_t*)cap_buf, sizeof(cap_buf)/sizeof(uint16_t)) != HAL_OK)
+        pc.printf("Error:HAL_I2S_Transmit_DMA\n\r");
+}
+
+static int first = true;
+
+
+void
+hilbert_transform_test()
+{
+#if 1
+    int freq = 800;
+    int ampl = 3000;
+    int rate = 48000;
+    for (int i = 0; i < CAPTURE_LEN; i++){
+        cap_buf[0][i*2] = sin(2*3.141592 * i * freq / rate) * ampl; // Lch
+        cap_buf[0][i*2+1] = 0;//sin(2*3.141592 * i * freq / rate) * ampl; // Rch 
+    }
+#endif
+    int cic_len = sizeof(cic_buf)/sizeof(uint32_t);
+    hilbert_transform_save_fir_state((uint32_t*)cap_buf[1]);
+    hilbert_transform((uint32_t*)cap_buf[0], (uint32_t*)fir_buf, sizeof fir_buf / sizeof(uint32_t), 1);
+    hilbert_transform_save_fir_state((uint32_t*)cap_buf[1]);
+    fir_resample_x4((uint32_t*)fir_state, (uint32_t*)cic_buf, cic_len);
+    cic_interpolate_x10(&cic, (uint32_t*)cic_buf, cic_len, (uint32_t*)dma_buf[0]);
+    
+    hilbert_transform((uint32_t*)cap_buf[0], (uint32_t*)fir_buf, sizeof fir_buf / sizeof(uint32_t), 1);
+    hilbert_transform_save_fir_state((uint32_t*)cap_buf[1]);
+    fir_resample_x4((uint32_t*)fir_state, (uint32_t*)cic_buf, cic_len);
+    cic_interpolate_x10(&cic, (uint32_t*)cic_buf, cic_len, (uint32_t*)dma_buf[1]);
+
+    hilbert_transform((uint32_t*)cap_buf[0], (uint32_t*)fir_buf, sizeof fir_buf / sizeof(uint32_t), 1);
+    hilbert_transform_save_fir_state((uint32_t*)cap_buf[1]);
+    fir_resample_x4((uint32_t*)fir_state, (uint32_t*)cic_buf, cic_len);
+    cic_interpolate_x10(&cic, (uint32_t*)cic_buf, cic_len, (uint32_t*)dma_buf[0]);
+    
+    hilbert_transform((uint32_t*)cap_buf[0], (uint32_t*)fir_buf, sizeof fir_buf / sizeof(uint32_t), 1);
+    hilbert_transform_save_fir_state((uint32_t*)cap_buf[1]);
+    fir_resample_x4((uint32_t*)fir_state, (uint32_t*)cic_buf, cic_len);
+    cic_interpolate_x10(&cic, (uint32_t*)cic_buf, cic_len, (uint32_t*)dma_buf[1]);
+#if 0
+    for (int i = 0; i < 20; i++){
+        pc.printf("%d, ", cic_buf[i*2]);
+    }
+    pc.printf("\n\r");
+#endif
+#if 1
+    for (int i = 0; i < 30; i += 2) {
+        pc.printf("%d:%d ", dma_buf[0][i], dma_buf[0][i+1]);
+    }
+    pc.printf("\n\r");
+    int acc_i = 0;
+    int acc_q = 0;
+    for (int i = 0; i < DMA_DATALEN; i += 2){
+        acc_i += dma_buf[0][i  ];
+        acc_q += dma_buf[0][i+1];
+    }
+    pc.printf("dma acc %d %d\n\r", acc_i, acc_q);
+#endif
+}
+
+uint32_t capbuf_average(int buf)
+{
+    int32_t acc_i = 0;
+    int32_t acc_q = 0;
+    for (int i = 0; i < CAPTURE_LEN; i += 2){
+        acc_i += cap_buf[buf][i+1];
+        acc_q += cap_buf[buf][i];
+    }
+    acc_i /= CAPTURE_LEN/2;
+    acc_q /= CAPTURE_LEN/2;
+    return __PKHBT(acc_i, acc_q, 16);
+}
+
+uint32_t capbuf_ave;
+uint32_t decay_ave;
+
+void decay_average(uint32_t in)
+{
+    uint32_t i = __PKHBT(decay_ave, in, 16);
+    uint32_t q = __PKHTB(in, decay_ave, 16);
+    i = __SMUAD(0x0001000f, i) >> 4;
+    q = __SMUAD(0x0001000f, q) >> 4;
+    decay_ave = __PKHBT(i, q, 16);
+}
+
+
+enum {
+    EVT_NONE = 0,
+    EVT_BUTTON_PRESSED,
+    EVT_HEADPHONE_PLUGGED,
+    EVT_HEADSET_PLUGGED,
+    EVT_UNPLUGGED
+};
+
+int
+process_event()
+{
+    int stat = I2CRead(0x18, 67) & 0x60;
+    int flag = I2CRead(0x18, 44);
+    char *msg = NULL;
+    int evt = 0;
+    if (flag & 0x10) {
+        if (stat == 0x60) {
+            msg = "Headset plugged";
+            evt = EVT_HEADSET_PLUGGED;
+        } else if (stat == 0x20) {
+            msg = "Headphone plugged";
+            evt = EVT_HEADPHONE_PLUGGED;
+        } else if (stat == 0x00) {
+            msg = "Unplugged";
+            evt = EVT_UNPLUGGED;
+        }
+    } else if (flag & 0x20) {
+        msg = "Button pressed";
+        evt = EVT_BUTTON_PRESSED;
+    }
+    if (msg)
+        pc.printf("%s\r\n", msg);
+    return evt;
+}
+
+enum {
+    MODE_NFM = 0,
+    MODE_AM,
+    MODE_USB,
+    MODE_LSB,
+    MODE_IQ,
+    MODE_NUM
+};
+
+int mode = MODE_USB;
+
+const char *mode_names[] = {
+    "NFM", "AM", "USB", "LSB", "IQ"
+};
+
+int main()
+{ 
+    pc.baud(9600);
+    pc.printf("AD9857 DDS IQ Synthesizer v0.0\r\n");
+    pc.printf("SystemCoreClock: %dHz\r\n", SystemCoreClock);
+    pc.printf("%s\r\n", mode_names[mode]);        
+    //i2c.frequency(20000);
+#if 0
+    pc.printf("%08x\r\n", cos_sin(0x8000));
+    pc.printf("%08x\r\n", cos_sin(0xC000));
+    pc.printf("%08x\r\n", cos_sin(0xFF7F));
+    pc.printf("%08x\r\n", cos_sin(0x0));
+    pc.printf("%08x\r\n", cos_sin(0x0080));
+    pc.printf("%08x\r\n", cos_sin(0x4000));
+    pc.printf("%08x\r\n", cos_sin(0x7fff));
+#endif
+    // pullup I2C bus     
+    pin_mode(PB_8, PullUp);
+    pin_mode(PB_9, PullUp);
+    
+    dma_init();
+    dds_init();
+    dds_setup();
+
+    fmmod_init(&fmmod);
+    
+    cic.s0 = __PKHBT(3, 3, 16); // adjust offset
+    interpolate_test();
+    //hilbert_transform_test();
+    //ddsdma_start();   
+
+    first = true;
+    init_tlv320aic3204();
+    i2s_init();
+    i2sdma_start();
+
+    while (1) {
+        //pc.printf("%08x\r\n", fmmod.vec);
+        //myled = !myled;
+        wait(1);
+        int evt = process_event();
+        switch(evt) {
+        case EVT_BUTTON_PRESSED:
+            mode = (mode + 1) % MODE_NUM;
+            pc.printf("%s\r\n", mode_names[mode]);        
+            break;    
+        case EVT_HEADSET_PLUGGED:
+            route_dac_headset();
+            break;
+        case EVT_UNPLUGGED:
+        case EVT_HEADPHONE_PLUGGED:
+            route_dac_linein();
+            break;    
+        }
+
+        //pc.printf("%08x %08x\r\n", decay_ave, capbuf_ave);
+        //pc.printf("%02x %02x\r\n", I2CRead(0x18, 67), I2CRead(0x18, 44));
+        //pc.printf("%d %d\r\n", dma_buf[0][100], dma_buf[0][101]);
+        //pc.printf("%04x %04x\r\n", (uint16_t)fir_buf[0], (uint16_t)fir_buf[1]);
+        //pc.printf("%08x\r\n", *(uint32_t*)cap_buf[0]);
+    } 
+}
+
+#if 0
+void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
+{
+    //myled = 0;
+}
+
+void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
+{
+    //myled = 1;
+}
+#endif
+
+void offset_capture_buffer(uint32_t offset, uint32_t *dst, int dst_len)
+{
+    int i;
+    for (i = 0; i < dst_len; i += 8) {
+        dst[0] = __QSUB16(dst[0], offset);
+        dst[1] = __QSUB16(dst[1], offset);
+        dst[2] = __QSUB16(dst[2], offset);
+        dst[3] = __QSUB16(dst[3], offset);
+        dst[4] = __QSUB16(dst[4], offset);
+        dst[5] = __QSUB16(dst[5], offset);
+        dst[6] = __QSUB16(dst[6], offset);
+        dst[7] = __QSUB16(dst[7], offset);
+        dst += 8;
+    }
+}
+
+static void
+mod_fm_func(int buf)
+{
+    frequency_modulation(&fmmod, (uint32_t*)cap_buf[buf], (uint32_t*)fir_buf, sizeof fir_buf / sizeof(uint32_t));
+}
+static void
+mod_am_func(int buf)
+{
+    amplitude_modulation((uint32_t*)cap_buf[buf], (uint32_t*)fir_buf, sizeof fir_buf / sizeof(uint32_t));
+}
+
+static void
+mod_usb_func(int buf)
+{
+    hilbert_transform((uint32_t*)cap_buf[buf], (uint32_t*)fir_buf, sizeof fir_buf / sizeof(uint32_t), 1);
+    if (buf)
+        hilbert_transform_save_fir_state((uint32_t*)cap_buf[2]);    
+}
+
+static void
+mod_lsb_func(int buf)
+{
+    hilbert_transform((uint32_t*)cap_buf[buf], (uint32_t*)fir_buf, sizeof fir_buf / sizeof(uint32_t), -1);
+    if (buf)
+        hilbert_transform_save_fir_state((uint32_t*)cap_buf[2]);    
+}
+
+static void
+mod_raw_func(int buf)
+{
+    memcpy(fir_buf, cap_buf[buf], sizeof fir_buf);
+}
+
+void (*mod_funcs[])(int) = {
+    mod_fm_func,
+    mod_am_func,
+    mod_usb_func,
+    mod_lsb_func,
+    mod_raw_func
+};
+
+void modulate_buffer_half(int buf)
+{
+    myled = 1;
+    offset_capture_buffer(decay_ave, (uint32_t*)cap_buf[buf], sizeof cap_buf[0] / sizeof(uint32_t));
+    //offset_capture_buffer(0x00780088, (uint32_t*)cap_buf[buf], sizeof cap_buf[0] / sizeof(uint32_t));
+    myled = 0;
+    myled = 1;
+    (*mod_funcs[mode])(buf);
+    myled = 0;
+    myled = 1;
+    int cic_len = sizeof(cic_buf)/sizeof(uint32_t);
+    fir_resample_x4((uint32_t*)fir_state, (uint32_t*)cic_buf, cic_len);
+    myled = 0;
+    myled = 1;
+    cic_interpolate_x10(&cic, (uint32_t*)cic_buf, cic_len, (uint32_t*)dma_buf[buf]);
+    myled = 0;
+}
+
+void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
+{
+    capbuf_ave = capbuf_average(0);
+    decay_average(capbuf_ave);
+    modulate_buffer_half(0);
+    if (first) {
+        ddsdma_start();
+        first = false;
+    }
+}
+
+void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
+{
+    capbuf_ave = capbuf_average(0);
+    decay_average(capbuf_ave);
+    modulate_buffer_half(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sat Mar 21 08:31:20 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/487b796308b0
\ No newline at end of file