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