您的位置:首页 > 其它

STM32之3路ADC同步转换

2018-10-06 12:00 183 查看

国庆也算是出去浪了几天,当然 回来也不能忘记学习。经过前些天的摸索与学习对STM32的ADC有了一个了解,下面是学习过程(个人学习观点,仅供参考,如有错误或改进之处还望指出,臣不甚感激)。
更为详细的讲解可以参考一下这位前辈的博客:https://www.geek-share.com/detail/2626022181.html
初识ADC有一种茫然的状态同时又会觉得它的功能强大,在stm32中有3个ADC,每个ADC都有16个转换通道,由此可知它的强大。首先通过它的引脚功能图可以看出每个引脚所对应的ADC转换通道。这样在编写程序的时候就不会把通道搞错。

有了上述的了解之后呢,还要记住ADC所能检测的电压范围是0~3.3V,如果检测的电压值大于3.3V就会把ADC烧坏,还有就是ADC转换的时钟频率,在72M的主控CPU中它的转换频率不能大于14M也就是6分频否则检测的值会不准确。采样周期为1.5个时钟周期。stm32的ADC转换有两种通道,规则通道和注入通道,注入通道可以抢占式地打断规则通道的采样,执行注入通道采样后,再执行之前的规则通道采样,和中断类似。当然本次学习是基于规则转换的,库函数操作。有了以上的了解之后来看看ADC的库函数:



对于一些基本的库函数就不列出来了,具体参考stm32固件库使用手册。这里来讲一下几个重要的库函数,第一个就是ADC_Init()在这个函数里有以下参数

其中ADC_Mode又包含了10种模式

ADC_ExternalTrigConv 函数里有多种触发方式,一般常用软件触发。


这里要注意ADC转换的数据对齐方式,因为stm32的ADC是12位的,而它的数据寄存器ADC_DR是16位的。至于是左对齐还是右对齐?在读取ADC的数据时是从高位开始的,所以一般右对齐就好。
ADC_SampleTime设定了选中通道的ADC 采样时间。
采样的周期越长准确度越高,一般设为239.5个周期。ADC_ExternalTrigInjectedConv 指定了所使用的注入转换启动触发。
一般使用软件触发。
转换的数据被存在了ADC_DR寄存器中,当多通道转换时后一次的数据会把前一次的覆盖所以要用到DMA进行数据处理。以下是DMA的库函数
重点理解DMA_Init,以下是它的参数



关于DMA_PeripheralBaseAddr该参数用以定义DMA 外设基地址。DMA_MemoryBaseAddr该参数用以定义DMA 内存基地址。这两个函数等会程序里会有说明。下面是库函数配置的步骤:
1 定义ADC、GPIO结构体
2 使能相关时钟,设置分频,IO引脚设为模拟输入
3 复位ADC,选择ADC外设,设定ADC工作模式
4 设置数据对齐方式,通道转换数目
5 使能指定ADC,ADC校准
6 设置转换通道模式,设置ADC通道,采样周期
7 DMA初始化

上程序:

#include "sys.h"
#include "ADC.h"
#define ADC1_2_DR_ADDRESS    ((u32)0x40012400+0x4c)
#define ADC3_DR_ADDRESS    ((u32)0x40013c00+0x4c)
#define NUM 100
__IO u32 ADC1_ConvertedValue[NUM];//防止数据传输溢出造成的错误
__IO u16 ADC3_ConvertedValue[NUM];
void adc_Init(void)
{
//定义结构体
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_ADC2|RCC_APB2Periph_ADC3, ENABLE);//使能所用功能时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12MHZ

RCC->APB2ENR|=1<<2;
GPIOA->CRH&=0XFFFFF000;//PA0~2上拉输入

RCC->APB2ENR|=1<<9;      //使能ADC时钟

//ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult ;//ADC双重模式,同步规则转换 ADC1->CR1|=6<<16 双重模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单通道转换
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发ADC转换 ADC1->CR2|=7<<16
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC 通道的数目
ADC_Init(ADC1, &ADC_InitStructure);//初始化ADC结构体
ADC_Cmd(ADC1, ENABLE);

