一、ADC原理
原理
两个内部信号源是内部温度采集和内部电压,内部参考电压是内部1.2V的基准电压,不随外部供电的改变而改变,如果外部供电电压不准,那么可以进行内部基准电压采集校准。
模拟看门狗可以进行自动检测阈值。
远离电路
8bit逐次逼近型ADC芯片,和STM32ADC原理相同。通过地址锁存和译码、通道选择开关进行ADC通道选择,通过比较器比较DAC和输入电压的大小,DAC通过加权网络电路实现。通过逐次逼近(二分法)SAR来不断逼近DAC和输入电压。最后通过锁存缓冲器进行输出DAC对应的模拟量数值。EOC是转换结束信号,Start转换开始信号,clock是时钟信号。Vref±是DAC参考电压,所有参考电压输入会影响转换误差。(采集模拟量/ADC最大量程*Vref,所以Vref的输入影响模拟量的反馈,若参考电压波动,那么反馈的模拟量也波动)
ADC内部电路
ADC端口输入->多路开关进行选择->最多4通道/最多16通道->注入通道(寄存器4*16)/规则通道(16bit)需要使用DMA转运,防止覆盖
触发ADC转换源(规则和注入组)
ADC预分频器 ,最大为14MHz。所以只能选择6和8分频,为12MHz和9MHz。
读取这些标志位就知道是否有对应的转换完成。这些标志位可以配置触发中断。
ADC通道和引脚复用的关系
四种采集模式(两个参数的组合):
扫描模式需要用到DMA将数据移出,防止被覆盖。
还有一种间断模式,可以每扫描几次就停止,需要再次触发才能继续。
触发控制
数据对齐:
一般使用右对齐,数据可以直接使用。右对齐数值会大16倍,若不需要这么高的精度,可以直接使用左对齐,使用高8bit即可。
转换时间:
采样时间是采样保持(采样时对当前模拟量的存储和使用时间)花费的时间。采样时间越大越能避免一些毛刺信号。若ADCCLK设置超过14MHz则正确性无法保证。
校准
电压采集电路:
1、RP1滑动变阻器可以输出一个0~3.3V的电压。(电阻最小千欧级别,在小容易烧毁和费点)
2、传感器输出电压电路,例如光敏热敏传感器等,其中N1等效可变电阻。使用R1进行分压,原理同电路1,N1小时,下拉作用增强,输出端电压下降,N1增大时,下拉作用减小,输出端受上拉电阻的作用,电压上升。(电路优先导通阻值小的电路,根据电阻进行分压,PA1出于中间电位,输出为R1分压后的电压)
3、当需要采集超出3.3V的电压时,借用分压原理,例如采集5V电压,上端17k电阻,下端33k电阻,经过分压,PA2处电压为总电压的17/(17+33)。PA2刚好为(3.3V,0V),建议分压电路最高采集超过10V电压,在高建议使用隔离放大器等。
二、DMA原理
若是内存到SRAM,则使用软件触发。若需要转运外设采集数据,则需要硬件触发。
存储器映像
DMA可以转运SRAM和APB外设的内容,不能转运flash内容,转运flash会出错。
对flash写入需要通过flash接口,进行按页擦除写入。
DMA流程框图
可以进行,外设->存储器,存储器->外设,存储器->存储器(flash-sram、sram-sram,flash为只读的)
软件触发为触发一次DMA(传输计数器降到0),将指定的次数转运完成。
起始地址表示转运的起始地址,可指定数据宽度Halfword、word等,地址是否自增表示转运完成后下次转运是否自增地址。若使用自动重装器,地址也会重装。
DMA使用条件:
1、开关打开
2、计数器>0
3、必须有触发信号
数据传输宽度说明
DMA中断
DMA1请求映像
M2M是触发选择位,EN是DMA通道使能位。
DMA2请求映像
数据转运DMA
ADC+DAM
ADC虽然每个通道采集完成后不会置相关标志位,在全部通道采集完成后才会置标志位。但是其实每个ADC通过采集完成后会触发DMA转运,可以完整的转运多个通道采集的数据。注意:ADC_DR地址不会变,DMA转运ADC地址不需要自增。
三、实例
1、单通道单次非连续采集-手动触发
使用规则组,每次只采集1个通道,不连续采集,每次使用手动触发。
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t RP_Value = 0;
int main(void){
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"RP:0000");
OLED_ShowString(2,1,"Voltage:0.0V");
while(1){
RP_Value = Get_RP();
OLED_ShowNum(1,4,RP_Value,4);
OLED_ShowNum(2,9,RP_Value*3.3/4095,1);
OLED_ShowNum(2,11,RP_Value*33/4095%10,1);
Delay_ms(500);
}
return 0;
}
AD.c
#include "stm32f10x.h" // Device header
/**
* @brief ADC1CH1初始化函数,单通道单次扫描非连续模式,无触发,手动开启转换
* @param
* @arg
* @param
* @arg
* @retval None
*/
void AD_Init(void){
//1、RCC时钟 配置ADCCLK分频器
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK-6分频-12MHz
//2、GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//3、配置多路开关,把GPIO通道接到规则组列表
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_1Cycles5);//采样时间选择1.5个ADC周期,1.5+12.5 =14MHz,ADCCLK = 12MHz,所以采样时间 = 14/12 = 1.17us
//4、配置ADC转换器
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续扫描或者单次扫描
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发扫描选择
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC模式,ADC1和ADC2独立转换
ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数量
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//多通道或者单通道扫描
ADC_Init(ADC1,&ADC_InitStructure);
//需要的话配置看门狗检测阈值
//5、需要的话开启中断,使用ITConfig开启对应的中断输出
//6、NVIC配置中断优先级
//7、开启ADC,然后进行校准//可以使用软件触发转换,可以手动获取当前采集值
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);//0为复位完成 1为正在复位
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);//0为校准完成 1为正在校准
}
/**
* @brief 配合AD_Init,获取电位计采集到的0-3.3V模拟量
* @param
* @arg
* @param
* @arg
* @retval None
*/
uint16_t Get_RP(void){
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);//完成后自动清除
return ADC_GetConversionValue(ADC1);
}
AD.h
#ifndef __AD_H
#define __AD_H
#include "stm32f10x.h" // Device header
void AD_Init(void);
uint16_t Get_RP(void);
#endif
AD采集会有抖动,若想设置一个阈值,高于阈值高操作,低于阈值低操作,在阈值见会有操作冲突。可使用同施密特触发器的原理,设置两个阈值,低于低阈值低操作,高于高阈值高操作,这样不会操作冲突。
也可以读取10/20个值,然后取平均值。
2、单通道连续采集-软件触发一次即可
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t RP_Value = 0;
int main(void){
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"RP:0000");
OLED_ShowString(2,1,"Voltage:0.00V");
while(1){
RP_Value = Get_RP();
OLED_ShowNum(1,4,RP_Value,4);
OLED_ShowNum(2,9,RP_Value*3.3/4095,1);
OLED_ShowNum(2,11,RP_Value*330/4095%100,2);
Delay_ms(500);
}
return 0;
}
AD.c
#include "stm32f10x.h" // Device header
/**
* @brief ADC1CH1初始化函数,单通道单次扫描非连续模式,无触发,手动开启转换
* @param
* @arg
* @param
* @arg
* @retval None
*/
void AD_Init(void){
//1、RCC时钟 配置ADCCLK分频器
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK-6分频-12MHz
//2、GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//3、配置多路开关,把GPIO通道接到规则组列表
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_1Cycles5);//采样时间选择1.5个ADC周期,1.5+12.5 =14MHz,ADCCLK = 12MHz,所以采样时间 = 14/12 = 1.17us
//4、配置ADC转换器
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续扫描或者单次扫描
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发扫描选择
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC模式,ADC1和ADC2独立转换
ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数量
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//多通道或者单通道扫描
ADC_Init(ADC1,&ADC_InitStructure);
//需要的话配置看门狗检测阈值
//5、需要的话开启中断,使用ITConfig开启对应的中断输出
//6、NVIC配置中断优先级
//7、开启ADC,然后进行校准//可以使用软件触发转换,可以手动获取当前采集值
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);//0为复位完成 1为正在复位
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);//0为校准完成 1为正在校准
//连续扫描,软件触发一次即可
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}
/**
* @brief 配合AD_Init,获取电位计采集到的0-303V模拟量
* @param
* @arg
* @param
* @arg
* @retval None
*/
uint16_t Get_RP(void){
return ADC_GetConversionValue(ADC1);
}
AD.h
#ifndef __AD_H
#define __AD_H
#include "stm32f10x.h" // Device header
void AD_Init(void);
uint16_t Get_RP(void);
#endif
3、多通道单次采集非连续-因没使用DMA,每次触发前修改通道完成功能。
程序值接了A0和A1,效果相同
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t RP_Value = 0;
int main(void){
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"RP:0000");
OLED_ShowString(2,1,"Voltage:0.00V");
OLED_ShowString(3,1,"Sensor:0000");
OLED_ShowString(4,1,"Voltage:0.00V");
while(1){
RP_Value = Get_SensorValue(ADC_Channel_0);
OLED_ShowNum(1,4,RP_Value,4);
OLED_ShowNum(2,9,RP_Value*3.3/4095,1);
OLED_ShowNum(2,11,RP_Value*330/4095%100,2);
RP_Value = Get_SensorValue(ADC_Channel_1);
OLED_ShowNum(3,8,RP_Value,4);
OLED_ShowNum(4,9,RP_Value*3.3/4095,1);
OLED_ShowNum(4,11,RP_Value*330/4095%100,2);
Delay_ms(100);
}
return 0;
}
AD.c
#include "stm32f10x.h" // Device header
/**
* @brief ADC1CH1初始化函数,单通道单次扫描非连续模式,无触发,手动开启转换
* @param
* @arg
* @param
* @arg
* @retval None
*/
void AD_Init(void){
//1、RCC时钟 配置ADCCLK分频器
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK-6分频-12MHz
//2、GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//4、配置ADC转换器
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续扫描或者单次扫描
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发扫描选择
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC模式,ADC1和ADC2独立转换
ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数量
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//多通道或者单通道扫描
ADC_Init(ADC1,&ADC_InitStructure);
//需要的话配置看门狗检测阈值
//5、需要的话开启中断,使用ITConfig开启对应的中断输出
//6、NVIC配置中断优先级
//7、开启ADC,然后进行校准//可以使用软件触发转换,可以手动获取当前采集值
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);//0为复位完成 1为正在复位
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);//0为校准完成 1为正在校准
}
/**
* @brief 配合AD_Init,获取电位计采集到的0-3.3V模拟量
* @param
* @arg
* @param
* @arg
* @retval None
*/
uint16_t Get_SensorValue(uint8_t ADC_Channel){
//3、配置多路开关,把GPIO通道接到规则组列表
ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_1Cycles5);//采样时间选择1.5个ADC周期,1.5+12.5 =14,ADCCLK = 12MHz,所以采样时间 = 14/12 = 1.17us
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
AD.h
#ifndef __AD_H
#define __AD_H
#include "stm32f10x.h" // Device header
void AD_Init(void);
uint16_t Get_SensorValue(uint8_t ADC_Channel);
#endif
4、 DMA数据转运
根据STM3存储器映像。
一般变量存在SRAM ,0x20000000起始。
const等常量存在Flash中,0x08000000起始后,存在程序代码的存储后。
外设存储器地址例如ADC->DR存在,0x40000000起始的后面。
如果想查一个寄存器的地址,就需要先查到外设地址,然后看单个寄存器的偏移(使用芯片查手册基本都是这样)
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint8_t DataB[4] = {0x00,0x00,0x00,0x00};
uint8_t DataA[4] = {0x01,0x02,0x03,0x04};
int main(void){
OLED_Init();
MyDMA_Init((uint32_t)&DataA[0],(uint32_t)&DataB[0],4);
OLED_ShowString(1,1,"DataA:");
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
OLED_ShowString(3,1,"DataB:");
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
while(1){
for(int i=0;i<4;i++){
DataA[i]++;
}
Delay_ms(1000);
startDMA1Channel1Software();
OLED_ShowString(1,1,"DataA:");
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
OLED_ShowString(3,1,"DataB:");
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
}
return 0;
}
MyDMA.c
#include "stm32f10x.h" // Device header
/**
* @brief DMA1初始化,进行存储器到存储器的转运
* @param AddrA:源地址
* @arg
* @param AddrB:目标地址
* @arg
* @param BufferSize:计数器大小,即DMA_Byte个数
* @arg
* @retval None
*/
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t BufferSize){
RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1EN,ENABLE);//使能DMA1时钟
//DMA初始化,理论上为外设->存储器,实际上根据配置
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//M2M软硬件触发选择,选择是否软件触发
DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器是否自增
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式,是否使用自动重装,自动重装和软件触发不能同时使用,会持续传输不能停止,软件触发DMA只要使能就会一直转运,计数清空后停止
DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//起始
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
}
/**
* @brief 软件触发DMA当计数器清零时停止,此函数配合进行DMA1软件再次触发
* @param
* @arg
* @param
* @arg
* @retval None
*/
void startDMA1Channel1Software(void){
while(DMA_GetCurrDataCounter(DMA1_Channel1) != 0 );
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,4);
DMA_Cmd(DMA1_Channel1,ENABLE);
}
MyDMA.h
#ifndef __MYDMA_H
#define __MyDMA_H
#include "stm32f10x.h" // Device header
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t BufferSize);
void startDMA1Channel1Software(void);
#endif
5、多通道连续扫描多次采集,使用DMA采集ADC1通道1和通道2的数据
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
int main(void){
OLED_Init();
MyDMA_AD_Init();
OLED_ShowString(1,1,"AD0:0000");
OLED_ShowString(2,1,"AD1:0000");
while(1){
OLED_ShowNum(1,5,ADC_Value[0],4);
OLED_ShowNum(2,5,ADC_Value[1],4);
Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间
}
return 0;
}
MyDMA.c
#include "stm32f10x.h" // Device header
uint16_t ADC_Value[4]={0};//ADC采集使用的数组
/**
* @brief DMA1初始化,进行存储器到存储器的转运
* @param AddrA:源地址
* @arg
* @param AddrB:目标地址
* @arg
* @param BufferSize:计数器大小,即DMA_Byte个数
* @arg
* @retval None
*/
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t BufferSize){
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//使能DMA1时钟
//DMA初始化,理论上为外设->存储器,实际上根据配置
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//M2M软硬件触发选择,选择是否软件触发
DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器是否自增
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式,是否使用自动重装,自动重装和软件触发不能同时使用,会持续传输不能停止,软件触发DMA只要使能就会一直转运,计数清空后停止
DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//起始
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
}
/**
* @brief 软件触发DMA当计数器清零时停止,此函数配合进行DMA1软件(MyDMA_Init函数)再次触发
* @param
* @arg
* @param
* @arg
* @retval None
*/
void startDMA1Channel1Software(void){
while(DMA_GetCurrDataCounter(DMA1_Channel1) != 0 );
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,4);
DMA_Cmd(DMA1_Channel1,ENABLE);
}
/**
* @brief DMA1_Channel1 + ADC1_Channel_0和ADC1_Channel_1,使用DMA1转运ADC1通道数据,
* @param
* @arg
* @param
* @arg
* @retval None
*/
void MyDMA_AD_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //开启DMA1的时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0、PA1、PA2和PA3引脚初始化为模拟输入
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); //规则组序列2的位置,配置为通道1
ADC_InitTypeDef ADC_InitStructure; //定义结构体变量
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换,使能,每转换一次规则组序列后立刻开始下一次转换
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式,使能,扫描规则组的序列,扫描数量由ADC_NbrOfChannel确定
ADC_InitStructure.ADC_NbrOfChannel = 4; //通道数,为4,扫描规则组的前4个通道
ADC_Init(ADC1, &ADC_InitStructure); //将结构体变量交给ADC_Init,配置ADC1
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//M2M软硬件触发选择,选择是否软件触发
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Value[0];//存储器
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器是否自增
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//传输模式,是否使用自动重装,自动重装和软件触发不能同时使用,会持续传输不能停止,软件触发DMA只要使能就会一直转运,计数清空后停止
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//起始,获取ADC1->DR寄存器的地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);//DMA开启
ADC_DMACmd(ADC1,ENABLE);//ADC到DMA开启
ADC_Cmd(ADC1,ENABLE);//ADC开启
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET);//0为复位完成 1为正在复位
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET);//0为校准完成 1为正在校准
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//ADC软件触发一次
}
MyDMA.h
#ifndef __MYDMA_H
#define __MyDMA_H
#include "stm32f10x.h" // Device header
extern uint16_t ADC_Value[4];
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t BufferSize);
void startDMA1Channel1Software(void);
void MyDMA_AD_Init(void);
#endif