STM32F030使用DMA读取ADC,校准之后数据错位解决方法

简单的来说就是关闭DMA之后再次初始化。
之前调试F051遇到过这个问题,当时需求精度不高,就屏蔽掉了。现在使用F030再次遇到,就跟踪仿真观察寄存器,找到折中的办法。
问题详细描述:
ADC初始化函数如下图:

/* ADC 初始化配置 */
static void MX_ADC_Init(void)
{
  LL_ADC_InitTypeDef ADC_InitStruct = {0};
  LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* ADC相关外设时钟使能 */
  LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_ADC1);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  /* DMA 控制器时钟使能 */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);


  /**ADC GPIO 配置:PA0   ------> ADC_IN0  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0 | LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* 配置ADC的全局特性:时钟、分辨率、数据对齐和转换次数 */
  ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV4;
  ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
  ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
  ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
  LL_ADC_Init(ADC1, &ADC_InitStruct);
	
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;//LL_ADC_REG_CONV_CONTINUOUS;//
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
  ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);

  /* 配置ADC通道 */

//  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_TEMPSENSOR);
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_VREFINT);
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_0);
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_1);
	
//	LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_TEMPSENSOR);
	LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_VREFINT);
	
  LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_239CYCLES_5);
  LL_ADC_DisableIT_EOC(ADC1);
  LL_ADC_DisableIT_EOS(ADC1);
	
  LL_ADC_StartCalibration(ADC1);
  while( LL_ADC_IsCalibrationOnGoing(ADC1));
	
  LL_ADC_Enable(ADC1);
	
  /* ADC DMA初始化 */
  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_LOW);
  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_WORD);
  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_WORD);

  LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_1,ADBufferSize);
  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)ADC_ConvertedValue);

  LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_1);
  LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_1);

	
  /* ADC DMA再次初始化 */
  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_LOW);
  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_WORD);
  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_WORD);

  LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_1,ADBufferSize);
  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)ADC_ConvertedValue);

  LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_1);

  LL_ADC_REG_StartConversion(ADC1); 
} 

在使用校准之后,ADC准确度能提升很大,建议还是加上
LL_ADC_StartCalibration(ADC1);
while( LL_ADC_IsCalibrationOnGoing(ADC1));

但是问题就在校准之后,ADC的DR寄存器会产生校准码,打开DMA后会进行一次传输。
试了很多办法,清除不掉这次传输。没办法只能关闭DMA之后,再次配置。ADC数据才能按照顺序存储在数组中。在这里插入图片描述 在这里插入图片描述

<think>嗯,用户想了解如何在STM32F030F4P6上使用标准库配置ADC的连续扫描模式扫描8个通道。首先,我需要确认这款芯片的ADC资源。STM32F030F4P6属于Cortex-M0系列,内置12位ADC,支持最多16个外部通道,但具体通道数量可能取决于封装。F4P6是TSSOP20封装,所以实际可用的外部ADC通道可能有限,不过用户提到8个通道,可能需要检查数据手册确认是否可行。 接下来,标准库的使用STM32F0的标准库可能和F1系列有所不同,需要确保用户正确安装库文件。然后,配置ADC的连续扫描模式。连续扫描模式意味着ADC在转换完所有选中的通道后自动重新启动转换,无需外部触发。 步骤大概包括:初始化ADC时钟、配置GPIO为模拟输入、设置ADC参数(如连续模式、扫描模式、数据对齐、通道数等)、配置通道的转换顺序、使能ADC校准、启动转换。然后处理数据,可能需要DMA,因为8个通道的数据需要存储,而连续扫描模式下使用DMA会更高效,避免CPU频繁中断。 需要注意的问题:ADC通道的编号是否正确,是否有重复或不可用的通道;采样时间设置是否足够;DMA配置是否正确,内存地址是否递增;中断是否需要(如果不用DMA的话);电压基准是否稳定;电源和地的噪声是否会影响ADC精度。 用户可能遇到的问题包括通道配置错误导致数据不对,DMA传输不成功,或者采样时间不足导致读数不准。需要提醒用户仔细检查通道映射,确认每个GPIO对应的ADC通道,并测试单个通道是否工作正常,再逐步增加到多个通道。 另外,STM32F0的ADC扫描模式可能需要设置连续转换模式(CONT位)和扫描模式(SCAN位),同时配置通道的转换序列。每个通道在序列中的顺序和长度需要正确设置,尤其是SQR寄存器的相关配置。在标准库中,可能需要使用ADC_RegularChannelConfig函数来逐个添加通道到规则组中。 最后,代码示例的结构应该包括初始化函数、ADC配置、DMA配置(如果使用)、中断处理(如果需要),以及主循环中的数据读取。需要确保用户正确启用相关时钟(如ADC时钟、DMA时钟、GPIO时钟),并注意ADC校准流程不能省略。</think>好的,我将分步骤为您讲解STM32F030F4P6标准库实现ADC连续扫描8个通道的配置方法: 一、硬件特性确认 1. STM32F030F4P6内置12位ADC,支持最多16个通道 2. 在TSSOP20封装中实际可用ADC通道:ADC_IN0~ADC_IN7(PA0-PA7) 二、配置步骤 1. 时钟使能 ```c RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); ``` 2. GPIO配置(以PA0-PA7为例) ```c GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_Init(GPIOA, &GPIO_InitStructure); ``` 3. ADC参数配置 ```c ADC_InitTypeDef ADC_InitStructure; ADC_StructInit(&ADC_InitStructure); ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_TRGO; // 随意配置,实际不使用 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; // 扫描方向 ADC_Init(ADC1, &ADC_InitStructure); ``` 4. 通道顺序配置 ```c ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_55_5Cycles); ADC_ChannelConfig(ADC1, ADC_Channel_1, ADC_SampleTime_55_5Cycles); // 继续添加通道2-7... ``` 5. DMA配置(必须使用) ```c RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_values; // 存储数组 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 8; // 8个通道 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); ``` 6. 校准与使能 ```c ADC_GetCalibrationFactor(ADC1); ADC_Cmd(ADC1, ENABLE); while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)); // 等待ADC就绪 ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); // 循环DMA请求 ADC_DMACmd(ADC1, ENABLE); ADC_StartOfConversion(ADC1); // 启动转换 ``` 三、关键点说明 1. 必须使用DMA传输,8通道数据将存储在`adc_values[8]`数组中 2. 采样率计算:$$ T_{conv} = (55.5 + 12.5) \times \frac{1}{14MHz} \approx 4.86\mu s $$ 3. 总采样周期:$$ T_{total} = 8 \times T_{conv} \approx 38.9\mu s $$ 四、常见问题处理 1. 数据错位:检查DMA内存地址递增设置 2. 采样值不稳定:增加采样周期时间 3. 通道顺序错误:确认ADC_ChannelConfig调用顺序 4. DMA不工作:检查通道分配(ADC1使用DMA1通道1) 五、优化建议 1. 在ADC初始化前执行校准 2. 保持VDDA电压稳定(推荐3.3V±10mV) 3. 避免高频数字信号干扰模拟输入 4. 定期执行软件校准: ```c ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1) != RESET); ``` 完整工程需要包含这些配置,实际使用时请注意: 1. 数组声明:`uint16_t adc_values[8];` 2. 包含标准库头文件:`#include "stm32f0xx_adc.h"` 3. 根据实际时钟频率调整采样周期参数
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值