基于AD9954实现正弦交流信号输出——附原理图、代码

最近本人在做基于Cortex-M4的电阻抗采集系统,正弦信号是由AD9954这一款芯片产生的,由于网上对于该芯片的介绍比较少,这里分享一下自己的使用调试心得,以便大家参考。本人才疏学浅,如果有错误,还请指正。

一、AD9954的电路设计

AD9954是一种直接数字合成器,能够在高达160MHz的频率下生成频率可变的模拟输出正弦波形。芯片手册对于该芯片的介绍已经很是详细了,这里就不再多展开介绍。下面直接干货介绍:

AD9954的芯片手册:AD9954 (Rev. C) (analog.com)

  1. 供电模块设计

首先要先解决AD9954芯片的供电问题,AD9954的供电电压是1.8V,主控芯片我选择的是STM32F407,控制IO口的电压是3.3V,由此我们需要一个5V->3.3V、5V->1.8V的电路,由于DDS是模拟信号,数字电源和模拟电源之间最好隔开,这里选择使用0欧电阻/电感分隔。原理图如下:

AMS1117-1.8数据手册:AMS1117-1.8_(AMS)AMS1117-1.8中文资料_价格_PDF手册

AMS1117-3.3数据手册:AMS1117-3.3_(国芯佳品)AMS1117-3.3中文资料_价格_PDF手册

  1. DDS模块设计

AD9954的接线如下,在DDS IOUT-和DDS IOUT+两个端口,可以产生相位相反的正弦直流电源。原理图的设计参考芯片手册的。

控制引脚的设置如下:

名称

引脚

名称

定义

IO UPDATE

PE3

DDS CS

PF12

DDS PS1

PE11

DDS SDO

PF11

DDS PS0

PE9

DDS IOSYNC

PF13

DDS OSK

PE7

DDS RESET

PF15

DDS SDIO

PG0

DDS PWR

PG1

DDS SCLK

PF14

  1. 仪表放大电路

经过测试,DDS IOUT-和DDS IOUT+两个端口 输出电压偏移为1.5V,幅值可调(最高0.3V)的直流正弦波信号,两个信号相位相反。我们需要将正弦直流信号转变为交流信号。

仪表放大器是非常高增益的差分放大电路,具有高输入阻抗和单端输出。具有高输入阻抗的三运放仪表放大器的典型示例如下:

仪表放大器电路的总电压增益的一般表达式为:

设计差分放大电路并使用Multisim软件对电路进行仿真,测得稳定的幅值为1V 的正弦电压信号:

仪表放大器电路的原理图如下,运算放大器选用AD8002,这里需要-5V供电。

AD8002:AD8002 (Rev. E) (analog.com)

二、AD9954的软件设计

上一篇章介绍了AD9954的电路设计,接下来是AD9954的代码,对于AD9954没有深入的了解,代码只能实现正弦频率以及幅值的设定。

使用的主控芯片为STM32F407,该代码引脚的初始化是针对本电路的,参考时,还要按实际情况进行修改。

DDS.c文件如下:

void PhyAD9954_Init(void)
{
    GPIO_AD9954_Init();            //GPIO初始化
    AD9954_RESET();                    //DDS复位
    delay_ms(300);                    //延迟300ms
    AD9954_CS = 0;
    //single tone
    AD9954_SENDBYTE(CFR1);//地址0写操作
    AD9954_SENDBYTE(0x02);//
    AD9954_SENDBYTE(0x10);
    AD9954_SENDBYTE(0x00);
    AD9954_SENDBYTE(0x40);//比较器power down      
//    AD9954_SENDBYTE(0x00);//比较器使能        

    AD9954_SENDBYTE(CFR2);//地址1写操作
    AD9954_SENDBYTE(0x00);//
    AD9954_SENDBYTE(0x08);
    #if fs>400
        #error "系统频率超过芯片最大值"
    #endif
    #if fs>=250
        AD9954_SENDBYTE(PLL_MULTIPLIER<<3|0x04|0X03);
    #else
        AD9954_SENDBYTE(PLL_MULTIPLIER<<3);
    #endif
    AD9954_CS=1;
}


// AD9954接口IO初始化
void GPIO_AD9954_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_9 | GPIO_Pin_11 | GPIO_Pin_13;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;               //普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;              //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;          //100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;                //下拉
    GPIO_Init(GPIOE, &GPIO_InitStructure);                      //初始化GPIO
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;   
    GPIO_Init(GPIOF, &GPIO_InitStructure);                      //初始化GPIO
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;   
    GPIO_Init(GPIOG, &GPIO_InitStructure);                      //初始化GPIO    
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;   
    GPIO_Init(GPIOG, &GPIO_InitStructure);                      //初始化GPIO 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;               //普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;              //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;          //100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;                //下拉
    GPIO_Init(GPIOF, &GPIO_InitStructure);                      //初始化GPIO

    AD9954_IOSY=0;
    AD9954_OSK=0;
    AD9954_PWR=0;
}


// 复位AD9954
void AD9954_RESET(void)
{
    AD9954_CS = 1;
    AD9954_RES = 0;
    delay_ms(10);
    AD9954_RES = 1;
    delay_ms(10);
    AD9954_RES = 0;
    delay_ms(10);
    AD9954_CS = 0;
    AD9954_SCLK = 0;
    PS0 = 0;
    PS1 = 0;
    IOUPDATE = 0;
    AD9954_CS = 1;
}


