ADC简介:
ADC(Analog-to-Digital Converter,模/ 数转化器)。也便是将模仿信号转化为数字信号进行处理,在存储或传输时,模数转化器简直必不可少。
STM32在片上集成的ADC外设十分强壮,我运用的斗争开发板是STM32F103VET6,归于增强型的CPU,它有18个通道,可测量16个外部和2个内部信号源。各通道的A/D转化能够单次,接连,扫描或接连形式履行,ADC的成果能够左对齐或右对齐方法存储在16位数据寄存器中。
ADC作业进程剖析:
咱们以ADC规矩通道转化进程来剖析,如上图,一切的器材都是环绕中心的模仿至数字转化器部分打开的。它的左端VREF+,VREF- 等ADC参阅电压,ADCx_IN0 ~ ADCx_IN15为ADC的输入信号通道,即某些GPIO引脚。输入信号经过这些通道被送到ADC器材,ADC器材需求收到触发信号才开端进行转化,如EXTI外部触发,定时器触发,也能够运用软件触发。ADC部件接受到触发信号后,在ADCCLK时钟的驱动下对输入通道的信号进行采样,并进行模数转化,其间ADCCLK是来自ADC预分频器。
ADC部件转化后的数值被保存到一个16位的规矩通道数据寄存器(或注入通道数据寄存器)中,咱们能够经过CPU指令或DMA把它读到内存(变量),模数转化之后,能够动身DMA恳求或许触发ADC转化完毕事情,假如装备了模仿看门狗,而且收集的电压大于阈值,会触发看门狗中止。
其实关于ADC采样,软件编程首要便是ADC的装备,当然我是根据DMA方法的,所以DMA的装备也是要害!话不多说看代码!
主函数:main.c
#include “printf.h”
#include “adc.h”
#include “stm32f10x.h”
extern __IO uint16_t ADC_ConvertedValue;
float ADC_ConvertedValueLocal;
void Delay(__IO uint32_t nCount)
{
for(;nCount !=0;nCount–);
}
int main(void)
{
printf_init();
adc_init();
printf(“******This is a ADC test******\n”);
while(1)
{
ADC_ConvertedValueLocal =(float) ADC_ConvertedValue/4096*3.3;
printf(“The current AD value =0x%04X\n”,ADC_ConvertedValue);
printf(“The current AD value =%f V\n”,ADC_ConvertedValueLocal);
Delay(0xffffee);
}
return 0;
}
留意ADC_ConvertedValueLocal保存了由转化值计算出来的电压值,计算公式是:实践电压值=ADC转化值 x LSB ,这儿因为我的板子VREF+接的参阅电压为3.3V,所以LSB=3.3/4096,STM32的ADC的精度为12位。
ADC与DMA装备:adc.c
#include “adc.h”
volaTIle uint16_t ADC_ConvertedValue;
void adc_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC”RCC_APB2Periph_ADC1,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_Init(GPIOC,&GPIO_InitStructure);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//ADC地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)
DMA_InitStructure.DMA_BufferSize = 1; //传输内容的巨细
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //内存地址固定
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_HalfWord ; //外设数据单位
DMA_InitStructure.DMA_MemoryDataSize =
DMA_MemoryDataSize_HalfWord ; //内存数据单位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; //DMA形式:循环传输
DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //优先级:高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //制止内存到内存的传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //装备DMA1的4通道
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC形式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //制止扫描方法
ADC_InitStructure.ADC_ConTInuousConvMode = ENABLE;//敞开接连转化形式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不运用外部触发转化
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //收集数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //要转化的通道数目
ADC_Init(ADC1, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);//装备ADC时钟,为PCLK2的8分频,即9Hz
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTIme_55Cycles5);//装备ADC1通道11为55.5个采样周期
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);//复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1));//等候校准寄存器复位完结
ADC_StartCalibration(ADC1);//ADC校准
while(ADC_GetCalibrationStatus(ADC1));//等候校准完结
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//因为没有选用外部触发,所以运用软件触发ADC转化
}
ADC装备仍是比较简单的,究竟只装备了单通道,仍是剖析一下吧!这儿我是把ADC1的通道11运用的GPIO引脚PC1装备成模仿输入形式,在作为ADC的输入时,有必要运用模仿输入。关于ADC通道,每个ADC通道对应一个GPIO引脚端口,GPIO的引脚在设为模仿输入形式后可用于模仿电压的输入。STM32F103VET6有三个ADC,这三个ADC共用16个外部通道。
DMA的全体装备为:运用DMA1的通道1,数据从ADC外设的数据寄存器(ADC1_DR_Address)转移到内存(ADC_ConvertedValue变量),内存外设地址都固定,每次传输的巨细为半字(16位),运用DMA循环传输形式。
DMA传输的外设地址,也便是ADC1的地址为0x40012400+0x4c,这个地址可查STM32 datasheet取得,如图;
这儿ADC预分频器的输入为高速外设时钟(PCLK2),运用RCC_ADCCLKConfig()库函数来设置ADC预分频的分频值,PCLK2常用时钟为72MHz,而ADCCLK有必要小于14MHz,所以这儿ADCCLK为PCLK2的6分频,即12MHz,而我的程序中仅仅随意设为8分频,9MHz,若期望ADC以最高频率14MHz运转,能够把PCLK2设置为56MHz,然后再4分频得到ACCLK。
ADC的转化时刻不只与ADC的时钟有关,还与采样周期有关。每个ADC通道能够设置为不同的采样周期。STM32的ADC采样时刻计算公式为:
T=采样周期+12.5个周期
公式中的采样周期便是函数中装备的 ADC_SampleTime,而后边加上的12.5个周期为固定值,则ADC1通道11的转化时刻为T=(55.5+12.5) x 1/9=7.56us。
弥补:在adc.c文件中界说了ADC_ConvertedValue变量,要留意这个变量是由要害字volatile润饰的,volatile的作用是让编译器不要去优化这个变量,这样每次用到这个变量时都要回到相应变量的内存中去取值,而假如不运用volatile进行润饰的话,ADC_ConvertedValue变量在被拜访的时分可能会直接从CPU的寄存器中取出(因为之前该变量被拜访过,也便是说之前就从内存中取出ADC_ConvertedValue的值保存到某个CPU寄存器中),之所以直接从寄存器中去取值而不去内存中取值,这是编译器优化代码的成果(拜访CPU寄存器比拜访内存快得多)。这儿的CPU寄存器指R0,R1等CPU通用寄存器,用于CPU运算及暂存数据,不是指外设中的寄存器。
因为ADC_ConvertedValue这个变量值随时都会被DMA控制器改动的,所以用volatile来润饰它,保证每次读取到的都是实时ADC转化值。
adc.h:
#ifndef _adc_H
#define _adc_H
#include “stm32f10x.h”
#include “stm32f10x_dma.h”
#include “stm32f10x_adc.h”
#define ADC1_DR_Address ((uint32_t)0x4001244c);
void adc_init(void);
#endif
效果图:
因为我的开发板没有滑动变阻器,所以我就将电压的输入端接入通用IO口的3V引脚。如图: