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