stm32多路ADC采集

希望采用stm32F1采集6路ADC,用来DMA把数据采集到数组中,但是结果不正确,代码如下:

u16 adc_original_data[6] = {0};


void  Adc_Init(void)
{     
    
    ADC_InitTypeDef ADC_InitStructure; 
    GPIO_InitTypeDef GPIO_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );//使能端口A时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );//使能ADC1通道时钟
    
    //初始化DMA
    DMA_DeInit(DMA1_Channel1);//将DMA的通道1寄存器重设为缺省值
    
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)( ADC1_BASE+0x4c);//DMA外设ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)adc_original_data;//DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读到内存
    DMA_InitStructure.DMA_BufferSize = 6;//总接收发数据长度
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址寄存器不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度为16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在循环模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;// 当使用一个DMA通道时,优先级设置不影响
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//DMA通道x没有设置为内存到内存传输
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);    
    
    // 使能 DMA 通道
    DMA_Cmd(DMA1_Channel1 , ENABLE);
    
    
    
    //PA1 作为模拟通道输入引脚                         
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        //模拟输入引脚
    GPIO_Init(GPIOA, &GPIO_InitStructure);    
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    
    ADC_DeInit(ADC1);//复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
    
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);//设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式
    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数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 6;//顺序进行规则转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure);//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
    
    // 配置ADC 通道的转换顺序和采样时间
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 4, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 5, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 6, ADC_SampleTime_239Cycles5);
    
    
    ADC_Cmd(ADC1, ENABLE);//使能指定的ADC1
    ADC_DMACmd(ADC1, ENABLE);//使能ADC DMA 请求
    
    ADC_ResetCalibration(ADC1);    //使能复位校准  
    while(ADC_GetResetCalibrationStatus(ADC1));    //等待复位校准结束
    
    ADC_StartCalibration(ADC1);     //开启AD校准
    while(ADC_GetCalibrationStatus(ADC1));     //等待校准结束
    
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使用软件触发ADC转换 
}

给一个电位器两端接的3.3V和GND,用万用表测量中间电位0.9V,把通道一接在电位器中间那个脚上
用串口把结果打印在电脑上明显不对

img

