STM32F401CCU使用memcpy后断电重启冻结问题求助
STM32F401CCU断电重启冻结故障排查与解决
故障现象
使用STM32F401CCU执行Flash存储相关代码后,断电再上电芯片直接冻结,仅能通过重新烧录固件恢复。代码执行时通过ST LINK验证Flash写入内容符合预期,但断电重启后触发致命故障。经测试定位到StringCreateStrForFlashOneProduct函数中的memcpy调用存在内存问题,但无法确定具体原因。
导致冻结的简化示例代码
static char send_save_string_final[2048]; char sssssend_char[2048] = "000000NewProduct"; char *StringCreateStrForFlashOneProduct(const int someint) { memcpy(sssssend_char, "000000008888008000000000888800800000000088880080", 48); memcpy(send_save_string_final, sssssend_char, 48); return send_save_string_final; }
实际使用的完整代码
函数一:生成超长char数组并写入Flash
static char output_all_sorts[6001] = ""; char all_save_string[6001] = ""; char sssssend_char_all_sorts[2048] = "00000AllProducts"; char append_char_numz_all_sorts[17] = "0000000000000000"; void FlashSaveAllSorts(void) { strcpy(sssssend_char_all_sorts, "00000AllProducts"); sssssend_char_all_sorts[2047] = 0; strcpy(output_all_sorts, "0"); output_all_sorts[6000] = 0; strcpy(all_save_string, "00000AllProducts"); all_save_string[6000] = 0; strcpy(append_char_numz_all_sorts, "0000000000000000"); append_char_numz_all_sorts[16] = 0; for (int doin_int = 0; doin_int <= 6; doin_int++) { strcpy(sssssend_char_all_sorts, StringCreateStrForFlashOneProduct(doin_int)); strcat(all_save_string, sssssend_char_all_sorts); strcpy(output_all_sorts, all_save_string); } strcpy(output_all_sorts, all_save_string); output_all_sorts[strlen(output_all_sorts)] = 0; Flash_Write_Data(0x08020000, output_all_sorts, 3000); }
函数二:生成单个产品的完整数据行
static char send_save_string_final[2048] = "0"; char sssssend_char[2048] = "000000NewProduct"; char append_char_numz[17] = "0000000000000000"; char *StringCreateStrForFlashOneProduct(const int someint) { strcpy(send_save_string_final, "0"); send_save_string_final[2047] = 0; strcpy(sssssend_char, "000000NewProduct"); sssssend_char[2047] = 0; strcpy(append_char_numz, "0000000000000000"); append_char_numz[16] = 0; strncpy(append_char_numz, StringCreateStringFromIntTwo(someint), 17); append_char_numz[16] = 0; strcat(sssssend_char, append_char_numz); for (int kk = 0; kk < 3; kk = kk + 1) { char append_char_one[17] = "0000000000000000"; for (int jj = 0; jj < 12; jj = jj + 1) { char append_char[17] = "0000000000000000"; memcpy(append_char, StringCreateStringFromIntTwo(tunable_vars_machine_for_flash[someint][kk][jj]), 17); strcat(sssssend_char, append_char); } memcpy(append_char_one, "000000000MenuEnd", 17); strcat(sssssend_char, append_char_one); } char append_char_end_zeros[17] = "0000000000000000"; memcpy(append_char_end_zeros, StringCreateStringFromIntTwo(0), 17); strcat(sssssend_char, append_char_numz); memcpy(send_save_string_final, sssssend_char, 2047); return send_save_string_final; }
函数三:生成固定16位格式的char数组(示例:000000000000001333)
char *StringCreateStringFromIntTwo(int base_int_base) { //clearprint("StringCreateStri"); int base_int = base_int_base; if (base_int == 0) { base_int = 999999; /// avoid saving zero to flash memory } static char send_char_final[17] = "0000000000000000"; char send_char[16] = "00000000"; static char send_char_sixteen_zeros[17] = "0000000000000000"; int legnewpigwphjh = strlen(send_char); char str_zero[2] = "0"; char str_two_zeros[3] = "00"; char str_three_zeros[4] = "000"; char str_four_zeros[5] = "0000"; char str_five_zeros[6] = "00000"; char str_six_zeros[7] = "000000"; char str_seven_zeros[8] = "0000000"; char str_eight_zeros[9] = "00000000"; char str_sixteen_zeros[17] = "0000000000000000"; int int_mem_length = countDigits(base_int); char str_mem_write_int[9]; sprintf(str_mem_write_int, "%d", base_int); if (int_mem_length == 7) { strcat(send_char, str_zero); } if (int_mem_length == 6) { strcat(send_char, str_two_zeros); } if (int_mem_length == 5) { strcat(send_char, str_three_zeros); } if (int_mem_length == 4) { strcat(send_char, str_four_zeros); } if (int_mem_length == 3) { strcat(send_char, str_five_zeros); } if (int_mem_length == 2) { strcat(send_char, str_six_zeros); } if (int_mem_length == 1) { strcat(send_char, str_seven_zeros); } strcat(send_char, str_mem_write_int); strcpy(send_char_final, str_sixteen_zeros); strcpy(send_char_final, send_char); return send_char_final; }
故障原因分析与解决思路
1. 字符串溢出导致内存破坏
StringCreateStrForFlashOneProduct中,sssssend_char数组长度为2048字节,但多次调用strcat拼接内容时未检查总长度是否超过上限。初始字符串+多次拼接的16字节段+固定内容,总长度极易超过2047字节(数组最后一位需留作终止符)。数组溢出会覆盖相邻内存区域,若覆盖了栈内存、全局变量或Flash操作关键寄存器数据,会导致系统状态异常,重启后因非法数据触发硬件错误或进入未定义状态,表现为冻结。
2. memcpy的错误使用
- 实际代码中
memcpy(append_char, StringCreateStringFromIntTwo(...), 17)虽看似安全,但StringCreateStringFromIntTwo返回的数组若未正确终止,会导致后续strcat越界。 - 最后一行
memcpy(send_save_string_final, sssssend_char, 2047):若sssssend_char实际长度不足2047字节,memcpy会读取数组末尾后的未知内存;若长度超过2047,会导致send_save_string_final溢出。
3. Flash写入的潜在问题
Flash_Write_Data(0x08020000, output_all_sorts, 3000)中固定写入长度3000字节,若output_all_sorts实际有效长度不符,写入的未初始化内存数据会在重启后被系统误读为程序代码或配置信息,引发崩溃。- STM32 Flash需按页擦除后写入,若
Flash_Write_Data未正确处理擦除逻辑,会导致Flash区域数据损坏,重启后触发总线错误。
解决步骤
严格控制字符串长度:
- 每次
strcat前计算当前长度+待拼接长度,确保不超过数组上限,例如if(strlen(sssssend_char) + strlen(append_char) < 2047)。 - 替换
strcat为strncat,明确指定最大拼接长度。
- 每次
修正memcpy使用方式:
- 字符串拷贝优先用
strncpy,确保终止符正确,例如strncpy(append_char, StringCreateStringFromIntTwo(...), 16)后手动设置append_char[16] = '\0'。 - 将最后一行
memcpy改为strncpy(send_save_string_final, sssssend_char, 2047),并设置send_save_string_final[2047] = '\0',避免读取未知内存。
- 字符串拷贝优先用
检查Flash写入逻辑:
- 确保
Flash_Write_Data先擦除目标页(STM32F401 Flash页大小为16KB)再写入。 - 用
strlen(output_all_sorts)获取实际有效长度,替换固定的3000字节写入长度。
- 确保
添加内存边界检查:
- 调试阶段添加断言,例如
assert(strlen(all_save_string) < 6000),快速定位溢出问题。
- 调试阶段添加断言,例如
内容的提问来源于stack exchange,提问作者Desblat17
相关产品推荐
相关产品推荐

