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/

Revision:
0:55201637d936
Child:
1:457ef59cce95
--- /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);
+}