stm32.cube(四)——HAL.ADC

一、Adc特性

1.1 Adc概述

Stm32的Adc具有12位的精度,共有16个外部通道和2个内部通道。不同通道的 A/D 转换可以在单一、连续、扫描或者间断模式下进行。它的其他特性还包括支持模拟看门狗和DMA。

1.2 Adc初始化

和大多数外设一样,Adc在使用前必须初始化时钟源,并从掉电模式唤醒该设备。建议在初始化Adc后立即运行一次校准,以减少准确性错误。

1.3 通道的选择

对于16个可复用的通道,可以将通道分成两种类型的组。常规组和注入组,组序列保存在寄存器ADC_SQRx和ADC_JSQR中。常规组可以包含最多16个通道,注入组最多包含4个通道。

注入组可以理解为常规组的一种中断,当注入组的采集被触发时,常规组的采集会被中断。直到注入组采集完之后,常规组才开始继续采集。

如果只想采集一个通道的数值,只将一个通道写入组里。想采集多个通道的数值,就将多个通道写入组里。当一个组包含多个通道时,要开启扫描模式, adc对组中的每一个通道根据寄存器里的序列进行一次转换。

1.4 触发方式

要触发一次ad转换,可以由内部软件触发,或者外部触发。要不要使用外部触发由控制寄存器里的EXTTRIG位来指定。

内部触发自然是通过写控制寄存器里的相应位来触发。而外部触发则可以有八种触发源可供选择,所以常规组和注入组在控制寄存器里各有3个位来指定哪个外部事件可触ad转换。外部触发源一般是定时器或者是外部中断线事件。

1.5 单一和连续

一次采集被由内部或者外部触发后,可以只采集一次,也可以一直连续采集。如果是一组通道,采集完最后一个通道时,会回到序列中的第一个通道继续采集。

每次采集完成后都会将转换后的数据存在一个寄存器里,并置位采集完成位,根据设定产生中断。

1.6 间断模式

间断模式是将常规和注入组里的序列再切割成更小的组。

比如一个常规组含有9个频道,利用间断模式并设置控制寄存器的位,可以将9个频道分成3组。这样一次触发只会采集3个通道,而不是采集9个通道,连续触发3次才能采集完9个通道。

1.7 采样频率

Adc模块允许对采样频率进行修改,以满足对采样速率的不同需求。考虑到Adc时序里需要一个稳定期,实际使用时还要面临各种DMA请求和中断嵌套,建议不要使用过高的采样频率,并考虑系统运行时负载的临界值。

可以通过修改ADC_SMPR1 和 ADC_SMPR2 寄存器中的 SMP[2:0]来设置采样频率。

1.8 模拟看门狗

设置相应的寄存器可以为Adc指定上下阀值,当采集到电压值超过阀值时,系统会产生中断。

1.9 DMA

当在扫描模式和连续模式下,显然每次都将保存采集值的寄存器数据读出来处理是来不及的。设置Adc的DMA使能,可以让每次转换过的数值都经DMA传到指定的内存空间里。

1.10 字节序

对于转换后的数据,可以设置左对齐或是右对齐,以支持不同的处理和传输的需要。

1.11 双adc模式

具有两个adc的设备可以使用的模式。在这种模式下,通常adc1做主设备,adc2做从设备。从设备的触发有主设备来发起。

主设备的常规组/注入组的触发成为从设备触发的条件,而主设备可以被设置为等待从设备采集一段时间后才开始采集,双adc模式下有许多种方式可以支持不同的主从同步。

二、Adc的库函数

HAL里的Adc函数相较stmlib里的结构更加简单,它的私有成员就不做叙述了,因为如何实现HAL的结构性代码并不是我们关心的问题。这里只描述一下Adc的输出函数。

2.1 初始化函数

同GPIO的初始化一样,Adc的初始化使用几个结构体来进行。不同的是由于Adc较之GPIO更加复杂,所以对Adc属性的描述分成了几个不同的结构体。

ADC_InitTypeDef结构体原型

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;

从上到下依次描述:

字节序、是否使用扫描模式、单一/连续模式、常规组序列、是否使用间断模式、间断模式中一个组的通道数量、外部触发的方式。

HAL_ADC_init函数的参数是一个指向ADC_HandleTypeDef结构体的指针,这个结构体的第一个元素,就是一个指向ADC_InitTypeDef结构体的指针。所以初始化ADC前,要先定义一个ADC_InitTypeDef结构体,赋值给每个成员变量。然后把它的地址写入一个ADC_HandleTypeDef结构体的成员。

ADC_InitTypeDef结构体原型

/** 
  * @brief  ADC handle Structure definition  
  */ 
typedef struct
{
  ADC_TypeDef                   *Instance;              /*!< Register base address */

  ADC_InitTypeDef               Init;                   /*!< ADC required parameters */

  __IO uint32_t                 NbrOfConversionRank ;   /*!< ADC conversion rank counter */

  DMA_HandleTypeDef             *DMA_Handle;            /*!< Pointer DMA Handler */

  HAL_LockTypeDef               Lock;                   /*!< ADC locking object */

  __IO HAL_ADC_StateTypeDef     State;                  /*!< ADC communication state */

  __IO uint32_t                 ErrorCode;              /*!< ADC Error code */
}ADC_HandleTypeDef;

