硬汉嵌入式论坛

 找回密码
 立即注册
查看: 228|回复: 6
收起左侧

[STM32H7] 使用定时器触发ADC采集,DMA双缓冲区传输,最终打印出来的数据和信号源给的数据不一致,波形失真是怎么回事?

[复制链接]

5

主题

16

回帖

31

积分

新手上路

积分
31
发表于 2025-4-13 00:04:43 | 显示全部楼层 |阅读模式

使用定时器触发ADC采样,DMA双缓冲区配置,信号发生器给一个1KHz的方波,采集后的波形如下:


不知道是什么问题导致的,其中采样率设置为100KHz,采样周期32.5和ADC时钟中期,ADC时钟频率为32MHz。

代码如下:


extern UART_HandleTypeDef g_uartHandle;
ALIGN_32BYTES(__attribute__((section (".RAM_D3"))) uint16_t g_adc2Buffer[128]);
__IO uint8_t s_DmaFlag = 0;  /* 1表示进DMA半传输完成中断,2表示进入DMA传输完成中断 */
__IO uint8_t g_adc_dma_sta = 0;

DMA_HandleTypeDef                   DmaHandle = {0};       
ADC_HandleTypeDef                   AdcHandle = {0};

#define ADC_NUM_MAX (sizeof(g_adcGpioList)/sizeof(g_adcGpioList[0]))
typedef struct
{
        GPIO_TypeDef *gpio;
        uint32_t pin;
}AdcGpio_t;

static AdcGpio_t g_adcGpioList[] =
{
        {GPIOC, GPIO_PIN_0},                //ADC2_INP4-->CH2+
        {GPIOC, GPIO_PIN_5},                //ADC2_INN4-->CH2-
};

/**
***********************************************************************
*@brief 初始化ADC GPIO
*@param       
*@return
***********************************************************************
**/
static void GpioInit(void)
{
        GPIO_InitTypeDef gpioInitTypeDef = {0};
       
        __HAL_RCC_GPIOC_CLK_ENABLE();
       
        gpioInitTypeDef.Mode = GPIO_MODE_ANALOG;
        for(uint8_t i = 0; i < ADC_NUM_MAX; i++)
        {
                gpioInitTypeDef.Pin = g_adcGpioList.pin;
                HAL_GPIO_Init(g_adcGpioList.gpio, &gpioInitTypeDef);
        }
}

static void AdcPreiphClkInit()
{
        RCC_PeriphCLKInitTypeDef rccPeriphClkInit = {0};
        /* 配置PLL2时钟为的72MHz,方便分频产生ADC最高时钟36MHz */
        rccPeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        rccPeriphClkInit.PLL2.PLL2M = 25;
        rccPeriphClkInit.PLL2.PLL2N = 504;
        rccPeriphClkInit.PLL2.PLL2P = 7;
        rccPeriphClkInit.PLL2.PLL2Q = 7;
        rccPeriphClkInit.PLL2.PLL2R = 7;
        rccPeriphClkInit.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0;
        rccPeriphClkInit.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
        rccPeriphClkInit.PLL2.PLL2FRACN = 0;
        rccPeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
        HAL_RCCEx_PeriphCLKConfig(&rccPeriphClkInit);
}


/*
*********************************************************************************************************
*        函 数 名: TIM1_Config
*        功能说明: 配置TIM1,用于触发ADC,当前配置的100KHz触发频率
*        形    参: 无                                                                          
*        返 回 值: 无
*********************************************************************************************************
*/
static void TIM1_Config(void)
{
        TIM_HandleTypeDef  htim ={0};
        TIM_OC_InitTypeDef sConfig = {0};

        /* 使能时钟 */  
        __HAL_RCC_TIM1_CLK_ENABLE();
    HAL_TIM_Base_DeInit(&htim);
   
    htim.Instance = TIM1;
        htim.Init.Period            = 1999;
        htim.Init.Prescaler         = 0;
        htim.Init.ClockDivision     = 0;
        htim.Init.CounterMode       = TIM_COUNTERMODE_UP;
        htim.Init.RepetitionCounter = 0;
        HAL_TIM_Base_Init(&htim);
   
    sConfig.OCMode     = TIM_OCMODE_PWM1;
    sConfig.OCPolarity = TIM_OCPOLARITY_LOW;

    /* 占空比50% */
    sConfig.Pulse = 1000;  
    HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1);
    /* 启动OC1 */
    HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1);
}


