打印
[STM32F4]

使用stm32f407,采用ssi方式读取mt6701编码器数据 ssi+dma中断方式

[复制链接]
148|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
cchwk|  楼主 | 2025-4-8 21:11 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
#申请原创#
使用stm32f407,采用ssi方式读取mt6701编码器数据,这是一个14bit的编码器,spi采用仅读取主模式,调试过程中遇到以下问题,在此记录解决过程如下:
Spi配置: 模式选择

Spi配置:时钟和边沿
常规方式测试
while (1)
  {            
                   g_6701_cnt++;
                   GPIOB->ODR&= (~(1<<15));
                   HAL_SPI_Receive(&hspi2,g_mt6701_rx_buf, 2, 100);            
                   GPIOB->ODR|= (1<<15);
                   g_debug_buf[0]= g_stc_svpwm_info_dq.ta;
                   g_debug_buf[1]= g_stc_svpwm_info_dq.tb;
                   g_debug_buf[2]= g_stc_svpwm_info_dq.tc;
                   g_debug_buf[3]= (real_t)(((uint16_t)g_mt6701_rx_buf[0] +(uint16_t)(g_mt6701_rx_buf[1]<<8)) >> 2);
                   fun_debug_commix_send_dis(g_debug_buf);
                   HAL_Delay(1);
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
  }
测试结果
DMA方式读取
Ioc文件配置
voidfun_set_mt6701_read_start(p_stc_mt6701_info pv)
{
         uint16_ttx_data = 0xffff;
         
         GPIOB->ODR&= (~(1<<15));
         pv->sta= e_mt6701_busy;
         HAL_SPI_Receive_DMA(&hspi2,(uint8_t*)pv->rx_data_buf, 2);
         __HAL_DMA_DISABLE_IT(&hdma_spi2_rx,DMA_IT_HT);//取消半传输完成中断
}
voidHAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
         if(SPI2== hspi->Instance)
         {
                   fun_mt6701_dma_cb();
         }
}
void fun_mt6701_dma_cb(void)
{        
         uint16_ttmp = 0;
         
         g_cb_cnt++;
         GPIOB->ODR|= (1<<15);
         tmp=((g_stc_mt6701_info.rx_data_buf[0]<<8)+g_stc_mt6701_info.rx_data_buf[1])>> 2;
         g_stc_mt6701_info.dif_val= (g_stc_mt6701_info.u16_val + 0x3fff - g_stc_mt6701_info.u16_old_val) %0x3fff;
         g_stc_mt6701_info.u16_old_val= g_stc_mt6701_info.u16_val;
         g_stc_mt6701_info.sta= e_mt6701_idle;
}
测试结果数据不更新。
检查代码基本没有啥问题,单步调试
进入dma中断
回调
Spidma中断相应
Spi结束判定

异常
原因查找结果是找不到spi结束标志,上示波器测试时钟信号发现时钟信号并未在传输结束后结束,即时钟信号脉冲数超过17个,查找stm32手册找到原因在主模式且仅接收模式下spi未关闭前clk信号始终存在,但是此时已经完成了数据的接收,解决的方法有很多种,本文使用修改库的方法解决,修改文件如下
文件stm32f4xx_hal_spi.c,修改
修改前
static HAL_StatusTypeDefSPI_EndRxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart)
{
  if((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction== SPI_DIRECTION_1LINE)
                                              || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY)))
  {
   /* Disable SPI peripheral */
   __HAL_SPI_DISABLE(hspi);               
  }
  /*Erratasheet: BSY bit may stay high at the end of a data transfer in Slave mode*/
  if(hspi->Init.Mode == SPI_MODE_MASTER)
  {
   if (hspi->Init.Direction != SPI_DIRECTION_2LINES_RXONLY)
    {
     /* Control the BSY flag */
     if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_BSY, RESET, Timeout,Tickstart) != HAL_OK)
     {
       SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
       return HAL_TIMEOUT;
     }
    }
   else
    {
     /* Wait the RXNE reset */
     if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, RESET, Timeout,Tickstart) != HAL_OK)
     {
       SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
       return HAL_TIMEOUT;
     }
    }
  }
else
  {
   /* Wait the RXNE reset */
   if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, RESET, Timeout,Tickstart) != HAL_OK)
    {
     SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
     return HAL_TIMEOUT;
    }
  }
