【STM32】DAC基本原理、寄存器、库函数(DAC一般步骤)

STM32F1xx官方资料:

《STM32中文参考手册V10》-第12章  数字模拟转换DAC


DAC的基本介绍

DAC的基本定义

Digital-to-Analog Converter的缩写。指数/模转换器或者数字/模拟转换器。是指将离散的数字信号转换为连续变量的模拟信号的器件。

典型的数字模拟转换器将表示一定比例电压值的数字信号转换为模拟信号

STM32的DAC模块是12位数字输入,电压输出型的DAC。

DAC的主要特征

  • 2个DAC转换器:每个转换器对应1个输出通道;
  • 8位或者12位单调输出;
  • 12位模式下数据左对齐或者右对齐;
  • 同步更新功能;
  • 噪声波形生成;
  • 三角波形生成;
  • 双DAC通道同时或者分别转换;
  • 每个通道都有DMA功能;
  • 外部触发转换;
  • 输入参考电压VREF+。


DAC的工作原理

DAC的工作框图


DAC模块的框图看起来比较复杂,接下来会一点一点地对它进行分析。

DAC引脚

在框图的四围是DAC的各个引脚,它们的名称、信号类型和作用见下图:


这里需要注意的是:STM32F103ZET6一共只有两个DAC_OUTx引脚,分别为DAC_OUT1(PA4)、DAC_OUT2(PA5)。并且在做DAC功能的时候,引脚GPIO模式应该是模拟输入!!!

为什么要使用模拟输入模式呢?因为一旦使能DACx通道后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了额外额外的干扰。


DAC转换

由框图可以看出,DAC受DORx寄存器直接控制的,但是不能直接往DORx寄存器写入数据,而是通过DHRx间接地传给DORx寄存器,实现对DAC的输出控制。

不能直接对寄存器DAC_DORx写入数据,任何输出到DAC通道x的数据都必须写入DAC_DHRx寄存器(数据实际写入DAC_DHR8Rx、DAC_DHR12Lx、DAC_DHR12Rx、DAC_DHR8RD、DAC_DHR12LD、或者DAC_DHR12RD寄存器)。

  • 如果没有选中硬件触发(寄存器DAC_CR1的TENx位置0),存入寄存器DAC_DHRx的数据会在一个APB1时钟周期后自动传至寄存器DAC_DORx;
  • 如果选中硬件触发(寄存器DAC_CR1的TENx位置1),数据传输在触发发生以后3个APB1时钟周期后完成。

一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间tSETTLING之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。


DAC数据格式

根据上面可知,数据写入DAC_DHRx寄存器,然而实际上DAC_DHRx一共有6个寄存器!这是为什么呢?这就和DAC数据格式有很大的关系了。

根据选择的配置模式,数据按照下文所述写入指定的寄存器:

单DAC通道x,有3种情况:

  • 8位数据右对齐:用户须将数据写入寄存器DAC_DHR8Rx[7:0]位(实际是存入寄存器DHRx[11:4]位);
  • 12位数据左对齐:用户须将数据写入寄存器DAC_DHR12Lx[15:4]位(实际是存入寄存器DHRx[11:0]位);
  • 12位数据右对齐:用户须将数据写入寄存器DAC_DHR12Rx[11:0]位(实际是存入寄存器DHRx[11:0]位)。

一般采用第三种方式:12位数据右对齐比较多。

根据对DAC_DHRyyyx寄存器的操作,经过相应的移位后,写入的数据被转存到DHRx寄存器中(DHRx是内部的数据保存寄存器x)。随后,DHRx寄存器的内容或被自动地传送到DORx寄存器,或通过软件触发或外部事件触发被传送到DORx寄存器。


双DAC通道,有3种情况:

  • 8位数据右对齐:用户须将DAC通道1数据写入寄存器DAC_DHR8RD[7:0]位(实际是存入寄存器DHR1[11:4]位),将DAC通道2数据写入寄存器DAC_DHR8RD[15:8]位(实际是存入寄存器DHR2[11:4]位);
  • 12位数据左对齐:用户须将DAC通道1数据写入寄存器DAC_DHR12LD[15:4]位(实际是存入寄存器DHR1[11:0]位),将DAC通道2数据写入寄存器DAC_DHR12LD[31:20]位(实际是存入寄存器DHR2[11:0]位);
  • 12位数据右对齐:用户须将DAC通道1数据写入寄存器DAC_DHR12RD[11:0]位(实际是存入寄存器DHR1[11:0]位),将DAC通道2数据写入寄存器DAC_DHR12RD[27:16]位(实际是存入寄存器DHR2[11:0]位)。


选择DAC触发

在框图的左上方,是指DAC转换可以由某外部事件触发(定时器计数器、外部中断线)。配置控制位TSELx[2:0]可以选择8个触发事件之一触发DAC转换。具体的外部触发的种类如下图所示:


DAC输出电压

当DAC的参考电压位VREF+的时候,数字输入经过DAC被线性地转换为模拟电压输出,其范围为0到VREF+。

任一DAC通道引脚上的输出电压满足下面的关系:

DAC输出 = VREF x (DOR / 4095)。

注意:此时数据格式:应该选择12位数据右对齐。

使能DAC输出缓存

DAC集成了2个输出缓存,可以用来减少输出阻抗,无需外部运放即可直接驱动外部负载。每个DAC通道输出缓存可以通过设置DAC_CR寄存器的BOFFx位来使能或者关闭。

但是,如果STM32的DAC输出缓存使能的话,虽然输出能力强一点,但是输出没有办法减到0,所以一般都不使用这个功能。


DAC相关配置寄存器

