文章目录

  • ADC电压采集DMA传输
  • ADC相关知识
    • 1.ADC引脚与相关的通道的关系
  • DMA相关知识
    • 1.DMA外设通道
  • 代码
    • 1.GPIO初始化配置
    • 2.ADC初始化配置
    • 3.DMA初始化配置
    • 4.配置信息整理,ADC转换
  • 总结

一、ADC相关知识

ADC引脚与相关的通道的关系,16个外部资源对应表。这里只列出了外部资源表具体ADC相关的知识可以去看ADC详细资料,这里当中对ADC通道有比较重要

通道号 ADC1 ADC2 ADC3
通道0 PA0 PA0 PA0
通道1 PA1 PA1 PA1
通道2 PA2 PA2 PA2
通道3 PA3 PA3 PA3
通道4 PA4 PA4 PF6
通道5 PA5 PA5 PF7
通道6 PA6 PA6 PF8
通道7 PA7 PA7 PF9
通道8 PB0 PB0 PF10
通道9 PB1 PB1 PF3
通道10 PC0 PC0 PC0
通道11 PC1 PC1 PC1
通道12 PC2 PC2 PC2
通道13 PC13 PC13 PC13
通道14 PC4 PC4 PF4

二、DMA相关知识

外设通道的选择主要目的是决定哪一个外设作为该数据流的源地址或者目标地址。这个也是DMA这个功能的精髓之一。这也是这里的重点。

三、代码

        1.GPIO初始化配置

//ADC GPIO初始化
void my_ADC_GPIO_Init(void)
{  
    my_gpio_init(GPIO_PA0_VOLTAGE_RUN);
    my_gpio_init(GPIO_PA1_VOLTAGE_RUN);
    my_gpio_init(GPIO_PA2_VOLTAGE_RUN);
    my_gpio_init(GPIO_PA3_VOLTAGE_RUN);
    my_gpio_init(GPIO_PA4_VOLTAGE_RUN);
    my_gpio_init(GPIO_PA5_VOLTAGE_RUN);
    my_gpio_init(GPIO_PA6_VOLTAGE_RUN);
    my_gpio_init(GPIO_PA7_VOLTAGE_RUN);
    
}


这里大家可能会不太理解,怎么这样可以配置,因为我把配置封装了一遍,大家可以能会不太理解,大家可以以平常配置GPIO的方式去配置就可以了。以下只是举一个栗子。

void my_GPIO_Init
{
 GPIO_InitTypeDef GPIO_InitStructure;

 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能 GPIOA 时钟

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道 5
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉
 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化

}

        2.ADC初始化配置

void my_ADC_Init(void)
{ 
    ADC_InitTypeDef ADC_InitStruct;
    ADC_CommonInitTypeDef ADC_CommonInitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    
    
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE); //ADC1 复位
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE); //复位结束
    
    ADC_TempSensorVrefintCmd(ENABLE);//使能内部温度传感器
    
    ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;    //独立模式
    ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //MDA 禁止直接访问 
    ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4;  //预分频4分频    //ADCCLK=PCLK2/4=84/4=21Mhz,ADC 时钟最好不要超过 36Mhz
    ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //两个采样阶段之间的延迟5个时钟
    ADC_CommonInit(&ADC_CommonInitStruct);
 
    ADC_StructInit(&ADC_InitStruct);
    ADC_InitStruct.ADC_ScanConvMode = ENABLE;                                //扫描模式, 多通道采集
    ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;                          //连续转换
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;                      //右对齐
    ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //禁止触发检测,使用软件触发.
    ADC_InitStruct.ADC_NbrOfConversion =  ADC_Channel_MAX;                                 //转换通道 3 个
    ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;                      //12位模式
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;       //软件触发.
    ADC_Init(ADC1, &ADC_InitStruct);
    
    ADC_Cmd(ADC1,ENABLE);
    
}

        3.DMA初始化配置

