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¡æ\r\n",((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配置, 体现在代码上的变化

  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\r\n",
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传输完成的中断

参考

Keil MDK STM32系列(六) 基于抽象外设库HAL的ADC模数转换的更多相关文章

  1. Keil MDK STM32系列(四) 基于抽象外设库HAL的STM32F401开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  2. Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  3. Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  4. Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  5. Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  6. Keil MDK STM32系列(七) STM32F4基于HAL的PWM和定时器

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  7. Keil MDK STM32系列(八) STM32F4基于HAL的PWM和定时器输出音频

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  8. Keil MDK STM32系列(五) 使用STM32CubeMX创建项目基础结构

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  9. AIR32F103(三) Linux环境基于标准外设库的项目模板

    目录 AIR32F103(一) 合宙AIR32F103CBT6开发板上手报告 AIR32F103(二) Linux环境和LibOpenCM3项目模板 AIR32F103(三) Linux环境基于标准外 ...

随机推荐

  1. 大小端(内存、寄存器、CPU)

    CPU能进行32位操作,关键是寄存器有32位,数据总线也有32位. 为了表示方便,我们可以把32位寄存器从左到右,与内存中一个双字的四个字节地址从低到高对应. 如果CPU把寄存器的左端定义为高位,则带 ...

  2. 资源分享 | PyTea:不用运行代码,静态分析pytorch模型的错误

    ​  前言  ​​​​​​​本文介绍一个Pytorch模型的静态分析器 PyTea,它不需要运行代码,即可在几秒钟之内扫描分析出模型中的张量形状错误.文末附使用方法. 本文转载自机器之心 编辑:CV技 ...

  3. vue项目中Webpack-dev-server的proxy用法

    问题:在VUE项目中,需要请求后台接口获取数据,这时往往会出现跨域问题 解决方法:在vue.config.js中devServer配置proxy 常用的场景 1. 请求/api/XXX现在都会代理到请 ...

  4. Kafka安装Kafka-Eagle可视化界面

    要先安装jdk 可以参考:https://www.cnblogs.com/pxblog/p/10512886.html 下载 http://download.kafka-eagle.org/ 上传到服 ...

  5. soui(1)之一个半透明的窗口

    一个样式 xml源码 <SOUI name="mainWindow" title="@string/title" bigIcon="ICON_L ...

  6. 【LeetCode】981. Time Based Key-Value Store 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcod ...

  7. 【LeetCode】106. Construct Binary Tree from Inorder and Postorder Traversal 解题报告

    [LeetCode]106. Construct Binary Tree from Inorder and Postorder Traversal 解题报告(Python) 标签: LeetCode ...

  8. 【LeetCode】343. Integer Break 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 数学解法 动态规划 日期 题目地址:https:// ...

  9. 1217 - Neighbor House (II)

    1217 - Neighbor House (II)    PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 3 ...

  10. 线程 IO流 网络编程 基础总结

    线程 进程---->进行中的程序 线程---->由进程创建 一个进程可以创建多个线程 并发:同一个时刻 多个任务交替执行 造成一种貌似同时进行的错觉 简单来说 单个cpu的多任务就是并发 ...