-
2022-02-15 11:40:07
ADC_F450.cpp
#include "Adc_F450.hpp" #include "main.h" #include <rtthread.h> /* STM32 所用管脚和ADC通道 PA4 --- ADC1_IN4 --- ADC24V 有 PA6 --- ADC1_IN6 --- ADCI24V 有 PC4 --- ADC1_IN14 --- ADC HALL1 有 PC5 --- ADC1_IN15 --- ADC HALL2 有 PC0 --- ADC1_IN10 --- ADC 5V 有 PC1 --- ADC1_IN11 --- ADC 3.3V 4GV 无 */ void Adc_F450::Init() { /* system clocks configuration */ /* enable GPIOC clock */ rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOC); /* enable ADC clock */ rcu_periph_clock_enable(RCU_ADC0); /* enable DMA clock */ rcu_periph_clock_enable(RCU_DMA1); /* config ADC clock */ adc_clock_config(ADC_ADCCK_PCLK2_DIV8); /* GPIO configuration */ /* config the GPIO as analog mode */ gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_4|GPIO_PIN_6); gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5); /* DMA configuration */ /* ADC_DMA_channel configuration */ dma_single_data_parameter_struct dma_single_data_parameter; /* ADC DMA_channel configuration */ dma_deinit(DMA1, DMA_CH0); /* initialize DMA single data mode */ dma_single_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0)); dma_single_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_single_data_parameter.memory0_addr = (uint32_t)adcValueU; dma_single_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_32BIT; dma_single_data_parameter.direction = DMA_PERIPH_TO_MEMORY; dma_single_data_parameter.number = 6; dma_single_data_parameter.priority = DMA_PRIORITY_HIGH; dma_single_data_mode_init(DMA1, DMA_CH0, &dma_single_data_parameter); dma_channel_subperipheral_select(DMA1, DMA_CH0, DMA_SUBPERI0); /* enable DMA circulation mode */ dma_circulation_enable(DMA1, DMA_CH0); /* enable DMA channel */ dma_channel_enable(DMA1, DMA_CH0); /* ADC configuration */ /* ADC mode config */ adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT); adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE); adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE); adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); /* ADC channel length config */ adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 6); /* ADC regular channel config */ adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_10, ADC_SAMPLETIME_15); adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_11, ADC_SAMPLETIME_15); adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_4, ADC_SAMPLETIME_15); adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_6, ADC_SAMPLETIME_15); adc_regular_channel_config(ADC0, 4, ADC_CHANNEL_14, ADC_SAMPLETIME_15); adc_regular_channel_config(ADC0, 5, ADC_CHANNEL_15, ADC_SAMPLETIME_15); /* ADC trigger config */ adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_T0_CH0); adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE); /* ADC DMA function enable */ adc_dma_request_after_last_enable(ADC0); adc_dma_mode_enable(ADC0); /* enable ADC interface */ adc_enable(ADC0); /* wait for ADC stability */ rt_thread_mdelay(1); /* ADC calibration and reset calibration */ adc_calibration_enable(ADC0); /* enable ADC software trigger */ adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); } void Adc_F450::SetRatio(uint8_t n, float _ratio) { adcRatio[n] = _ratio; } void Adc_F450::PeriodRun(void) { uint8_t i; for(i = 0; i < 6; i++) { adcValueF[i] = adcValueU[i] * adcRatio[i]; } }
ADC_F450.hpp
#pragma once #include "Bsp/bsp.hpp" #include "LIB/LIB_MATH/M_math.hpp" class Adc_F450:public Adc { public: Adc_F450(){} void Init(void); void SetRatio(uint8_t n, float _ratio); void PeriodRun(); };
ADC.hpp
#pragma once #include <stdint.h> class Adc { public: Adc(){} uint32_t adcValueU[6]; float adcValueF[6]; float adcRatio[6]; virtual void Init(void){} virtual void SetRatio(uint8_t n, float _ratio){} virtual void PeriodRun(){} };
BSP_F450.cpp
#include "BSP_F450.hpp" #include "main.h" #include "IO_F450.hpp" BSP_F450::BSP_F450() { // mcuClock = new McuClock_F4(); led_s = new IO_F450(GPIOD,GPIO_PIN_13); debugUart = new UART0_F450(); LoRa = new UART1_F450(); sewagePump = new PWM_T0CH0_F450(); cleanWaterPump = new PWM_T0CH2_F450(); cleanWaterValve = new IO_F450(GPIOC,GPIO_PIN_3); acRelay = new IO_F450(GPIOE,GPIO_PIN_7); masterSwitch = new IO_F450(GPIOC,GPIO_PIN_3); emergencyStopSwitch = new IO_F450(GPIOC,GPIO_PIN_3); PowerSwitch = new IO_F450(GPIOB,GPIO_PIN_0); powerControl = new IO_F450(GPIOB,GPIO_PIN_1); cleanWaterLevel[0] = new IO_F450(GPIOD,GPIO_PIN_9); cleanWaterLevel[1] = new IO_F450(GPIOD,GPIO_PIN_10); sewageWaterLevel[0] = new IO_F450(GPIOD,GPIO_PIN_11); sewageWaterLevel[1] = new IO_F450(GPIOD,GPIO_PIN_12); power_4G = new IO_F450(GPIOC,GPIO_PIN_12); ws2812b_Spi = new MasterSPI_F450(); gAdc = new Adc_F450(); } void BSP_F450::Init(void) { // mcuClock->Init(); led_s->Init(); debugUart->Init(); LoRa->Init(); sewagePump->Init(20000); cleanWaterPump->Init(20000); ws2812b_Spi->Init(); gAdc->Init(); voltage_5V = &gAdc->adcValueF[0]; voltage_4G = &gAdc->adcValueF[1]; voltage_bus = &gAdc->adcValueF[2]; current_bus = &gAdc->adcValueF[3]; current_Charge[0] = &gAdc->adcValueF[4]; current_Charge[1] = &gAdc->adcValueF[5]; gAdc->SetRatio(0,0.001611328125);// x / 4096 * 3.3 * 2.0 gAdc->SetRatio(1,0.001611328125);// x / 4096 * 3.3 * 2.0 gAdc->SetRatio(2,0.012890625);//x / 4096 * 3.3 * 16.0 gAdc->SetRatio(3,1);// gAdc->SetRatio(4,1);// gAdc->SetRatio(5,1);// } //uint32_t HAL_GetTick(void) //{ return osKernelGetTickCount(); //} extern "C" { void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } }
BSP_F450.hpp
#pragma once #include "BSP_config.h" #include "Bsp/BSP.hpp" #include "McuClock_F450.hpp" #include "LED_S_F450.hpp" #include "IO_F450.hpp" #include "MasterSPI_F450.hpp" #ifdef BSP_USING_ADC1 #include "Adc_F4.hpp" #endif #ifdef BSP_USING_UART0 #include "Uart0_F450.hpp" #endif #ifdef BSP_USING_UART1 #include "Uart1_F450.hpp" #endif #include "PWM_F450.hpp" #include "ADC_F450.hpp" //#include "PB_F7.hpp" //#include "HALL_F7.hpp" //#include "Uart3_F7.hpp" //#include "ENC_HALL_F7.hpp" //#include "LEDS_F7.hpp" //#include "CAN1_F7.hpp" //#include "IIC1_F7.hpp" class BSP_F450:public BSP { public: BSP_F450(); void Init(void); };
BSP.hpp
#pragma once #include "Bsp/McuClock.hpp" #include "Bsp/IO.hpp" #include "Bsp/LED.hpp" #include "Bsp/UART.hpp" #include "bsp/pwm.hpp" #include "bsp/MasterSPI.hpp" #include "bsp/ADC.hpp" //#include "adc.hpp" //#include "../Bsp/CAN.hpp" //#include "../Bsp/IIC.hpp" class BSP { public: BSP(){} McuClock *mcuClock; IO *led_s; UART *debugUart; UART *LoRa; PWM *sewagePump; PWM *cleanWaterPump; IO *cleanWaterValve; IO *acRelay; IO *masterSwitch; IO *emergencyStopSwitch; IO *PowerSwitch; IO *powerControl; IO *cleanWaterLevel[2]; IO *sewageWaterLevel[2]; IO *power_4G; MasterSPI *ws2812b_Spi; Adc *gAdc; float *voltage_bus; float *current_bus; float *voltage_5V; float *voltage_4G; float *current_Charge[2]; // Adc *gAdc; // UART *gUSB_UART; // UART *gUSB_UART; // CAN *gcan; // IIC *gIIC; virtual void Init(void){} };
更多相关内容 -
stm32f103 adc dma - HAL库版本3.zip
2019-07-15 13:27:26使用stm32f103 hal库版本,使用adc的 dma功能,配置相关通道,利用PA0、PA1、PA4引脚采集0—3.3V模拟电压。 -
STM32H7 ADC DMA cubeMX工程
2019-11-12 14:00:35该工程在cubeMX工具配置下完成ADC通过DMA进行数据传输,该工程解决了默认配置环境下DMA无法正常传输数据的问题 -
电子-基于战舰V3STM32F103多通道ADCDMA中断方式HAL库.rar
2019-09-05 16:39:09电子-基于战舰V3STM32F103多通道ADCDMA中断方式HAL库.rar,单片机/嵌入式STM32-F0/F1/F2 -
hal库定时器触发adc dma采集
2018-06-26 11:16:38基于stm32f103c8t6通过定时触发单通道adc dma采集数据 -
电子-定时器3更新事件触发ADCDMA连续转换DMA连续传输.rar
2019-09-05 17:00:25电子-定时器3更新事件触发ADCDMA连续转换DMA连续传输.rar,单片机/嵌入式STM32-F0/F1/F2 -
stm32f407 双通道ADC DMA采集代码
2018-03-17 13:44:21使用HAL库进行stm32f407 双通道ADC DMA采集代码,第一通道接PA3,接受光敏电阻的信息,第二通道是单片机内部的温度通道。并用串口进行输出 -
电子-多通道ADCDMA及3ADCDMAAWD.pdf
2019-09-05 15:52:33电子-多通道ADCDMA及3ADCDMAAWD.pdf,单片机/嵌入式STM32-F0/F1/F2 -
电子-多通道ADCDMA.zip
2019-09-05 14:21:17电子-多通道ADCDMA.zip,单片机/嵌入式STM32-F0/F1/F2 -
电子-阿波罗4294路ADCDMA实验.zip
2019-09-05 18:00:41电子-阿波罗4294路ADCDMA实验.zip,综合电子技术正点原子技术支持板块 -
F103多通道ADC+DMA_STM32F103_straightb4u_adc_adc多通道采集_dma
2021-09-11 15:24:04使用DMA加速F103的多通道ADC采集速度 -
电子-3个通道ADCDMA串口.zip
2019-09-05 15:05:37电子-3个通道ADCDMA串口.zip,单片机/嵌入式STM32-F0/F1/F2 -
STM32 LL 库 ADC DMA的坑
2022-03-22 13:41:47一路排查初始化配置,连续转换(注意需要2路以上才可以打开这个选项),DMA传送地址,传送方向,传送内存大小,传送内存地址递增,ADC1转换的数据是16位,传送的目的地址是16位宽度,DMA循环模式,等等等等,都没问题 ...一、问题
- ADC配置DMA后,发现一直没有数据更新
二、排查
-
一路排查初始化配置,连续转换(注意需要2路以上才可以打开这个选项),DMA传送地址,传送方向,传送内存大小,传送内存地址递增,ADC1转换的数据是16位,传送的目的地址是16位宽度,DMA循环模式,等等等等,都没问题
但是这里能看到一个奇怪的现象,DMA的初始化,只是打开时钟,设置NVIC
而DMA配置,位于ADC初始化里面static void MX_DMA_Init(void) { /* Init with LL driver */ /* DMA controller clock enable */ LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1); /* DMA interrupt init */ /* DMA1_Channel1_IRQn interrupt configuration */ NVIC_SetPriority(DMA1_Channel1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0)); NVIC_EnableIRQ(DMA1_Channel1_IRQn); }
static void MX_ADC1_Init(void) { /* USER CODE BEGIN ADC1_Init 0 */ /* USER CODE END ADC1_Init 0 */ LL_ADC_InitTypeDef ADC_InitStruct = {0}; LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0}; LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0}; LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; /* Peripheral clock enable */ LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1); LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA); /**ADC1 GPIO Configuration PA4 ------> ADC1_IN4 PA5 ------> ADC1_IN5 */ GPIO_InitStruct.Pin = LL_GPIO_PIN_4|LL_GPIO_PIN_5; GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; LL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* ADC1 DMA Init */ /* ADC1 Init */ LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY); LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_HIGH); LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR); LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT); LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT); LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD); LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD); /* USER CODE BEGIN ADC1_Init 1 */ /* USER CODE END ADC1_Init 1 */ /** Common config */ ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT; ADC_InitStruct.SequencersScanMode = LL_ADC_SEQ_SCAN_ENABLE; LL_ADC_Init(ADC1, &ADC_InitStruct); ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_INDEPENDENT; LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct); ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE; ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS; ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE; ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS; ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED; LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct); /** Configure Regular Channel */ LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_4); LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_1CYCLE_5); /** Configure Regular Channel */ LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_5); LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_5, LL_ADC_SAMPLINGTIME_1CYCLE_5); /* USER CODE BEGIN ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */ }
-
查看到生成的初始化,先执行了ADC初始化,再执行DMA初始化,而此时,DMA时钟未打开,配置无效,从而导致了问题!若配置DMA参数时,DMA时钟还没打开,则配置不生效!
/* Initialize all configured peripherals */ MX_GPIO_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); MX_DMA_Init(); MX_USART3_UART_Init();
-
这里给出一下需要用户自己额外设置的函数,可用于STM32F103系列
///> adc 部分宏定义 #define ADC_DMA_BUF_CH 2 /* 一共2个通道 */ #define ADC_DMA_BUF_LEN 8 /* 每个通道8个数据缓存 */ #define ADC_DMA_BUF_SIZE (ADC_DMA_BUF_LEN*ADC_DMA_BUF_CH) /* 总BUF大小 */ static uint16_t tAD[ADC_DMA_BUF_CH]; static uint16_t adcBuf[ADC_DMA_BUF_SIZE]; void adc_init() { /* Set DMA transfer addresses of source and destination */ LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA), (uint32_t)&adcBuf, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);//配置DMA,将DMA与ADC1链接到一起 /* Set DMA transfer size */ LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_1,ADC_DMA_BUF_SIZE); //LL_DMA_SetPeriphAddress(DMA1,LL_DMA_CHANNEL_1,LL_ADC_DMA_GetRegAddr(ADC1,LL_ADC_DMA_REG_REGULAR_DATA)); //LL_DMA_SetMemoryAddress(DMA1,LL_DMA_CHANNEL_1,(uint32_t)&adcBuf[0]); /* Enable the DMA transfer */ LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_1);//使能DMA传输 LL_ADC_Enable(ADC1);//打开ADC LL_ADC_StartCalibration(ADC1); //开始校准 while (LL_ADC_IsCalibrationOnGoing(ADC1)); //等待校准完成 /* Start ADC group regular conversion */ LL_ADC_REG_StartConversionSWStart(ADC1);//启动ADC组常规转换,SWStart 与cubmx配置中 ADCs_Regular_ConversionMode 的 External Trigger Conversion Source ADC相对应 LL_ADC_REG_SetDMATransfer(ADC1,LL_ADC_REG_DMA_TRANSFER_UNLIMITED);//开始转换 //LL_ADC_REG_StartConversion(ADC1); } uint8_t adc_get( uint16_t *adc_data_buf) { if ( LL_DMA_IsActiveFlag_TC1(DMA1) ) { uint16_t i,j; uint16_t * tadBuf; tadBuf = & adcBuf[0]; for(i = 0; i < ADC_DMA_BUF_CH; i++) { tAD[i] = 0; } for(i = 0; i < ADC_DMA_BUF_CH; i++) { for(j = 0; j < ADC_DMA_BUF_LEN; j++) { tAD[i] = tAD[i] + tadBuf[j*ADC_DMA_BUF_CH + i]; } tAD[i] = tAD[i] / ADC_DMA_BUF_LEN; } memcpy(adc_data_buf,tAD,ADC_DMA_BUF_CH*2); /* 注意memcpy 还是按照8位copy 所以要*2进行copy */ return 1; } return 0; }
-
电子-阿波罗F767板子四路ADCDMA.zip
2019-09-05 18:00:11电子-阿波罗F767板子四路ADCDMA.zip,综合电子技术正点原子技术支持板块 -
电子-12通道ADCDMA标志位等待获取STM32F1标准库.rar
2019-09-05 16:39:07电子-12通道ADCDMA标志位等待获取STM32F1标准库.rar,单片机/嵌入式STM32-F0/F1/F2 -
STM32 adcdma方式存储用3.2tft液晶显示程序
2015-06-04 21:03:05利用STM32自带的adc转换多路模拟信号,以dma方式传输到3.3寸tft液晶屏显示。 -
RTThread-ADC_DMA.rar
2020-07-10 18:43:57目前RTThread的adc设备驱动采用轮询法,比较浪费资源,且读取麻烦,这是我的某个项目中对RTThread的ADC设备驱动添加DMA方式 -
STM32F4之dacdma输出正弦波加adcdma多通道采集
2020-05-25 16:49:30正点原子探索者系列 STM32F4之dacdma高速输出正弦波加adcdma多通道高速采集信号,TFTLCD显示 -
STM32F407 ADC DMA 12通道采样
2018-12-09 14:38:23利用STM32F407的DMA传输实现ADC 12通道交替采样,已经在项目中成功使用 -
HAL ADC DMA转换数据异常
2022-04-06 11:51:14示例:使用STM32 CUBE MX 配置ADC 使用DMA方式读取数据。MCU:STM32F105RC 问题描述 例如:按照如下方式配置DMA,自动生成代码,初始化后开启校准和采集,数组里数据异常,我接入的是1.13V电压,按理应该有AD值在 ...项目场景:
示例:使用STM32 CUBE MX 配置ADC 使用DMA方式读取数据。MCU:STM32F105RC
问题描述
例如:按照如下方式配置DMA,自动生成代码,初始化后开启校准和采集,数组里数据异常,我接入的是1.13V电压,按理应该有AD值在 1.13/3.3*4096约等于1400的值,实际上只有10。
APP 中接收数据代码:
//校准ADC HAL_ADCEx_Calibration_Start(&hadc1); //开启DMA转换 HAL_ADC_Start_DMA(&hadc1,ad_value,6);
解决方案:
通过对比之前能够得到正确结果的程序,发现本次CUBE MX生成的初始化代码中 MX_DMA_Init()放置在了MX_ADC1_Init()之后,而之前是先配置MDA再配置ADC初始化,原因尚不可知,作此纪录。
-
STM32 ADC DMA通道数减小引发的异常问题及解决
2022-05-12 15:12:17ADC控制器 STM32一共有3个ADC控制器:ADC1、ADC2、ADC3。 18个通道 STM32的ADC多达18个通道:16个外部通道和2个内部信号源。 16个外部通道:芯片上有16个引脚是能够接到模拟电压上进行电压值检测的。 2个内部...一、基础知识
在讲具体问题之前先列出STM32系列单片机ADC部分的一些知识:
ADC控制器
STM32一共有3个ADC控制器:ADC1、ADC2、ADC3。
18个通道
STM32的ADC多达18个通道:16个外部通道和2个内部信号源。
- 16个外部通道:芯片上有16个引脚是能够接到模拟电压上进行电压值检测的。
- 2个内部信号源 :一个是内部温度传感器,一个是内部参考电压。
一共支持23个引脚支持ADC,包括21个外部和2个内部信号源。
STM32F10x系列芯片ADC通道和引脚对应关系
如下图所示:
ADC的转换模式
- 单次转换模式:ADC只执行一次转换。
- 连续转换模式:转换结束以后立刻开始新的转换。
- 扫描模式:ADC扫描被规则通道和注入通道选中的全部通道,在每一个组的每一个通道上执行单次转换。在每一个转换结束时,这一组的下一个通道被自动转换。若是设置了CONT位(开启了连续 转换模式),转换不会在选择组的最后一个通道上中止,而是再次从选择组的第一个通道继续转换。
- 间断模式:触发一次,转换一个通道,再触发,再转换。在所选转换通道循环,由触发信号启动新一轮的转换,直到转换完成为止。
扫描模式简单地说是一次对全部所选中的通道进行转换。假设开了ch0、ch1、ch4、ch5,通道0转换完之后就会自动依次转换通道1、4、5,直到转换完,这个过程不能被打断。若是开启了连续转换模式,则会在转换完ch5以后开始新一轮的转换。
这就引入了间断模式,可以说是对扫描模式的一种补充。它能够把ch0、ch1、ch4、ch5这四个通道进行分组。既能够分成0、1一组,4、5一组;也能够每一个通道单独配置为一组。这样每一组转换以前都须要先触发一次。
ADC单通道
- 只进行一次ADC转换:配置为“单次转换模式”,扫描模式关闭。ADC通道转换一次后,就中止转换,等待再次使能后才会从新转换;
- 进行连续ADC转换:配置为“连续转换模式”,扫描模式关闭。ADC通道转换一次后,接着进行下一次转换,不断连续。
ADC多通道
- 只进行一次ADC转换:配置为“单次转换模式”,扫描模式使能。ADC的多个通道,按照配置的顺序依次转换一次后,就中止转换,等待再次使能后才会从新转换;
- 进行连续ADC转换:配置为“连续转换模式”,扫描模式使能。ADC的多个通道,按照配置的顺序依次转换一次后,接着进行下一次转换,不断连续。
也就是说:多通道必须使能扫描模式。
左对齐或右对齐
由于ADC获得的数据是12位精度的,而数据存储在16位数据寄存器中,因此ADC的存储结果能够分为左对齐或右对齐方式(12位),如下表所示:
ADC输入通道
从ADCx_INT0-ADCx_INT15对应三个ADC的16个外部通道,进行模拟信号转换。此外,还有两个内部通道:温度检测和内部电压检测。选择对应通道以后,便会选择对应GPIO引脚,相关的引脚定义和描述可参见数据手册。
注入通道,规则通道
在选择了ADC的相关通道引脚以后,在模拟至数字转换器中有两个通道:注入通道、规则通道。
规则通道至多16个,注入通道至多4个。规则通道
规则通道是关于你正常运行的程序,看它的名字就能够知道,很规矩,就是正常执行程序。
注入通道
注入通道能够打断规则通道,听它的名字就知道不安分,若是在规则通道转换过程当中,有注入通道进行转换,那么就要先转换完注入通道,等注入通道转换完成后,再回到规则通道的转换流程。
DMA
ADC还支持DMA触发,规则和注入通道转换结束后会产生DMA请求,用于将转换好的数据传输到内存。
注意,只有ADC1和ADC3能够产生DMA请求。
CubeMX中ADC配置项
- Data Alignment (数据对齐方式)
对应Init.DataAlign。设置数据的对齐方式。
- Scan Conversion Mode(扫描模式)
对应Init.ScanConvMode。是否开启扫描模式。若是只是用了一个通道的话,DISABLE就能够了(也只能DISABLE);若是使用了多个通道的话,会自动设置为ENABLE。
- Continuous Conversion Mode(连续转换模式)
对应Init.Continuous。设置为ENABLE,即连续转换;若是设置为DISABLE,则是单次转换。二者的区别在于连续转换直到全部的数据转换完成后才中止转换,而单次转换则只转换一次数据就中止,要再次触发转换才能够进行转换。
- Discontinuous Conversion Mode(间断模式)
对应Init.DiscontinuousConvMode。
- Number OF Conversion(转换通道数)
对应Init.NbrOfConversion。用到多少个通道就设置为多少。
- Extenal Trigger Conversion Source (外部触发转换源)
对应Init.ExternalTrigConv。设定ADC的触发方式。
- Rank(转换顺序)
对应sConfig.Rank。多个通道时会有多个Rank,能够设定每一个通道的转换顺序。
- Sampling Time(通道采样时间)
对应sConfig.SamplingTime。
ADC相关接口函数
开启(启动)ADC
开启(启动)ADC有3种模式,分别是:轮询模式、中断模式、DMA模式。
- 轮询模式开启ADC
HAL_ADC_Start
- 中断轮询模式开启ADC
HAL_ADC_Start_IT
- DMA模式开启ADC
HAL_ADC_Start_DMA
关闭(停止)ADC
相应地,关闭(停止)ADC也有3中模式,分别是:轮询模式、中断模式和DMA模式。
- 轮询模式关闭ADC
HAL_ADC_Stop
- 中断模式关闭ADC
HAL_ADC_Stop_IT
- DMA模式关闭ADC
HAL_ADC_Stop_DMA
ADC校准
HAL_ADCEx_Calibration_Start
注:F4系列不支持此接口
读取ADC转换值
HAL_ADC_GetValue
等待转换结束函数
HAL_ADC_PollForConversion
ADC中断回调函数
HAL_ADC_ConvCpltCallback
注:转换完成后回调,DMA模式下DMA传输完成后调用
规则通道配置
HAL_ADC_ConfigChannel
看门狗配置
HAL_ADC_AnalogWDGConfig
二、所遇问题
项目中原来使用的ADC通道数为16,新的项目只使用了4路AD,因此通道数缩减为4。针对于次变化,对旧有程序做了相关的修改,主要涉及AD部分以及GPIO部分。但是,进行修改后出现了问题:原本运行正常的程序,变得响应非常慢,甚至无响应;而且读的AD值也不对,大多数数据本来有值,但一段时间后会无故变为0。仔细检查程序,并没有发现由老版本程序到新版本程序所做出的改变有什么问题和错误。进一步地,发现最基本的memset函数变得非常慢,而老版本程序中则无此问题。
正在百思不得其解、没有头绪的时候,一片博客文章如久旱逢甘雨般地出现了,地址如下:【STM32+cubemx】0008 HAL库开发:ADC的四种用法:轮询、中断、DMA、定时器触发_xiaobaibai_2021的博客-CSDN博客_adc中断触发方式
其中有这样一段内容,描述和解决的正是笔者所遇到的问题:
注意这里的HAL_ADC_Start_DMA(&hadc1, ADC_Value, 16); 最后这个参数16,表示的是DMA搬移数据的次数;向ADC_Value及其之后的地址搬移16个数据;我们这里设置了2个通道,所以从首地址开始填入的是交替的两个通道的数据:ADC0、ADC1、ADC0、ADC1…一共16个数。
这里设置为16,是因为太小的数值很快就会执行完一个循环,产生DMA传输中断,时间太短的话会频繁产生中断,导致一直在中断中执行,没有时间执行主循环中的语句。这里是增加搬移数据的次数;也可以设置AD采样时间长一些,那么每次的转换时间变长,也不会产生过多的中断。
另外这里的校准函数一定要放在启动ADC、DMA之前,否则会占用一个通道,之后的AD通道顺序会有问题。
由于老板的通道数为16,DMA传输中断并不十分频繁,因此没有出现问题;而新版本中由于通道数大幅缩减至4,因此DMA传输中断频繁发生,导致移植在中断中执行,宏观上就表现为程序反应慢或者干脆无反应,以及其它一些匪夷所思的问题。
按照上面文章给出的建议,有两种解决方案:
(1)将通道数增大,增加循环搬移数据的次数。这样会有一些冗余数据,如:将通道数设置为8,虽然问题能够解决,但是数据将是这样:ADC_CH0、ADC_CH1、ADC_CH2、ADC_CH3、ADC_CH0、ADC_CH1、ADC_CH2、ADC_CH3;而实际上只需要一次ADC_CH0、ADC_CH1、ADC_CH2、ADC_CH3。
(2)将AD采样时间设置长一些。这样每次的转换时间会变长。
最终笔者选用了第二种解决方案,将sConfig.SamplingTime由ADC_SAMPLETIME_1CYCLE_5改为了ADC_SAMPLETIME_7CYCLE_5。
经过测试,之前的问题现象都消失了,宏观上和老版本程序的表现一致了。
-
STM32测试程序 ADC+DMA+串口发送
2020-08-20 03:36:01STM32测试程序 ADC+DMA+串口发送。全代码奉献(操作寄存器) -
STM32F4ADC DMA寄存器版本.zip
2020-08-12 16:34:35STM32F407 ADC双通道采样程序(寄存器版本),网上的寄存器版本比较少,使用ADC采集两路数据,然后使用DMA传输数据,通过串口打印出来,有一定的参考价值 -
STM32 ADC DMA数据不稳定的解决方案
2021-03-05 19:52:38在项目开发中,经常需要用到ADC采样的做电压检测,而且多通道ADC检测的情况比较多,所以本篇基于此要求采用了ADC DMA的方法,下面先给出基础代码(STM32F030)! #define ADC1_DR_Address 0x40012440 //对应需要检测的... -
STM32Cube教程系列 - ADCDMA篇
2020-11-09 11:08:12STM32Cube教程系列 - ADCDMA篇一、为什么要出这个教程?二、配置方法1.Cube配置ADC部分2.Cube配置DMA部分3.代码内容添加总结 一、为什么要出这个教程? 之所以想着出这么一个教程,是希望帮助那些初学嵌入式同时又是... -
TIM-ADC-DMA-FFT.zip
2020-12-14 22:16:16STM32F429IGT6,用时钟来控制触发频率,然后触发ADC采样,利用DMA搬运到内存,然后进行FFT, -
基于STM32CUBUMX的ADC DMA采集配置DEMO工程
2020-03-09 21:01:42第七章STM32CUBUMX ADC DMA采集配置使用工程,以上就是我提供的工程源码。 大家可以下载去测试测试参考参考。 感谢谢谢大家的支持,本系列工程一律免费哦!欢迎大家下载指点,有什么不会的可以找我! -
ADC DMA模式.rar
2021-05-14 15:52:55使用DMA来配置ADC -
STM32的ADC+DMA+TIM采集交流信号.zip
2021-12-14 22:03:31使用cubemx生成的ADC+DMA+TIM采集交流信号的例程 -
STM32 HAL库之DMA模式ADC
2021-01-06 08:28:54HAL_ADC_Init() 用户自行调用 加载ADC属性 //ADC句柄声明 ADC_HandleTypeDef AdcHandle; AdcHandle.Instance = ADC1; AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; AdcHandle.Init.LowPowerAutoWait ...