STM32多通道ADC。无人序乐时出乎意料的行为
我已将ADC功能添加到我的Nucleo-F446RE开发委员会中。 4个频道,启用DMA,扫描和连续转换模式,DMA连续请求启用,每个频道的示例时间有所不同。我将在这篇文章底部张贴代码(所有HAL,全部在STM32Cubemx中完成)。
当通道未吞噬时,我发现了一些奇怪的行为(例如,模拟通道销打开)。所有四个通道都将在没有连接的通道的情况下悬停在0.9V左右。如果我将3.3V源添加到频道0,它将显示3.3V,但是CH1将显示2.5V,CH2将显示1.9V,CH3 1.6V。瀑布效应。如果我将3.3V源转移到CH1并将其余的人口不足,并且瀑布效应循环回到CH0,则瀑布效应是相同的。
如果我给每个频道自己的来源,它们都会正确显示它们,但是当不动说的频道时,这些频道会受到人口稠密的频道的影响。为什么这是?我发现一些消息来源说这是因为样品+持有电容器,解决方案是纠正采样时间,但是我玩了很多时间,随着时间的流逝,从非常速度到尽可能慢的采样(我只是只是对在1KHz采样数据感兴趣,但是ADC转换似乎至少比此幅度要高),但并没有进行更改。我想知道将模拟通道引脚配置更改为下拉是否会有所帮助,但没有更改。
我希望这不是太关心的事情,因为频道在填充时看起来正确,但是也许即使我想避免的填充时,我也没有看到一些背景影响。我敢肯定,我没有对电路进行优化,所以对此的任何建议也很棒。 STM32 ADC DMA的在线教程和示例有很多带有单个频道的教程和示例,但并不是很多具有多通道的教程和示例。我也没有发现STM32提供的例子太有用了,而且通常看起来很低。
ADC定义
主时钟180MHz,APB2 Prescaler 2 = 90MHz,尽管我也将其丢给了16(11.25MHz)的预定器,这无济于事)
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
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 = 4;
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_480CYCLES;
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;
sConfig.SamplingTime = ADC_SAMPLETIME_112CYCLES;
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_4;
sConfig.Rank = 3;
sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
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_8;
sConfig.Rank = 4;
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
(
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ADC1 GPIO Configuration
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA4 ------> ADC1_IN4
PB0 ------> ADC1_IN8
*/
GPIO_InitStruct.Pin = analog1_Pin|analog2_Pin|analog3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = analog4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(analog4_GPIO_Port, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream0;
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_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_NORMAL;
hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
/* ADC1 interrupt Init */
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
模拟读取代码
(Analog_scale每次通道每1kHz一次调用一次)
#include "dma.h"
#include "adc.h"
#include "analog.h"
volatile uint32_t analogBuffer[4];
void analog_init()
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&analogBuffer, 4);
}
uint16_t analog_scale(char ch)
{
return (uint16_t)(((analogBuffer[ch] * 3.3) / 4096.0) * 1000.0);
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&analogBuffer, 4);
HAL_GPIO_TogglePin(test4_GPIO_Port, test4_Pin);
}
I have added ADC functionality to my Nucleo-F446RE development board. 4 channels, DMA enabled, scan and continuous conversion mode enabled, DMA continuous requests enabled, varying sample time per channel. I'll post code at the bottom of this post (all HAL, all done in STM32CubeMX).
I have found some strange behaviour when the channels are unpopulated (e.g., analog channel pin left open). All four channels will hover at around 0.9V with no channels connected. If I add a 3.3V source to channel 0, it'll show 3.3V, but CH1 will show 2.5V, CH2 will show 1.9V, CH3 1.6V. A waterfall effect. That waterfall effect is the same if I move the 3.3V source to CH1 and leave the rest unpopulated, and the waterfall effect loops back around to CH0.
If I give each channel their own source, they'll all show them correctly, but when unpopulated the channels are influenced by the populated channel. Why is this? I have found some sources saying that this is because of the sample+hold capacitor, and the solution is to correct the sampling times, but I have played a lot with the times going from very fast to as slow as possible sampling (I am only interested in sampling the data at 1kHz, but the ADC conversion seems to be, at a minimum, a magnitude above this), but it doesn't make a change. I wondered if changing the analog channel pin configuration to pull-down would help, but again no change.
I am hoping that this isn't something to be too concerned about, as the channels appear correct when populated, but perhaps there is some background influence that I am not seeing even when populated that I want to avoid. I am certain I haven't optimised my circuit, so any advice on that would also be great. There are lots of tutorials and examples online for STM32 ADC DMA with a single channel, but not so many with multi-channel. I also don't find the STM32 provided examples to be too helpful and often seem very inefficient.
ADC definitions
(main clock 180MHz, APB2 prescaler 2 = 90MHz, although I have also dropped it to a prescaler of 16 (11.25MHz) which didn't help)
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
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 = 4;
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_480CYCLES;
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;
sConfig.SamplingTime = ADC_SAMPLETIME_112CYCLES;
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_4;
sConfig.Rank = 3;
sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
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_8;
sConfig.Rank = 4;
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
DMA definition
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ADC1 GPIO Configuration
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA4 ------> ADC1_IN4
PB0 ------> ADC1_IN8
*/
GPIO_InitStruct.Pin = analog1_Pin|analog2_Pin|analog3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = analog4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(analog4_GPIO_Port, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream0;
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_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_NORMAL;
hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
/* ADC1 interrupt Init */
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
Analog read code
(analog_scale is called once per channel every 1kHz)
#include "dma.h"
#include "adc.h"
#include "analog.h"
volatile uint32_t analogBuffer[4];
void analog_init()
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&analogBuffer, 4);
}
uint16_t analog_scale(char ch)
{
return (uint16_t)(((analogBuffer[ch] * 3.3) / 4096.0) * 1000.0);
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&analogBuffer, 4);
HAL_GPIO_TogglePin(test4_GPIO_Port, test4_Pin);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这不是软件问题,而是正常的硬件行为。
如果ADC引脚浮动,它们将“收集”流浪电压,例如,从相邻样品中收集电压并容纳电容器,电压参考或在PCB或附加电缆上的痕迹中诱导的任何电压。
您看到的“瀑布”效果仅仅是您的输入电压在频道0或1通过样品耦合,并将电容器和电阻从一个通道到下一个通道,由多路复用器寄生电容传递:从一个传输中传递少量电荷在通过通道切换时,要通往下一个的电压路径,当连接打开时,该电荷没有流动路径,除非通过ADC,从而导致伪电压读数。
为了防止这种情况,请将所有未使用的通道都接地,使用适当的下拉电阻(10 kohm应该可以…),或者如果您想要软件解决方案:将所有未使用的通道乘以0。
That's not a Software issue, it's normal hardware behavior.
If ADC pins are floating, they "gather" stray voltages, e.g. from adjacent Sample and Hold Capacitors, from the Voltage Reference or any voltage that is induced in the traces on the PCB or attached cables.
The "Waterfall" effect you see, is simply your input voltage on Channel 0 or 1 coupling through the sample and hold capacitors and resistors from one channel to the next, transferred by the multiplexers parasitic capacitances: a small amount of charge is transferred from one voltage path to the next while switching through the channels, and this charge has no path to flow when the connections are open, except through the ADC, resulting in a pseudo-voltage reading.
To prevent this, tie all unused channels to ground, using appropriate pull down resistors (10 kOhm should be OK …), or if you want a software solution: multiply all unused channels with 0.