硬汉嵌入式论坛

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

[ADC] ADC双缓冲采样波形丢失问题

[复制链接]

2

主题

14

回帖

20

积分

新手上路

积分
20
发表于 2019-11-11 10:22:37 | 显示全部楼层 |阅读模式
我用H7开发板采集信号发生器生成的50HZ信号,程序是再V7例程上面改的,程序大概流程是用双缓冲保存采集的数据然后再把保存的数据不断的写入到SDRAM,再从SDRAM读取并用串口发送出来。采样率低的时候波形是完整的,但是一把采样率调高到1M就不完整了,之前以为是cache的原因但是例程里面有考虑这个呀,为什么我的数据会出现丢失呢?
/*
*********************************************************************************************************
*        函 数 名: TIM1_Config
*        功能说明: 配置TIM1,用于触发ADC,当前配置的100KHz触发频率
*        形    参: 无                                                                          
*        返 回 值: 无
*********************************************************************************************************
*/
static void TIM1_Config(uint16_t load)
{
        TIM_OC_InitTypeDef sConfig = {0};


        /* 使能时钟 */  
        __HAL_RCC_TIM1_CLK_ENABLE();

    /*-----------------------------------------------------------------------
                bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下:

        System Clock source       = PLL (HSE)
        SYSCLK(Hz)                = 400000000 (CPU Clock)
        HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
        AHB Prescaler             = 2
        D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)
        D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)
        D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)
        D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)

        因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz;
        因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
        APB4上面的TIMxCLK没有分频,所以就是100MHz;

        APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
        APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17

        APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5

    TIM12CLK = 200MHz/(Period + 1) / (Prescaler + 1) = 200MHz / 2000 / 1 = 100KHz
        ----------------------------------------------------------------------- */  
    HAL_TIM_Base_DeInit(&htim);

    htim.Instance = TIM1;
                htim.Init.Period            = 199;
                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 = 100;  
    if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)
    {

    }

    /* 启动OC1 */
    if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK)
    {

    }
}




void bsp_InitADC(uint32_t addr, uint16_t count, uint16_t load)
{
        ADC_ChannelConfTypeDef   sConfig = {0};
        GPIO_InitTypeDef         GPIO_InitStruct;

       
  /* ## - 1 - 配置ADC采样的时钟 ####################################### */
#if defined (ADC_CLOCK_SOURCE_PLL)
        /* 配置PLL2时钟为的72MHz,方便分频产生ADC最高时钟36MHz */
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        PeriphClkInitStruct.PLL2.PLL2M = 25;
        PeriphClkInitStruct.PLL2.PLL2N = 504;
        PeriphClkInitStruct.PLL2.PLL2P = 7;
        PeriphClkInitStruct.PLL2.PLL2Q = 7;
        PeriphClkInitStruct.PLL2.PLL2R = 7;
        PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0;
        PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
        PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
        PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);  
        }
#elif defined (ADC_CLOCK_SOURCE_AHB)

  /* 使用AHB时钟的话,无需配置,默认选择*/

#endif

        /* ## - 2 - 配置ADC采样使用的引脚 ####################################### */
        __HAL_RCC_GPIOA_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        /* ## - 3 - 配置ADC采样使用的时钟 ####################################### */
        __HAL_RCC_DMA1_CLK_ENABLE();
        DmaHandle.Instance                 = DMA1_Stream1;            /* 使用的DMA1 Stream1 */
        DmaHandle.Init.Request             = DMA_REQUEST_ADC1;            /* 请求类型采用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此位不起作用,用于外设突发 */

    /* 初始化DMA */
    if(HAL_DMA_Init(&DmaHandle) != HAL_OK)
    {

    }

    /* 开启DMA1 Stream1的中断 */
    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);

    /* 关联ADC句柄和DMA句柄 */
        __HAL_LINKDMA(&AdcHandle, DMA_Handle, DmaHandle);

        /* ## - 4 - 配置ADC ########################################################### */
        __HAL_RCC_ADC12_CLK_ENABLE();
        AdcHandle.Instance = ADC1;
       
