最近本人在做基于Cortex-M4的电阻抗采集系统,正弦信号是由AD9954这一款芯片产生的,由于网上对于该芯片的介绍比较少,这里分享一下自己的使用调试心得,以便大家参考。本人才疏学浅,如果有错误,还请指正。
一、AD9954的电路设计
AD9954是一种直接数字合成器,能够在高达160MHz的频率下生成频率可变的模拟输出正弦波形。芯片手册对于该芯片的介绍已经很是详细了,这里就不再多展开介绍。下面直接干货介绍:
AD9954的芯片手册:AD9954 (Rev. C) (analog.com)
供电模块设计
首先要先解决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手册
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 |
仪表放大电路
经过测试,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 文件中:文章来源:https://www.toymoban.com/news/detail-469943.html
#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的初始化和正弦波频率及幅值的修改:文章来源地址https://www.toymoban.com/news/detail-469943.html
PhyAD9954_Init(); //DDS初始化
AD9954_SETFRE(100000); //设置DDS频率 这里设置为100kHz
Write_ASF(0X3FFF); //设置DDS幅值 0X0000~0X3FFF对应峰峰值0mv~500mv(左右)
到了这里,关于基于AD9954实现正弦交流信号输出——附原理图、代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!