目录
ADC,模数转换。在STM32F103VET6中,有3个ADC,精度为12位,每个ADC最多有16个通道。其中ADC1和ADC2都有16个外部通道,ADC13根据CPU引脚的不同通道数也不同,一般都有8个外部通道。
ADC框图
ADC的输入范围:Vref- < Vin < Vref+
在设计原理图时一般把VREF-和VSSA接地,VREF+和VDDA接3.3V,使得ADC的输入范围在0~3.3V。
如果想让输入范围变宽,使得可以测试负电压或更高的正电压,我们可以在外部加一个电压调理电路,把需要转换的电压抬升或者降低到0~3.3V。
ADC的输入通道(对应表)
ADC的外部通道:规则通道、注入通道
规则通道最多有16路,注入通道最多有4路。
如果在规则通道转换过程中,有注入通道插队,那么就要先转换完注入通道,等注入通道转换完成后,再回到规则通道的转换流程。所以,注入通道只有在规则通道存在时才会出现。
ADC的转换序列(规则通道、注入通道)
ADC的转换控制
寄存器控制转换:ADC_CR2:ADON位控制(1开始转换,0停止转换)。
触发式转换(内部定时器触发和外部IO触发):由ADC_CR2:EXTSEL[2:0]和JEXTSEL[2:0]位控制。
ADC_CR2:EXTSEL[2:0]用于选择规则通道的触发源。
ADC_CR2:EXTTRIG用于激活规则通道的触发源(1开始转换,0停止转换)。
ADC_CR2:JEXTSEL[2:0]用于选择注入通道的触发源。
ADC_CR2:JEXTTRIG用于激活注入通道的触发源(1开始转换,0停止转换)。
其中ADC3的规则转换和注入转换的触发源与ADC1和ADC2有所不同。
ADC的转换时间
ADC_CLK(ADC输入时钟):由PCLK2分频而来(/2/4/6/8,分频因子由RCC_CFGR:ADCPRE[1:0]设置),最高频限定为14MhZ。而PCLK2 = HCLK = 72M。
采样时间:ADC使用若干个ADC_CLK周期对输入的电压进行采样(采样周期数通过ADC_SMPR1:SMP[2:0]和ADC_SMPR2:SMP[2:0]设置,ADC_SMPR2:SMP[2:0]控制通道10~17,ADC_SMPR1:SMP[2:0]控制通道0~9)。每个通道可以分别用不同的时间采样,最小的采样周期是1.5个(最快采样),采样周期 = 1/ADC_CLK。
转换时间:与ADC_CLK和采样时间有关。Tconv = 采样时间 + 12.5个周期。如当ADC_CLK = 14MHz时,采样时间设置为1.5个周期,则Tconv = 1.5 + 12.5 = 14个周期 = 1us。但一般PCLK2为72M,经过分频后ADC_CLK最大也只是12M,所以当采样时间设置为1.5个周期时,Tconv = 1.5 + 12.5 = 14个周期 = 1.17us。
ADC的数据寄存器
规则通道的数据寄存器存放在ADC_DR寄存器,注入通道的数据寄存器存放在ADC_JDRx寄存器(x=1..4)。
规则通道的数据寄存器(32位)
低16位用于单ADC时,高16位在ADC1中双ADC下保存ADC2转换的规则数据。ADC精度为12位,不管是高16位还是低16位,都放不满,需要指定对齐方式(由ADC_CR2:ALIGN位决定)。
当通道转换完成后就应该立即把数据取走或者开启DMA把数据传输到内存,不然就会造成数据覆盖,最常见做法是开启DMA传输。
注入通道的数据寄存器(32位)
高16位保留,低16位有效。由于各注入组最多4个通道,而注入通道的数据寄存器也有4个,所以不会产生数据覆盖问题。但也需要指定对齐方式(由ADC_CR2:ALIGN位决定)。
中断
规则通道转换结束中断
可以产生规则通道转换结束中断,还可以产生DMA请求,把转换好的数据存储在内存里面。要注意的是,只有ADC1和ADC3可以产生DMA请求。一般我们在使用ADC的时候都会开启DMA传输。
注入通道转换结束中断
可以产生注入通道转换结束中断,还可以产生DMA请求,把转换好的数据存储在内存里面。要注意的是,只有ADC1和ADC3可以产生DMA请求。一般我们在使用ADC的时候都会开启DMA传输。
模拟看门狗中断
转换后的模拟电压低于低阈值或者高于高阈值时,就会产生中断。前提是我们开启了模拟看门狗中断,低阈值和高阈值由 ADC_LTR寄存器和ADC_HTR寄存器设置。
数据读取
可以通过中断读取,通过中断服务函数保存数据。
可以通过DMA读取,保存数据在内存中。
使用中断的优点:读取的数据较少或读取速度较慢的,可以用中断。
使用DMA的优点:速度快。方便,不需要写中断服务函数。
电压转换
模拟电压经过ADC转换后,是一个12位的数字值。在设计原理图时一般把VREF-和VSSA接地,VREF+和VDDA接3.3V,使得ADC的输入范围在0~3.3V。因此12位满量程(2^12)-3.3V,0-0V。
如果转换后的数值为X,X对应的模拟电压为Y,那么:Y/X=3.3/4096。Y=3.3X/4096。
ADC的初始化结构体
typedef struct
{
uint32_t ADC_Mode; //工作模式
FunctionalState ADC_ScanConvMode; //扫描(多通道)or 单次(单通道)模式
FunctionalState ADC_ContinuousConvMode; //连续/单次转换
uint32_t ADC_ExternalTrigConv; //转换触发信号选择
uint32_t ADC_DataAlign; //数据寄存器对齐方式
uint8_t ADC_NbrOfChannel; //采集通道数
}ADC_InitTypeDef;
对于ADC_Mode工作模式(单ADC和双ADC)
#define ADC_Mode_Independent ((uint32_t)0x00000000) //独立模式,一般单ADC时使用。 #define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000) //混合的同步规则+同步注入模式 #define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000) //混合的同步规则+交替触发模式 #define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000) //混合同步注入+快速交叉模式 #define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000) //混合同步注入+慢速交叉模式 #define ADC_Mode_InjecSimult ((uint32_t)0x00050000) //同步注入模式 #define ADC_Mode_RegSimult ((uint32_t)0x00060000) //同步规则模式 #define ADC_Mode_FastInterl ((uint32_t)0x00070000) //快速交叉模式 #define ADC_Mode_SlowInterl ((uint32_t)0x00080000) //慢速交叉模式 #define ADC_Mode_AlterTrig ((uint32_t)0x00090000) //交替触发模式
模式 说明 独立模式 单ADC时使用。或者双模式下每个ADC独立工作。 同步注入模式(双ADC模式) ADC1(主)和ADC2(从)同时转换一个注入通道组。
转换的数据存储在每个ADC接口的ADC_JDRx寄存器中。
同步规则模式(双ADC模式) ADC1(主)和ADC2(从)同时转换一个规则通道组。
ADC1转换的结果存放在ADC1_DR的低16位,ADC2转换的结果存放在ADC1_DR的高16位。
快速交叉模式 ADC1和ADC2交替采集一个规则通道组(通常为一个通道)。
当ADC2触发后,ADC1需要等待7个ADC_CLK才能触发。
慢速交叉模式 ADC1和ADC2交替采集一个规则通道组(只能为一个通道)。
当ADC2触发后,ADC1需要等待14个ADC_CLK才能触发。
交替触发模式 ADC1和ADC2轮流采集注入通道组。
当ADC1所有通道采集完毕后再采集ADC2的通道,如此循环。
跟交叉模式不一样。
混合的同步规则+同步注入模式 规则组同步转换被中断,以启动注入组的同步转换。
分开两个模式来理解就可以了,区别就是注入组可以中断规则组的转换。
混合的同步规则+交替触发模式 规则组同步转换被中断,以启动注入组交替触发转换。
分开两个模式来理解就可以了,区别就是注入组可以中断规则组的转换。
混合的同步注入+交叉模式 交叉转换可以被同步注入模式中断。
这种情况下,交叉转换被中断,以启动注入组的同步转换。
对于ADC_ExternalTrigConv转换触发信号选择
#define ADC_ExternalTrigConv_T1_CC1 ((uint32_t)0x00000000) //对于ADC1和ADC2 #define ADC_ExternalTrigConv_T1_CC2 ((uint32_t)0x00020000) //对于ADC1和ADC2 #define ADC_ExternalTrigConv_T2_CC2 ((uint32_t)0x00060000) //对于ADC1和ADC2 #define ADC_ExternalTrigConv_T3_TRGO ((uint32_t)0x00080000) //对于ADC1和ADC2 #define ADC_ExternalTrigConv_T4_CC4 ((uint32_t)0x000A0000) //对于ADC1和ADC2 #define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((uint32_t)0x000C0000) //对于ADC1和ADC2 #define ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000) //对于ADC1、ADC2和ADC3 #define ADC_ExternalTrigConv_None ((uint32_t)0x000E0000) //对于ADC1、ADC2和ADC3,不需要外部触发,软件来开启 #define ADC_ExternalTrigConv_T3_CC1 ((uint32_t)0x00000000) //对于ADC3 #define ADC_ExternalTrigConv_T2_CC3 ((uint32_t)0x00020000) //对于ADC3 #define ADC_ExternalTrigConv_T8_CC1 ((uint32_t)0x00060000) //对于ADC3 #define ADC_ExternalTrigConv_T8_TRGO ((uint32_t)0x00080000) //对于ADC3 #define ADC_ExternalTrigConv_T5_CC1 ((uint32_t)0x000A0000) //对于ADC3 #define ADC_ExternalTrigConv_T5_CC3 ((uint32_t)0x000C0000) //对于ADC3
ADC的常见配置流程
初始化ADC的GPIO。
初始化ADC初始化结构体。
配置ADC时钟,配置通道的转换顺序和采样时间。
RCC_ADCCLKConfig(RCC_PCLK2_Div8); ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);
使能ADC转换完成中断,配置中断优先级。
ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE);
使能ADC,准备开始转换。
校准ADC。
// 初始化ADC 校准寄存器 ADC_ResetCalibration(ADCx); // 等待校准寄存器初始化完成 while(ADC_GetResetCalibrationStatus(ADCx)); // ADC开始校准 ADC_StartCalibration(ADCx); // 等待校准完成 while(ADC_GetCalibrationStatus(ADCx));
软件触发ADC,真正开始转换。
ADC_SoftwareStartConvCmd(ADCx, ENABLE);
编写中断服务函数,读取ADC转换数据。
编写main函数,把转换的数据打印出来。
HAL库_DMA
DMA配置
DMA_HandleTypeDef hdma_adcx;
ADC_HandleTypeDef ADC_Handle;
ADC_ChannelConfTypeDef ADC_Config;
static void Rheostat_ADC_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitStructure.Pin = GPIO_PIN_1;
GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
GPIO_InitStructure.Pull = GPIO_NOPULL ;
HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
}
static void Rheostat_ADC_Mode_Config(void)
{
// ------------------DMA Init 结构体参数 初始化--------------------------
__HAL_RCC_DMA1_CLK_ENABLE();
// 数据传输通道
hdma_adcx.Instance = DMA1_Channel1;
hdma_adcx.Init.Direction=DMA_PERIPH_TO_MEMORY;; //存储器到外设
hdma_adcx.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
hdma_adcx.Init.MemInc=DMA_MINC_DISABLE; //存储器增量模式
hdma_adcx.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;//外设数据长度:16位
hdma_adcx.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位
hdma_adcx.Init.Mode= DMA_CIRCULAR; //外设普通模式
hdma_adcx.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
//初始化DMA流,流相当于一个大的管道,管道里面有很多通道
HAL_DMA_Init(&hdma_adcx);
//将DMA句柄关联到ADC句柄的hdma_adcx
__HAL_LINKDMA( &ADC_Handle,hdma_adcx,DMA_Handle);
//---------------------------------------------------------------------------
RCC_PeriphCLKInitTypeDef ADC_CLKInit;
// 开启ADC时钟
ADC_CLKInit.PeriphClockSelection=RCC_PERIPHCLK_ADC; //ADC外设时钟
ADC_CLKInit.AdcClockSelection=RCC_ADCPCLK2_DIV8; //分频因子6时钟为72M/8=9MHz
HAL_RCCEx_PeriphCLKConfig(&ADC_CLKInit); //设置ADC时钟
ADC_Handle.Instance=ADC1;
ADC_Handle.Init.DataAlign=ADC_DATAALIGN_RIGHT; //右对齐
ADC_Handle.Init.ScanConvMode=DISABLE; //非扫描模式
ADC_Handle.Init.ContinuousConvMode=ENABLE; //连续转换
ADC_Handle.Init.NbrOfConversion=1; //1个转换在规则序列中 也就是只转换规则序列1
ADC_Handle.Init.DiscontinuousConvMode=DISABLE; //禁止不连续采样模式
ADC_Handle.Init.NbrOfDiscConversion=0; //不连续采样通道数为0
ADC_Handle.Init.ExternalTrigConv=ADC_SOFTWARE_START; //软件触发
HAL_ADC_Init(&ADC_Handle); //初始化
//---------------------------------------------------------------------------
ADC_Config.Channel = RHEOSTAT_ADC_CHANNEL;
ADC_Config.Rank = 1;
// 采样时间间隔
ADC_Config.SamplingTime = ADC_SAMPLETIME_55CYCLES_5 ;
// 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期
HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);
HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t*)&ADC_ConvertedValue, 1);
}
DMA结构体
typedef struct
{
uint32_t DataAlign;
uint32_t ScanConvMode;
uint32_t ContinuousConvMode;
uint32_t NbrOfConversion;
uint32_t DiscontinuousConvMode;
uint32_t NbrOfDiscConversion;
uint32_t ExternalTrigConv;
}ADC_InitTypeDef;
typedef struct
{
uint32_t Channel;
uint32_t Rank;
uint32_t SamplingTime;
}ADC_ChannelConfTypeDef;
typedef struct
{
uint32_t WatchdogMode;
uint32_t Channel;
uint32_t ITMode;
uint32_t HighThreshold;
uint32_t LowThreshold;
uint32_t WatchdogNumber;
}ADC_AnalogWDGConfTypeDef;
typedef struct
{
ADC_TypeDef *Instance;
ADC_InitTypeDef Init;
DMA_HandleTypeDef *DMA_Handle;
HAL_LockTypeDef Lock;
__IO uint32_t State;
__IO uint32_t ErrorCode;
}ADC_HandleTypeDef;
DMA函数
__HAL_ADC_ENABLE(__HANDLE__);
__HAL_ADC_DISABLE(__HANDLE__);
//__HANDLE__,ADC_IT_EOC|ADC_IT_JEOC|ADC_IT_AWD
__HAL_ADC_ENABLE_IT(__HANDLE__, __INTERRUPT__);
__HAL_ADC_DISABLE_IT(__HANDLE__, __INTERRUPT__);
__HAL_ADC_GET_IT_SOURCE(__HANDLE__, __INTERRUPT__);
//__HANDLE__,ADC_FLAG_STRT|ADC_FLAG_JSTRT|ADC_FLAG_EOC|ADC_FLAG_JEOC|ADC_FLAG_AWD
__HAL_ADC_GET_FLAG(__HANDLE__, __FLAG__);
__HAL_ADC_CLEAR_FLAG(__HANDLE__, __FLAG__);
__HAL_ADC_RESET_HANDLE_STATE(__HANDLE__);
ADC_IS_ENABLE(__HANDLE__);
HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_DeInit(ADC_HandleTypeDef *hadc);
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc);
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout);
HAL_StatusTypeDef HAL_ADC_PollForEvent(ADC_HandleTypeDef* hadc, uint32_t EventType, uint32_t Timeout);
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
void HAL_ADC_IRQHandler(ADC_HandleTypeDef* hadc);
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc);
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc);
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc);
HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef* hadc, ADC_ChannelConfTypeDef* sConfig);
HAL_StatusTypeDef HAL_ADC_AnalogWDGConfig(ADC_HandleTypeDef* hadc, ADC_AnalogWDGConfTypeDef* AnalogWDGConfig);
uint32_t HAL_ADC_GetState(ADC_HandleTypeDef* hadc);
uint32_t HAL_ADC_GetError(ADC_HandleTypeDef *hadc);
HAL_StatusTypeDef ADC_Enable(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef ADC_ConversionStop_Disable(ADC_HandleTypeDef* hadc);
void ADC_StabilizationTime(uint32_t DelayUs);
void ADC_DMAConvCplt(DMA_HandleTypeDef *hdma);
void ADC_DMAHalfConvCplt(DMA_HandleTypeDef *hdma);
void ADC_DMAError(DMA_HandleTypeDef *hdma);