#if defined (ADC_CLOCK_SOURCE_PLL)
        AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_ASYNC_DIV2;          /* 采用PLL异步时钟,2分频,即72MHz/2 = 36MHz */
#elif defined (ADC_CLOCK_SOURCE_AHB)
        AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;      /* 采用AHB同步时钟,4分频,即200MHz/4 = 50MHz */
#endif
        AdcHandle.Init.Resolution            = ADC_RESOLUTION_16B;            /* 16位分辨率 */
        AdcHandle.Init.ScanConvMode          = ADC_SCAN_DISABLE;              /* 禁止扫描,因为仅开了一个通道 */
        AdcHandle.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;           /* EOC转换结束标志 */
        AdcHandle.Init.LowPowerAutoWait      = DISABLE;                       /* 禁止低功耗自动延迟特性 */
        AdcHandle.Init.ContinuousConvMode    = DISABLE;                       /* 禁止自动转换,采用的定时器触发转换 */
        AdcHandle.Init.NbrOfConversion       = 1;                             /* 使用了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;                         /* 禁止过采样 */

    /* 初始化ADC */
        if (HAL_ADC_Init(&AdcHandle) != HAL_OK)
        {

        }

        /* 校准ADC,采用偏移校准 */
        if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
        {

        }

        /* 配置ADC通道  */
        sConfig.Channel      = ADC_CHANNEL_3;              /* 配置使用的ADC通道 */
        sConfig.Rank         = ADC_REGULAR_RANK_1;          /* 采样序列里的第1个 */
        sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;     /* 采样周期 */
        sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* 单端输入 */
        sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* 无偏移 */
        sConfig.Offset = 0;                                 /* 无偏移的情况下,此参数忽略 */

        if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
        {

        }

        /* ## - 5 - 配置ADC的定时器触发 ####################################### */
        TIM1_Config(load);

        /* ## - 6 - 启动ADC的DMA方式传输 ####################################### */
        if (HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)addr, count) != HAL_OK)
        {

        }
}


void Stop_adc(void)
{
        HAL_TIM_OC_Stop(&htim, TIM_CHANNEL_1);
        HAL_ADC_Stop_DMA(&AdcHandle);
}

/*
*********************************************************************************************************
*        函 数 名: DMA1_Stream1_IRQHandler
*        功能说明: DMA1 Stream1中断服务程序
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
extern uint16_t Buff1[20*1024];
void DMA1_Stream1_IRQHandler(void)
{
        /* 传输完成中断 */
        if((DMA1->LISR & DMA_FLAG_TCIF1_5) != RESET)
        {
                HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
                SCB_InvalidateDCache_by_Addr((uint32_t *)(&Buff1[10240]), 20480);
                Sample_Data.flag = 2;       
                /* 清除标志 */
                DMA1->LIFCR = DMA_FLAG_TCIF1_5;
        }

        /* 半传输完成中断 */   
        if((DMA1->LISR & DMA_FLAG_HTIF1_5) != RESET)
        {
                HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);       
                SCB_InvalidateDCache_by_Addr((uint32_t *)(&Buff1[0]), 20480);               
                Sample_Data.flag = 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;
        }
}

sap_1M.png
sap_100k.png
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-11 10:24:25 | 显示全部楼层
图片一个是1M采样率,一个是100k采样率,STMH7系列不可能处理不了1M采样率啊
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107046
QQ
发表于 2019-11-11 10:36:00 | 显示全部楼层
你的数据怎么刷新出来的,是不是刷新速度没有跟上ADC采样速度。
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-11 10:51:06 | 显示全部楼层
eric2013 发表于 2019-11-11 10:36
你的数据怎么刷新出来的,是不是刷新速度没有跟上ADC采样速度。

数据我是先保存后读取的,数据采集时,每存满一个缓存主循环就把这个缓存的数据转移到SDRAM,直到采集到了我指定大小的数据之后才停止。调了好久了,真是无语了,400M主频怎么就会丢失数据呢
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107046
QQ
发表于 2019-11-11 10:53:29 | 显示全部楼层
mvvm 发表于 2019-11-11 10:51
数据我是先保存后读取的,数据采集时,每存满一个缓存主循环就把这个缓存的数据转移到SDRAM,直到采集到 ...

