STM32 ADC1單通道連續轉換,轉換數據通過DMA傳輸給USART1數據寄存器並發送


      STM32的DMA可以完成外設到內存,內存到外設的直接數據傳輸。使用DMA傳輸即可讓數據繞開CPU,數據不需要進出SRAM。在DMA傳輸過程中,CPU可以進行其他操作,DMA與CPU分時使用系統總線。

       於是我就想到,DMA能不能完成外設到外設的直接數據傳輸呢?因此我嘗試着做了本次實例。

      ADC單通道連續采集數據,通過DMA傳輸給串口發送給上位機。DMA控制器使用系統總線,直接將ADC數據寄存器的數據傳輸給串口發送數據寄存器,對串口發送數據寄存器的寫操作將觸發串口傳輸,從而將數據發送給上位機。

      為了讓ADC1轉換與串口發送同步,將DMA傳輸模式設置為常規(一次傳輸),即完成指定數量數據的傳輸后,DMA將自動關閉,而不再響應DMA請求(當傳輸模式為循環模式時,DMA配置完成后,當有DMA請求信號時,DMA便開始工作,不停地通過系統總線傳輸數據),打開ADC的轉換完成中斷,並且在中斷函數中重新開啟DMA。這樣。ADC每完成一個數據的轉換,便觸發一次DMA傳輸,傳輸完成后DMA自動關閉,下一次ADC轉換完成時,重新開啟DMA,如此反復便實現了ADC1轉換與串口發送的同步。而在整個過程中,CPU可以做其他工作。

下面為核心代碼:

ADC1 GPIO配置(ADC1通道8對應PB0的復用功能):

    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);   //開啟GPIOB外設時鍾  (STM32在對外設寄存器操作之前需要開啟相應外設時鍾)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        //設置為模擬輸入
    GPIO_Init(GPIOB, &GPIO_InitStructure);               //模擬輸入

ADC1模式配置:

    ADC_InitTypeDef ADC_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  //開啟ADC1外設時鍾
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //獨立ADC模 
    ADC_InitStructure.ADC_ScanConvMode = DISABLE ;      //禁止掃描模式,掃描模式用於多通道采集
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;    //開啟連續轉換模式,即不停地進行ADC轉換
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    //不使用外部觸發轉換
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;     //采集數據右對齊
    ADC_InitStructure.ADC_NbrOfChannel = 1;         //要轉換的通道數目1
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);         //打開ADC1轉換完成中斷 

    RCC_ADCCLKConfig(RCC_PCLK2_Div8);  //配置ADC時鍾,為PCLK2的8分頻率
    ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_55Cycles5); //配置ADC1通道8,通道轉換順序為1, 轉換時間為55.5個時鍾周期           
    ADC_DMACmd(ADC1, ENABLE);  //打開ADC1的DMA請求,即ADC轉換完成后將觸發DMA開始傳輸
    ADC_Cmd(ADC1, ENABLE);  //打開ADC1
    ADC_ResetCalibration(ADC1);   //復位校准寄存器
    while(ADC_GetResetCalibrationStatus(ADC1));  //等待校准寄存器復位完成
    ADC_StartCalibration(ADC1);                 //ADC校准
    while(ADC_GetCalibrationStatus(ADC1));      //等待ADC校准完成
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);     //打開ADC軟件觸發

 DMA1配置:

    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  //開啟DMA1外設時鍾
    DMA_DeInit(DMA1_Channel1);                          //默認設置
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;     //Peripheral指向 ADC1數據寄存器
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&USART1->DR;       //Memory指向 USART1發送數據寄存器
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;             //方向由 ADC——>USART1
    DMA_InitStructure.DMA_BufferSize = 2;                          //發送兩個數據 (由於ADC精度為12位,數據寄存器為16位,分兩個字節發送)
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;// Peripheral按字節自增1指向ADC1數據寄存器
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;       // Memory指向串口發送數據寄存器不變(串口數據寬度為8位)  
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;    //數據寬度1字節
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;            //數據寬度1字節
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;        //一次傳輸
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;  //高優先級
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;         //禁止M2M
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);      
    DMA_Cmd(DMA1_Channel1, ENABLE);                     //開啟DMA1通道1,ADC1的DMA通道為DMA1通道1