static void Dma1Init(void)
{
        __HAL_RCC_DMA1_CLK_ENABLE();
        DmaHandle.Instance                 = DMA1_Stream1;                    /* 使用的DMA1 Stream1 */
        DmaHandle.Init.Request             = DMA_REQUEST_ADC2;                    /* 请求类型采用DMA_REQUEST_ADC1 */  
        DmaHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;            /* 传输方向是从外设到存储器 */  
        DmaHandle.Init.PeriphInc           = DMA_PINC_DISABLE;                /* 外设地址自增禁止 */
        DmaHandle.Init.MemInc              = DMA_MINC_ENABLE;                 /* 存储器地址自增使能 */  
        DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;          /* 外设数据传输位宽选择半字,即16bit */     
        DmaHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;          /* 存储器数据传输位宽选择半字,即16bit */   
        DmaHandle.Init.Mode                = DMA_CIRCULAR;                    /* 循环模式 */   
        DmaHandle.Init.Priority            = DMA_PRIORITY_LOW;               /* 优先级低 */  
        DmaHandle.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;            /* 禁止FIFO*/
        DmaHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;         /* 禁止FIFO此位不起作用,用于设置阀值 */
        DmaHandle.Init.MemBurst            = DMA_MBURST_SINGLE;               /* 禁止FIFO此位不起作用,用于存储器突发 */
        DmaHandle.Init.PeriphBurst         = DMA_PBURST_SINGLE;               /* 禁止FIFO此位不起作用,用于外设突发 */
       
        HAL_DMA_Init(&DmaHandle);
       
         /* 开启DMA1 Stream1的中断 */
        HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 2, 0);
        HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
   
    /* 关联ADC句柄和DMA句柄 */
        __HAL_LINKDMA(&AdcHandle, DMA_Handle, DmaHandle);
}


static void Adc2Init(void)
{
        ADC_ChannelConfTypeDef           sConfig = {0};

        __HAL_RCC_ADC12_CLK_ENABLE();
        AdcHandle.Instance = ADC2;
        AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_ASYNC_DIV2;                  /* 采用PLL异步时钟,2分频,即72MHz/2 = 36MHz */
        AdcHandle.Init.Resolution            = ADC_RESOLUTION_16B;                    /* 16位分辨率 */
        AdcHandle.Init.ScanConvMode          = ADC_SCAN_ENABLE;                      /* 禁止扫描,因为仅开了一个通道 */   //++++
        AdcHandle.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;                   /* EOC转换结束标志 */
        AdcHandle.Init.LowPowerAutoWait      = DISABLE;                               /* 禁止低功耗自动延迟特性 */
        AdcHandle.Init.ContinuousConvMode    = ENABLE;                               /* 禁止自动转换,采用的定时器触发转换 */  //++
        AdcHandle.Init.NbrOfConversion       = 2;                                     /* 使用了1个转换通道 */
        AdcHandle.Init.DiscontinuousConvMode = DISABLE;                               /* 禁止不连续模式 */
        AdcHandle.Init.NbrOfDiscConversion   = 1;                                     /* 禁止不连续模式后,此参数忽略,此位是用来配置不连续子组中通道数 */
        AdcHandle.Init.ExternalTrigConv      = ADC_EXTERNALTRIG_T1_CC1;            /* 定时器1的CC1触发 */
        AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_RISING;    /* 上升沿触发 */
        AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; /* DMA循环模式接收ADC转换的数据 */
        AdcHandle.Init.Overrun                  = ADC_OVR_DATA_OVERWRITTEN;        /* ADC转换溢出的话,覆盖ADC的数据寄存器 */
        AdcHandle.Init.OversamplingMode         = DISABLE;     

        HAL_ADC_Init(&AdcHandle);   
        HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_DIFFERENTIAL_ENDED);

        /* 配置ADC通道  */
        sConfig.Channel      = ADC_CHANNEL_4;              /* 配置使用的ADC通道 */
        sConfig.Rank         = ADC_REGULAR_RANK_1;          /* 采样序列里的第1个 */
        sConfig.SamplingTime = ADC_SAMPLETIME_32CYCLES_5;     /* 采样周期 */
        sConfig.SingleDiff   = ADC_DIFFERENTIAL_ENDED;            /* 单端输入 */
        sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* 无偏移 */
        sConfig.Offset = 0;                                 /* 无偏移的情况下,此参数忽略 */
        HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
       
        TIM1_Config();
       
        HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)g_adc2Buffer, 128);
}