才1M的速度,我F4的一代示波器8.4Msps都不会丢失。
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-11 11:09:04 | 显示全部楼层
eric2013 发表于 2019-11-11 10:53
才1M的速度,我F4的一代示波器8.4Msps都不会丢失。

对呀,我也很无语了,你说ADC配置有问题把,它又能采,哥你能帮我分析分析哪里的问题码?cache应该不会影响吧,我是按照你的方式做的
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-11 17:28:14 | 显示全部楼层
自己顶一下
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107046
QQ
发表于 2019-11-12 07:55:23 | 显示全部楼层
mvvm 发表于 2019-11-11 11:09
对呀,我也很无语了,你说ADC配置有问题把,它又能采,哥你能帮我分析分析哪里的问题码?cache应该不会影 ...

你现在的主RAM空间用的那个? AXI SRAM吗,MPU如何配置的
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-12 17:33:08 | 显示全部楼层
eric2013 发表于 2019-11-12 07:55
你现在的主RAM空间用的那个? AXI SRAM吗,MPU如何配置的

MPU我用的是你们例程里的,我在里面改了改

void MPU_Config(void)
{
        MPU_Region_InitTypeDef MPU_InitStruct;

        /* 禁止 MPU */
        HAL_MPU_Disable();

        /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x24000000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;//我改了这里
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;       
//        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
//        MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

        HAL_MPU_ConfigRegion(&MPU_InitStruct);
       
       
        /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x60000000;
        MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;       
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;        /* 不能用MPU_ACCESS_CACHEABLE;会出现2次CS、WE信号 */
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
       
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
       
       
        /* 配置SRAM4的属性为Write through, read allocate,no write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x38000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;       
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

        /*使能 MPU */
        HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107046
QQ
发表于 2019-11-12 17:40:22 | 显示全部楼层
mvvm 发表于 2019-11-12 17:33
MPU我用的是你们例程里的,我在里面改了改

void MPU_Config(void)

你当前的主控RAM用的那个
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-12 17:54:27 | 显示全部楼层
eric2013 发表于 2019-11-12 17:40
你当前的主控RAM用的那个

H743,我刚才把SCB_InvalidateDCache_by_Addr((uint32_t *)(&Buff1[0]), 2048);                这一句注释掉采出来的是连续的,说明之前就是cache没设置好,只是偶尔还是会有不连续现象出现。
2019-11-12 175254.png
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-13 12:52:09 | 显示全部楼层
问题解决了
回复

使用道具 举报

13

主题

223

回帖

262

积分

高级会员

积分
262
发表于 2019-11-13 20:19:13 | 显示全部楼层

什么原因?
回复

使用道具 举报

13

主题

223

回帖

262

积分

高级会员

积分
262
发表于 2019-11-13 20:21:17 | 显示全部楼层
mvvm 发表于 2019-11-12 17:33
MPU我用的是你们例程里的,我在里面改了改

void MPU_Config(void)

你串口波特率多少?
回复

使用道具 举报

13

主题

223

回帖

262

积分

高级会员

积分
262
发表于 2020-1-15 18:15:02 | 显示全部楼层
楼主,你这个波形也有不完整的地方,这个问题解决了吗?
213.png
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107046
QQ
发表于 2020-1-15 21:14:42 | 显示全部楼层
miaoqiongb 发表于 2020-1-15 18:15
楼主,你这个波形也有不完整的地方,这个问题解决了吗?

楼主的解决了,他的是Cache没有处理好
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2020-2-13 19:40:49 | 显示全部楼层
楼主,你的问题怎么解决的啊,我也遇到同样的问题了
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107046
QQ
发表于 2020-2-13 21:23:06 | 显示全部楼层
ahhj6000 发表于 2020-2-13 19:40
楼主,你的问题怎么解决的啊,我也遇到同样的问题了

他是Cache没有处理好,简单些,将ADC所使用的RAM空间关闭了Cache即可解决。
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-15 12:29 , Processed in 0.215951 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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