关于STM32H743 recv()数据错误的问题分析

问题描述

中位机使用TCP方式接收数据时可能出现数据错误,出错概率极高,大约接收300字节内必出。错误如下:

 

最终结论

中位机使用ST厂商自带的HAL库,该源代码在关闭网络接收的DCache(数据缓存)功能时有bug,可能导致部分区域的DCache未被关闭,从而导致TCP接收时可能从不正确的数据来源区拷贝数据,从而出错。

软件环境

HAL库版本号: STM32H7xx HAL V1.9.0

STM32CubeMX版本: 6.1.0

中位机MCU: STM32H743

LWIP版本: 2.1.2

分析过程

1 思路

出于对网络通信可靠性的极度信任,首先怀疑自己收到数据后的解析与处理有问题。我司通信协议为自定义,较为繁杂,代码中有需要操作数据指针的地方,存在修改接收到数据的可能性。

验证

屏蔽掉数据解析与处理函数后测试,错误依旧,由此定位问题出在网络接收流程;

思路

本次使用的LWIP版本为2.1.2,而本人之前用过的成熟版本为1.4.1,虽说理论上LWIP应该不会存在这么大的坑,但不管怎样先试试。

验证

开启LWIP_DEBUG及相关调试宏,结果发现错误依旧,且LWIP协议栈一切正常,无任何警告输出;

3思路

有无可能是TCP接收存储区不够大、从而导致数据接收溢出?

验证

分析其底层网络接收机制,发现其接收机制为循环队列,队列容量为4,每块大小为1524字节。

 

 

  1. 修改lwipopt.h中相关宏(ETH_RX_BUFFER_SIZE),将其由1524字节增大为2000,错误依旧;
  2. 增大队列容量,将队列容量增大为8,错误依旧;

4 思路

TCP接收顶层函数是一个Task,其优先级为HIGH,已然高于其它任何用户Task。考虑到本工程使用了LWIP,其内部会自行创建几个网络Task,有无可能是TCP接收Task优先级不够高、被其它线程抢占?

验证

1)修改TCP接收任务的优先级为最高(osPriorityRealtime),错误依旧;

2)增大TCP接收任务的堆栈,错误依旧;

思路

网上有同行说可能是CPU处理能力不够,每接收一包后sleep一下。

验证

修改后错误依旧;

思路

网络接收通常会使用DMA,且STM32H743芯片有1级DCache功能。有无可能是DCache未及时刷新导致的数据一致性问题?

验证

    1)查找网络接收数组的分配位置,固定为0x30040200;

 

   2)查找代码中对该存储区域的RAM属性设置,发现未禁止DCache,即:使用DCache;

 

为保证DMA与Cache不产生一致性的问题,STM32官方提供了一系列关闭或清空Cache的库函数。按个人经验,为保险起见,在底层接收函数中应该有规避一致性问题的动作。查询底层接收代码,果然有所发现:

 

按字面来看,应该是OK的。继续往里追查SCB_InvalidateDCache_by_Addr()函数,又有所发现:

 

其中SCB_InvalidateDCache_by_Addr()是ST官方提供的函数。

查数据手册得知,STM32H7平台的Cache操作是以Cache Line作为最小单位的,一个Cache Line是32字节。

正常情况下,该函数的第1个参数addr操作的地址应该要求32字节对齐,第2个参数dsize应该是32字节的整数倍。这样才能保证对Cache的操作是真正以Cache Line为单位。否则一旦传入的addr不按32字节对齐,则将出现问题。果断追查TCP接收循环队列的首地址,果然存在不对齐情况。手动修改之,经测试,完美通过!

 

反思

32字节对齐其实既可以在调用时执行,也可以放在SCB_InvalidateDCache_by_Addr()函数内执行。我个人倾向后者,毕竟调用者究竟是否理解该机制是不敢保证的。

 