附DMA請求映射:

 

串口USART1 GPIO配置(USART1 Tx對應PA9的復用功能,USART1 Rx對應PA10的復用功能):

    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //開啟GPIOA時鍾
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //配置為復用推挽輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //配置懸浮輸入
    GPIO_Init(GPIOA, &GPIO_InitStructure);

串口USART1 模式配置:

    USART_InitTypeDef USART_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);  //開啟USART1外設時鍾
    
    USART_InitStructure.USART_BaudRate = 1228800;    //設置波特率為1228800
    USART_InitStructure.USART_WordLength = USART_WordLength_8b; //設置數據字長為8bit
    USART_InitStructure.USART_StopBits = USART_StopBits_1;      //停止位1bit
    USART_InitStructure.USART_Parity = USART_Parity_No ;        //無奇偶校驗
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  //無硬件流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  //設置為發送和接收模式
    USART_Init(USART1, &USART_InitStructure);                             
    USART_Cmd(USART1, ENABLE);  //開啟USART1 

NVIC中斷配置:

    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);    //中斷優先級組1(1位搶占優先級,3位從優先級)
    NVIC_InitStructure.NVIC_IRQChannel  = ADC1_2_IRQn;   //ADC1,ADC2中斷
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//先占優先級0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority   = 1;   //從優先級1
    NVIC_InitStructure.NVIC_IRQChannelCmd   = ENABLE;     
    NVIC_Init(&NVIC_InitStructure);   

ADC1轉換完成中斷服務程序(在stm32f10x_it.c中編輯):

void ADC1_2_IRQHandler(void)
{
    if(ADC_GetITStatus(ADC1,ADC_IT_EOC))
  {
              /*重啟DMA*/ 
    DMA_Cmd(DMA1_Channel1,DISABLE);               
    DMA_SetCurrDataCounter(DMA1_Channel1,2);     //設置還要轉換的數據個數
    DMA_Cmd(DMA1_Channel1,ENABLE);
    ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);      //清除轉換完成標志
  }
}

 

 在stm32f10x_conf.h(stm32f10x.h頭文件中包含stm32f10x_conf.h)中需要取消的注釋:

#include "stm32f10x_adc.h"         //配置ADC所需的頭文件
//#include "stm32f10x_bkp.h"
//#include "stm32f10x_can.h"
//#include "stm32f10x_cec.h"
//#include "stm32f10x_crc.h"
//#include "stm32f10x_dac.h"
//#include "stm32f10x_dbgmcu.h"
#include "stm32f10x_dma.h"       //配置DMA所需的頭文件
//#include "stm32f10x_exti.h"
//#include "stm32f10x_flash.h"
//#include "stm32f10x_fsmc.h"
#include "stm32f10x_gpio.h"    //配置GPIO所需的頭文件
//#include "stm32f10x_i2c.h"
//#include "stm32f10x_iwdg.h"
//#include "stm32f10x_pwr.h"
#include "stm32f10x_rcc.h"     //配置時鍾所需的頭文件
//#include "stm32f10x_rtc.h"
//#include "stm32f10x_sdio.h"
//#include "stm32f10x_spi.h"
//#include "stm32f10x_tim.h"
#include "stm32f10x_usart.h"   //配置USART所需的頭文件
//#include "stm32f10x_wwdg.h"
#include "misc.h"      //配置NVIC所需的頭文件

 需要添加進工程庫文件有:

 

 

測試結果:

當AD模擬輸入Vdd時,上位機串口接收到如下數據:

當AD模擬輸入Vss時,上位機串口接收到如下數據:

 后來發現數據有問題,DMA將數據傳輸給USART1->DR后,串口需要時間將DR並行轉移給發送移位寄存器,在這期間USART1->DR不能改變。即在下次寫USART1->之前需要while (!(USART1->SR & USART_FLAG_TXE));檢測轉移完成。發現利用DMA將數據傳輸至外設時候,外設必須為高速外設,或者有緩沖區才行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM