STM32-ADC

目录

ADC框图

ADC的输入范围:Vref- < Vin < Vref+

ADC的输入通道(对应表)

ADC的外部通道:规则通道、注入通道

ADC的转换序列(规则通道、注入通道)

ADC的转换控制

ADC的转换时间

ADC的数据寄存器

规则通道的数据寄存器(32位)

注入通道的数据寄存器(32位)

中断

规则通道转换结束中断

注入通道转换结束中断

模拟看门狗中断

数据读取

电压转换

ADC的初始化结构体

ADC的常见配置流程

HAL库_DMA

DMA配置

DMA结构体

DMA函数


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);
  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值