Bootstrap

STM32H723串口接收全为0 & CAN错误中断问题

第一次发现问题

环境:STM32H723ZGT6 UART1 PA9/PA10 空闲中断+DMA接收数据
现象:代码打印串口接收数据,串口助手每隔100ms发一次数据,,,打印结果是偶尔接收数据正确,大部分是接收到的全为0
原因:排查下来是M7核开启D-Cache缓存导致的
解决办法:
由于要用到以太网和LWIP,必须要开启缓存,按照网上的方法在MPU配置那里关掉缓存、Buff和共享也不行,
所以采用的是每次调用内核函数,清空缓存。

void UartRxIdleCallback(void)
{
	uint16_t unLen;
	
    if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET))
    {
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);
       
		unLen = UART_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx) - u16UARTDMAOffset;
		
		SCB_InvalidateDCache_by_Addr(u8UARTBuff, UART_BUF_SIZE);
		SCB_CleanDCache();
		
		if(unLen && unLen <= UART_BUF_SIZE)
		UartDataRxCallBack(&u8UARTBuff[u16UARTDMAOffset], unLen);
		
		u16UARTDMAOffset += unLen;
    }
}

记录一下,结束。


第二次发现问题,
代码使用了FDCAN通讯,在使用中发现每次串口收到数据之后,fdcan1的一个变量值变成0了。
导致在进入CAN中断时,HAL库代码读取到了错误状态,一直进HAL_FDCAN_ErrorCallback。

一开始还怀疑是FDCAN的设置有问题,查了一圈寄存器,最后发现是hfcan1这个句柄的ttcan结构体指针的值变成0了。
指针变成0,相关的状态码和寄存器也就读成乱码了。

如下图串口收到数据时,SCB_InvalidateDCache_by_Addr 无效化缓存,直接读取数据到buff中。
最开始的时候,只是为了解决问题,以为调用了SCB_InvalidateDCache_by_Addr就万事大吉了,

后来debug走到函数里面发现,传参输入的 addr 地址 和 size 大小 都必须要是32的整数倍。

而我的buff,就是普普通通的全局变量定义,如图中编译器分配的地址是0x24000218,不是32的整数倍。

恰好,hfdcan1的地址就紧挨着这个buff,那么当我调用 SCB_InvalidateDCache_by_Addr 无效化缓存时,影响到了 hfdcan1->ttcan 的值,导致最终出现问题。
调试截图

/**
  \brief   D-Cache Invalidate by address
  \details Invalidates D-Cache for the given address.
           D-Cache is invalidated starting from a 32 byte aligned address in 32 byte granularity.
           D-Cache memory blocks which are part of given address + given size are invalidated.
  \param[in]   addr    address
  \param[in]   dsize   size of memory block (in number of bytes)
*/
__STATIC_FORCEINLINE void SCB_InvalidateDCache_by_Addr (void *addr, int32_t dsize)
{
  #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
    if ( dsize > 0 ) { 
       int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U));
      uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */;
    
      __DSB();

      do {
        SCB->DCIMVAC = op_addr;             /* register accepts only 32byte aligned values, only bits 31..5 are valid */
        op_addr += __SCB_DCACHE_LINE_SIZE;
        op_size -= __SCB_DCACHE_LINE_SIZE;
      } while ( op_size > 0 );

      __DSB();
      __ISB();
    }
  #endif
}

最终在buff定义是,给它指定地址为32的整数倍,解决了。
attribute((at(0x24000220))) uint8_t u8UARTBuff[UART_BUF_SIZE];
volatile uint16_t u16UARTDMAOffset = 0;

总结下来就是,
一是要利用好缓存无效化函数,注意内存对齐问题
二是H7内核中,关于DMA的使用,要注意缓存刷新的问题

;