av无码国产在线看岛国_日韩精品无码专区国产av影院_在线主播精品国产99热_国模无码一区二区久久_97影院九七理论片男女高清_中文字幕一级毛片_亚洲国产av无码专区亚洲av_成人黄色小说在线观看_H动漫无遮挡在线看中文_国产午夜精品理论片免费视频播放

您好,歡迎訪問(wèn)上?,F(xiàn)易電子元器件有限公司網(wǎng)站!

021-51870898
021-51870898   18019251567
4新聞資訊
您的位置: 首頁(yè) ->  新聞資訊 -> 單片機(jī)

?STM32之ADC實(shí)例(基于DMA方式)

文章出處:?jiǎn)纹瑱C(jī) 責(zé)任編輯:上?,F(xiàn)易電子元器件有限公司 發(fā)表時(shí)間:2020-11-10

ADC簡(jiǎn)介:

    ADC(Analog-to-Digital Converter,模/ 數(shù)轉(zhuǎn)換器)。也就是將模擬信號(hào)轉(zhuǎn)換為數(shù)字信號(hào)進(jìn)行處理,在存儲(chǔ)或傳輸時(shí),模數(shù)轉(zhuǎn)換器幾乎必不可少。

   STM32在片上集成的ADC外設(shè)非常強(qiáng)大,我使用的奮斗開(kāi)發(fā)板是STM32F103VET6,屬于增強(qiáng)型的CPU,它有18個(gè)通道,可測(cè)量16個(gè)外部和2個(gè)內(nèi)部信號(hào)源。各通道的A/D轉(zhuǎn)換可以單次,連續(xù),掃描或間斷模式執(zhí)行,ADC的結(jié)果可以左對(duì)齊或右對(duì)齊方式存儲(chǔ)在16位數(shù)據(jù)寄存器中。



ADC工作過(guò)程分析:

   我們以ADC規(guī)則通道轉(zhuǎn)換過(guò)程來(lái)分析,如上圖,所有的器件都是圍繞中間的模擬至數(shù)字轉(zhuǎn)換器部分展開(kāi)的。它的左端VREF+,VREF- 等ADC參考電壓,ADCx_IN0 ~ ADCx_IN15為ADC的輸入信號(hào)通道,即某些GPIO引腳。輸入信號(hào)經(jīng)過(guò)這些通道被送到ADC器件,ADC器件需要收到觸發(fā)信號(hào)才開(kāi)始進(jìn)行轉(zhuǎn)換,如EXTI外部觸發(fā),定時(shí)器觸發(fā),也可以使用軟件觸發(fā)。ADC部件接受到觸發(fā)信號(hào)后,在ADCCLK時(shí)鐘的驅(qū)動(dòng)下對(duì)輸入通道的信號(hào)進(jìn)行采樣,并進(jìn)行模數(shù)轉(zhuǎn)換,其中ADCCLK是來(lái)自ADC預(yù)分頻器。

    ADC部件轉(zhuǎn)換后的數(shù)值被保存到一個(gè)16位的規(guī)則通道數(shù)據(jù)寄存器(或注入通道數(shù)據(jù)寄存器)中,我們可以通過(guò)CPU指令或DMA把它讀到內(nèi)存(變量),模數(shù)轉(zhuǎn)換之后,可以出發(fā)DMA請(qǐng)求或者觸發(fā)ADC轉(zhuǎn)換結(jié)束事件,如果配置了模擬看門狗,并且采集的電壓大于閾值,會(huì)觸發(fā)看門狗中斷。

   其實(shí)對(duì)于ADC采樣,軟件編程主要就是ADC的配置,當(dāng)然我是基于DMA方式的,所以DMA的配置也是關(guān)鍵!話不多說(shuō)看代碼!

主函數(shù):main.c

#include "printf.h"  

#include "adc.h"  

#include "stm32f10x.h"  

  

extern __IO uint16_t ADC_ConvertedValue;  

float ADC_ConvertedValueLocal;  

void Delay(__IO uint32_t nCount)  

{  

   for(;nCount !=0;nCount--);  

}  

int main(void)  

{    

  printf_init();      

  adc_init();  

  printf("******This is a ADC test******\n");  

          

  while(1)  

    {  

        ADC_ConvertedValueLocal =(float) ADC_ConvertedValue/4096*3.3;  

        printf("The current AD value =0x%04X\n",ADC_ConvertedValue);  

    printf("The current AD value =%f V\n",ADC_ConvertedValueLocal);  

          

    Delay(0xffffee);  

    }  

  return 0;   

}  

