历史上的今天
返回首页

历史上的今天

今天是:2025年03月12日(星期三)

正在发生

2019年03月12日 | 【STM32】DAC基本原理、寄存器、库函数(DAC一般步骤)

2019-03-12 来源:eefocus

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

 

}

 }


推荐阅读

史海拾趣

Baumer Electric Ag公司的发展小趣事

Baumer Electric Ag公司创立于1952年,初期主要专注于微动开关的生产。面对市场竞争的激烈,公司创始人Herbert Baumer凭借其敏锐的市场洞察力和坚定的决心,带领团队不断研发新产品,提升技术水平。1960年,公司成功推出首个程序控制系列,标志着Baumer在电控生产流程监控和控制领域取得了重要突破。

Fullywell Semiconductor Co Ltd公司的发展小趣事

随着市场的不断扩大,Fullywell意识到需要进一步提升产能以满足客户需求。于是,公司在2010年启动了大规模的产能扩张计划,投资建设了一座现代化的晶圆制造厂。同时,公司也加大了对国际市场的开拓力度,与多家国际知名电子企业建立了战略合作关系。这些举措不仅显著提升了Fullywell的产能和产品质量,也使其在国际市场上的知名度大幅提升,进一步巩固了其在行业内的地位。

HARTING公司的发展小趣事

为了进一步拓宽业务范围和提升技术实力,Fullywell在2015年进行了一次重要的收购。公司成功收购了某家专注于传感器技术的初创企业,将其先进的技术和产品线纳入麾下。这次收购不仅丰富了Fullywell的产品线,还使其在物联网、智能家居等新兴领域具备了更强的竞争力。随后,公司进行了业务整合,优化了资源配置,实现了优势互补,为公司的持续发展注入了新的动力。

Actel公司的发展小趣事

面对市场需求的不断变化和技术的不断进步,Fullywell始终保持着对研发的重视和投入。在2020年,公司研发团队经过数年的努力,成功攻克了多项关键技术难题,推出了新一代的智能电源管理芯片。这款芯片在能效比、集成度和可靠性等方面均达到了行业领先水平,赢得了市场的广泛好评。Fullywell借此机会实现了产品的迭代升级,进一步巩固了其在电源管理芯片市场的领先地位。

Electro Adapter Inc公司的发展小趣事

作为一家以技术为核心的企业,EA公司始终将技术创新作为公司发展的重中之重。公司不断引进新技术、新工艺,加强与高校、科研机构的合作,共同研发具有自主知识产权的新产品。在智能电源适配器领域,EA公司率先推出了一系列具有智能化、网络化功能的产品,引领了行业潮流。这些产品的推出不仅提高了公司的市场竞争力,也为客户提供了更加便捷、高效的使用体验。

AdaptivEnergy公司的发展小趣事

Electro Adapter Inc公司(以下简称EA公司)的成立源于创始人对电子行业未来发展的深刻洞察。在创立初期,EA公司主要生产一些基本的电源适配器,面临着激烈的市场竞争和资金短缺的双重压力。然而,创始人凭借其深厚的技术背景和敏锐的市场洞察力,不断研发新产品,提升产品质量,逐渐在市场上站稳了脚跟。特别是在一次技术革新中,EA公司率先推出了一款高效能、低成本的适配器,赢得了客户的青睐,也为公司的发展奠定了坚实的基础。

问答坊 | AI 解惑

关于低频信号发生器的问题,请高手们指教

我的电路设计和程序都编写好了,但是输出波形很不理想,只有方波OK,三角波,锯齿波,正弦波都失真很厉害,求大侠们帮小弟解决下这个问题! ORG 0000H MAIN:          MOV A,#00H      MO ...…

查看全部问答>

运算放大器基本电路30例

运算放大器基本电路30例…

查看全部问答>

2440-camera-display????

在camera驱动的Display_Cam_Image 中 memcpy((void *)(FRAMEBUF_BASE+ (240*pos_y + pos_x) + y*240*2),(void *)buffer_rgb,(QCIF_XSIZE)*2); 这句中 FRAMEBUF_BASE+ (240*pos_y + pos_x) + y*240*2 这个地址为什么要这样计算???…

查看全部问答>

关于ARM+DSP双核开发系统的问题

我们现在做的一个关于嵌入式课题在开发系统选型时遇到了一点问题,因为我们的系统既需要良好的GUI又需要较强大的信号处理能力,因此我们不得不把目光放到了arm+dsp双核开发系统上,但过去的双核开发系统应用相对较少,大多是选取一片独立的arm芯片 ...…

查看全部问答>

讨论:各位在单片机中用什么算法去做菜单?

讨论:各位在单片机中用什么算法去做菜单?   比如我要用89s52和smc1602做个菜单。这个菜单有很多层的,即有子菜单。   不知道各位用的是什么方法和算法?   小弟我一开始就用树和链表方式做的。不好。 & ...…

查看全部问答>

自己做个数子示波器,液晶屏是用那款好!

动手做个示波器,液晶显示屏不知用那款好些.有的说用 NS 12864C,也有的的说用 NS 12864R 有点拿不准.想请教高手.用那款实用好些,二者有何区别.请给欲我答复.感激.谢谢!…

查看全部问答>

【低功耗】LDO的选用技术

LDO 是新一代的集成电路稳压器,它与三端稳压器最大的不同点在于,LDO 是一个自耗很低 的微型片上系统(SoC)。LDO 按 其 静 态 耗 流 来 分 , 分 为 OmniPowerTM / MicroPowerTM / NanoPowerTM 三种产品。OmniPowerTM LDO 的静态电流在 10 ...…

查看全部问答>

深入浅出ARM7

深入浅出ARM7-LPC213x_214x(上).pdf 深入浅出ARM7_LPC213x__214x_下册.pdf…

查看全部问答>

各位大侠看看这个Atmega128单片机接收数据代码有啥问题,谢谢!怀疑是中断接收那里

#include #include #include #define fosc 8000000 #define baud 4800 #define uchar unsigned char #define uint unsigned int char rev_buf[80];        //接收缓存      /*uchar rev ...…

查看全部问答>

cyclone v使用报告——内存内存控制器

使用Altera的UniPHY控制器,采用软核和硬核两种模式,使用的时候一般是PHY跟controller一起使用,例化的时候已经将两者合为一体了。使用软核或硬核的区别只是下图的一个check box是否选上。 本来还好好的,默认没有选上,一点击使用硬核之后,图表 ...…

查看全部问答>