1. 程式人生 > >SRM32學習筆記(8)——ADC和DAC

SRM32學習筆記(8)——ADC和DAC

補充 一次 說明 否則 onf 允許 包括 特性 而不是

1、ADC簡介

STM32 擁有 1~3 個 ADC(STM32F101/102 系列只有 1 個 ADC)STM32F103至少擁有2個ADC,STM32F103ZE包含3個ADC,這些 ADC 可以獨立使用,也可以使用雙重模式(提高采樣率)。STM32 的 ADC 是 12 位逐次逼近型模擬數字轉換器。它有 18 個通道,可測量 16 個外部2 個內部信號源。各通道的 A/D 轉換可以單次連續掃描間斷模式執行。ADC 的結果可以左對齊或右對齊方式(12位)存儲在 16 位數據寄存器中。模擬看門狗特性允許應用程序檢測輸入電壓是否超出用戶定義的高/低閥值。

技術分享圖片

STM32的ADC最大轉換率為1HZ

,也就是轉換時間為 1us(在 ADCCLK=14M,采樣周期為 1.5 個 ADC 時鐘下得到),不要讓 ADC 的時鐘超過 14M,否則將導致結果準確度下降。否則將導致結果準確度下降。

STM32 將 ADC 的轉換分為 2 個通道組:規則通道組註入通道組規則通道相當於你正常運行的程序,而註入通道呢,就相當於中斷。在你程序正常執行的時候,中斷是可以打斷你的執行的。同這個類似,註入通道的轉換可以打斷規則通道的轉換, 在註入通道被轉換完成之後,規則通道才得以繼續轉換。

STM32 的單次轉換模式下的相關設置,使用庫函數的函數來設定使用 ADC1 的通道 1 進行 AD 轉換。這裏需要說明一下,使用到的庫函數分布在 stm32f10x_adc.c 文件和 stm32f10x_adc.h 文件中。其詳細設置步驟:

1.1 開啟 PA 口時鐘和 ADC1 時鐘,設置 PA1 為模擬輸入

STM32F103ZET6 的 ADC 通道 1 在 PA1 上,所以,我們先要使能 PORTA 的時鐘和 ADC1時鐘,然後設置 PA1 為模擬輸入。使能 GPIOA 和 ADC 時鐘用 RCC_APB2PeriphClockCmd 函數,設置 PA1 的輸入方式,使用 GPIO_Init 函數即可。

1.2 復位 ADC1,同時設置 ADC1 分頻因子

開啟 ADC1 時鐘之後,我們要復位 ADC1, 將 ADC1 的全部寄存器重設為缺省值之後我們就可以通過 RCC_CFGR 設置 ADC1 的分頻因子。分頻因子要確保 ADC1 的時鐘(ADCCLK)不要超過 14Mhz。 這個我們設置分頻因子位 6,時鐘為 72/6=12MHz

,庫函數的實現方法是:

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

 ADC 時鐘復位的方法是:

ADC_DeInit(ADC1);
1.3 初始化 ADC1 參數,設置 ADC1 的工作模式以及規則序列的相關信息  

在設置完分頻因子之後,我們就可以開始 ADC1 的模式配置了,設置單次轉換模式觸發方式選擇數據對齊方式都在這一步實現。同時,我們還要設置 ADC1 規則序列的相關信息,我們這裏只有一個通道,並且是單次轉換的,所以設置規則序列中通道數為 1。這些在庫函數中是通過函數 ADC_Init 實現的,下面我們看看其定義:

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);

  從函數定義可以看出,第一個參數是指定 ADC 號。這裏我們來看看第二個參數,跟其他外設初始化一樣,同樣是通過設置結構體成員變量的值來設定參數。

