资讯详情

FreeRTOS LCD

📅 2026/10/16 2:54:15 | 华诺云谱 👁 阅读
FreeRTOS LCD
一张显示不出来的图STM32 SPI LCD 调试记一张 160×120 的 RGB565 图片死活不肯在 LCD 上露脸。查了半天问题居然藏在最不起眼的地方——一个函数参数的类型。起因最近在 STM32F103 上点了一块 SPI 接口的 LCD 屏驱动 IC 走的是经典的0x2A / 0x2B / 0x2CCASET / RASET / RAMWR三连。初始化代码写完先丢了一张 100 像素的小测试图进去——红绿各半屏幕乖乖分了两色看起来一切正常。然后我把image1160×120 的真实图片接上去屏幕瞬间摆烂什么都不显示。第一反应图片数据有问题打开image1.h里面整整齐齐const uint16_t image1[160 * 120] { 0xFFFF, 0xFFFF, 0xFFFF, ... };数据在、数组大小对、#include也加上了。不是数据的问题。第二反应显示区域设错了看ShowImage1// setColumnEdge列起点60列终点179 dataToSend[0] 0x00; dataToSend[1] 0x3C; // 60 dataToSend[2] 0x00; dataToSend[3] 0xB3; // 179 setColumnEdge(dataToSend); // setRowEdge行起点80行终点239 dataToSend[0] 0x00; dataToSend[1] 0x50; // 80 dataToSend[2] 0x00; dataToSend[3] 0xEF; // 239 setRowEdge(dataToSend); showImage((uint16_t *)image1, 160 * 120);列宽 179−601 120行高 239−801 160正好对应 120×160 的图片屏幕坐标系里列宽120、行高160。区域也没设错。第三反应那就只剩showImage了顺着调用链往下看static void showImage(uint16_t *image, uint16_t Size) { SendCommand(0x2c); SendData((uint8_t *)image, Size); }Size是uint16_t看起来没问题。再看SendDatastatic void SendData(uint8_t *pData, uint8_t Size); // ← 看这里uint8_t Size。一个uint8_t最大只能装到 255。而160 × 120 19200。19200 75 × 256截成uint8_t之后正好是0。于是HAL_SPI_Transmit(hspi1, pData, 0, HAL_MAX_DELAY)一个字节都不发LCD 自然收不到任何像素数据image1 当然显示不出来。那张小测试图为什么能显示初始化里那段uint16_t image[100] {[0 ... 49] 0x00f8, [50 ... 99] 0xe007}; showImage(image, 200);传的是200没超过 255没被截断所以能正常显示。正是这个能显示的假象把我骗了——问题被小数据的巧合掩盖了。还有一个藏得更深的坑即便把SendData的Size改成uint16_tShowImage1还是显示不全。因为showImage((uint16_t *)image1, 160 * 120); // 传的是像素数而SendData→HAL_SPI_Transmit的Size是字节数。RGB565 每像素 2 字节应该传160 × 120 × 2 38400。对照ShowImage2showImage((uint16_t *)image2, 160 * 120 * 2); // 这里传的是字节数写对了两个函数一个传像素数、一个传字节数写法不一致。ShowImage1少了那个× 2。更巧的是38400 150 × 256截成uint8_t后仍然是 0——所以即便只改Size类型而不补× 2还是显示不出来反过来即便补了× 2而不改Size类型也还是显示不出来。两个 bug 互相掩护单改哪一个都没用必须一起改。顺手发现的第三个坑命名和命令对不上static void setColumnEdge(uint8_t *pData) { SendCommand(0x2b); ... } // 0x2B 是 RASET行 static void setRowEdge(uint8_t *pData) { SendCommand(0x2a); ... } // 0x2A 是 CASET列函数名和实际发的命令正好反着。ShowImage1里因为把 120 当列、160 当行一起调了数据凑巧对得上显示区域所以没暴露出来。但ShowImage2里只调了setColumnEdge、漏了setRowEdge——这又是一个被巧合掩盖的隐患。修复三处改动SendData的Size从uint8_t改成uint16_tHAL_SPI_Transmit本来就是uint16_t类型也对得上。ShowImage1里160 * 120改成160 * 120 * 2跟ShowImage2对齐。可选把setColumnEdge/setRowEdge的命令或函数名对调一致。改完image1 终于露脸了。复盘为什么这个 bug 这么难找回头看这个 bug 有几个反直觉的特征1. 小数据能跑大数据就崩。单元测试如果只拿几十字节的小图测永远发现不了 255 的上限。SPI 这种传一个是一个的接口参数类型决定了天花板天花板不撞上去是看不见的。2. 两个 bug 互相掩护。Size类型截断 少乘× 2两个问题恰好都让结果变成 0。改一个不动会让人误以为不是这里的问题转头去别处找原因。3. 命名误导。setColumnEdge实际发的是行命令看代码的人会被名字带偏反而觉得逻辑没问题。4. 编译器不报错。uint16_t→uint8_t的窄化转换GCC 默认是不警告的要-Wconversion才会提示。这就让 bug 顺顺当当通过了编译。教训接口参数类型要和底层 API 对齐。HAL_SPI_Transmit的Size是uint16_t自己包一层SendData时千万别手贱写成uint8_t。像素数和字节数要在注释里写清楚。否则下游调用者包括一个月后的自己一定会搞混。函数名要名副其实。名字一旦反了比没有名字还坑。打开-Wconversion。这种窄化转换的坑编译器一眼就能看出来没必要让人肉调试。小数据的能跑不能证明没问题。尤其是涉及上限的代码一定要拿超过上限的数据测一遍。一个uint8_t害我查了半小时。写下来希望下次有人踩到同样的坑时能少走这段弯路。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