硬汉嵌入式论坛

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

[STM32H7] 请教:DMA方式实现频率捕获中的MPU设置

[复制链接]

12

主题

178

回帖

214

积分

高级会员

积分
214
发表于 2022-6-6 16:34:48 | 显示全部楼层 |阅读模式
如题,这几天在调试DMA方式通过TIM Capture实现 10KHz左右的PWM频率测量,参考 bsp_tim_capture.c 相关设置如下:

/** 32-bytes Alignment is needed for cache maintenance purpose */
ALIGN_32BYTES(__attribute__((section (".RAM_D3"))) uint32_t tim_capture[TIM_CAP_BUFF_SIZE]);   

/* 注:SCT文件中与 RAM_D3 相关的定义:
RW_IRAM4 0x38000000 0x00010000  {  ; RW data - 64KB SRAM4(0x38000000)
   .ANY (+RW +ZI)
   *(.RAM_D3)
  }
*/

/* 相关MPU配置 */
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.TypeExtField     = MPU_TEX_LEVEL0;
MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;

MPU_InitStruct.Number           = MPU_REGION_NUMBER4; /* 其余MPU设置没有列出 */
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* 初始化 */
void bsp_Tim_Init(void)
{   
    /* 配置GPIO */
    {
        GPIO_InitTypeDef   GPIO_InitStruct;

        /* 使能GPIO时钟 */
        __HAL_RCC_GPIOA_CLK_ENABLE();

        GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull         = GPIO_NOPULL;
        GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
        GPIO_InitStruct.Pin         = GPIO_PIN_6;
        HAL_GPIO_Init( GPIOA, &GPIO_InitStruct );
    }

    /* 配置DMA */
    {
        /* Enable DMA clock */
        __HAL_RCC_DMA1_CLK_ENABLE();

        dmaHandle.Instance                 = DMA1_Stream0;
        dmaHandle.Init.Request             = DMA_REQUEST_TIM3_CH1;
        dmaHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;
        dmaHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
        dmaHandle.Init.MemInc              = DMA_MINC_ENABLE;
        dmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
        dmaHandle.Init.MemDataAlignment   = DMA_MDATAALIGN_WORD;
        dmaHandle.Init.Mode                       = DMA_CIRCULAR;    // DMA_CIRCULAR; DMA_NORMAL
        dmaHandle.Init.Priority                     = DMA_PRIORITY_VERY_HIGH;
        dmaHandle.Init.FIFOMode                = DMA_FIFOMODE_DISABLE;

        /* Deinitialize  & Initialize the DMA for new transfer */
        HAL_DMA_DeInit(&dmaHandle);

        if( HAL_DMA_Init( &dmaHandle ) != HAL_OK )
        {
            pErrorHandler( __FILE__, __LINE__ );
            return ;
        }

        /* Associate the DMA handle */
        __HAL_LINKDMA(&timHandle, hdma[TIM_DMA_ID_CC1], dmaHandle);

        /* NVIC configuration for DMA Input data interrupt */
        HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);         
    }

    /* 配置TIM */
    {
        TIM_IC_InitTypeDef sICConfig;
        TIM_MasterConfigTypeDef sMasterConfig = {0};

        __HAL_RCC_TIM3_CLK_ENABLE();

        /* Set TIMx instance */
        timHandle.Instance         = TIM3;
        timHandle.Init.Prescaler   = 1; /* 2分频 */
        timHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
        timHandle.Init.Period      = 0xFFFF;   /* TIM3是16位 */
        timHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
        timHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
        if (HAL_TIM_IC_Init(&timHandle) != HAL_OK)
        {
            pErrorHandler(__FILE__, __LINE__);
            return ;
        }

        /* 从cubemx中摘抄过来的 */
        sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
        sMasterConfig.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;
        if (HAL_TIMEx_MasterConfigSynchronization(&timHandle, &sMasterConfig) != HAL_OK)
        {
            pErrorHandler(__FILE__, __LINE__);
        }

        /*##-2- Configure the Input Capture channel ################################*/
        sICConfig.ICPolarity  = TIM_INPUTCHANNELPOLARITY_RISING;//TIM_ICPOLARITY_BOTHEDGE;
        sICConfig.ICSelection = TIM_ICSELECTION_DIRECTTI;
        sICConfig.ICPrescaler = TIM_ICPSC_DIV1;
        sICConfig.ICFilter    = 0;
        if (HAL_TIM_IC_ConfigChannel(&timHandle, &sICConfig, TIM_CHANNEL_1) != HAL_OK)
        {
            pErrorHandler(__FILE__, __LINE__);
            return ;
        }

        __HAL_TIM_DISABLE(&timHandle);

        /* Enable the DMA Stream */
        HAL_DMA_Start_IT(timHandle.hdma[TIM_DMA_ID_CC1], (uint32_t)&timHandle.Instance->CCR1, (uint32_t)tim_capture, TIM_CAP_BUFF_SIZE);

        /* Enable the TIM Capture/Compare 1 DMA request */      
        __HAL_TIM_ENABLE_DMA(&timHandle, TIM_DMA_CC1);

        /* Enable the Input Capture channel */
        TIM_CCxChannelCmd(timHandle.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);

        /* Enable the Peripheral */
        __HAL_TIM_ENABLE(&timHandle);      
    }
}

