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

文章讲述了在STM32H723ZGT6开发中遇到的两个问题:UART接收数据不稳定(因D-Cache启用导致)和FDCAN通信中变量值异常(内存对齐问题影响)。作者分享了解决方案,包括使用缓存无效化函数处理内存对齐和DMA操作时的缓存刷新问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一次发现问题

环境: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的使用,要注意缓存刷新的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值