注意ADC_ConvertedValueLocal保存了由轉(zhuǎn)換值計(jì)算出來(lái)的電壓值,計(jì)算公式是:實(shí)際電壓值=ADC轉(zhuǎn)換值 x LSB ,這里由于我的板子VREF+接的參考電壓為3.3V,所以LSB=3.3/4096,STM32的ADC的精度為12位。

ADC與DMA配置:adc.c


#include "adc.h"  

volatile uint16_t ADC_ConvertedValue;  

void adc_init()  

{  

    GPIO_InitTypeDef GPIO_InitStructure;  

    ADC_InitTypeDef ADC_InitStructure;  

    DMA_InitTypeDef DMA_InitStructure;  

      

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC"RCC_APB2Periph_ADC1,ENABLE);      

  

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;        

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;  

    GPIO_Init(GPIOC,&GPIO_InitStructure);  

    DMA_DeInit(DMA1_Channel1);  

    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//ADC地址  

    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //內(nèi)存地址  

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(從外設(shè)到內(nèi)存)  

    DMA_InitStructure.DMA_BufferSize = 1; //傳輸內(nèi)容的大小  

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設(shè)地址固定  

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //內(nèi)存地址固定  

    DMA_InitStructure.DMA_PeripheralDataSize =   

    DMA_PeripheralDataSize_HalfWord ; //外設(shè)數(shù)據(jù)單位  

    DMA_InitStructure.DMA_MemoryDataSize =   

    DMA_MemoryDataSize_HalfWord ;    //內(nèi)存數(shù)據(jù)單位  

    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular  ; //DMA模式:循環(huán)傳輸  

    DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //優(yōu)先級(jí):高  

    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;   //禁止內(nèi)存到內(nèi)存的傳輸  

      

    DMA_Init(DMA1_Channel1, &DMA_InitStructure);  //配置DMA1的4通道  

    DMA_Cmd(DMA1_Channel1,ENABLE);  

   

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //獨(dú)立ADC模式  

    ADC_InitStructure.ADC_ScanConvMode = DISABLE;  //禁止掃描方式  

    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//開(kāi)啟連續(xù)轉(zhuǎn)換模式   

    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部觸發(fā)轉(zhuǎn)換  

    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集數(shù)據(jù)右對(duì)齊  

    ADC_InitStructure.ADC_NbrOfChannel = 1; //要轉(zhuǎn)換的通道數(shù)目  

    ADC_Init(ADC1, &ADC_InitStructure);  

      

    RCC_ADCCLKConfig(RCC_PCLK2_Div8);//配置ADC時(shí)鐘,為PCLK2的8分頻,即9Hz  

    ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5);//配置ADC1通道11為55.5個(gè)采樣周期   

      

    ADC_DMACmd(ADC1,ENABLE);  

    ADC_Cmd(ADC1,ENABLE);  

  

    ADC_ResetCalibration(ADC1);//復(fù)位校準(zhǔn)寄存器  

    while(ADC_GetResetCalibrationStatus(ADC1));//等待校準(zhǔn)寄存器復(fù)位完成  

  

    ADC_StartCalibration(ADC1);//ADC校準(zhǔn)  

    while(ADC_GetCalibrationStatus(ADC1));//等待校準(zhǔn)完成  

  

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//由于沒(méi)有采用外部觸發(fā),所以使用軟件觸發(fā)ADC轉(zhuǎn)換  

}  

ADC配置還是比較簡(jiǎn)單的,畢竟只配置了單通道,還是分析一下吧!這里我是把ADC1的通道11使用的GPIO引腳PC1配置成模擬輸入模式,在作為ADC的輸入時(shí),必須使用模擬輸入。對(duì)于ADC通道,每個(gè)ADC通道對(duì)應(yīng)一個(gè)GPIO引腳端口,GPIO的引腳在設(shè)為模擬輸入模式后可用于模擬電壓的輸入。STM32F103VET6有三個(gè)ADC,這三個(gè)ADC公用16個(gè)外部通道。