return HAL_OK;
}
//修改后
static HAL_StatusTypeDefSPI_EndRxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart)
{
  if((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction== SPI_DIRECTION_1LINE)
                                              || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY)))
  {
   /* Disable SPI peripheral */
   __HAL_SPI_DISABLE(hspi);
         
//修改的这里
/* USER CODE BEGIN Init */
                   if(hspi->Init.Direction== SPI_DIRECTION_2LINES_RXONLY)
                   {
                            returnHAL_OK;
                   }
/* USER CODE END Init */
//修改结束
                  
  }
  /*Erratasheet: BSY bit may stay high at the end of a data transfer in Slave mode*/
  if(hspi->Init.Mode == SPI_MODE_MASTER)
  {
   if (hspi->Init.Direction != SPI_DIRECTION_2LINES_RXONLY)
    {
     /* Control the BSY flag */
      if (SPI_WaitFlagStateUntilTimeout(hspi,SPI_FLAG_BSY, RESET, Timeout, Tickstart) != HAL_OK)
     {
       SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
       return HAL_TIMEOUT;
     }
    }
   else
    {
     /* Wait the RXNE reset */
     if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, RESET, Timeout,Tickstart) != HAL_OK)
     {
       SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
       return HAL_TIMEOUT;
     }
    }
  }
else
  {
   /* Wait the RXNE reset */
   if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, RESET, Timeout,Tickstart) != HAL_OK)
    {
     SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
     return HAL_TIMEOUT;
    }
  }
return HAL_OK;
}
再次测试:
遗留的问题:这种方式破坏了库的一致性,且使用cubemx重新编码后会出现覆盖问题,不是妥善的解决方式。
想了一种方法通过测试也能达到同样的效果且不修改库函数
void DMA1_Stream3_IRQHandler(void)
{
  /*USER CODE BEGIN DMA1_Stream3_IRQn 0 */
         SPI_HandleTypeDef*hspi = (SPI_HandleTypeDef *)(((DMA_HandleTypeDef*)&hdma_spi2_rx)->Parent);
         
         __HAL_SPI_DISABLE(hspi);
         if(SPI2== hspi->Instance)
         {
                   fun_mt6701_dma_cb();
         }        
  /*USER CODE END DMA1_Stream3_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi2_rx);
  /*USER CODE BEGIN DMA1_Stream3_IRQn 1 */
  /*USER CODE END DMA1_Stream3_IRQn 1 */
}
这种方式需要将如下部分注释,原因是在中断入口处已经关闭spi有可能会正常进入中断但是不能保证每次均进入,故将fun_mt6701_dma_cb();放在DMA1_Stream3_IRQHandler函数中。
voidHAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
         if(SPI2== hspi->Instance)
         {
                   //fun_mt6701_dma_cb();
         }
}
测试结果如下
仅在DMA1_Stream3_IRQHandler函数关闭spiHAL_SPI_RxCpltCallback保持中断回调:
void DMA1_Stream3_IRQHandler(void)
{
  /*USER CODE BEGIN DMA1_Stream3_IRQn 0 */
         SPI_HandleTypeDef*hspi = (SPI_HandleTypeDef *)(((DMA_HandleTypeDef*)&hdma_spi2_rx)->Parent);
         
         __HAL_SPI_DISABLE(hspi);
//      if(SPI2== hspi->Instance)
//      {
//               fun_mt6701_dma_cb();
//      }        
  /*USER CODE END DMA1_Stream3_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi2_rx);
  /*USER CODE BEGIN DMA1_Stream3_IRQn 1 */
  /*USER CODE END DMA1_Stream3_IRQn 1 */
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef*hspi)
{
         if(SPI2== hspi->Instance)
         {
                   fun_mt6701_dma_cb();
         }
}
测试结果,使能次数远大于中断回调次数,结果如下:
仅在DMA1_Stream3_IRQHandler函数关闭spi,且使用中断回调函数,HAL_SPI_RxCpltCallback保持注释中断回调:
void DMA1_Stream3_IRQHandler(void)
{
  /*USER CODE BEGIN DMA1_Stream3_IRQn 0 */
         SPI_HandleTypeDef*hspi = (SPI_HandleTypeDef *)(((DMA_HandleTypeDef*)&hdma_spi2_rx)->Parent);
         
         __HAL_SPI_DISABLE(hspi);
         if(SPI2== hspi->Instance)
         {
                   fun_mt6701_dma_cb();
         }        
  /*USER CODE END DMA1_Stream3_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi2_rx);
  /*USER CODE BEGIN DMA1_Stream3_IRQn 1 */
  /*USER CODE END DMA1_Stream3_IRQn 1 */
}
voidHAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
         if(SPI2== hspi->Instance)
         {
                   //fun_mt6701_dma_cb();
         }
}
使能次数和中断回调次数相同,测试结果如下:
也可以试试ll库的方式解决,有兴趣的可以试试。
在此解释一下为什么一定要使用中断加dma的方式来读取mt6701的数据,我所使用的foc的框架是tim触发adcadc中断中处理foc坐标变换,svpwm等,如果在中断中直接读取编码器的值会造成中断服务函数耗时较长,使用dma中断方式读取编码器数据,使foc具有更好的中断响应,提高转速上限。
电机驱动svpwm波形空载如下图所示,非常nice,这个传感器很优秀
附件是ioc文件和mt6701的手册。



中断4.png (15.7 KB )

中断4.png

MT6701调试记录.rar

1.28 MB

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

3

帖子

0

粉丝