//ADC_DeInit(ADC2);
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult ;//ADC双重模式,同步规则
4000
转换
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单通道转换
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换h
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发ADC转换 ADC2->CR2|=7<<16
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC 通道的数目
ADC_Init(ADC2, &ADC_InitStructure);//初始化ADC结构体
ADC_Cmd(ADC2, ENABLE);

//ADC_DeInit(ADC3);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC3独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单通道转换
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发ADC转换
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC 通道的数目
ADC_Init(ADC3, &ADC_InitStructure);//初始化ADC结构体
ADC_Cmd(ADC3, ENABLE);

//ADC校准
ADC_ResetCalibration(ADC1);//重置指定的ADC 的校准寄存器
ADC_ResetCalibration(ADC2);
ADC_ResetCalibration(ADC3);
while(ADC_GetResetCalibrationStatus(ADC1)&&ADC_GetResetCalibrationStatus(ADC2)&&ADC_GetResetCalibrationStatus(ADC3));//获取ADC 重置校准寄存器的状态
ADC_StartCalibration(ADC1);//开始指定ADC 的校准状态
ADC_StartCalibration(ADC2);
ADC_StartCalibration(ADC3);
while(ADC_GetCalibrationStatus(ADC1)&&ADC_GetCalibrationStatus(ADC2)&&ADC_GetCalibrationStatus(ADC3));//获取指定ADC 的校准程序

//设置指定ADC 的规则组通道,设置它们的转化顺序和采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1,ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1,ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1,ADC_SampleTime_239Cycles5);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)&&ADC_GetFlagStatus(ADC2, ADC_FLAG_EOC)&&ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC));//等待转换结果

//使能指定的ADC 的软件转换启动功
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
ADC_SoftwareStartConvCmd(ADC2, ENABLE);
ADC_SoftwareStartConvCmd(ADC3, ENABLE);
}

DMA初始化:

void DMA_InIt(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1|RCC_AHBPeriph_DMA2, ENABLE);

DMA_DeInit(DMA1_Channel1);//DMA1 ADC1通道

DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_2_DR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC1_ConvertedValue;//存放地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据传输的来源 DST则为目的地
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设寄存器地址不递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存寄存器地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//数据宽度为16 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//数据宽度为16 位
DMA_InitStructure.DMA_BufferSize =  NUM;// 传输数据量的大小
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//工作在循环缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

DMA_DeInit(DMA2_Channel5);//DMA2 ADC3通道

DMA_InitStructure.DMA_PeripheralBaseAddr = ADC3_DR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC3_ConvertedValue;//存放地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据传输的来源 DST则为目的地
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设寄存器地址不递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存寄存器地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度为16 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//数据宽度为16 位
DMA_InitStructure.DMA_BufferSize =  NUM;// 传输数据量的大小
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//工作在循环缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel5, &DMA_InitStructure);

DMA_Cmd(DMA1_Channel1, ENABLE);//使能或者失能指定的通道
DMA_Cmd(DMA2_Channel5, ENABLE);

//使能ADC的DMA请求
ADC_DMACmd(ADC1, ENABLE);
ADC_DMACmd(ADC2, ENABLE);
ADC_DMACmd(ADC3, ENABLE);
}

//转换值传出:
float v_avr = 0;
void Get_Average(void)
{
u16 i = 0;
u32 adc1_sum = 0;
for(i=0; i<NUM;i++)
{
adc1_sum += ADC1_ConvertedValue[i]%65536;
}
v_avr = adc1_sum/NUM/4096.0*3300;
}

由于在学习中只用到了一个IO用于检测电压,故只写了一个传出值。
*问题:DMA的地址怎么选?*如何快速的找到这个寄存器的地址,首先打开参考手册,移动到2.3节—寄存器映像,也就是第28页,这里列出了STM32内部地址的划分,找到你要的外设基地址,例如ADC1的基地址为0x4001 2400 。然后再你需要的外设关于寄存器的地方找到寄存器的偏移地址,如ADC1的DR寄存器的偏移地址为0x4c,基地址加偏移地址就得到这个寄存器的地址了。
C语言知识:定义extern变量后不能赋以初值,否则会报错。外部变量在编译时赋值,只赋值一次。
总结:之前写的程序逻辑混乱,事件发生的顺序被颠倒导致转换出现问题,一定要仔细检查!理清步骤!

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: