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 來介紹。其內部結構圖如下所示:

YaRN大模型_建立時間

 

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 芯片管腳功能介紹如下:

YaRN大模型_YaRN大模型_02

 DAC 各通道輸出電壓計算公式如下:

YaRN大模型_#dsp_03

 其中 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 芯片工作時序,如下圖所示:

YaRN大模型_#dsp_04

 當進行 DAC 轉換時,在 CLK 的每個下降沿數據被鎖存到數據終端。當所有數 據位送入之後,LOAD 為低脈衝將數據從串行輸入寄存器轉移到選定的 DAC 輸出 通道。具體由哪個通道輸出,就需要通過 A1 和 A0 位控制,這兩個位也叫做通道 選擇位,其值對應通道輸出如下:

YaRN大模型_寄存器_05

 

RNG 位控制 DAC 的輸出範圍。當 RNG =0 時,輸出範圍是所施加的基準電壓和 GND;當間 RNG=1,輸出範圍為所施加的基準電壓和 GND 的兩倍。

3.硬件設計

YaRN大模型_數據_06

 

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