typedef struct
{
uint32_t ADC_Mode; //設置 ADC 的模式  獨立模式,註入同步模式
FunctionalState ADC_ScanConvMode;   //設置是否開啟掃描模式
FunctionalState ADC_ContinuousConvMode; //設置是否開啟連續轉換模式
uint32_t ADC_ExternalTrigConv;    //設置啟動規則轉換組轉換的外部事件   
uint32_t ADC_DataAlign;   //設置 ADC 數據對齊方式是左對齊還是右對齊             
uint8_t ADC_NbrOfChannel; //設置規則序列的長度 
}ADC_InitTypeDef; 
初始化範例:
  ADC_InitTypeDef ADC_InitStructure;
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  //ADC 工作模式:獨立模式
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;  //AD 單通道模式
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  //AD 單次轉換模式
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   //轉換由軟件而不是外部觸發啟動
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  //ADC 數據右對齊
  ADC_InitStructure.ADC_NbrOfChannel = 1;  //順序進行規則轉換的 ADC 通道的數目 1
ADC_Init(ADC1, &ADC_InitStructure);  //根據指定的參數初始化外設 ADCx  
1.4 使能 ADC 並校準  

在設置完了以上信息後,我們就使能 AD 轉換器執行復位校準AD 校準,註意這兩步是必須的!不校準將導致結果很不準確。

使能指定的 ADC 的方法是:

ADC_Cmd(ADC1, ENABLE);  //使能指定的 ADC1

  執行復位校準的方法是:

ADC_ResetCalibration(ADC1);

  執行 ADC 校準的方法是:

ADC_StartCalibration(ADC1);    //開始指定 ADC1 的校準狀態

  記住,每次進行校準之後要等待校準結束。 這裏是通過獲取校準狀態來判斷是否校準是否結束。下面我們一一列出復位校準和 AD 校準的等待結束方法:

while(ADC_GetResetCalibrationStatus(ADC1));  //等待復位校準結束
while(ADC_GetCalibrationStatus(ADC1));    //等待校 AD 準結束
1.5 讀取 ADC 值

在上面的校準完成之後, ADC 就算準備好了。接下來我們要做的就是設置規則序列 1 裏面的通道采樣順序,以及通道的采樣周期,然後啟動 ADC 轉換。在轉換結束後,讀取 ADC 轉換結果值就是了。這裏設置規則序列通道以及采樣周期的函數是:

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel,uint8_t Rank, uint8_t ADC_SampleTime);

  我們這裏是規則序列中的第 1 個轉換,同時采樣周期為 239.5,所以設置為:

ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );

  軟件開啟 ADC 轉換的方法是:

ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的 ADC1 的軟件轉換

  啟動功能開啟轉換之後,就可以獲取轉換 ADC 轉換結果數據,方法是:

ADC_GetConversionValue(ADC1);

  同時在 AD 轉換中,我們還要根據狀態寄存器的標誌位來獲取 AD 轉換的各個狀態信息。庫函數獲取 AD 轉換的狀態信息的函數是:

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)

  比如我們要判斷 ADC1 的轉換是否結束,方法是:

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待轉換結束
1.6 通過以上幾個步驟的設置,我們就能正常的使用 STM32 的 ADC1 來執行 AD 轉換操作
/** 初始化ADC
 * 這裏我們僅以規則通道為例
 * 我們默認將開啟通道0~3    
 */
void Adc_Init(void)
{ 
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure; 
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE );     //使能ADC1通道時鐘
    RCC_ADCCLKConfig(RCC_PCLK2_Div6); //設置ADC分頻因子6   72M/6=12,ADC最大時間不能超過14M
    
    //PA1 作為模擬通道輸入引腳 
    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_InitStructure.ADC_Mode = ADC_Mode_Independent;    //ADC工作模式:ADC1和ADC2工作在獨立模式
    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通道的數目
    ADC_Init(ADC1, &ADC_InitStructure);    //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器 
    
    ADC_Cmd(ADC1, ENABLE);        //使能指定的ADC1    
    ADC_ResetCalibration(ADC1);    //使能復位校準 
    
    while(ADC_GetResetCalibrationStatus(ADC1));    //等待復位校準結束
    
    ADC_StartCalibration(ADC1);        //開啟AD校準
 
    while(ADC_GetCalibrationStatus(ADC1));     //等待校準結束
    
}
       