这是main里打印的代码:

    while(1)
    {
        for(i=0;i<=5;i++){
            voltage[i] = (float)(adc_original_data[i]*(3.3/4096));
            printf("通道%d的值 = %f \r\n",i+1,voltage[i]);
        }
        printf("\r\n");
        printf("\r\n");
        printf("\r\n");
        delay_ms(10);

麻烦看看代码哪不对,感谢

不用DMA的功能,从ADC的寄存器里读取ADC的值是否正确?

  • 帮你找了个相似的问题, 你可以看下: https://ask.csdn.net/questions/7619303
  • 这篇博客你也可以参考下:STM32F1xx 多路ADC 使用DMA转换结果顺序错乱问题
  • 除此之外, 这篇博客: STM32F103的ADC基于DMA方式传输数据发生错位现象解决中的 STM32F103的ADC基于DMA方式传输数据发生错位现象解决 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 以下回答由chatgpt基于相关博客总结生成:

    根据问题描述和参考资料,可能存在以下几个可能导致ADC采集结果异常的问题:

    1. 参考电压问题:检查VDDA和VDD引脚是否正确连接,以及VREF+引脚是否大于2.5V,确保参考电压设置正确。

    2. ADC通道转换模式问题:检查代码中是否正确选择了合适的ADC转换模式。根据参考资料中的说明,可以尝试使用单通道连续转换模式(CONT=1, SCAN=0)或多通道连续扫描转换模式(CONT=1, SCAN=1)来结合DMA进行数据传输。

    3. DMA设置问题:确认DMA设置正确,包括DMA通道和传输方向,以确保数据能够正确传输到数组中。

    4. ADC精度设置问题:检查是否正确配置了ADC的精度,确保精度设置与所需的采样精度一致。

    5. 电源电压问题:检查芯片的电源电压是否稳定,如果电源电压波动较大,可能会对ADC采集结果产生影响。

    根据以上可能的问题,可以逐步排查,修改代码和配置,以解决ADC采集结果异常的问题。以下是一种可能的解决方案示例:

    #include "stm32f1xx_hal.h"
    
    // 定义ADC数据缓冲区
    #define ADC_BUFFER_SIZE 6
    uint16_t adcBuffer[ADC_BUFFER_SIZE];
    
    ADC_HandleTypeDef hadc1;
    DMA_HandleTypeDef hdma_adc1;
    
    // 初始化ADC和DMA
    void ADC_DMA_Init(void)
    {
      // 使能ADC1时钟
      __HAL_RCC_ADC1_CLK_ENABLE();
      // 使能DMA1时钟
      __HAL_RCC_DMA1_CLK_ENABLE();
    
      // 配置ADC1
      hadc1.Instance = ADC1;
      hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE; // 开启扫描模式
      hadc1.Init.ContinuousConvMode = ENABLE; // 开启连续转换模式
      hadc1.Init.NbrOfConversion = ADC_BUFFER_SIZE; // 设置通道数量
      hadc1.Init.DiscontinuousConvMode = DISABLE; // 关闭间断模式
      hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 手动触发转换
      hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 数据右对齐
      hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; // 每次扫描转换结束后触发中断
      if (HAL_ADC_Init(&hadc1) != HAL_OK)
      {
        Error_Handler();
      }
    
      // 配置ADC通道
      ADC_ChannelConfTypeDef sConfig = {0};
      sConfig.Channel = ADC_CHANNEL_1; // 选择通道1
      sConfig.Rank = 1;
      sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5; // 设置采样时间
      if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
      {
        Error_Handler();
      }
    
      // 配置DMA
      hdma_adc1.Instance = DMA1_Channel1; // 选择DMA1通道1
      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; // 外设数据对齐为HALFWORD(16位)
      hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; // 内存数据对齐为HALFWORD(16位)
      hdma_adc1.Init.Mode = DMA_CIRCULAR; // 循环模式
      hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH; // 设置DMA优先级为高
      if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
      {
        Error_Handler();
      }
    
      // 关联ADC和DMA
      __HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
    
      // 使能DMA传输
      if (HAL_DMA_Start(&hdma_adc1, (uint32_t)&ADC1->DR, (uint32_t)adcBuffer, ADC_BUFFER_SIZE) != HAL_OK)
      {
        Error_Handler();
      }
    
      // 启动ADC转换
      if (HAL_ADC_Start(&hadc1) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    // ADC DMA中断处理函数
    void DMA1_Channel1_IRQHandler(void)
    {
      // 处理DMA中断请求
      HAL_DMA_IRQHandler(&hdma_adc1);
    }
    
    // ADC转换完成中断回调函数
    void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
    {
      // 数据处理代码
      // 处理前一半数据
    }
    
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
    {
      // 数据处理代码
      // 处理后一半数据
    }
    
    int main()
    {
      HAL_Init();
    
      // 配置系统时钟、GPIO等
    
      ADC_DMA_Init();
    
      while (1)
      {
        // 等待数据处理完成
      }
    }
    

    上述代码中,通过使用DMA和中断来实现ADC采集,并将结果保存到数组中。确保配置了正确的参考电压、ADC通道转换模式和相关DMA设置。在ADC转换完成后的中断回调函数中可以添加数据处理的代码,可根据需求将数据打印到串口或其他方式进行处理。根据具体情况,可以在主循环中等待数据处理完成后继续进行其他操作。

    以上是一种解决问题的思路和示例代码,具体的解决方案还需要根据具体的硬件和软件环境进行调试和优化。如果还存在其他问题或者有其他细节需要说明,请提供更多的信息进行进一步分析和解答。

单独采集ADC结果正确的,实在是头秃