DMA的整體配置為:使用DMA1的通道1,數(shù)據(jù)從ADC外設(shè)的數(shù)據(jù)寄存器(ADC1_DR_Address)轉(zhuǎn)移到內(nèi)存(ADC_ConvertedValue變量),內(nèi)存外設(shè)地址都固定,每次傳輸?shù)拇笮榘胱郑?6位),使用DMA循環(huán)傳輸模式。


DMA傳輸?shù)耐庠O(shè)地址,也就是ADC1的地址為0x40012400+0x4c,這個(gè)地址可查STM32 datasheet獲得,如圖;



要特別注意ADC轉(zhuǎn)換時(shí)間配置,由于ADC時(shí)鐘頻率越高,轉(zhuǎn)換速度越快,那是不是就把ADC的時(shí)鐘頻率設(shè)的越大越好呢?其實(shí)不然,根據(jù)ADC時(shí)鐘圖可知,ADC時(shí)鐘有上限值,即不能超過(guò)14MHz,如圖:


這里ADC預(yù)分頻器的輸入為高速外設(shè)時(shí)鐘(PCLK2),使用RCC_ADCCLKConfig()庫(kù)函數(shù)來(lái)設(shè)置ADC預(yù)分頻的分頻值,PCLK2常用時(shí)鐘為72MHz,而ADCCLK必須小于14MHz,所以這里ADCCLK為PCLK2的6分頻,即12MHz,而我的程序中只是隨便設(shè)為8分頻,9MHz,若希望ADC以最高頻率14MHz運(yùn)行,可以把PCLK2設(shè)置為56MHz,然后再4分頻得到ACCLK。

ADC的轉(zhuǎn)換時(shí)間不僅與ADC的時(shí)鐘有關(guān),還與采樣周期有關(guān)。每個(gè)ADC通道可以設(shè)置為不同的采樣周期。STM32的ADC采樣時(shí)間計(jì)算公式為:

  T=采樣周期+12.5個(gè)周期

公式中的采樣周期就是函數(shù)中配置的 ADC_SampleTime,而后邊加上的12.5個(gè)周期為固定值,則ADC1通道11的轉(zhuǎn)換時(shí)間為T=(55.5+12.5) x 1/9=7.56us。

補(bǔ)充:在adc.c文件中定義了ADC_ConvertedValue變量,要注意這個(gè)變量是由關(guān)鍵字volatile修飾的,volatile的作用是讓編譯器不要去優(yōu)化這個(gè)變量,這樣每次用到這個(gè)變量時(shí)都要回到相應(yīng)變量的內(nèi)存中去取值,而如果不使用volatile進(jìn)行修飾的話,ADC_ConvertedValue變量在被訪問(wèn)的時(shí)候可能會(huì)直接從CPU的寄存器中取出(因?yàn)橹霸撟兞勘辉L問(wèn)過(guò),也就是說(shuō)之前就從內(nèi)存中取出ADC_ConvertedValue的值保存到某個(gè)CPU寄存器中),之所以直接從寄存器中去取值而不去內(nèi)存中取值,這是編譯器優(yōu)化代碼的結(jié)果(訪問(wèn)CPU寄存器比訪問(wèn)內(nèi)存快得多)。這里的CPU寄存器指R0,R1等CPU通用寄存器,用于CPU運(yùn)算及暫存數(shù)據(jù),不是指外設(shè)中的寄存器。

         因?yàn)锳DC_ConvertedValue這個(gè)變量值隨時(shí)都會(huì)被DMA控制器改變的,所以用volatile來(lái)修飾它,確保每次讀取到的都是實(shí)時(shí)ADC轉(zhuǎn)換值。

adc.h:

#ifndef _adc_H  

#define _adc_H  

#include "stm32f10x.h"  

#include "stm32f10x_dma.h"  

#include "stm32f10x_adc.h"  

#define ADC1_DR_Address  ((uint32_t)0x4001244c);  

  

void adc_init(void);  

  

#endif 

效果圖:


由于我的開(kāi)發(fā)板沒(méi)有滑動(dòng)變阻器,所以我就將電壓的輸入端接入通用IO口的3V引腳。如圖:


上海現(xiàn)易電子元器件有限公司 版權(quán)所有 未經(jīng)授權(quán)禁止復(fù)制或鏡像

CopyRight 2020-2025 29711999.com.cn All rights reserved   滬ICP備2020031792號(hào)

電話

易經(jīng)理

18019251567

微信

易經(jīng)理

易經(jīng)理

頂部

咨詢