ADC1多通道_DMA_内部温度传感器+内部VREFINT

本文详细介绍了如何利用ADC1的16和17通道采集内部温度传感器数据,并通过DMA通道1进行内存存储与串口输出的操作流程,包括时钟配置、DMA初始化、中断处理等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现目标:

  使用ADC1的16和17通道(对应内部温度传感器和内部VREFINT),DMA通道1(Only)管理,存储到内存并串口输出。

Ready

  • 时钟
    • ADC:RCC_APB2Periph_ADC1,RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    • DMA:RCC_AHBPeriph_DMA1
  • NVIC:DMA1_Channel1_IRQn //传输完成中断DMA_IT_TC
  • DMA:
    • 起点/终点地址
    • 数据数量/单位
    • 传输方向
    • 地址是否自动加
    • 单次/连续模式
    • 是否M2M
    • 中断:DMA_IT_TC
  • ADC:
    • ADC间合作方式:ADC_Mode = ADC_Mode_Independent
    • 单次/连续
    • 是否开启扫描模式
    • 触发方式:ADC_ExternalTrigConv_None;//软件触发
    • 数据对齐
    • 通道数

    • 逐个配置通道:

      ADC_RegularChannelConfig(ADC1, 16, 1, ADC_SampleTime_239Cycles5 );
      ADC_RegularChannelConfig(ADC1, 17, 2, ADC_SampleTime_239Cycles5 );


    • 打开DMA信号:ADC_DMACmd(ADC1,ENABLE);
  • Sensor:ADC_TempSensorVrefintCmd(ENABLE);//打开内部温度和电压传感器的DMA连接

Go

#include "delay.h"#include "sys.h"
#include "usart.h"
#include "timer.h"
u16 data[2];
void NVIC_ADC1_DMA1_Init()
{
    DMA_InitTypeDef DMA_InitStructure; 
    ADC_InitTypeDef ADC_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel1_IRQn ;
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);   
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    ADC_DeInit(ADC1);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//规则组和注入组独立
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_InitStructure.ADC_ContinuousConvMode =ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 2;
  ADC_Init(ADC1, &ADC_InitStructure);
    
    ADC_TempSensorVrefintCmd(ENABLE);//打开内部温度和电压传感器的DMA连接
    ADC_RegularChannelConfig(ADC1, 16, 1, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, 17, 2, ADC_SampleTime_239Cycles5 );
    
    ADC_DMACmd(ADC1,ENABLE);
    ADC_Cmd(ADC1, ENABLE);


    ADC_ResetCalibration(ADC1);    //重置指定的ADC1的复位寄存器

  while(ADC_GetResetCalibrationStatus(ADC1));    //获取ADC1重置校准寄存器的状态,设置状态则等待

    ADC_StartCalibration(ADC1);     //

    while(ADC_GetCalibrationStatus(ADC1));        //获取指定ADC1的校准程序,设置状态则等待

    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (uint32_t) & (ADC1->DR); //外设寄存器基地址
    DMA_InitStructure.DMA_MemoryBaseAddr =(u32)data; //RAM基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
    DMA_InitStructure.DMA_BufferSize = 2;//Size
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设的地址是否自动增加
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //RAM的地址是否自动增加
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize =DMA_MemoryDataSize_HalfWord; //传输单位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA_Mode_Normal; //是否不断传输
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先组
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //是不是RAM2RAM
    DMA_Init(DMA1_Channel1, &DMA_InitStructure); 
    
    DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE ); //DMA中断(别忘了NVIC)

    DMA_Cmd(DMA1_Channel1,ENABLE);//开启DMA


}
int main(void)
{    
        
    NVIC_ADC1_DMA1_Init();
    uart_init(9600);
    delay_init();
    printf("START\n");
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(1);
}
void DMA1_Channel1_IRQHandler()
{
    if(DMA_GetITStatus(DMA_IT_TC)==SET)
    {
        double tp=(1.43-data[0]*(3.3/4096))/0.0043+25;    
        printf("TEMORI:%x\tTEM:%f\tVOT:%x\n",data[0],tp,data[1]);
        DMA_ClearITPendingBit(DMA_IT_TC);
    }
}

  • DMA数据单位:

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

  DMA_InitStructure.DMA_MemoryDataSize =DMA_MemoryDataSize_HalfWord; 

转载于:https://www.cnblogs.com/2cats/p/3592953.html

