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);
	}
}

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