STM32学习心得二十六:DAC数模转换实验

记录一下,方便以后翻阅~
主要内容:
1) DAC数模转换原理;
2) 寄存器和库函数介绍;
3) 相关实验代码解读。
实验功能:系统启动后,按WK_UP键,输出电压加200点,对应电压值200*3.3/4096,按KEY1,输出电压值减200点,每次按键,输出电压值会传至串口调试助手上。
官方资料:《STM32中文参考手册V10》第12章——数字模拟转换DAC
硬件连接
在这里插入图片描述1. 数模转换原理
STM32的DAC模块是12位数字输入,电压输出型的DAC。DAC可以配置为8位或12位模式,也可与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC模块有2个输出通道,每个通道有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可通过引脚输入参考电压VREF+以获得更精确的转换结果。
2. DAC模块特点
2.1 2个DAC转换器,每个转换器对应1个输出通道;
2.2 8位或者12位单调输出;
2.3 12位模式下数据左对齐或者右对齐;
2.4 同步更新功能;
2.5 噪声波形生成;
2.6 三角波形生成;
2.7 双DAC通道同时或者分别转换;
2.8 每个通道都有DMA功能。
3. DAC模块框图
在这里插入图片描述
3.1 VDDA和VSSA为DAC模块模拟部分的供电;
3.2 Vref+则是DAC模块的参考电压;
3.3 数字不是写入DORx寄存器里,是写入在DHRx寄存器里;
3.4 DAC_OUTx就是DAC的输出通道了(对应PA4或PA5引脚)。
在这里插入图片描述
DAC_OUT1 ->PA4
DAC_OUT2 ->PA5
在这里插入图片描述
4. DAC转换
在这里插入图片描述
5. DAC数据格式
在这里插入图片描述
6. 选择DAC触发
在这里插入图片描述
7. DAC输出电压
在这里插入图片描述
8. 使能DAC通道
在这里插入图片描述
9. 使能DAC输出缓存
在这里插入图片描述
如果使能DAC输出缓存的话,虽然输出能力强一点,但是输出没法到0。
10. DAC相关寄存器一览
在这里插入图片描述
10.1 DAC控制寄存器DAC_CR
在这里插入图片描述
10.2 DAC通道1的12位右对齐数据保持寄存器DAC_DHR12R1
在这里插入图片描述
10.3 DAC通道1的12位左对齐数据保持寄存器DAC_DHR12L1
在这里插入图片描述
10.4 DAC通道1的8位右对齐数据保持寄存器DAC_DHR8R1
在这里插入图片描述
10.5 DAC通道1数据输出寄存器DAC_DOR1
在这里插入图片描述
11. DAC一般配置步骤
11.1 使能PA口时钟,设置PA4为模拟输入;
11.2 使能DAC1时钟;
11.3 初始化DAC,设置DAC工作模式;
11.4 使能DAC转换通道;
11.5 设置DAC输出值。
12. 相关实验代码解读
12.1 adc.h头文件代码解读

#ifndef __ADC_H
#define __ADC_H 
#include "sys.h"
//申明三个函数//
void Adc_Init(void);
u16  Get_Adc(u8 ch); 
u16 Get_Adc_Average(u8 ch,u8 times);  
#endif 

12.2 adc.c文件代码解读

 #include "adc.h"
 #include "delay.h"
 //配置ADC1一般步骤://
 //1)开启PA口时钟和ADC1时钟,设置PA1为模拟输入;//
 //2)复位ADC1,同时设置ADC1分频因子;//
 //3)初始化ADC1参数,设置ADC1的工作模式以及规则序列的相关信息;//
 //4)使能ADC并校准;//
 //5)配置规则通道参数;//
 //6)开启软件转换;//
 //7)等待转换完成,读取ADC值。//
 //编写Adc_Init初始化函数//                   
void  Adc_Init(void)
{  
 ADC_InitTypeDef  ADC_InitStructure; 
 GPIO_InitTypeDef GPIO_InitStructure;
 //使能GPIOA和ADC1通道时钟//
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE );   
 RCC_ADCCLKConfig(RCC_PCLK2_Div6);                   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M//
 //配置GPIO参数,引脚1,模拟输入//                         
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;       //模拟输入引脚
 GPIO_Init(GPIOA, &GPIO_InitStructure); 
 ADC_DeInit(ADC1);                                   //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值//
 //配置ADC_Init参数//
 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                  //独立模式//
 ADC_InitStructure.ADC_ScanConvMode = DISABLE;                       //单通道模式//
 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;                 //单次转换模式//
 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //软件触发启动//
 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;              //ADC数据右对齐//
 ADC_InitStructure.ADC_NbrOfChannel = 1; 
 ADC_Init(ADC1, &ADC_InitStructure);    
 ADC_Cmd(ADC1, ENABLE);                      //使能指定的ADC1//
 //以下四个函数用于校准//
 ADC_ResetCalibration(ADC1);                 //使能复位校准//   
 while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束//
 ADC_StartCalibration(ADC1);                 //开启AD校准//
 while(ADC_GetCalibrationStatus(ADC1));      //等待校准结束//
}      
u16 Get_Adc(u8 ch)   
{
 //设置指定ADC的规则组通道,四个入口参数//
 ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );          
 ADC_SoftwareStartConvCmd(ADC1, ENABLE);          //使能指定的ADC1的软件转换启动功能// 
 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));  //等待转换结束//
 return ADC_GetConversionValue(ADC1);             //返回最近一次ADC1规则组的转换结果//
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
 u32 temp_val=0;
 u8 t;
 for(t=0;t<times;t++)
 {
  temp_val+=Get_Adc(ch);
  delay_ms(5);
 }
 return temp_val/times;
}  

