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/

main.cpp

Committer:
edy555
Date:
2015-03-23
Revision:
6:ed82052bd505
Parent:
5:75c26157a53a

File content as of revision 6:ed82052bd505:

#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;

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);
    int32_t sum_i = __SMUAD(0x0001000f, i) / 16;
    int32_t sum_q = __SMUAD(0x0001000f, q) / 16;
    decay_ave = __PKHBT(sum_i, sum_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_NFM;//MODE_USB;

const char *mode_names[] = {
    "NFM", "AM", "USB", "LSB", "IQ"
};


void hilbert_transform_test();
void pseudo_noise_test();


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);
    // 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(12, 0, 16); // adjust offset
    //pseudo_noise_test();
    //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));
        if (pc.readable()) {
            pc.getc();
            uint32_t *buf = (uint32_t *)dma_buf;
            pc.printf("%08x %08x %08x %08x\r\n", buf[0], buf[1], buf[2], buf[3]);
        }
        //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(1);
    decay_average(capbuf_ave);
    modulate_buffer_half(1);
}

void
pseudo_noise_test()
{
#define RAND(N)     ((((float)rand() / RAND_MAX) * 2 * N) - N)
    int ampl = 10000;
    for (int i = 0; i < CAPTURE_LEN/2; i++){
        cap_buf[0][i*2] = RAND(ampl);
        cap_buf[0][i*2+1] = RAND(ampl); // Rch
        cap_buf[1][i*2] = RAND(ampl); // Lch
        cap_buf[1][i*2+1] = RAND(ampl); // Rch
    }
    modulate_buffer_half(0);
    modulate_buffer_half(1);
    modulate_buffer_half(0);
    modulate_buffer_half(1);
    ddsdma_start();
    pc.printf("Pseudo noise test\r\n");
    while(1);
    /* no reach */
}

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
}