/* 中断处理函数 */
void DMA1_Stream0_IRQHandler( void )
{   
    /* LISR: DMA low interrupt status register */
    if( (DMA1->LISR & DMA_FLAG_TCIF0_4) != RESET )
    {
                /*
                   1、使用此函数要特别注意,第1个参数地址要32字节对齐,第2个参数要是32字节的整数倍。
                   2、进入传输完成中断,当前DMA正在使用缓冲区的前半部分,用户可以操作后半部分。
                */
                SCB_InvalidateDCache_by_Addr((uint32_t *)tim_capture, TIM_CAP_BUFF_SIZE);
//        SCB_InvalidateDCache();

                /* 清除标志:
         * 注意位的清除方式:This bit is set by hardware.
         * It is cleared by software writing 1 to the corresponding bit in the DMA_LIFCR register
         */
                DMA1->LIFCR |= DMA_FLAG_TCIF0_4;
    }

        /* 传输错误中断 */
        if((DMA1->LISR & DMA_FLAG_TEIF0_4) != RESET)
        {        
                /* 清除标志 */
                DMA1->LIFCR |= DMA_FLAG_TEIF0_4;
        }

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


故障现象:
在中断中打断点发现,无论采用 SCB_InvalidateDCache_by_Addr((uint32_t *)tim_capture, TIM_CAP_BUFF_SIZE) 还是 SCB_InvalidateDCache(),
tim_capture 中的数据不是每次都全部更新;
而且,有些相邻两个数据之间的差值明显偏离正确值。

请问硬汉,是MPU配置哪儿有问题吗?谢谢
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106882
QQ
发表于 2022-6-6 17:36:36 | 显示全部楼层
直接关闭读写Cache测试是否正常。
回复

使用道具 举报

12

主题

178

回帖

214

积分

高级会员

积分
214
 楼主| 发表于 2022-6-6 18:49:42 | 显示全部楼层
eric2013 发表于 2022-6-6 17:36
直接关闭读写Cache测试是否正常。

MPU设置为如下
            MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
            MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
            MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
            MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
每次进入中断数据都有更新。

但是:有些相邻两个数据之间的差值明显偏离正确值。

劳烦再帮我看看是哪儿的问题吧,谢谢
回复

使用道具 举报

19

主题

62

回帖

119

积分

初级会员

积分
119
发表于 2022-6-6 21:57:55 | 显示全部楼层
SCB_InvalidateDCache_by_Addr((uint32_t *)tim_capture, TIM_CAP_BUFF_SIZE);
改成
SCB_InvalidateDCache_by_Addr((uint32_t *)tim_capture, TIM_CAP_BUFF_SIZE+32);
回复

使用道具 举报

12

主题

178

回帖

214

积分

高级会员

积分
214
 楼主| 发表于 2022-6-7 14:43:45 | 显示全部楼层
问题差不多解决了,原来的设置存在的问题

1. tim分频系数小,时钟过快,导致误触发,将分频增大至19,即时钟调整至10MHz
2. 设置滤波系数
3. 修改为单次传输,DMA_NORMAL,在中断里面处理完数据后再重新开启DMA传输   __HAL_DMA_ENABLE(timHandle.hdma[TIM_DMA_ID_CC1]);
4. 如果是连续传输 DMA_CIRCULAR ,需要双缓冲
谢谢各位
回复

使用道具 举报

82

主题

402

回帖

668

积分

金牌会员

积分
668
QQ
发表于 2022-8-7 17:26:30 | 显示全部楼层
我也碰到类似问题  不知道如何解决
有个脑洞的想法,H7-TOOL能否实时显示示波器输出1KHZ信号的上升时间和下降时间?
https://www.armbbs.cn/forum.php? ... 9&fromuid=18397
(出处: 硬汉嵌入式论坛)

武汉天纵鹏元科技有限公司。承接嵌入式项目开发,相关技术交流。STM32,物联网,工业控制方向。QQ  408137104
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-9 07:42 , Processed in 0.198049 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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