12.3 dac.h头文件代码解读

#ifndef __DAC_H
#define __DAC_H  
#include "sys.h"     
//申明两个函数//            
void Dac1_Init(void);     
void Dac1_Set_Vol(u16 vol);
#endif

12.4 dac.c文件代码解读

#include "dac.h"
//DAC1配置步骤包括://
//1)使能PA口时钟,设置PA4为模拟输入;//
//2)使能DAC1时钟//
//3)初始化DAC,设置DAC工作模式//
//4)使能DAC转换通道//
//5)设置DAC输出值//
//DAC通道1输出初始化函数//
void Dac1_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;
 DAC_InitTypeDef  DAC_InitType;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );              //使能GPIOA通道时钟//
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE );                //使能DAC通道时钟// 
 //GPIOA配置,引脚4,模拟输入//
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;                           //引脚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初始化配置//    
 DAC_InitType.DAC_Trigger=DAC_Trigger_None;                          //不使用触发功能//
 DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;            //不使用波形发生//
 DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;  //屏蔽、幅值设置,通常配合波形发生使用//
 DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;            //DAC1输出缓存关闭//
 DAC_Init(DAC_Channel_1,&DAC_InitType);
 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值//
}

12.5 main.c文件代码解读

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"    
#include "dac.h"
#include "adc.h"
 int main(void)
 {  
 u16 adcx;
 float temp; 
 u16 dacval=0;
 u8 key;
 delay_init();        //延时函数初始化//  
 uart_init(115200);   //串口初始化为115200//
 KEY_Init();          //初始化按键程序//
 LED_Init();          //LED端口初始化//
 LED0=1;
 LED1=1;
 Adc_Init();          //ADC初始化//
 Dac1_Init();         //DAC初始化//
 DAC_SetChannel1Data(DAC_Align_12b_R, 0);          
 while(1)
 {
  key=KEY_Scan(0);     
  if(key==WKUP_PRES)
  {   
   if(dacval<4000)
   {
    dacval+=200;
    LED0=0;                                        //加电压时,LED0亮//
   }
    DAC_SetChannel1Data(DAC_Align_12b_R, dacval);  //dacval值<4000时,DAC输出值加200,否则输出值为4000// 
  }else if(key==KEY1_PRES) 
  {
   if(dacval>200)
   {
    dacval-=200;
    LED1=0;                                        //减电压时,LED1亮//
   }
   else dacval=0;
    DAC_SetChannel1Data(DAC_Align_12b_R, dacval);  //dacval值>200时,DAC输出值减200,否则输出值为0// 
  }  
  if(key==KEY1_PRES||key==WKUP_PRES)               //WKUP或KEY1按下了//
  {  
   adcx=DAC_GetDataOutputValue(DAC_Channel_1);     //读取DAC传至ADC的值//
   printf("电压原始值:%d\n",adcx);                 
   temp=(float)adcx*(3.3/4096);                    //归一化电压值//    
   printf("电压实际值:%f\n",temp);
  }     
  delay_ms(500); 
  LED0=1; 
  LED1=1;   
 }
 }

13. 实验结果
在这里插入图片描述
旧知识点
1)复习如何新建工程模板,可参考STM32学习心得二:新建工程模板
2)复习基于库函数的初始化函数的一般格式,可参考STM32学习心得三:GPIO实验-基于库函数
3)复习寄存器地址,可参考STM32学习心得四:GPIO实验-基于寄存器
4)复习位操作,可参考STM32学习心得五:GPIO实验-基于位操作
5)复习寄存器地址名称映射,可参考STM32学习心得六:相关C语言学习及寄存器地址名称映射解读
6)复习时钟系统框图,可参考STM32学习心得七:STM32时钟系统框图解读及相关函数
7)复习延迟函数,可参考STM32学习心得九:Systick滴答定时器和延时函数解读
8)复习ST-LINK仿真器的参数配置,可参考STM32学习心得十:在Keil MDK软件中配置ST-LINK仿真器
9)复习ST-LINK调试方法,可参考STM32学习心得十一:ST-LINK调试原理+软硬件仿真调试方法
10)复习如何对GPIO进行复用,可参考STM32学习心得十二:端口复用和重映射
11)复习串口通信相关知识,可参考STM32学习心得十四:串口通信相关知识及配置方法
12)复习ADC原理及一般配置步骤,可参考STM32学习心得二十三:ADC转换原理及模数转换实验STM32学习心得二十四:内部温度传感器原理及实验STM32学习心得二十五:光敏传感器原理及实验

猜你喜欢

转载自blog.csdn.net/Leisure_ksj/article/details/106322398