文章目录
一、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中断当中,中断当中把传输到自己的内存当中在取出来,在做后面的操作处理。至于要怎么处理这份数据,就看项目的要求了。
大家有什么疑问的话,可以评论区留言哦。