硬汉嵌入式论坛

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

[FMC] AD7606 DMA双缓冲方式,刚启动的一段时间DMA读到的数据为0问题

[复制链接]

5

主题

6

回帖

21

积分

新手上路

积分
21
发表于 2023-11-26 23:29:44 | 显示全部楼层 |阅读模式
本帖最后由 wtasbgg 于 2023-11-26 23:36 编辑

DMA双缓冲方式,启动转换时,DMA读到的数据前部分为0,这个一段时间感觉像是固定的,他采样率越高,启动开始的时候读到的数据为0 的时候越多,所有通道读出的数据都是0,感觉是FMC没有正常读到一样。后面读到的数据又正常。
10KHZ的采样率读到的数据,只取前4通道的数据,数据如下。
00 00 00 00 00 00 00 00 4E 1A 60 1A 74 1A 64 1A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 53 1A 65 1A 79 1A 69 1A 53 1A 65 1A 79 1A 69 1A 53 1A 65 1A 79 1A 69 1A 53 1A 65 1A 79 1A 69 1A 53 1A 65 1A 79 1A 69 1A 53 1A 65 1A 79 1A 69 1A 4D 1A 5F 1A 72 1A 63 1A 4D 1A 5F 1A 72 1A 63 1A 4D 1A 5F 1A 72 1A 63 1A 4D 1A 5F 1A 72 1A 63 1A 4D 1A 5F 1A 72 1A 63 1A 4D 1A 5F 1A 72 1A 63 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C 1A 5E 1A 72 1A 62 1A 4C

代码如下



/* DMA传输完成回调函数,弱定义 */
__weak void AD7606_DmaCplCb(DMA_HandleTypeDef *hdma)
{
//        HAL_MDMA_Start_IT(&MDMA_Handle, (uint32_t)&g_sAd7606Buf[8], (uint32_t)&adc_buff[adc_num] , 8, 1);
        memcpy(&adc_buff[adc_num],&g_sAd7606Buf[8],8);
        adc_num+=4;
        if(adc_num>max_num)
        {
                adc_over=1;
                AD7606_StopRecord();
        }
}

/* DMA半传输完成回调函数,弱定义 */
__weak void AD7606_DmaHalfCplCb(DMA_HandleTypeDef *hdma)
{
//        HAL_MDMA_Start_IT(&MDMA_Handle, (uint32_t)&g_sAd7606Buf[0], (uint32_t)&adc_buff[adc_num] ,8, 1);
        memcpy(&adc_buff[adc_num],&g_sAd7606Buf[0],8);
        adc_num+=4;
}

/* DMA中断服务程序 */
void TIMx_UP_DMA_IRQHandler(void)
{
        HAL_DMA_IRQHandler(&TIMDMA);
}