您好!感谢您的提问。关于您的问题,下面是一份可能的解决方案: 1. 准备工作 首先,需要在阿里云上创建一个物联网平台实例,并创建好该实例下的设备。在创建设备时,需要获取到该设备的三元组信息(设备名称、设备密钥和设备ID)。 其次,需要购买一块BC26模块,可以通过串口和STM32连接,用于实现与阿里云之间的通信。 2. 程序设计 接下来,需要在STM32上编写程序,实现如下功能: 1) 初始化BC26模块,并将其连接到网络。 2) 建立与阿里云的TCP连接。 3) 构造符合阿里云IoT平台数据格式的JSON数据包,并通过TCP连接发送给阿里云。 4) 处理从阿里云接收到的数据。 下面是一份代码示例: ```c #include <stdio.h> #include "stm32f1xx.h" #include "hw_config.h" #include "usart2.h" #include "bc26.h" /* 阿里云IoT平台设备参数 */ #define DEVICE_NAME "your_device_name" #define DEVICE_SECRET "your_device_secret" #define DEVICE_ID "your_device_id" /* MQTT服务器相关参数 */ #define MQTT_SERVER "your_iot_mqtt_server" #define MQTT_PORT 1883 /* JSON数据包结构体 */ typedef struct { char version[8]; char method[8]; char product_key[32]; char device_name[32]; char timestamp[16]; int msgid; char params[128]; } IOT_JSON_Struct; /* 发送JSON数据包到阿里云 */ int iot_send_data(char *data, int len) { int ret = 0; char buffer[1024]; /* 构造JSON数据包 */ IOT_JSON_Struct json; memset(&json, 0, sizeof(json)); strcpy(json.version, "1.0"); strcpy(json.method, "thing.event.property.post"); strcpy(json.product_key, "your_product_key"); strcpy(json.device_name, DEVICE_NAME); sprintf(json.timestamp, "%llu", HAL_GetTick()); json.msgid = HAL_GetTick() % 10000; memcpy(json.params, data, len); /* 将JSON数据包转换为字符串 */ sprintf(buffer, "{\"id\":%d,\"version\":\"%s\",\"params\":%s,\"method\":\"%s\",\"productKey\":\"%s\",\"deviceName\":\"%s\",\"timestamp\":\"%s\"}\r\n", json.msgid, json.version, json.params, json.method, json.product_key, json.device_name, json.timestamp); /* 发送JSON数据包到阿里云 */ ret = bc26_tcp_send(MQTT_SERVER, MQTT_PORT, buffer, strlen(buffer)); if (ret < 0) { return -1; } return 0; } /* 接收并处理阿里云的响应 */ int iot_recv_data(char *data, int len) { char buffer[1024]; int ret = 0; ret = bc26_tcp_recv(buffer, len, 5000); if (ret < 0) { return -1; } /* 处理阿里云的响应 */ //TODO return 0; } /* 主函数 */ int main(void) { char data[] = "{\"temperature\":25.0,\"humidity\":50.0}"; SystemInit(); SysTick_Config(SystemCoreClock / 1000); USART2_Config(); bc26_init(); /* 连接网络 */ bc26_cmd("AT+CGATT=1", 5000); bc26_cmd("AT+CGDCONT=1,\"IP\",\"your_apn\"", 5000); bc26_cmd("AT+CGACT=1,1", 5000); /* 连接MQTT服务器 */ bc26_tcp_connect(MQTT_SERVER, MQTT_PORT, 5000); /* 发送数据到阿里云 */ iot_send_data(data, strlen(data)); /* 等待阿里云的响应 */ iot_recv_data(data, sizeof(data)); /* 关闭TCP连接 */ bc26_tcp_close(); /* 断开网络 */ bc26_cmd("AT+CGACT=0,1", 5000); while (1); } ``` 在以上代码中,iot_send_data()函数用于构造并发送JSON数据包到阿里云,iot_recv_data()函数用于接收并处理阿里云的响应。 注:以上代码仅为示例代码,具体实现方式请根据实际需求进行调整。 希望以上解决方案对您有所帮助,如有疑问请随时提问。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值