//獲得ADC值 //ch:通道值 0~3 u16 Get_Adc(u8 ch) { //設置指定ADC的規則組通道,一個序列,采樣時間 ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采樣時間為239.5周期 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; } 

補充 (STM32的內部溫度傳感器):

STM32 有一個內部的溫度傳感器,可以用來測量 CPU 及周圍的溫度(TA)。該溫度傳感器在內部ADCx_IN16 輸入通道相連接,此通道把傳感器輸出的電壓轉換成數字值。溫度傳感器模擬輸入推薦采樣時間 17.1μs。STM32 的內部溫度傳感器支持的溫度範圍為:-40~125度。精度比較差,為±1.5℃左右。

STM32 內部溫度傳感器的使用很簡單,只要設置一下內部 ADC,並激活其內部通道就差不多了 。

STM32 內部溫度傳感器使用的步驟了,如下:

(1)設置 ADC,開啟內部溫度傳感器。

ADC_TempSensorVrefintCmd(ENABLE); //開啟內部溫度傳感器功能:

  (2)讀取通道 16 的 AD 值,計算結果。

在設置完之後,我們就可以讀取溫度傳感器的電壓值了

//初始化ADC
//這裏我們僅以規則通道為例
//我們默認將開啟通道0~3    
void T_Adc_Init(void) //ADC通道初始化
{
    ADC_InitTypeDef ADC_InitStructure; 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1    , ENABLE );     //使能GPIOA,ADC1通道時鐘
  
    RCC_ADCCLKConfig(RCC_PCLK2_Div6); //分頻因子6時鐘為72M/6=12MHz

       ADC_DeInit(ADC1); //將外設 ADC1 的全部寄存器重設為缺省值
 
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //ADC工作模式:ADC1和ADC2工作在獨立模式
    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通道的數目
    ADC_Init(ADC1, &ADC_InitStructure);    //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器

    ADC_TempSensorVrefintCmd(ENABLE); //開啟內部溫度傳感器
    
 
    ADC_Cmd(ADC1, ENABLE);    //使能指定的ADC1

    ADC_ResetCalibration(ADC1);    //重置指定的ADC1的復位寄存器

    while(ADC_GetResetCalibrationStatus(ADC1));    //獲取ADC1重置校準寄存器的狀態,設置狀態則等待

    ADC_StartCalibration(ADC1);     //

    while(ADC_GetCalibrationStatus(ADC1));        //獲取指定ADC1的校準程序,設置狀態則等待
}
u16 T_Get_Adc(u8 ch) 
    {
 
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );    //ADC1,ADC通道3,第一個轉換,采樣時間為239.5周期                  
 
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);        //使能指定的ADC1的軟件轉換啟動功能
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待轉換結束
    return ADC_GetConversionValue(ADC1);    //返回最近一次ADC1規則組的轉換結果
    }

//得到ADC采樣內部溫度傳感器的值
//取10次,然後平均
u16 T_Get_Temp(void)
    {
    u16 temp_val=0;
    u8 t;
    for(t=0;t<10;t++)
        {
        temp_val+=T_Get_Adc(ADC_Channel_16);     //TampSensor
        delay_ms(5);
        }
    return temp_val/10;
    }

 //獲取通道ch的轉換值
//取times次,然後平均
u16 T_Get_Adc_Average(u8 ch,u8 times)
{
    u32 temp_val=0;
    u8 t;
    for(t=0;t<times;t++)
    {
        temp_val+=T_Get_Adc(ch);
        delay_ms(5);
    }
    return temp_val/times;
}

2、DAC簡介

STM32 的 DAC 模塊(數字/模擬轉換模塊)是 12 位數字輸入電壓輸出型的DAC。DAC 可以配置為 8 位或 12 位模式,也可以與 DMA 控制器配合使用。DAC工作在 12 位模式時,數據可以設置成左對齊或右對齊。DAC 模塊有 2 個輸出通道,每個通道都有單獨的轉換器。在雙DAC 模式下,2 個通道可以獨立地進行轉換,也可以同時進行轉換並同步地更新 2 個通道的輸出。DAC 可以通過引腳輸入參考電壓 VREF+以獲得更精確的轉換結果。