<think>嗯,用户提到使用ADCDMA采集时,ADC_CHANNEL_VREFINT和其他通道的数据不准确。我需要先理解这个问题的可能原因,然后一步步分析。 首先,VREFINT内部参考电压通道,通常用于监测实际的VREF+电压,以便校准其他通道的ADC值。如果VREFINT和其他通道同时采集时数据不准,可能是因为参考电压本身波动了。但VREFINT的稳定性受温度和工作条件影响,尤其是在高负载或高温下,可能会有波动。 然后,DMA配置是否正确?比如,DMA的缓冲区是否足够大,传输的数据宽度是否匹配ADC的分辨率。如果配置错误,可能导致数据错位或者丢失,进而影响所有通道的读数。 接下来是采样时间的问题。不同的ADC通道可能需要不同的采样时间,尤其是内部通道如VREFINT,通常需要更长的采样时间,因为其内部电路阻抗较高。如果采样时间不足,会导致采样不充分,数值不准确。需要检查ADC配置中的采样时间设置,确保VREFINT通道有足够的采样周期。 另外,多通道扫描时,是否有启用连续转换模式?如果DMA传输未正确配置为循环模式,或者ADC的触发方式不合适,可能导致数据更新不及时,出现数据错乱。需要确认ADCDMA的工作模式是否匹配,是否能够持续正确地传输数据。 还有电源噪声的问题。ADC的参考电压容易受到电源噪声的影响,尤其是在其他外设工作时,比如GPIO切换、PWM输出等,可能会引入噪声,导致VREFINT读数波动,进而影响其他通道的校准结果。需要检查PCB布局,确保模拟部分的电源和地线稳定,添加适当的去耦电容。 此外,校准步骤是否正确执行?ADC在上电后通常需要执行校准,如果未校准或校准过程中存在干扰,可能导致偏差。需要确认代码中是否有执行ADC校准的步骤,并且在校准期间没有其他高噪声的活动。 还有可能通道之间的交叉干扰。当多个通道切换时,如果切换速度过快,之前的通道电压未完全放电,可能会影响下一个通道的测量。特别是高阻抗的信号源更容易受此影响。增加通道切换后的延迟,或者调整采样顺序,可能会有所帮助。 最后,软件处理的问题。比如,DMA传输的数据是否正确对齐,是否有正确的数据处理和校准算法。例如,VREFINT的值需要用来计算实际电压,进而校准其他通道。如果处理算法有误,即使原始数据正确,最终结果也会出错。需要检查代码中的计算公式,确保正确使用VREFINT的读数进行校准。 可能的解决方案包括:检查并调整采样时间,确保VREFINT有足够的采样周期;验证DMA配置是否正确,特别是数据宽度和缓冲区大小;检查电源和地线的稳定性,减少噪声干扰;执行ADC校准;调整通道切换顺序或增加延迟;以及确认软件校准算法的正确性。</think>在STM32的ADC多通道DMA采集中,ADC_CHANNEL_VREFINT内部参考电压通道)与其他通道数据不准确的问题可能由以下原因导致: --- ### **1. VREFINT的物理特性** - **温度敏感性**:VREFINT电压值会随芯片温度变化(典型值1.3V,但温度系数约±1mV/℃) - **负载波动**:当VREFINT与其他高电流外设(如GPIO、PWM)同时工作时,电源噪声会直接影响其稳定性 - **解决方案**: - 采样前关闭高噪声外设 - 增加电源滤波电容(例如并联10μF钽电容+100nF陶瓷电容) --- ### **2. 采样时间不足** - **关键问题**:VREFINT通道的等效阻抗较高(约40kΩ),需更长的采样时间 - **计算公式**: ``` $$ T_{sample} \geq 9.5 \times R_{source} \times C_{ADC} $$ $$ \text{例如:} R_{source}=40k\Omega, C_{ADC}=8pF \Rightarrow T_{sample} \geq 3.04\mu s $$ ``` - **配置建议**: - 对VREFINT通道单独设置最长采样时间(如ADC_SAMPLETIME_480CYCLES) - 其他普通通道使用较短采样时间(如ADC_SAMPLETIME_15CYCLES) --- ### **3. DMA配置问题** - **典型错误**: ```c // 错误示例:未考虑数据对齐 HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, 4); ``` - **正确配置**: ```c // 12位ADC应使用16位数组存储 volatile uint16_t adc_buf[4]; HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, 4); ``` - **检查要点**: - DMA数据宽度必须匹配ADC分辨率(12位ADC16位存储) - 缓冲区地址必须对齐到4字节边界(使用`__attribute__((aligned(4)))`) --- ### **4. 校准时序错误** - **正确校准流程**: 1. 上电延迟至少10ms(等待电源稳定) 2. 执行ADC校准: ```c HAL_ADCEx_Calibration_Start(&hadc1); ``` 3. 每次温度变化超过5℃时重新校准 --- ### **5. 软件滤波优化** - **滑动窗口滤波示例**: ```c #define FILTER_DEPTH 8 uint16_t vref_filter(FILTER_DEPTH) = {0}; void update_vref(uint16_t new_val) { static uint8_t index = 0; vref_filter[index] = new_val; index = (index + 1) % FILTER_DEPTH; } uint16_t get_avg_vref(void) { uint32_t sum = 0; for(int i=0; i<FILTER_DEPTH; i++) sum += vref_filter[i]; return sum / FILTER_DEPTH; } ``` --- ### **6. 交叉干扰验证方法** 1. 单独采集VREFINT通道10次,记录波动范围 2. 单独采集其他通道10次,记录波动 3. 同时采集多通道(含VREFINT10次 4. 对比两组数据的方差: - 若单独采集时稳定(方差<0.5%),混合采集时恶化(方差>2%),则存在通道干扰 --- ### **推荐调试步骤** 1. **最小化系统测试**:关闭所有无关外设,仅保留ADCDMA 2. **示波器监测**: - 测量VREF引脚电压纹波(应小于10mVpp) - 检查ADC时钟是否超频(通常不超过14MHz) 3. **数据校验**: ```c // 在DMA完成中断中校验数据 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if(adc_buf[0] < 100 || adc_buf[0] > 4095) { // 12位ADC范围 Error_Handler(); } } ``` 通过上述优化,通常可将VREFINT通道的采样误差控制在±0.5%以内。实际项目中,建议定期用外部基准源校准VREFINT值以提高长期稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值