SRM32學習筆記(8)——ADC和DAC
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
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