void AdcDrvInit(void)
{
        AdcPreiphClkInit();
        GpioInit();
        Dma1Init();
        Adc2Init();
}

/**
* @brief       ADC DMA采集中断服务函数
* @param       无
* @retval      无
*/
uint8_t s_dmaFlag = 0;


void DMA1_Stream1_IRQHandler(void)
{
        if((DMA1->LISR & DMA_FLAG_TCIF1_5) != RESET)
        {
                s_dmaFlag = 2;
                DMA1->LIFCR = DMA_FLAG_TCIF1_5;
        }
        if((DMA1->LISR & DMA_FLAG_HTIF1_5) != RESET)
        {
                s_dmaFlag = 1;
                DMA1->LIFCR = DMA_FLAG_HTIF1_5;
        }
                /* 传输错误中断 */
        if((DMA1->LISR & DMA_FLAG_TEIF1_5) != RESET)
        {
                /* 清除标志 */
                DMA1->LIFCR = DMA_FLAG_TEIF1_5;
        }

        /* 直接模式错误中断 */
        if((DMA1->LISR & DMA_FLAG_DMEIF1_5) != RESET)
        {
                /* 清除标志 */
                DMA1->LIFCR = DMA_FLAG_DMEIF1_5;
        }
       
         g_adc_dma_sta = 1;
}

void bsp_GetAdcValues(void)
{
        uint32_t values;
        uint32_t sam;
        uint8_t byte1;
        uint8_t byte2;
        float temp;
       
        /* 当前DMA操作是后半个缓冲,读取前半个缓冲的前4个数值求平均 */
        if(s_dmaFlag == 1)
        {
                DISABLE_INT();
                s_dmaFlag = 0;              
                for(uint16_t i = 0; i < 64; i++)
                {
                         values = g_adc2Buffer;
                        printf("%d\r\n", g_adc2Buffer);

                }
                ENABLE_INT();
        }
        /* 当前DMA操作是后前个缓冲,读取后半个缓冲的前4个数值求平均 */
        else if(s_dmaFlag == 2)
        {
                DISABLE_INT();
                s_dmaFlag = 0;
                for(uint16_t j = 0; j < 64; j++)
                {
                        values = g_adc2Buffer[j+64];               
                        printf("%d\r\n", g_adc2Buffer[j]);
                }

                ENABLE_INT();
        }
       

}

回复

使用道具 举报

5

主题

16

回帖

31

积分

新手上路

积分
31
 楼主| 发表于 2025-4-13 00:07:13 | 显示全部楼层
上述波形,如图所示。采集的波形有点看不懂怎么回事,找不到问题出在哪里了
微信图片编辑_20250413000536.jpg
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2025-4-13 09:05:05 | 显示全部楼层
你的 g_adc2Buffer[128]);好像没有处理MPU Cache。可以考虑直接在程序开头直接调用SCB_DisableDCache
回复

使用道具 举报

5

主题

16

回帖

31

积分

新手上路

积分
31
 楼主| 发表于 2025-4-13 09:36:08 | 显示全部楼层
我没有开cache
回复

使用道具 举报

5

主题

16

回帖

31

积分

新手上路

积分
31
 楼主| 发表于 2025-4-13 09:42:44 | 显示全部楼层
eric2013 发表于 2025-4-13 09:05
你的 g_adc2Buffer[128]);好像没有处理MPU Cache。可以考虑直接在程序开头直接调用SCB_DisableDCache

汉哥,我初始化的时候就失能了cache
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2025-4-14 08:37:32 | 显示全部楼层
langlangago 发表于 2025-4-13 09:42
汉哥,我初始化的时候就失能了cache

这个没问题的话,那就得检查下软件配置了,比如我看你实际程序里面仅配置了一个通道

那你这个参数配置应该仅设置为1

AdcHandle.Init.NbrOfConversion       = 2;                                     /* 使用了1个转换通道 */
回复

使用道具 举报

15

主题

49

回帖

94

积分

初级会员

积分
94
发表于 2025-4-14 08:49:23 | 显示全部楼层
定时器触发,AdcHandle.Init.ContinuousConvMode    = ENABLE;                               /* 禁止自动转换,采用的定时器触发转换 */  //++
这个要disable
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-4-26 01:38 , Processed in 0.428233 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表