TM32 的 DAC 模塊主要特點有:

① 2 個 DAC 轉換器:每個轉換器對應 1 個輸出通道

② 8 位或者 12 位單調輸出

③ 12 位模式下數據左對齊或者右對齊

④ 同步更新功能

⑤ 噪聲波形生成

⑥ 三角波形生成

⑦ 雙 DAC 通道同時或者分別轉換

⑧ 每個通道都有 DMA 功能

使用庫函數的方法來設置 DAC 模塊的通道 1 來輸出模擬電壓,其詳細設置步驟如下:

2.1 開啟 PA 口時鐘,設置 PA4 為模擬輸入。

STM32F103ZET6 的 DAC 通道 1 在 PA4 上,所以,我們先要使能 PORTA 的時鐘,然後設置 PA4 為模擬輸入DAC 本身是輸出,但是為什麽端口要設置為模擬輸入模式呢?因為一旦使能 DACx 通道之後,相應的 GPIO 引腳(PA4 或者 PA5)會自動與 DAC 的模擬輸出相連,設置為輸入,是為了避免額外的幹擾。

使能 GPIOA 時鐘:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );    //使能 PORTA 時鐘

  設置 PA1 為模擬輸入只需要設置初始化參數即可:

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        //模擬輸入
2.2 使能 DAC1 時鐘

同其他外設一樣,要想使用,必須先開啟相應的時鐘。 STM32 的 DAC 模塊時鐘是由 APB1提供的,所以我們調用函數 RCC_APB1PeriphClockCmd()設置 DAC 模塊的時鐘使能。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE );    //使能 DAC 通道時鐘
3.3 初始化 DAC,設置 DAC 的工作模式  

該部分設置全部通過 DAC_CR 設置實現,包括:DAC 通道 1 使能、DAC 通道 1 輸出緩存關閉不使用觸發不使用波形發生器等設置。這裏 DMA 初始化是通過函數 DAC_Init 完成的:

void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct)

參數設置結構體類型 DAC_InitTypeDef 的定義:  

typedef struct
{
uint32_t DAC_Trigger; //設置是否使用觸發功能
uint32_t DAC_WaveGeneration; //設置是否使用波形發生
uint32_t DAC_LFSRUnmask_TriangleAmplitude; //設置屏蔽/幅值選擇器,這個變量只在使用波形發生器的時候才有用
uint32_t DAC_OutputBuffer;  //設置輸出緩存控制位
}DAC_InitTypeDef;

  實例代碼:

DAC_InitTypeDef DAC_InitType;
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 輸出緩存關閉 
DAC_Init(DAC_Channel_1,&DAC_InitType);    //初始化 DAC 通道 1
2.4 使能 DAC 轉換通道  

初始化 DAC 之後,理所當然要使能 DAC 轉換通道,庫函數方法是:

DAC_Cmd(DAC_Channel_1, ENABLE);   //使能 DAC1
2.5 設置 DAC 的輸出值 

通過前面 4 個步驟的設置,DAC 就可以開始工作了,我們使用 12 位右對齊數據格式,所以我們通過設置 DHR12R1,就可以在 DAC 輸出引腳(PA4)得到不同的電壓值了。庫函數的函數是:

DAC_SetChannel1Data(DAC_Align_12b_R, 0);      //左對齊

  第一個參數設置對齊方式,可以為 12 位右對齊 DAC_Align_12b_R,12 位左對齊DAC_Align_12b_L 以及 8 位右對齊 DAC_Align_8b_R 方式。

第二個參數就是 DAC 的輸入值了,這個很好理解,初始化設置為 0。 

這裏,還可以讀出 DAC 的數值,函數是:

DAC_GetDataOutputValue(DAC_Channel_1);
//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值
}

  

SRM32學習筆記(8)——ADC和DAC