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


    Keil MDK STM32系列

    配置 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¡æ
    ",((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
      • Data Peripheral->Half Word, Memory->Half Word
    • NVIC Settings

      • ADC1 global interrupt: Enabled unchecked
      • DMA2 stream4 global interrupt: Enabled checked

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

    1. stm32f4xx_hal_conf.h 去掉了ADC的注释
    #define HAL_ADC_MODULE_ENABLED
    
    1. stm32f4xx_it.h 增加了方法声明
    void DMA2_Stream4_IRQHandler(void);
    
    1. stm32f4xx_it.c 增加了对应的typeDef和方法定义
    extern DMA_HandleTypeDef hdma_adc1;
    
    void DMA2_Stream4_IRQHandler(void)
    {
      HAL_DMA_IRQHandler(&hdma_adc1);
    }
    
    1. 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);
      }
    
    }
    
    1. 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 %d
    ", 
          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         */
      void                       (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);     /*!< DMA Half transfer complete callback    */
      void                       (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma);       /*!< DMA transfer complete Memory1 callback */
      void                       (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);   /*!< DMA transfer Half complete Memory1 callback */
      void                       (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);        /*!< DMA transfer error callback            */
      void                       (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma);        /*!< DMA transfer Abort callback            */  
      __IO uint32_t              ErrorCode;                                                        /*!< DMA Error code                          */
      uint32_t                   StreamBaseAddress;                                                /*!< DMA Stream Base Address                */
      uint32_t                   StreamIndex;                                                      /*!< DMA Stream Index                       */
    }DMA_HandleTypeDef;
    

    其中处理接收完成的方法是 XferCpltCallback , 这个在 stm32f4xx_hal_adc.c 中, 被指定为相应的静态方法

    stm32f4xx_hal_adc.c

    HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)
    {
      //...
      /* Set the DMA transfer complete callback */
      hadc->DMA_Handle->XferCpltCallback = ADC_DMAConvCplt;
    

    对应不同外设, 指定的方法是不同的, 例如对于uart, stm32f4xx_hal_uart.c中指定的是

    HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    {
      //...
      /* Set the UART DMA transfer complete callback */
      huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;
    

    对于ADC, 再进一步在ADC_DMAConvCplt()方法中定义了处理方法

    static void ADC_DMAConvCplt(DMA_HandleTypeDef *hdma)   
    {
      //...
        /* Conversion complete callback */
    #if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
        hadc->ConvCpltCallback(hadc);
    #else
        HAL_ADC_ConvCpltCallback(hadc);
    #endif /* USE_HAL_ADC_REGISTER_CALLBACKS */
      }
      else /* DMA and-or internal error occurred */
      {
        if ((hadc->State & HAL_ADC_STATE_ERROR_INTERNAL) != 0UL)
        {
          /* Call HAL ADC Error Callback function */
    #if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
          hadc->ErrorCallback(hadc);
    #else
          HAL_ADC_ErrorCallback(hadc);
    #endif /* USE_HAL_ADC_REGISTER_CALLBACKS */
        }
      else
      {
          /* Call DMA error callback */
          hadc->DMA_Handle->XferErrorCallback(hdma);
        }
      }
    }
    

    所以, 开发时只需要定义 HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) 和 HAL_ADC_ErrorCallback(ADC_HandleTypeDef* hadc)方法, 就能处理DMA传输完成的中断

    参考

  • 相关阅读:
    如何将你的AIR程序正常显示在iPhone5上?
    升级Flash Builder 4.7中的AIR SDK
    Flex AIR加载时全屏显示
    在Mac系统的安装Flash Builder 4.7开发工具
    What's XPath?
    XPath节点
    What's XML?
    65个源码下载网站
    [导入]轻松掌握Ajax.net系列教程
    MemberShip 示例
  • 原文地址:https://www.cnblogs.com/milton/p/15315791.html
Copyright © 2020-2023  润新知