void my_DMA_Init(void)
{
    
    DMA_InitTypeDef DMA_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
    
    DMA_InitStruct.DMA_Channel = DMA_Channel_0;              //通道0
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;     //外设到寄存器
    DMA_InitStruct.DMA_Memory0BaseAddr = (u32)Run_Voltage;   //存储到的外部内存  
    DMA_InitStruct.DMA_PeripheralBaseAddr = ADC_Voltage;     //ADC1内存地址
    DMA_InitStruct.DMA_BufferSize = ADC_Channel_MAX;                       //一次传输的数据量为3
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;         //优先级高
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据大小也为半字,跟外设数据大小相同
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;   // 外设数据大小为半字,即两个字节    
    DMA_InitStruct.DMA_MemoryInc =  DMA_MemoryInc_Enable;           //存储器地址固定
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   //外设地址只有一个,地址不用递增.   
    DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;                    //循环传输
    DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;             //是否开启FIFO模式
    DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;  //选择FIFO阈值
    DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;            //存储器突发传输
    DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;    //外设突发传输配置
    DMA_Init(DMA2_Stream0, &DMA_InitStruct);
    
    DMA_ITConfig(DMA2_Stream0, DMA_IT_TC|DMA_IT_TE, ENABLE);
    
    DMA_Cmd(DMA2_Stream0,ENABLE);
    
    NVIC_InitStruct.NVIC_IRQChannel = DMA2_Stream0_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;    
    NVIC_Init(&NVIC_InitStruct); 
    
}

这里要多提一句的是ADC1的偏移地址,大家可以参考中文数据手册,里面有详细写了每一个外设的偏移地址,ADC1的偏移地址是0X4C。

还有一个就是存放数据的内存地址了,static u16 Run_Voltage[ADC_Channel_MAX];

#ifndef __RUN_VOLTAGE__H_
#define __RUN_VOLTAGE__H_
#include "common.h"
#include "Mygpio.h"
#include "Tasking.h"
#include "stm32f4xx_dma.h"
#include "stm32f4xx_adc.h"

#define ADC_Voltage     ((u32)ADC1+0x4C)


typedef enum
{
    PA0_ADC_Voltage,     //引脚PA0
    PA1_ADC_Voltage,     //引脚PA1
    PA2_ADC_Voltage,     //引脚PA2
    PA3_ADC_Voltage,     //引脚PA3
    PA4_ADC_Voltage,     //引脚PA4
    PA5_ADC_Voltage,     //引脚PA5
    PA6_ADC_Voltage,     //引脚PA6
    PA7_ADC_Voltage,     //引脚PA7
    
    CPU_ADC_Voltage,     //CPU温度
    ADC_Channel_MAX,
}task_ADC;




void my_ADC_GPIO_Init(void);
void my_DMA_Init(void);
void my_ADC_Init(void);
void Task_Voltage(void);

void Voltage_Process(void);   //电压采集处理
#endif

4.配置信息整理,ADC转换

void Task_Voltage(void)
{
    task_ADC Comply = (task_ADC)0;
    my_ADC_GPIO_Init();
    my_DMA_Init();
    my_ADC_Init();    
    for(Comply = (task_ADC)0; Comply< ADC_Channel_MAX; Comply++)
    {
         ADC_RegularChannelConfig(ADC1, Adc_Channel_ID[Comply], Comply+1 ,ADC_SampleTime_480Cycles);        
    }   
    //在最后一次传输后启用或禁用 ADC DMA 请求(单 ADC 模式)
    ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);   
    ADC_DMACmd(ADC1, ENABLE);          //使能ADC DMA  
    ADC_Cmd(ADC1, ENABLE);
    ADC_SoftwareStartConv(ADC1);       //开始传输
    
    My_Add_task(LY_VOLTAGE_Run,Voltage_Process, 200, 8);        //设置执行系统运行ADC采集任务
}






void DMA2_Stream0_IRQHandler(void)
{
    u8 temp = 0, j = 0;
    if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) == ENABLE)
    {
        for(temp = 0 ;temp < ADC_Channel_MAX; temp++)
        {
            for(j = 0; j < ADC_Channel_MAX; j++)
            {
                ADC_Converter[temp][j]=Run_Voltage[temp];               
            }           
        }
    }  
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
}


总结

配置成功的话,ADC1每一次转换都会到DMA2中断当中,中断当中把传输到自己的内存当中在取出来,在做后面的操作处理。至于要怎么处理这份数据,就看项目的要求了。

大家有什么疑问的话,可以评论区留言哦。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