Keil MDK STM32系列(六) 基于抽象外设库HAL的ADC模数转换

发布者:快乐的天使最新更新时间:2022-08-15 来源: csdn关键字:Keil  MDK  STM32系列  ADC  模数转换 手机看文章 扫描二维码
随时随地手机看文章

配置 ADC

模式: 如果只启用了一个ADC, 这里只能配置为Independent mode

时钟分频: 这个选项是ADC的预分频器, 可设置为2/4/6/8, 决定了一个ADC时钟周期. 加入设置为2, 由于ADC是挂载在APB2总线(84M)上, 所以一个ADC时钟便是84 * M/2=42M

分辨率: 最高为12位分辨率, 分辨率越高转换时间越长

数据对齐方式: 如果选择12位分辨率, 右对齐, 得到的结果最大便是4096.

扫描模式: 转换完一个通道会不会继续转换下一个通道

连续转换模式: 使能的话转换将连续进行

不连续转换模式: 当使能多个转换通道时, 可单独设置不连续转换通道.

DMA连续请求: 是否连续请求DMA.

EOC标志设置: 当有多个转换通道时, 是每转换完一个通道设置一次EOC标志还是所有通道都转换完设置一次EOC标志.

转换的通道数:

触发模式: 可选择软件触发, 外部触发或定时器事件触发

秩序列表: 设置转换周期数和转换顺序

注入通道设置

窗口看门狗模式

配置 ADC 为主动请求模式

while (1)

{

  /*##-1- Start the conversion process #######################################*/  

  HAL_ADC_Start(&hadc1);


  /*##-2- Wait for the end of conversion #####################################*/  

   /*  Before starting a new conversion, you need to check the current state of 

              the peripheral; if it’s busy you need to wait for the end of current

              conversion before starting a new one.

              For simplicity reasons, this example is just waiting till the end of the 

              conversion, but application may perform other tasks while conversion 

              operation is ongoing. */

  HAL_ADC_PollForConversion(&hadc1, 50);


  /* Check if the continous conversion of regular channel is finished */  

  if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC)) 

  {

      /*##-3- Get the converted value of regular channel  ######################*/

      AD_Value = HAL_ADC_GetValue(&hadc1);

      printf("MCU Temperature : %.1f¡ærn",((AD_Value*3300/4096-760)/2.5+25));

  }

  HAL_Delay(1000);

}

配置 ADC 为多通道连续扫描DMA模式

开ADC的IN0/IN1两个通道


在Pinout图上, 将PA0和PA1设为ADC1_IN0和ADC2_IN1

配置时钟


ADC1相关配置


ADCs_Common_Settings


Mode: Independent mode

ADC_Settings


Clock Prescaler: PCLK2 divided by 4 可以在时钟配置页看到PCLK2的值

Resolution: 12bits (15 ADC Clock cycles) 采样精度12bit, 此时每次采样需要15个时钟周期, 8bit对应11个时钟周期

Data Alignment: Right alignment

Scan Conversion Mode: Enabled

Continuous Conversion Mode: Enabled --> for DMA

Discontinuous Conversion Mode: Disabled

DMA Continuous Requests: Enabled

End Of Conversion Selection: EOC flag at the end of single channel conversion

ADC_Regular_ConversionMode


Number of Conversion: 2 --> 2 channels

External Trigger Conversion Source: Regular Conversion launched by software

External Trigger Conversion Edge: None

Rank: 1: Choose channel 0

Rank: 2: Choose channel 1

ADC_Injected_ConversionMode


Number of Conversions: 0

DMA相关配置


ADC1


Stream: DMA2 Stream 4

Direction: Peripheral To Memory

Priority: High

DMA Request Settings


Mode: Circular

Increment Address: Memory

Datawidth: Peripheral->Half Word, Memory->Half Word

NVIC Settings


ADC1 global interrupt: Enabled unchecked

DMA2 stream4 global interrupt: Enabled checked

ADC+DMA配置, 体现在代码上的变化

stm32f4xx_hal_conf.h 去掉了ADC的注释

#define HAL_ADC_MODULE_ENABLED

stm32f4xx_it.h 增加了方法声明

void DMA2_Stream4_IRQHandler(void);

stm32f4xx_it.c 增加了对应的typeDef和方法定义

extern DMA_HandleTypeDef hdma_adc1;


void DMA2_Stream4_IRQHandler(void)

{

  HAL_DMA_IRQHandler(&hdma_adc1);

}

stm32f4xx_hal_msp.c

extern DMA_HandleTypeDef hdma_adc1;


void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)

