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學習心得二十五:光敏傳感器原理及實驗

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章