DSP DAC数模转换
1.DAC介绍1.1 DAC简介DAC(Digital to analog converter)即数字模拟转换器,它可以将数字信 号转换为模拟信号。它的功能与 ADC 相反。在常见的数字信号系统中,大部分传 感器信号被转化成电压信号,而 ADC 把电压模拟信号转换成易于计算机存储、 处理的数字编码,由计算机处理完成后,再由 DAC 输出电压模拟信号,该电压 模拟信号常常用来驱动某些执行器件,使人类
1.DAC介绍
1.1 DAC简介
DAC(Digital to analog converter)即数字模拟转换器,它可以将数字信 号转换为模拟信号。它的功能与 ADC 相反。在常见的数字信号系统中,大部分传 感器信号被转化成电压信号,而 ADC 把电压模拟信号转换成易于计算机存储、 处理的数字编码,由计算机处理完成后,再由 DAC 输出电压模拟信号,该电压 模拟信号常常用来驱动某些执行器件,使人类易于感知。如音频信号的采集及还 原就是这样一个过程。
DAC 的主要技术指标如下:
(1)分辨率
DAC 的分辨率是输入数字量的最低有效位(LSB)发生变化时,所对应的输出 模拟量(电压或电流)的变化量。它反映了输出模拟量的最小变化值。分辨率与 输入数字量的位数有确定的关系,可以表示成 FS /(2^n)。FS 表示满量程输入 值,n 为二进制位数。对于 5V 的满量程,采用8位的 DAC 时,分辨率为 5V/256 =19.5mV;当采用 12 位的 DAC 时,分辨率则为 5V/4096=1.22mV。显然,位数 越多分辨率就越高。
(2)线性度
线性度(也称非线性误差)是实际转换特性曲线与理想直线特性之间的最大 偏差。常以相对于满量程的百分数表示。如±1%是指实际输出值与理论值之差 在满刻度的±1%以内。
(3)绝对精度和相对精度
绝对精度(简称精度)是指在整个刻度范围内,任一输入数码所对应的模拟 量实际输出值与理论值之间的最大误差。绝对精度是由 DAC 的增益误差(当输入 数码为全 1 时,实际输出值与理想输出值之差)、零点误差(数码输入为全0时, DAC 的非零输出值)、非线性误差和噪声等引起的。绝对精度(即最大误差)应 小于 1 个 LSB。相对精度与绝对精度表示同一含义,用最大误差相对于满刻度的 百分比表示。
(4)建立时间
建立时间是指输入的数字量发生满刻度变化时,输出模拟信号达到满刻度值 的±1/2LSB 所需的时间。是描述 D/A 转换速率的一个动态指标。根据建立时间 的长短,可以将 DAC 分成超高速(<1μS)、高速(10~1μS)、中速(100~10 μS)、低速(≥100μS)几档。
1.1 DAC 工作原理
了解了 DAC 基本概念及特性后,再来看下其工作原理,下面以T型电阻网络 DAC 来介绍。其内部结构图如下所示:
DAC 输出电压计算公式:V0=Vref*z/256
公式中的 z 表示单片机给的数字量,vref 为参考电压,通常我们是接在系统 电源上,即 5V,数值 256 表示 DAC 精度为 8 位。
DAC 主要由数字寄存器、模拟电子开关、位权网络、求和运算放大器和基准 电压源(或恒流源)组成。用存于数字寄存器的数字量的各位数码,分别控制对 应位的模拟电子开关,使数码为 1 的位在位权网络上产生与其位权成正比的电流 值,再由运算放大器对各电流值求和,并转换成电压值。
上述的模拟电子开关都分别接着一个分压的器件,比如说电阻。模拟开关的 个数取决于 DAC 的精度。那么 N 个电子开关就把基准电压分为 N 份(并不是平 均分哦),而这些开关根据输入的二进制每一位数据对应开启或者关闭,把分压 的器件上的电压引入输出电路中。
2 TLV5620 芯片介绍
TLV5620 是一个四通道 8 位数模转换(DAC)器件,3V-3.6V 单电源供电,3 线串行总线,可与 COMS 器件兼容,非常容易与微处理器或微控制器设备连接。它具有 11 位命令字,包括八位数据、两个 DAC 选择位和一个范围位,后者允许 进行选择 1 或 2 倍的输出范围。DAC 寄存器是双缓冲的,允许一组完整的新值写 入到设备,然后所有的 DAC 输出通过控制 LDAC 同时更新。DAC 可产生一个介于 参考电压和 GND 两者之间的一到两倍的输出电压。
该器件通常应用于可编程电压源、数字控制放大器/衰减器、移动通信、自 动检测设备、过程监测和控制、信号合成等领域。
TLV5620 芯片管脚功能介绍如下:
DAC 各通道输出电压计算公式如下:
其中 REF 为芯片 TLV5620 接入的参考电压,开发板上已经将 VREFA、B、C、D 均接在 1.9V 上,所以 REF=1.9V。CODE 为数字值,范围是 0-255,RNG 为串行控 制字中的一位,取值范围是 0 或 1。
TLV5620 芯片进行 DAC 转换时,数据的加载有两种方式,一种是通过 LOAD 管脚控制,此时 LDAC 脚为低电平;另一种是通过 LDAC 管脚控制。我们采用第一 种方式,即将 LDAC 脚接地,通过 LOAD 脚控制转换及更新。下面我们看下 DAC 芯片工作时序,如下图所示:
当进行 DAC 转换时,在 CLK 的每个下降沿数据被锁存到数据终端。当所有数 据位送入之后,LOAD 为低脉冲将数据从串行输入寄存器转移到选定的 DAC 输出 通道。具体由哪个通道输出,就需要通过 A1 和 A0 位控制,这两个位也叫做通道 选择位,其值对应通道输出如下:
RNG 位控制 DAC 的输出范围。当 RNG =0 时,输出范围是所施加的基准电压和 GND;当间 RNG=1,输出范围为所施加的基准电压和 GND 的两倍。
3.硬件设计
4. 软件设计
本章所要实现的功能是:系统运行时,每间隔 1 秒使 TLV5620 的 4 个通道分 别输出不同电压值,并且通过串口将数值输出显示,D1 指示灯闪烁提示系统运 行正常。
(1)DAC
void InitSpiaGpio()
{
EALLOW;
/* Enable internal pull-up for the selected pins */
// Pull-ups can be enabled or disabled by the user.
// This will enable the pullups for the specified pins.
// Comment out other unwanted lines.
GpioCtrlRegs.GPBPUD.bit.GPIO54 = 0; // Enable pull-up on GPIO54 (SPISIMOA)
GpioCtrlRegs.GPBPUD.bit.GPIO55 = 0; // Enable pull-up on GPIO55 (SPISOMIA)
GpioCtrlRegs.GPBPUD.bit.GPIO56 = 0; // Enable pull-up on GPIO56 (SPICLKA)
GpioCtrlRegs.GPBPUD.bit.GPIO57 = 0; // Enable pull-up on GPIO57 (SPISTEA)
// GpioCtrlRegs.GPBPUD.bit.GPIO54 = 0; // Enable pull-up on GPIO54 (SPISIMOA)
// GpioCtrlRegs.GPBPUD.bit.GPIO55 = 0; // Enable pull-up on GPIO55 (SPISOMIA)
// GpioCtrlRegs.GPBPUD.bit.GPIO56 = 0; // Enable pull-up on GPIO56 (SPICLKA)
// GpioCtrlRegs.GPBPUD.bit.GPIO57 = 0; // Enable pull-up on GPIO57 (SPISTEA)
/* Set qualification for selected pins to asynch only */
// This will select asynch (no qualification) for the selected pins.
// Comment out other unwanted lines.
// GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 3; // Asynch input GPIO16 (SPISIMOA)
// GpioCtrlRegs.GPAQSEL2.bit.GPIO17 = 3; // Asynch input GPIO17 (SPISOMIA)
// GpioCtrlRegs.GPAQSEL2.bit.GPIO18 = 3; // Asynch input GPIO18 (SPICLKA)
// GpioCtrlRegs.GPAQSEL2.bit.GPIO19 = 3; // Asynch input GPIO19 (SPISTEA)
GpioCtrlRegs.GPBQSEL2.bit.GPIO54 = 3; // Asynch input GPIO16 (SPISIMOA)
GpioCtrlRegs.GPBQSEL2.bit.GPIO55 = 3; // Asynch input GPIO17 (SPISOMIA)
GpioCtrlRegs.GPBQSEL2.bit.GPIO56 = 3; // Asynch input GPIO18 (SPICLKA)
GpioCtrlRegs.GPBQSEL2.bit.GPIO57 = 3; // Asynch input GPIO19 (SPISTEA)
/* Configure SPI-A pins using GPIO regs*/
// This specifies which of the possible GPIO pins will be SPI functional pins.
// Comment out other unwanted lines.
// GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 1; // Configure GPIO16 as SPISIMOA
// GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 1; // Configure GPIO17 as SPISOMIA
// GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 1; // Configure GPIO18 as SPICLKA
// GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 1; // Configure GPIO19 as SPISTEA
GpioCtrlRegs.GPBMUX2.bit.GPIO54 = 1; // Configure GPIO54 as SPISIMOA
GpioCtrlRegs.GPBMUX2.bit.GPIO55 = 1; // Configure GPIO55 as SPISOMIA
GpioCtrlRegs.GPBMUX2.bit.GPIO56 = 1; // Configure GPIO56 as SPICLKA
GpioCtrlRegs.GPBMUX2.bit.GPIO57 = 1; // Configure GPIO57 as SPISTEA
EDIS;
}
void TLV5620_Init(void)
{
EALLOW;
SysCtrlRegs.PCLKCR0.bit.SPIAENCLK = 1; // SPI-A
EDIS;
/*初始化GPIO;*/
InitSpiaGpio();
EALLOW;
GpioCtrlRegs.GPAMUX2.bit.GPIO26 = 0; // 配置GPIO为GPIO口
GpioCtrlRegs.GPADIR.bit.GPIO26 = 1; // 定义GPIO输出引脚
GpioCtrlRegs.GPAPUD.bit.GPIO26 = 0; // 禁止上啦 GPIO引脚
EDIS;
SpiaRegs.SPICCR.all =0x0a;///进入初始状态,数据在上升沿输出,自测禁止,11位数据模式
SpiaRegs.SPICTL.all =0x0006; // 使能主机模式,正常相位,使能主机发送,禁止接收
//溢出中断,禁止SPI中断;
SpiaRegs.SPIBRR =0x0031; //SPI波特率=37.5M/50 =0.75MHZ;
SpiaRegs.SPICCR.all =0x8a; //退出初始状态;
SpiaRegs.SPIPRI.bit.FREE = 1; // 自由运行
SET_LOAD;
}
///大家要知道这里所定义的各个变量的含义,channel是4个通道的地址(00,01,10,11)
/// rng是输出范围的倍数,可以是0或1。
/// dat是0~256数据
void DAC_SetChannelData(unsigned char channel,unsigned char rng,unsigned char dat)
{
Uint16 dacvalue=0;
//注意这里的有效数据是11位,SPI初始化中也进行了定义
dacvalue = ((channel<<14) | (rng<<13) | (dat<<5));
while(SpiaRegs.SPISTS.bit.BUFFULL_FLAG ==1);//判断SPI的发送缓冲区是否是空的,等于0可写数据
SpiaRegs.SPITXBUF = dacvalue; //把发送的数据写入SPI发送缓冲区
while( SpiaRegs.SPISTS.bit.BUFFULL_FLAG==1); //当发送缓冲区出现满标志位时,开始琐存数据
ClEAR_LOAD;
DELAY_US(2);
SET_LOAD;
DELAY_US(10);
}
void main()
{
int i=0;
Uint16 dacvalue=64;
float dac_vol;
Uint16 dac_temp=0;
char dacbuf[6];
InitSysCtrl();
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
LED_Init();
TIM0_Init(150,200000);//200ms
UARTa_Init(4800);
TLV5620_Init();
while(1)
{
i++;
if(i%1000==0)
{
DAC_SetChannelData(0,0,dacvalue);
dac_vol=dacvalue*1.9/255;
dac_temp=dac_vol*100;
dacbuf[0]=dac_temp/100+0x30;
dacbuf[1]='.';
dacbuf[2]=dac_temp%100/10+0x30;
dacbuf[3]=dac_temp%100%10+0x30;
dacbuf[4]='V';
dacbuf[5]='\0';
UARTa_SendString("\r\nCH1_VOL=");
UARTa_SendString(dacbuf);
DAC_SetChannelData(1,0,dacvalue*2);
dac_vol=dacvalue*2*1.9/255;
dac_temp=dac_vol*100;
dacbuf[0]=dac_temp/100+0x30;
dacbuf[1]='.';
dacbuf[2]=dac_temp%100/10+0x30;
dacbuf[3]=dac_temp%100%10+0x30;
dacbuf[4]='V';
dacbuf[5]='\0';
UARTa_SendString("\r\nCH2_VOL=");
UARTa_SendString(dacbuf);
DAC_SetChannelData(2,0,dacvalue*3);
dac_vol=dacvalue*3*1.9/255;
dac_temp=dac_vol*100;
dacbuf[0]=dac_temp/100+0x30;
dacbuf[1]='.';
dacbuf[2]=dac_temp%100/10+0x30;
dacbuf[3]=dac_temp%100%10+0x30;
dacbuf[4]='V';
dacbuf[5]='\0';
UARTa_SendString("\r\nCH3_VOL=");
UARTa_SendString(dacbuf);
DAC_SetChannelData(3,0,dacvalue*4-1);
dac_vol=dacvalue*4*1.9/255;
dac_temp=dac_vol*100;
dacbuf[0]=dac_temp/100+0x30;
dacbuf[1]='.';
dacbuf[2]=dac_temp%100/10+0x30;
dacbuf[3]=dac_temp%100%10+0x30;
dacbuf[4]='V';
dacbuf[5]='\0';
UARTa_SendString("\r\nCH4_VOL=");
UARTa_SendString(dacbuf);
}
DELAY_US(1*1000);
}
}
(2)DAC_ADC
void InitSpiaGpio()
{
EALLOW;
/* Enable internal pull-up for the selected pins */
// Pull-ups can be enabled or disabled by the user.
// This will enable the pullups for the specified pins.
// Comment out other unwanted lines.
GpioCtrlRegs.GPBPUD.bit.GPIO54 = 0; // Enable pull-up on GPIO54 (SPISIMOA)
GpioCtrlRegs.GPBPUD.bit.GPIO55 = 0; // Enable pull-up on GPIO55 (SPISOMIA)
GpioCtrlRegs.GPBPUD.bit.GPIO56 = 0; // Enable pull-up on GPIO56 (SPICLKA)
GpioCtrlRegs.GPBPUD.bit.GPIO57 = 0; // Enable pull-up on GPIO57 (SPISTEA)
// GpioCtrlRegs.GPBPUD.bit.GPIO54 = 0; // Enable pull-up on GPIO54 (SPISIMOA)
// GpioCtrlRegs.GPBPUD.bit.GPIO55 = 0; // Enable pull-up on GPIO55 (SPISOMIA)
// GpioCtrlRegs.GPBPUD.bit.GPIO56 = 0; // Enable pull-up on GPIO56 (SPICLKA)
// GpioCtrlRegs.GPBPUD.bit.GPIO57 = 0; // Enable pull-up on GPIO57 (SPISTEA)
/* Set qualification for selected pins to asynch only */
// This will select asynch (no qualification) for the selected pins.
// Comment out other unwanted lines.
// GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 3; // Asynch input GPIO16 (SPISIMOA)
// GpioCtrlRegs.GPAQSEL2.bit.GPIO17 = 3; // Asynch input GPIO17 (SPISOMIA)
// GpioCtrlRegs.GPAQSEL2.bit.GPIO18 = 3; // Asynch input GPIO18 (SPICLKA)
// GpioCtrlRegs.GPAQSEL2.bit.GPIO19 = 3; // Asynch input GPIO19 (SPISTEA)
GpioCtrlRegs.GPBQSEL2.bit.GPIO54 = 3; // Asynch input GPIO16 (SPISIMOA)
GpioCtrlRegs.GPBQSEL2.bit.GPIO55 = 3; // Asynch input GPIO17 (SPISOMIA)
GpioCtrlRegs.GPBQSEL2.bit.GPIO56 = 3; // Asynch input GPIO18 (SPICLKA)
GpioCtrlRegs.GPBQSEL2.bit.GPIO57 = 3; // Asynch input GPIO19 (SPISTEA)
/* Configure SPI-A pins using GPIO regs*/
// This specifies which of the possible GPIO pins will be SPI functional pins.
// Comment out other unwanted lines.
// GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 1; // Configure GPIO16 as SPISIMOA
// GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 1; // Configure GPIO17 as SPISOMIA
// GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 1; // Configure GPIO18 as SPICLKA
// GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 1; // Configure GPIO19 as SPISTEA
GpioCtrlRegs.GPBMUX2.bit.GPIO54 = 1; // Configure GPIO54 as SPISIMOA
GpioCtrlRegs.GPBMUX2.bit.GPIO55 = 1; // Configure GPIO55 as SPISOMIA
GpioCtrlRegs.GPBMUX2.bit.GPIO56 = 1; // Configure GPIO56 as SPICLKA
GpioCtrlRegs.GPBMUX2.bit.GPIO57 = 1; // Configure GPIO57 as SPISTEA
EDIS;
}
void TLV5620_Init(void)
{
EALLOW;
SysCtrlRegs.PCLKCR0.bit.SPIAENCLK = 1; // SPI-A
EDIS;
/*初始化GPIO;*/
InitSpiaGpio();
EALLOW;
GpioCtrlRegs.GPAMUX2.bit.GPIO26 = 0; // 配置GPIO为GPIO口
GpioCtrlRegs.GPADIR.bit.GPIO26 = 1; // 定义GPIO输出引脚
GpioCtrlRegs.GPAPUD.bit.GPIO26 = 0; // 禁止上啦 GPIO引脚
EDIS;
SpiaRegs.SPICCR.all =0x0a;///进入初始状态,数据在上升沿输出,自测禁止,11位数据模式
SpiaRegs.SPICTL.all =0x0006; // 使能主机模式,正常相位,使能主机发送,禁止接收
//溢出中断,禁止SPI中断;
SpiaRegs.SPIBRR =0x0031; //SPI波特率=37.5M/50 =0.75MHZ;
SpiaRegs.SPICCR.all =0x8a; //退出初始状态;
SpiaRegs.SPIPRI.bit.FREE = 1; // 自由运行
SET_LOAD;
}
///大家要知道这里所定义的各个变量的含义,add是4个通道的地址(00,01,10,11)
/// RNG是输出范围的倍数,可以是0或1。
/// VOL是0~256数据
void DAC_SetChannelData(unsigned char channel,unsigned char rng,unsigned char dat)
{
Uint16 dacvalue=0;
//注意这里的有效数据是11位,SPI初始化中也进行了定义
dacvalue = ((channel<<14) | (rng<<13) | (dat<<5));
while(SpiaRegs.SPISTS.bit.BUFFULL_FLAG ==1);//判断SPI的发送缓冲区是否是空的,等于0可写数据
SpiaRegs.SPITXBUF = dacvalue; //把发送的数据写如SPI发送缓冲区
while( SpiaRegs.SPISTS.bit.BUFFULL_FLAG==1); //当发送缓冲区出现满标志位时,开始琐存数据
ClEAR_LOAD;
DELAY_US(2);
SET_LOAD;
DELAY_US(10);
}
#define ADC_MODCLK 3
#define ADC_CKPS 0x1 // ADC module clock = HSPCLK/2*ADC_CKPS = 25.0MHz/(1*2) = 12.5MHz
#define ADC_SHCLK 0xf // S/H width in ADC module periods = 16 ADC clocks
void ADC_Init(void)
{
EALLOW;
SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1; // ADC
EDIS;
// Specific clock setting for this example:
EALLOW;
SysCtrlRegs.HISPCP.all = ADC_MODCLK; // HSPCLK = SYSCLKOUT/ADC_MODCLK
EDIS;
InitAdc(); // For this example, init the ADC
// Specific ADC setup for this example:
AdcRegs.ADCTRL1.bit.ACQ_PS = ADC_SHCLK;
AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_CKPS;
AdcRegs.ADCTRL1.bit.SEQ_CASC = 1; // 1 Cascaded mode
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0;
AdcRegs.ADCTRL1.bit.CONT_RUN = 1; // Setup continuous run
// Start SEQ1
AdcRegs.ADCTRL2.all = 0x2000;
}
Uint16 Read_ADCValue(void)
{
while (AdcRegs.ADCST.bit.INT_SEQ1== 0);
AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;
return AdcRegs.ADCRESULT0>>4;
}
void main()
{
int i=0;
Uint16 dacvalue=64;
float dac_vol;
Uint16 dac_temp=0;
char dacbuf[6];
float adc_vol;
Uint16 adc_temp=0;
InitSysCtrl();
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
LED_Init();
TIM0_Init(150,200000);//200ms
UARTa_Init(4800);
TLV5620_Init();
ADC_Init();
while(1)
{
i++;
if(i%1000==0)
{
DAC_SetChannelData(1,0,dacvalue*2);
dac_vol=dacvalue*2*1.9/255;
dac_temp=dac_vol*100;
dacbuf[0]=dac_temp/100+0x30;
dacbuf[1]='.';
dacbuf[2]=dac_temp%100/10+0x30;
dacbuf[3]=dac_temp%100%10+0x30;
dacbuf[4]='V';
dacbuf[5]='\0';
UARTa_SendString("\r\nCH2_VOL=");
UARTa_SendString(dacbuf);
adc_vol=(float)Read_ADCValue()*3.3/4095;
adc_temp=adc_vol*100;
dacbuf[0]=adc_temp/100+0x30;
dacbuf[1]='.';
dacbuf[2]=adc_temp%100/10+0x30;
dacbuf[3]=adc_temp%100%10+0x30;
dacbuf[4]='V';
dacbuf[5]='\0';
UARTa_SendString("\r\nADC_CH1_VOL=");
UARTa_SendString(dacbuf);
}
DELAY_US(1*1000);
}
}
更多推荐
所有评论(0)