{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  if(hadc->Instance==ADC1)

  {

    __HAL_RCC_ADC1_CLK_ENABLE();


    __HAL_RCC_GPIOA_CLK_ENABLE();

    /**ADC1 GPIO Configuration

    PA0-WKUP     ------> ADC1_IN0

    PA1     ------> ADC1_IN1

    */

    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;

    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

    GPIO_InitStruct.Pull = GPIO_NOPULL;

    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


    /* ADC1 DMA Init */

    /* ADC1 Init */

    hdma_adc1.Instance = DMA2_Stream4;

    hdma_adc1.Init.Channel = DMA_CHANNEL_0;

    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;

    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;

    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;

    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

    hdma_adc1.Init.Mode = DMA_CIRCULAR;

    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;

    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)

    {

      Error_Handler();

    }


    __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

  }


}


void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc)

{

  if(hadc->Instance==ADC1)

  {

    /* Peripheral clock disable */

    __HAL_RCC_ADC1_CLK_DISABLE();


    /**ADC1 GPIO Configuration

    PA0-WKUP     ------> ADC1_IN0

    PA1     ------> ADC1_IN1

    */

    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0|GPIO_PIN_1);


    /* ADC1 DMA DeInit */

    HAL_DMA_DeInit(hadc->DMA_Handle);

  }


}

main.c

ADC_HandleTypeDef hadc1;

DMA_HandleTypeDef hdma_adc1;



static void MX_ADC1_Init(void)

{

  ADC_ChannelConfTypeDef sConfig = {0};


  hadc1.Instance = ADC1;

  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;

  hadc1.Init.Resolution = ADC_RESOLUTION_12B;

  hadc1.Init.ScanConvMode = ENABLE;

  hadc1.Init.ContinuousConvMode = ENABLE;

  hadc1.Init.DiscontinuousConvMode = DISABLE;

  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;

  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;

  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

  hadc1.Init.NbrOfConversion = 2;

  hadc1.Init.DMAContinuousRequests = ENABLE;

  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

  if (HAL_ADC_Init(&hadc1) != HAL_OK)

  {

    Error_Handler();

  }

  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.

  */

  sConfig.Channel = ADC_CHANNEL_0;

  sConfig.Rank = 1;

  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;

  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

  {

    Error_Handler();

  }

  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.

  */

  sConfig.Channel = ADC_CHANNEL_1;

  sConfig.Rank = 2;

  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

  {

    Error_Handler();

  }

}


/**

  * Enable DMA controller clock

  */

static void MX_DMA_Init(void)

{


  __HAL_RCC_DMA2_CLK_ENABLE();


  HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 0, 0);

  HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);

}



最后, 在main.c中增加用于存储DMA数据的数组, 将数组地址传给HAL_ADC_Start_DMA()开启DMA传输就可以得到数据了.


DMA数组大小和中断的问题

数组的大小与HAL_ADC_Start_DMA()方法第三个参数length一致, 这里length代表的是数据的个数. 在设置这个大小时, 如果开启了DMAx_Streamx_IRQn的中断, 要考虑sConfig.SamplingTime指定的采样时间不能太短, 太短的话会一直卡在中断里(因为中断什么都不做也需要时间). 这个与SYSCLK大小无关, 在两个通道采样时


如果这里指定的值为ADC_SAMPLETIME_3CYCLES, 这个数组大小至少为6, 如果等于4采样循环会卡住

如果指定的值为ADC_SAMPLETIME_15CYCLES, 这个数组大小至少为4

如果指定的值为ADC_SAMPLETIME_28CYCLES, 数组大小可以为2

如果不需要使用DMA中断, 可以在 MX_DMA_Init()方法中, 将HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);这句注释掉或者改成HAL_NVIC_DisableIRQ(DMA2_Stream4_IRQn);指定禁用它, 这个数组就可以设到最小(和采样通道数一致)了.


uint16_t ADC_Value[6];


main(void) {

   HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC_Value, 6);    // Enable DMA transfer

   while (1)

  {

    printf("%d %d %d %drn", 

      ADC_Value[0], ADC_Value[1], ADC_Value[2], ADC_Value[3]);

    HAL_Delay(100);

  }

}


DMA中断处理回调

查看代码可以看到, 在stm32f4xxx_hal_dma.h中, 定义的 DMA_HandleTypeDef 类型中, 包含了几个对应中断的处理方法


typedef struct __DMA_HandleTypeDef

{

  DMA_Stream_TypeDef         *Instance;                                                        /*!< Register base address                  */

  DMA_InitTypeDef            Init;                                                             /*!< DMA communication parameters           */ 

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

  __IO HAL_DMA_StateTypeDef  State;                                                            /*!< DMA transfer state                     */

  void                       *Parent;                                                          /*!< Parent object state                    */ 

  void                       (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);         /*!< DMA transfer complete callback         */

[1] [2]
关键字:Keil  MDK  STM32系列  ADC  模数转换 引用地址:Keil MDK STM32系列(六) 基于抽象外设库HAL的ADC模数转换

上一篇:STM32F401+nRF24L01无线传输音频(对讲机原型)
下一篇:Keil MDK STM32系列(七) STM32F4基于HAL的PWM和定时器

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多每日新闻

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved