/** \brief Sets the DC offset for the selected path (I or Q) in the selected AD9963s \param baseaddr Base memory address of w3_ad_controller pcore \param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS \param iqSel Select I or Q path; must be AD_CHAN_I or AD_CHAN_Q \param dco DC offset to apply, in [0,1024] */ int ad_set_TxDCO(u32 baseaddr, u32 csMask, u8 iqSel, u16 dco) { //Sanity check inputs if( ((csMask & (AD_CTRL_ALL_RF_CS)) == 0) || (dco>1023)) return -1; if(iqSel == AD_CHAN_I) { //AUXIO2=DAC10A=I DAC TxDCO ad_spi_write(baseaddr, csMask, 0x49, ((dco & 0x3FF) >> 2)); //DAC10A data[9:2] - 8 MSB ad_spi_write(baseaddr, csMask, 0x4A, (dco & 0x3)); //DAC10A {6'b0, data[1:0]} - 2 LSB }
static void config_adxl362(spi_device dev) { uint8_t cmd[3]; /* reset ADXL362 */ cmd[0] = ADXL362_CMD_WRITE_REG; cmd[1] = ADXL362_REG_SOFT_RESET; cmd[2] = ADXL362_RESET_CODE; ad_spi_write(dev, cmd, sizeof(cmd)); OS_DELAY_MS(1); /* * configure filter control register * range: 4g * halved bandwidth: true * external sampling trigger: false * output data rate: 25 Hz */ cmd[0] = ADXL362_CMD_WRITE_REG; cmd[1] = ADXL362_REG_FILTER_CTL; cmd[2] = ADXL362_FILTER_CTL_CODE; ad_spi_write(dev, cmd, sizeof(cmd)); /* * configure power control register * external clock: false * low noise: ultralow noise mode * wake-up mode: false * autosleep: false * measure mode: measure */ cmd[0] = ADXL362_CMD_WRITE_REG; cmd[1] = ADXL362_REG_POWER_CTL; cmd[2] = ADXL362_POWER_CTL_CODE; ad_spi_write(dev, cmd, sizeof(cmd)); /* wait for first measurement's value */ OS_DELAY_MS(1000 / 25 + 1); }
/** \brief Initializes the AD controller. This function must be called once at boot before any AD or RF operations will work \param baseaddr Base memory address of w3_ad_controller pcore \param clkdiv Clock divider for SPI serial clock (set to 3 for 160MHz bus) */ int ad_init(u32 baseaddr, u32 adSel, u8 clkdiv) { u32 rstMask, reg5c, reg72, reg5c_check, reg72_check; if((adSel & AD_CTRL_ALL_RF_CS) == 0) { print("ad_init: Invalid adSel parameter!\n"); return -1; } rstMask = 0; reg5c_check = 0; reg72_check = 0; if(adSel & RFA_AD_CS) {rstMask |= ADCTRL_REG_CONFIG_MASK_RFA_AD_RESET; reg5c_check |= 0x00000008; reg72_check |= 0x00000001;} if(adSel & RFB_AD_CS) {rstMask |= ADCTRL_REG_CONFIG_MASK_RFB_AD_RESET; reg5c_check |= 0x00000800; reg72_check |= 0x00000100;} if(adSel & RFC_AD_CS) {rstMask |= ADCTRL_REG_CONFIG_MASK_RFC_AD_RESET; reg5c_check |= 0x00080000; reg72_check |= 0x00010000;} if(adSel & RFD_AD_CS) {rstMask |= ADCTRL_REG_CONFIG_MASK_RFD_AD_RESET; reg5c_check |= 0x08000000; reg72_check |= 0x01000000;} //Toggle AD resets (active low), Set SPI clock divider Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, 0); Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, (clkdiv & ADCTRL_REG_CONFIG_MASK_CLKDIV) | rstMask); //Toggle soft reset, set SDIO pin to bidirectional (only way to do SPI reads) ad_spi_write(baseaddr, (adSel), 0x00, 0xBD); //SDIO=1, LSB_first=0, reset=1 ad_spi_write(baseaddr, (adSel), 0x00, 0x99); //SDIO=1, LSB_first=0, reset=0 //Confirm the SPI ports are working //AD9963 reg5C should be 0x08 always, reg72 is 0x1 on boot reg5c = (ad_spi_read(baseaddr, (adSel), 0x5C))®5c_check; reg72 = (ad_spi_read(baseaddr, (adSel), 0x72))®72_check; if((reg5c != reg5c_check) || (reg72 != reg72_check)) { xil_printf("First AD SPI read was wrong: addr[5C]=0x%08x (should be 0x%08x), addr[72]=0x%08x (should be 0x%08x)\n", reg5c, reg5c_check, reg72, reg72_check); print("Asserting AD9963 resets\n"); Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, 0); return -1; } /* Default AD9963 configuration: -External ref resistor (NOTE: apparent datasheet bug!) -Full-duplex mode (Tx data on TXD port, Rx data on TRXD port) -Power up everything except: -DAC12A, DAC12B, AUXADC (all unconnected on PCB) -DLL -Clocking: -DLL disabled -ADC clock = DAC clock = ext clock (nominally 80MHz) -Tx path: -Data in 2's complement (NOTE: datasheet bug!) -TXCLK is input at TXD sample rate -TXD is DDR, I/Q interleaved, I first -Tx interpolation filters bypassed -Tx gains: -Linear gain set to 100% -Linear-in-dB gain set to -3dB -DAC RSET set to 100% -Tx DCO DACs: -Enabled, configured for [0,+2]v range -Set to mid-scale output (approx equal to common mode voltage of I/Q diff pairs) -Rx path: -Data in 2's complement (NOTE: datasheet bug!) -TRXCLK is output at TRXD sample rate -TRXD is DDR, I/Q interleaved, I first -Decimation filter bypassed -RXCML output enabled (used by ADC driver diff amp) -ADC common mode buffer off (required for DC coupled inputs) -Rx I path negated digitally (to match swap of p/n traces on PCB) */ //Power on/off blocks ad_spi_write(baseaddr, (adSel), 0x40, 0x00); //DAC12A, DAC12B off ad_spi_write(baseaddr, (adSel), 0x60, 0x00); //DLL off, everything else on ad_spi_write(baseaddr, (adSel), 0x61, 0x03); //LDOs on, AUXADC off //xil_printf("AD TEST: reg61=0x%08x\n", ad_spi_read(baseaddr, (adSel), 0x61)); //Clocking setup // [7]=0: ADCCLK=ext clk // [6]=0: DACCLK=ext clk // [4]=0: disable DLL input ref // [3:0]: DLL divide ratio (only 1, 2, 3, 4, 5, 6, 8 valid) ad_spi_write(baseaddr, (adSel), 0x71, 0x01); //DLL ref input off, ADCCLK=extCLK, DACCLK=extCLK, DLL_DIV=1 //Reference resistor selection // Datasheet says reg62[0]=0 for internal resistor, reg62[0]=1 for external resistor // But experimentally DAC currents are much more stable with temperature when reg62[0]=0 // I'm guessing this bit is flipped in the datasheet ad_spi_write(baseaddr, (adSel), 0x62, 0x00); //Clock disables and DCS // [7:3]=0: Enable internal clocks to ADCs/DACs // [1:0]=0: Set ADCCLK=ext clock (no division) // [2]=0: Enable ADC duty cycle stabilizer (recommended for ADC rates > 75MHz) ad_spi_write(baseaddr, (adSel), 0x66, 0x00); //Enable internal clocks, enable DCS //Aux DACs (Tx DC offset adjustment) // DAC10B=Q DCO, DAC10A=I DCO // DAC outputs update after LSB write (configured by reg40[0]) ad_spi_write(baseaddr, (adSel), 0x45, 0x88); //DAC10B on, full scale = [0,+2]v ad_spi_write(baseaddr, (adSel), 0x46, 0x80); //DAC10B data[9:2] ad_spi_write(baseaddr, (adSel), 0x47, 0x00); //DAC10B {6'b0, data[1:0]} ad_spi_write(baseaddr, (adSel), 0x48, 0x88); //DAC10A on, full scale = [0,+2]v ad_spi_write(baseaddr, (adSel), 0x49, 0x80); //DAC10A data[9:2] ad_spi_write(baseaddr, (adSel), 0x50, 0x00); //DAC10A {6'b0, data[1:0]} //ADC common mode buffer: disabled for DC coupled inputs ad_spi_write(baseaddr, (adSel), 0x7E, 0x01); //Spectral inversion // Invert RxI (reg3D[2]=1) to match PCB // TxI, TxQ also swapped on PCB, but ignored here since -1*(a+jb) is just one of many phase shifts the Tx signal sees ad_spi_write(baseaddr, (adSel), 0x3D, 0x04); //Invert RxI to match PCB (board swaps +/- for better routing) //Rx clock and data order/format // [7:1]=[0 x 1 0 1 0 1] to match design of ad_bridge input registers (TRXD DDR relative to TRXCLK, I data first) // [0]=1 for 2's compliment (Datasheet says reg32[0]=0 for 2's compliment, but experiments show 0 is really straight-binary) ad_spi_write(baseaddr, (adSel), 0x32, 0x2B); //Full-duplex mode (DACs/TXD and ADCs/TRXD both active all the time) ad_spi_write(baseaddr, (adSel), 0x3F, 0x01); //FD mode //Tx data format (datasheet bug! reg[31][0] is flipped; 1=2's complement, 0=straight binary) //0x17 worked great with latest ad_bridge (where TXCLK is ODDR(D1=1,D0=0,C=ad_ref_clk_90) and TXD are ODDR (D1=I,D2=Q,C=ad_ref_clk_0)) ad_spi_write(baseaddr, (adSel), 0x31, 0x17); //Txdata=DDR two's complement, //Tx/Rx data paths ad_spi_write(baseaddr, (adSel), 0x30, 0x3F); //Bypass all rate change filters, enable Tx/Rx clocks // ad_spi_write(baseaddr, (adSel), 0x30, 0x37); //INT0 on, enable Tx/Rx clocks // ad_spi_write(baseaddr, (adSel), 0x30, 0x27); //INT0+INT1 on, enable Tx/Rx clocks // ad_spi_write(baseaddr, (adSel), 0x30, 0x23); //INT0+INT1+SRCC on, enable Tx/Rx clocks //ADC RXCML output buffer requires special register process (see AD9963 datasheet pg. 21 "sub serial interface communications") ad_spi_write(baseaddr, (adSel), 0x05, 0x03); //Address both ADCs ad_spi_write(baseaddr, (adSel), 0x0F, 0x02); //Enable RXCML output ad_spi_write(baseaddr, (adSel), 0x10, 0x00); //Set I/Q offset to 0 ad_spi_write(baseaddr, (adSel), 0xFF, 0x01); //Trigger ADC param update ad_spi_write(baseaddr, (adSel), 0x05, 0x00); //De-Address both ADCs //REFIO adjustment: set to default of 0.8v ad_spi_write(baseaddr, (adSel), 0x6E, 0x40); //Tx gains (it seems these registers default to non-zero, and maybe non-I/Q-matched values; safest to set them explicitly after reset) //I/Q GAIN1[5:0]: Fix5_0 value, Linear-in-dB, 0.25dB per bit // 0-25=>0dB-+6dB, 25-32:+6dB, 33-41:-6dB, 41-63=>-6dB-0dB ad_spi_write(baseaddr, (adSel), 0x68, 0); //IGAIN1 ad_spi_write(baseaddr, (adSel), 0x6B, 0); //QGAIN1 //I/Q GAIN2[5:0]: Fix5_0 value, Linear +/-2.5%, 0.08% per bit // 0:+0, 31:+max, 32:-max, 63:-0 ad_spi_write(baseaddr, (adSel), 0x69, 0); //IGAIN2 ad_spi_write(baseaddr, (adSel), 0x6C, 0); //QGAIN2 //I/Q RSET[5:0]: Fix5_0, Linear +/-20%, 0.625% per bit // 0:-0, 31:-max, 32:+max, 63:+0 ad_spi_write(baseaddr, (adSel), 0x6A, 0); //IRSET ad_spi_write(baseaddr, (adSel), 0x6D, 0); //QRSET //Digital output drive strengths: all 8mA ad_spi_write(baseaddr, (adSel), 0x63, 0x55); //2 bits each: TRXD TRXIQ TRXCLK TXCLK //Disable Tx and Rx BIST modes ad_spi_write(baseaddr, (adSel), 0x50, 0x00); //Tx BIST control ad_spi_write(baseaddr, (adSel), 0x51, 0x00); //Rx BIST control //Finally enable TX CLK output from ad_bridge // This is a register bit in the ad_controller, connected to the ad_bridge in the XPS project // that controls the OBUFT for the TXCLK output, to avoid drive fights pre-init Xil_Out32((baseaddr + ADCTRL_REG_CONFIG), (Xil_In32((baseaddr + ADCTRL_REG_CONFIG)) | ADCTRL_REG_CONFIG_TXCLK_OUT_EN)); return 0; }