项目场景:
合宙Esp32C3(¥9.9简约版),连了个1.69寸spi屏(240*280),通过WIFI获取并显示“和风天气”的信息。
使用vs2022加Visual Micro插件作为开发环境。
在花了2天解决问题后,实在忍不住,记录一下,希望少点人掉这个大坑里面^_^
问题描述
开始一切尚好,除了Deep search和编译确实相当慢(电脑是FX8300的老CPU,程序改动大的时候,平均5~8分钟左右才能上传),断点也完全没有win程序环境那么方便有效。
在解析出JSON数据后,各种莫名其妙崩溃,板子反复重启;串口反馈原因多种多样:什么指针异常、没有权限、栈粉碎【第一次搜索出这个高大上的词,感觉异常无语/惊人^_^】……(实际报错都是英文,比如:Guru Meditation Error: Core 0 panic‘ed (Load access fault). Exception was unhand)
程序崩溃点,位于调用方法执行完毕,返回的时候,感觉栈确实粉碎了,程序无论如何都回不到父级。
//这里涉及两个class:CWeatherData跟CNetWeather,位于同一个cpp文件中;
//只要通过m_pWS指针,调用CWeatherData中的方法,比如MakeStr(),程序就会大概率崩溃
CWeatherData::CWeatherData()
{
m_pBuf = new char[2000];
memset(m_pBuf, 0, 2000);
}
int CWeatherData::MakeStr(WeatherField* pW)
{
int pos = 0;
m_Weather.temp = m_pBuf + pos;
pos += 1 + sprintf(m_pBuf + pos, "%s℃ ", pW->temp);
m_Weather.feelsLike = m_pBuf + pos;
pos += 1 + sprintf(m_pBuf + pos, "体感温度:%s℃ ", pW->feelsLike);
m_Weather.obsTime = m_pBuf + pos;
pos += 1 + sprintf(m_pBuf + pos, "观测时间:%s ", pW->obsTime);//观测时间
//....................
return 1;
}
CNetWeather::CNetWeather()
{
m_UesdBufferSize = 0;
m_pZipDataBuffer = new uint8_t[3000];
m_pJsonStr = NULL;
m_JsonStrLen = 0;
m_pWS = new CWeatherData;
}
原因分析:
Arduino的一对.cpp/.h文件里面不能写两个或多个不同的平级class,否则就会各种崩溃【尤其是执行包含sprintf()函数调用的方法,有时连Serial.println()都会导致崩溃】
至于嵌套class如何,没有测试……
解决方案:
把两个class放到不同的文件中,问题解决。
这个问题迷惑人的关键是:编译、链接、上载没有任何异常或错误提示,在调用第二个class的方法前,程序执行一切正常;一旦调用第二个class的方法,程序大概率直接崩溃,板子自动重启,难以查找问题。
另外附录一些我遇到的,真正的程序Bug导致反复重启的情况:
1、TridentTD_EasyFreeRTOS32,默认线程栈只给了2k字节,在网络操作的时候,很容易爆栈导致反复重启,需要适当加大。
2、TFT_eSPI,必须全局静态实例化,如果试图用new来动态实例化,也会反复重启。
3、在多线程环境下,TFT_eSPI 必须由单一线程渲染【常见的各种程序:QT、MFC、Winform、Android等等,基本上都不允许跨线程修改UI,只能由UI主线程来修改UI本身,其它工作线程直接操作修改UI会导致严重错误】。估计付出足够代价来同步/类似Dx12那样搞围栏之类是可以多线程的,但在ESP32的环境下,就单线程吧😁。
4、代码编码问题,UTF-8编码的cpp文件,串口输出调试才能正常显示中文(波特率也要匹配),此时中文编码是UTF-8,可不一定是一个汉字2字节,不少汉字是3字节甚至4字节长1个^_^。
5、数组越界、指针未指向已分配的空间等等。