// 往AD9954发送一个字节的内容
void AD9954_SENDBYTE(u8 dat)
{
    u8 i;
    for (i = 0;i<8;i++)
    {
        AD9954_SCLK = 0;
        if (dat & 0x80)
        {
            AD9954_SDIO = 1;
        }
        else
        {
            AD9954_SDIO = 0;
        }
        AD9954_SCLK = 1;
        dat <<= 1;
    }
}


// 往AD9954接收一个字节的内容
u8 AD9954_ReadByte(void)
{
    u8 i,dat=0;
    for (i = 0;i<8;i++)
    {
        AD9954_SCLK = 0;
        dat|=AD9954_SDO;
        AD9954_SCLK = 1;
        dat <<= 1;
    }
    return dat;
}



//产生一个更新信号,更新AD9954内部寄存器
void UPDATE(void)
{
    IOUPDATE=0;
//    delay_us(100); 
    IOUPDATE = 1;
    delay_us(2);
    IOUPDATE = 0;
}

u32 Get_FTW(double Real_fH)
{
        return (u32)(fH_Num*Real_fH);
}


/*********************************************************************************************************
** 函数名称 :void AD9954_SETFRE(float f)
** 函数功能 :设置AD9954当前的频率输出,采用的是单一频率输出
** 函数说明 :因为采用的浮点数进行计算,转换过程中会出现误差,通过调整可以精确到0.1Hz以内
** 入口参数 :欲设置的频率值
** 出口参数 :无
*********************************************************************************************************/
void AD9954_SETFRE(double f)//single tone
{
    u32 date;
    AD9954_CS = 0;
    
    date = Get_FTW(f);//det=(f/fclk)x2^32=10.7374xf
    AD9954_SENDBYTE(FTW0);//FTW0地址
    AD9954_SENDBYTE((u8)(date >> 24));//频率控制字
    AD9954_SENDBYTE((u8)(date >> 16));
    AD9954_SENDBYTE((u8)(date >> 8));
    AD9954_SENDBYTE((u8)date);
    AD9954_CS=1;
    UPDATE();
    
    #ifdef EIT_USART_DEBUG
                printf("\n\r DDS输出频率为:%.2f Hz \n\r" ,f);            
    #endif
}



/*********************************************************************************************************
** 函数名称 :void Write_ASF(u16 factor)
** 函数功能 :改变scale factor数值,改变DAC输出幅度
** 函数说明 :写入最大为0X3FFF,最小为0,最大峰峰值约500mv
** 入口参数 :无
** 出口参数 :无
*********************************************************************************************************/
void Write_ASF(u16 factor)  //
{
    AD9954_CS = 0;

    AD9954_SENDBYTE(0x02);//幅度    
    AD9954_SENDBYTE(factor >> 8);
    AD9954_SENDBYTE(factor);
    AD9954_CS = 1;
    UPDATE();

}

DDS.h文件如下:

#ifndef __PHY_DDS_H
#define __PHY_DDS_H    

#include "Public_Config.h"    //IO口控制

/*  引脚设置
**      AD9954_CS----------PF12         OUT
**      AD9954_SCLK--------PF14         OUT
**      AD9954_SDIO--------PG0         OUT
**      AD9954_OSK---------PE7       OUT
**      PS0----------------PE9        OUT
**        PS1----------------PE11         OUT
**      IOUPDATE-----------PE13          OUT
**        AD9954_SDO---------PF11          IN
**      AD9954_IOSY--------PF13          OUT
**      AD9954_RES---------PF15        OUT
**      AD9954_PWR---------PG1        OUT
*/

#define AD9954_CS     PFout(12)            
#define AD9954_SCLK PFout(14)            
#define AD9954_SDIO PGout(0)            
#define AD9954_OSK     PEout(7)            
#define PS0         PEout(9)        
#define PS1         PEout(11)            
#define IOUPDATE     PEout(13)            
#define AD9954_SDO    PFin(11)        
#define AD9954_IOSY PFout(13)        
#define AD9954_RES     PFout(15)        
#define AD9954_PWR     PGout(1)    


#define CFR1    0X00            
#define CFR2    0X01
#define ASF     0X02
#define ARR1    0X03
#define FTW0    0X04
#define POW0    0X05
#define FTW1    0X06
#define NLSCW   0X07
#define PLSCW   0X08
#define RSCW0   0X07
#define RSCW1   0X08
#define RSCW2   0X09
#define RSCW3   0X0A
#define RAM     0X0B

#define No_Dwell 0X80

typedef enum {
DownScan  =   0,
UpScan,
DoubleScan
}ScanMode;              


void PhyAD9954_Init(void);                 //DDS初始化
void AD9954_SETFRE(double f);               //设置频率
void Write_ASF(u16 data);                  //设置幅值

#endif

DDS.h文件中涉及的IO空控制,封装在 Public_Config.h 文件中:

#ifndef __PUBLIC_CONFIG_H
#define __PUBLIC_CONFIG_H


#define APP_VECT_TAB_OFFSET     0       //中断向量表设置

//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入

#endif

在Main函数中,只需要调用以下三个函数,就可以实现AD9954的初始化和正弦波频率及幅值的修改:

    PhyAD9954_Init();                 //DDS初始化
    AD9954_SETFRE(100000);           //设置DDS频率 这里设置为100kHz
    Write_ASF(0X3FFF);        //设置DDS幅值 0X0000~0X3FFF对应峰峰值0mv~500mv(左右)

  • 14
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

河狸打捞员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值