可以看到它除了包含一个指向ADC_InitTypeDef的指针之外,还包含指向寄存器的指针、互斥锁、一个描述状态的变量、一个保存错误代码的变量、一个指向DMA结构体的指针。所有与对Adc进行操作的函数都使用这个结构体的指针作为参数。

HAL有一个自己的锁,这样即使程序员在多个线程里没有使用信号量来对外设进行读写控制,HAL层也会独立保证对外设进行特殊操作时的原子性。

ADC_InitTypeDef结构体只描述了一部分Adc的属性。所以有ADC_ChannelConfTypeDef结构体来描述单个通道在组序列里的排序位置及它的工作速率,ADC_AnalogWDFConfTypeDef结构体来描述一个模拟看门狗的阀值和中断模式。而HAL_ADC_ConfigChannel和HAL_ADC_AnalogWDGconfig()函数用来处理这两个结构体。

在初始化Adc时钟和使能设备后,至少需要设置四个结构体,调用三个函数,才能将Adc初始化完毕。

2.2 Adc的操作

开始与结束

HAL_StatusTypeDef       HAL_ADC_Start(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef       HAL_ADC_Stop(ADC_HandleTypeDef* hadc);

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);

HAL_ADC_START()、HAL_ADC_START_DMA()、HAL_ADC_START_IT()三个函数分别表示开始一次采集(直接软触发或者等待外部触发),后两个函数表示数据用DMA传输和使能中断。中断处理函数是HAL_ADC_IRQHander()。

callback函数

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);

Adc中断、DMA传输、看门狗超过阀值、发生Adc错误,这些函数返回前都调用了Callback函数,用来在非中断模式下处理Adc数据,如果想进行一些操作可以直接修改Callback函数。

取转换后的数值

/* ADC retrieve conversion value intended to be used with polling or interruption */
uint32_t                HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);

HAL_ADC_GetValue()用来区寄存器值。

等待函数

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);

在非中断和DMA模式下,采集数据需要等待一次采集的完成,或者某些事件的发生。HAL_ADC_PollForConversion函数和HAL_ADC_PollForEvent函数用来等待这两者。

三、例子

只用一个通道采集的初始化例子

static void ADC_Config(void)
{
  ADC_ChannelConfTypeDef   sConfig;
  ADC_AnalogWDGConfTypeDef AnalogWDGConfig;

  /* Configuration of ADCx init structure: ADC parameters and regular group */
  AdcHandle.Instance = ADCx;

  AdcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;
  AdcHandle.Init.ScanConvMode          = ADC_SCAN_DISABLE;              /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
#if defined ADC_TRIGGER_FROM_TIMER
  AdcHandle.Init.ContinuousConvMode    = DISABLE;   HAL_ADC_Init                    /* Continuous mode disabled to have only 1 conversion at each conversion trig */
#else
  AdcHandle.Init.ContinuousConvMode    = ENABLE;                        /* Continuous mode to have maximum conversion speed (no delay between conversions) */
#endif
  AdcHandle.Init.NbrOfConversion       = 1;                             /* Parameter discarded because sequencer is disabled */
  AdcHandle.Init.DiscontinuousConvMode = DISABLE;                       /* Parameter discarded because sequencer is disabled */
  AdcHandle.Init.NbrOfDiscConversion   = 1;                             /* Parameter discarded because sequencer is disabled */
#if defined ADC_TRIGGER_FROM_TIMER
  AdcHandle.Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_Tx_TRGO;  /* Trig of conversion start done by external event */
#else
  AdcHandle.Init.ExternalTrigConv      = ADC_SOFTWARE_START;            /* Software start to trig the 1st conversion manually, without external event */
#endif

  if (HAL_ADC_Init(&AdcHandle) != HAL_OK)
  {
    /* ADC initialization error */
    Error_Handler();
  }

  /* Configuration of channel on ADCx regular group on sequencer rank 1 */
  /* Note: Considering IT occurring after each ADC conversion if ADC          */
  /*       conversion is out of the analog watchdog window selected (ADC IT   */
  /*       enabled), select sampling time and ADC clock with sufficient       */
  /*       duration to not create an overhead situation in IRQHandler.        */
  sConfig.Channel      = ADCx_CHANNELa;
  sConfig.Rank         = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;

  if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
  {
    /* Channel Configuration Error */
    Error_Handler();
  }

  /* Set analog watchdog thresholds in order to be between steps of DAC       */
  /* voltage.                                                                 */
  /*  - High threshold: between DAC steps 1/2 and 3/4 of full range:          */
  /*                    5/8 of full range (4095 <=> Vdda=3.3V): 2559<=> 2.06V */
  /*  - Low threshold:  between DAC steps 0 and 1/4 of full range:            */
  /*                    1/8 of full range (4095 <=> Vdda=3.3V): 512 <=> 0.41V */

  /* Analog watchdog 1 configuration */
  AnalogWDGConfig.WatchdogMode = ADC_ANALOGWATCHDOG_ALL_REG;
  AnalogWDGConfig.Channel = ADCx_CHANNELa;
  AnalogWDGConfig.ITMode = ENABLE;
  AnalogWDGConfig.HighThreshold = (RANGE_12BITS * 5/8);
  AnalogWDGConfig.LowThreshold = (RANGE_12BITS * 1/8);
  if (HAL_ADC_AnalogWDGConfig(&AdcHandle, &AnalogWDGConfig) != HAL_OK)
  {
    /* Channel Configuration Error */
    Error_Handler();
  }

}

你可能感兴趣的:(嵌入式)