static void AD7606_SetTIMOutPWM(TIM_TypeDef* TIMx, uint32_t _ulFreq)
{
        TIM_OC_InitTypeDef sConfig = {0};        
        GPIO_InitTypeDef   GPIO_InitStruct;
        uint16_t usPeriod;
        uint16_t usPrescaler;
        uint32_t uiTIMxCLK;
        uint32_t pulse;

        
        /* 配置时钟 */
        CONVST_RCC_GPIO_CLK_ENABLE();
        CONVST_TIM3_CLK_ENABLE();
        TIMx_UP_DMA_STREAM_CLK_ENABLE();
        
        /* 配置引脚 */
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = CONVST_AF;
        GPIO_InitStruct.Pin = CONVST_PIN;
        HAL_GPIO_Init(CONVST_GPIO, &GPIO_InitStruct);
        
    /*-----------------------------------------------------------------------
                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

        ----------------------------------------------------------------------- */
        if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM15) || (TIMx == TIM16) || (TIMx == TIM17))
        {
                /* APB2 定时器时钟 = 200M */
                uiTIMxCLK = SystemCoreClock / 2;
        }
        else        
        {
                /* APB1 定时器 = 200M */
                uiTIMxCLK = SystemCoreClock / 2;
        }

        if (_ulFreq < 100)
        {
                usPrescaler = 10000 - 1;                                                        /* 分频比 = 10000 */
                usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1;                /* 自动重装的值, usPeriod最小值200, 单位50us */
                pulse = usPeriod;                                       /* 设置低电平时间50us,注意usPeriod已经进行了减1操作 */
        }
        else if (_ulFreq < 3000)
        {
                usPrescaler = 100 - 1;                                                        /* 分频比 = 100 */
                usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;        /* 自动重装的值, usPeriod最小值666,单位500ns */
                pulse = usPeriod-1;                                   /* 设置低电平时间1us,注意usPeriod已经进行了减1操作 */
        }
        else        /* 大于4K的频率,无需分频 */
        {
                usPrescaler = 0;                                                                /* 分频比 = 1 */
                usPeriod = uiTIMxCLK / _ulFreq - 1;                                /* 自动重装的值, usPeriod最小值1000,单位5ns */
                pulse = usPeriod - 199;                                      /* 设置低电平时间1us,注意usPeriod已经进行了减1操作 */
        }
   
        /*  PWM频率 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)*/
        TimHandle.Instance = TIMx;
        TimHandle.Init.Prescaler         = usPrescaler;         /* 用于设置定时器分频 */
        TimHandle.Init.Period            = usPeriod;            /* 用于设置定时器周期 */
        TimHandle.Init.ClockDivision     = 0;                   /* 用于指示定时器时钟 (CK_INT) 频率与死区发生器以及数字滤波器(ETR、 TIx)
                                                                   所使用的死区及采样时钟 (tDTS) 之间的分频比*/
        TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;  /* 用于设置计数模式,向上计数模式 */
        TimHandle.Init.RepetitionCounter = 0;                   /* 用于设置重复计数器,仅 TIM1 和 TIM8 有,其它定时器没有 */
        TimHandle.Init.AutoReloadPreload = 0;                   /* 用于设置定时器的 ARR 自动重装寄存器是更新事件产生时写入有效 */
        
        if (HAL_TIM_PWM_DeInit(&TimHandle) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);               
        }
        
        if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);
        }

        /* 配置定时器PWM输出通道 */
        sConfig.OCMode       = TIM_OCMODE_PWM1;         /* 配置输出比较模式 */
        sConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;     /* 设置输出高电平有效 */
        sConfig.OCFastMode   = TIM_OCFAST_DISABLE;      /* 关闭快速输出模式 */
        sConfig.OCNPolarity  = TIM_OCNPOLARITY_HIGH;    /* 配置互补输出高电平有效 */
        sConfig.OCIdleState  = TIM_OCIDLESTATE_SET;     /* 空闲状态时,设置输出比较引脚为高电平 */
        sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;  /* 空闲状态时,设置互补输出比较引脚为低电平 */

        /* 占空比 */
        sConfig.Pulse = pulse;
        if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, CONVST_TIMCH) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);
        }
        
        /* 使能定时器中断  */
        __HAL_TIM_ENABLE_DMA(&TimHandle, TIM_DMA_UPDATE);
        if (HAL_TIM_PWM_Start(&TimHandle, CONVST_TIMCH) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);
        }
        /* 定时器UP更新触发DMA传输 */               
        TIMDMA.Instance                 = TIMx_UP_DMA_STREAM;      /* 例化使用的DMA数据流 */
        TIMDMA.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;     /* 使能FIFO*/
        TIMDMA.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 用于设置阀值 */
        TIMDMA.Init.MemBurst            = DMA_MBURST_INC8;               /* 用于存储器突发 */
        TIMDMA.Init.PeriphBurst         = DMA_PBURST_INC8;               /* 用于外设突发 */
        TIMDMA.Init.Request             = TIMx_UP_DMA_REQUEST;     /* 请求类型 */  
        TIMDMA.Init.Direction           = DMA_PERIPH_TO_MEMORY;    /* 传输方向是从外设到存储器 */  
        TIMDMA.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */
        TIMDMA.Init.MemInc              = DMA_MINC_ENABLE;         /* 存储器地址自增使能 */  
        TIMDMA.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据传输位宽选择半字,即16bit */
        TIMDMA.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD; /* 存储器数据传输位宽选择半字,即16bit */   
        TIMDMA.Init.Mode                = DMA_CIRCULAR;                    /* 循环模式 */
        TIMDMA.Init.Priority            = DMA_PRIORITY_VERY_HIGH;        /* 优先级低 */
        
         /* 复位DMA */
        if(HAL_DMA_DeInit(&TIMDMA) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);     
        }
        
         /* 初始化DMA */
        if(HAL_DMA_Init(&TIMDMA) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);     
        }
        
        /* 关联DMA句柄到TIM */
        //__HAL_LINKDMA(&TimHandle, hdma[TIM_DMA_ID_UPDATE], TIMDMA);        
        
        /* 配置DMA中断 */
        HAL_NVIC_SetPriority(TIMx_UP_DMA_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(TIMx_UP_DMA_IRQn);
        
        /* 注册半传输完成中断和传输完成中断 */
        HAL_DMA_RegisterCallback(&TIMDMA, HAL_DMA_XFER_CPLT_CB_ID, AD7606_DmaCplCb);
        HAL_DMA_RegisterCallback(&TIMDMA, HAL_DMA_XFER_HALFCPLT_CB_ID, AD7606_DmaHalfCplCb);
        __HAL_DMA_CLEAR_FLAG(&TIMDMA,DMA_FLAG_TCIF1_5);
        __HAL_DMA_CLEAR_FLAG(&TIMDMA,DMA_FLAG_HTIF1_5);
        /* 启动DMA传输 */
        HAL_DMA_Start_IT(&TIMDMA, (uint32_t)AD7606_BASE, (uint32_t)g_sAd7606Buf, AD7606_BUFSIZE);

}



回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2023-11-27 11:27:20 | 显示全部楼层
这个不正常的有点多。

可以启动1秒后,再开始作为正式数据,看看后续的是否都正常。
回复

使用道具 举报

5

主题

6

回帖

21

积分

新手上路

积分
21
 楼主| 发表于 2023-11-27 13:15:33 | 显示全部楼层
eric2013 发表于 2023-11-27 11:27
这个不正常的有点多。

可以启动1秒后,再开始作为正式数据,看看后续的是否都正常。

我试过,和启动无关。我手动断点等待几秒再启动DMA,也是一样
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-4-29 07:46 , Processed in 0.162284 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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