DAC控制寄存器(DAC_CR)


作用:配置DAC通道使能、触发使能、输出缓存、噪声输出使能、三角波输出使能。

DAC软件触发寄存器(DAC_SWTRIGR)


作用:配置DAC通道软件触发使能。

DAC通道数据保持寄存器(DAC_DHRxRx)

DAC通道1的12位右对齐数据保持寄存器(DAC_DHR12R1)


DAC通道1的12位左对齐数据保持寄存器(DAC_DHR12L1)


DAC通道1的8位右对齐数据保持寄存器(DAC_DHR8R1)


除了DAC通道1,还有DAC通道2的对应的三个数据保持寄存器。

DAC数据输出寄存器(DAC_DORx)

DAC通道1数据输出寄存器(DAC_DOR1)


除了DAC通道1,还有DAC通道2的对应的数据输出寄存器。


DAC相关配置库函数

  • 1个初始化函数
void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct);

作用:配置外部触发的方式、噪声波生成、三角波生成、输出缓存。

  • 1个使能函数
void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState);

作用:配置DAC某个通道使能。

  • 3个输出值函数
void DAC_SetChannel1Data(uint32_t DAC_Align, uint16_t Data);
void DAC_SetChannel2Data(uint32_t DAC_Align, uint16_t Data);
uint16_t DAC_GetDataOutputValue(uint32_t DAC_Channel);

作用:前两个向各自通道以某种对齐方式放入数值,第三个获得某个通道输出的数值。


DAC一般步骤

实验目标:利用按键控制STM32内部DAC的通道1来输出电压,同时通过ADC1的通道1采集DAC的输出电压。

  • 开启输出引脚、DAC时钟,设置为模拟输入。调用函数:RCC_APBxPeriphClockCmd();
  • 初始化DAC,设置DAC的工作模式。调用函数:DAC_Init();
  • 使能DAC转换通道。调用函数:DAC_Cmd();
  • 设置DAC的输出值。调用函数:DAC_SetChannelxData()。

下面按照这个一般步骤来进行一个简单的DAC程序:

//DAC通道1输出初始化
void Dac1_Init(void)
{
  
	GPIO_InitTypeDef GPIO_InitStructure;
	DAC_InitTypeDef DAC_InitType;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );	  //使能PORTA通道时钟
   	RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE );	  //使能DAC通道时钟 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; 		 //模拟输入
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_4)	;//PA.4 输出高
					
	DAC_InitType.DAC_Trigger=DAC_Trigger_None;	//不使用触发功能 TEN1=0
	DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
	DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
	DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;	//DAC1输出缓存关闭 BOFF1=1
    DAC_Init(DAC_Channel_1,&DAC_InitType);	 //初始化DAC通道1

	DAC_Cmd(DAC_Channel_1, ENABLE);  //使能DAC1
  
    DAC_SetChannel1Data(DAC_Align_12b_R, 0);  //12位右对齐数据格式设置DAC值

}

//设置通道1输出电压
//vol:0~3300,代表0~3.3V
void Dac1_Set_Vol(u16 vol)
{
	float temp=vol;
	temp/=1000;
	temp=temp*4096/3.3;
	DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值
}

 int main(void)
 {	 
	u16 adcx;
	float temp;
 	u8 t=0;	 
	u16 dacval=0;
	u8 key;
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200
	KEY_Init();			  //初始化按键程序
 	LED_Init();			     //LED端口初始化
	LCD_Init();			 	 //LCD初始化
	usmart_dev.init(72);	//初始化USMART	
 	Adc_Init();		  		//ADC初始化
	Dac1_Init();				//DAC初始化

	POINT_COLOR=RED;//设置字体为红色 
	LCD_ShowString(60,50,200,16,16,"WarShip STM32");	
	LCD_ShowString(60,70,200,16,16,"DAC TEST");	
	LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(60,110,200,16,16,"2015/1/15");	
	LCD_ShowString(60,130,200,16,16,"WK_UP:+  KEY1:-");	
	//显示提示信息											      
	POINT_COLOR=BLUE;//设置字体为蓝色
	LCD_ShowString(60,150,200,16,16,"DAC VAL:");	      
	LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V");	      
	LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
	
	 DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0	    	      
	while(1)
	{
		t++;
		key=KEY_Scan(0);			  
		if(key==WKUP_PRES)
		{		 
			if(dacval<4000)dacval+=200;
		 DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值	
		}else if(key==KEY1_PRES)	
		{
			if(dacval>200)dacval-=200;
			else dacval=0;
		  DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值
		}	 
	 if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了
		{	  
			adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
			LCD_ShowxNum(124,150,adcx,4,16,0);     	//显示DAC寄存器值
			temp=(float)adcx*(3.3/4096);			//得到DAC电压值
			adcx=temp;
 			LCD_ShowxNum(124,170,temp,1,16,0);     	//显示电压值整数部分
 			temp-=adcx;
			temp*=1000;
			LCD_ShowxNum(140,170,temp,3,16,0X80); 	//显示电压值的小数部分
 			adcx=Get_Adc_Average(ADC_Channel_1,10);		//得到ADC转换值	  
			temp=(float)adcx*(3.3/4096);			//得到ADC电压值
			adcx=temp;
 			LCD_ShowxNum(124,190,temp,1,16,0);     	//显示电压值整数部分
 			temp-=adcx;
			temp*=1000;
			LCD_ShowxNum(140,190,temp,3,16,0X80); 	//显示电压值的小数部分
			LED0=!LED0;	   
			t=0;
		}	    
		delay_ms(10);	

	}
 }


猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80095881