☰
AT7456E视频字符叠加芯片实战:STM32 SPI驱动与OSD显示
2026/9/28 15:10:11 网站建设 项目流程

1. 项目缘起:为什么图传系统里非得塞一颗字符叠加芯片

搞过无人机图传的兄弟都清楚,飞控数据、电池电压、飞行高度、GPS星数这些信息,如果只能回到地面站才能看,那飞起来心里是没底的。尤其是FPV飞行或者行业应用场景里,飞手需要实时看到关键OSD信息,这就催生了字符叠加芯片的需求。AT7456E就是在这个背景下被大量采用的方案,它本质上是一颗NTSC/PAL视频字符叠加芯片,能直接在模拟视频信号上叠加文字和图形,不需要额外的视频编码器。

我最早接触这颗芯片是在一个农业植保机的项目上,当时客户要求图传画面上必须实时显示亩用量、飞行速度和剩余药量。最初想用STM32直接驱动视频DAC做叠加,实测下来CPU占用率太高,而且同步头处理很麻烦。后来换成AT7456E,SPI配置好寄存器之后,芯片自己完成行场同步和字符点阵生成,MCU只需要在需要更新数据的时候写几个寄存器就行,负担一下子降下来了。

这颗芯片的核心价值在于三点:第一,它兼容标准的NTSC和PAL制式,直接串在摄像头和发射机之间就能工作;第二,内置了256个字符的显示内存,支持自定义字符点阵;第三,SPI接口配置灵活,STM32F103这种级别的MCU就能轻松驱动。适合谁参考?做无人机图传、FPV眼镜、模拟视频监控、车载倒车影像叠加的嵌入式工程师,尤其是那些需要在视频上叠加实时数据的场景。

注意:AT7456E是3.3V供电的芯片,但它的视频输入输出是模拟信号,布线的时候要特别注意模拟地和数字地的隔离,否则画面上会出现横纹干扰。

2. 硬件设计:从原理图到PCB布局的实战要点

2.1 最小系统连接与电源处理

AT7456E的引脚不算多,但每一个都有讲究。核心引脚包括:视频输入(VIN)、视频输出(VOUT)、SPI接口(SCLK、SDI、SDO、CS)、复位引脚(RESET)、以及晶振输入。我一般推荐用27MHz的无源晶振,配合两个22pF的负载电容,实测下来起振很稳定。电源部分,芯片需要3.3V的数字电源和模拟电源,虽然数据手册上说可以共用,但我在实际项目中会用一个磁珠把数字3.3V和模拟3.3V隔开,再各自加一个0.1uF和10uF的退耦电容。

视频输入部分,摄像头出来的信号一般幅值是1Vpp左右,需要经过一个交流耦合电容(通常0.1uF)再进入VIN引脚。这里有个坑:如果你用的摄像头输出阻抗不是75欧姆,画面可能会出现亮度异常。我习惯在VIN前面加一个75欧姆的对地电阻,确保阻抗匹配。视频输出端同样需要75欧姆的驱动电阻,然后直接接到图传发射机的输入端。

SPI接口这边,AT7456E支持SPI模式0和模式3,我一般用模式0(CPOL=0,CPHA=0)。CS片选信号建议用硬件片选,虽然软件片选也能用,但在高速更新OSD数据的时候,硬件片选能避免时序错乱。SDO引脚如果不需要回读寄存器,可以悬空,但建议还是接上,调试的时候能读回寄存器值确认配置是否成功。

2.2 PCB布局的避坑经验

PCB布局这块我踩过不少坑。第一次画板子的时候,把晶振放在了板子边缘,结果图传画面上总是有细密的竖条纹。后来用示波器一看,晶振波形上叠加了很大的噪声。解决办法很简单:晶振尽量靠近芯片,走线尽量短,晶振下方铺地铜皮,并且不要走任何其他信号线。

模拟视频走线要远离SPI数字信号线,如果实在避不开,中间加一根地线做隔离。我通常会把视频输入输出走线放在顶层,SPI走线放在底层,中间用完整的地平面隔开。电源走线要足够宽,至少15mil以上,确保压降不会影响芯片工作。

还有一个细节:AT7456E的RESET引脚内部有上拉,但上电时序如果太慢,芯片可能初始化失败。我一般会在RESET引脚上接一个10k上拉电阻和一个0.1uF电容到地,形成一个简单的RC复位电路,时间常数大概1ms,实测下来很稳。

3. SPI配置全流程:从寄存器初始化到字符显示

3.1 SPI底层驱动配置

STM32F103的SPI外设配置AT7456E,我一般用SPI1,时钟分频到18MHz左右,也就是72MHz的APB2时钟四分频。为什么不用更高的速度?因为AT7456E的SPI最高支持20MHz,但实际跑下来18MHz最稳定,再高的话,如果PCB走线稍长,就容易出现数据错误。

配置步骤用CubeMX的话很简单:SPI模式选全双工主模式,数据宽度8位,CPOL=0,CPHA=0,NSS软件管理,预分频系数选4。生成代码之后,需要自己封装读写函数。写寄存器的时候,先拉低CS,发送寄存器地址(最高位为0表示写),再发送数据,最后拉高CS。读寄存器的时候,地址最高位为1,然后发送一个空字节读取返回值。

void AT7456E_WriteReg(uint8_t reg, uint8_t data) { HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, &reg, 1, 100); HAL_SPI_Transmit(&hspi1, &data, 1, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); } uint8_t AT7456E_ReadReg(uint8_t reg) { uint8_t txData = reg | 0x80; uint8_t rxData = 0; HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, &txData, 1, 100); HAL_SPI_Receive(&hspi1, &rxData, 1, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); return rxData; }

提示:HAL_SPI_Transmit和HAL_SPI_Receive的超时参数不要设成0,否则一旦SPI总线被干扰,程序会卡死。我一般设100ms,足够完成一次传输。

3.2 寄存器初始化序列

AT7456E的寄存器不少,但真正需要配置的就那么几个关键寄存器。我整理了一份最小初始化序列,实测下来能正常工作:

寄存器地址功能推荐值说明
0x00视频模式配置0x08NTSC模式,PAL的话改成0x0C
0x01视频输出配置0x00正常输出,不启用测试模式
0x02同步控制0x00内部同步,自动检测行场
0x04OSD使能0x01开启字符叠加
0x05字符内存起始地址0x00从第一个字符开始
0x06显示控制0x00正常显示,不闪烁
0x10行偏移0x20根据实际画面调整
0x11列偏移0x30根据实际画面调整

初始化的时候,先写0x00配置视频制式,然后写0x04开启OSD,最后写0x10和0x11调整显示位置。这里有个细节:0x10和0x11的值需要根据你的摄像头和显示屏实际效果来微调,我一般会写一个测试字符,然后一边看画面一边改这两个寄存器的值,直到字符出现在理想位置。

3.3 字符点阵数据的写入

AT7456E内置了256个字符的显示内存,每个字符是12x18的点阵,占用54个字节。芯片出厂时已经预置了一套标准ASCII字符,如果你只需要显示英文和数字,直接调用预置字符就行。但如果你要显示中文或者自定义图标,就需要自己生成点阵数据然后写入。

写入自定义字符的流程是这样的:先写0x05寄存器指定字符内存的起始地址,然后连续写入54个字节的点阵数据。每个字节代表一行,12个像素用两个字节表示,高字节在前。我一般用PC端的小工具生成点阵数据,然后导出成C数组,直接嵌入代码里。

const uint8_t customChar[54] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ... 省略中间数据 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; void AT7456E_LoadCustomChar(uint8_t charIndex) { AT7456E_WriteReg(0x05, charIndex * 54); for (int i = 0; i < 54; i++) { AT7456E_WriteReg(0x06, customChar[i]); } }

注意:写入点阵数据的时候,SPI速度不要太快,我试过用18MHz写入,偶尔会出现数据错位。后来降到9MHz就稳定了。如果你发现自定义字符显示乱码,先检查SPI速度。

4. 显示控制与动态更新:让OSD数据实时刷新

4.1 字符显示内存的映射关系

AT7456E的显示内存是30列x16行的字符网格,总共480个字符位置。每个位置对应一个字符索引,写入索引值就能在对应位置显示字符。比如你想在第一行第一列显示字符'A',就往地址0x00写'A'的ASCII码。地址的计算方式是:行号乘以30加上列号。

我一般会定义一个二维数组来管理显示内容,然后写一个刷新函数,把数组内容批量写入芯片。批量写入的时候,先写0x05寄存器设置起始地址,然后连续写数据,芯片会自动递增地址。这样比逐个写地址快很多。

void AT7456E_UpdateDisplay(uint8_t displayBuf[16][30]) { AT7456E_WriteReg(0x05, 0x00); for (int row = 0; row < 16; row++) { for (int col = 0; col < 30; col++) { AT7456E_WriteReg(0x06, displayBuf[row][col]); } } }

实测下来,全屏刷新一次大概需要2ms左右,对于OSD数据更新来说完全够用。如果你只更新部分区域,可以只写变化的那几个字符,进一步减少SPI占用。

4.2 动态数据的格式化与显示

无人机OSD数据一般包括:飞行模式、电压、电流、高度、速度、GPS星数、回家方向等。这些数据需要实时更新,我一般用定时器每100ms触发一次刷新。数据格式化的部分,用sprintf把数值转成字符串,然后逐个字符填入显示缓冲区。

void UpdateOSDData(void) { char buf[16]; sprintf(buf, "VOLT:%5.1fV", batteryVoltage); for (int i = 0; i < strlen(buf); i++) { displayBuf[0][i] = buf[i]; } sprintf(buf, "ALT:%5.1fm", altitude); for (int i = 0; i < strlen(buf); i++) { displayBuf[1][i] = buf[i]; } AT7456E_UpdateDisplay(displayBuf); }

这里有个性能优化的点:不要每次刷新都全屏写入,只写入变化的行。我一般会记录上一次的显示内容,对比之后只更新有变化的字符位置。这样SPI占用能降低80%以上,对于STM32F103这种资源有限的MCU来说很重要。

4.3 闪烁与反色显示的实现

AT7456E支持字符闪烁和反色显示,通过0x06寄存器的特定位来控制。闪烁功能在显示报警信息的时候很有用,比如电压过低的时候让电压值闪烁。反色显示可以用来高亮重要数据,比如GPS星数少于6颗的时候,把星数反色显示提醒飞手。

实现方式是在写入字符数据的时候,把最高位设置为1表示反色,设置为0表示正常。闪烁的话,需要配合一个定时器,每隔500ms切换一次显示状态。我一般会在刷新函数里加一个标志位,根据标志位决定是否写入反色数据。

实操心得:闪烁频率不要太高,500ms到1s比较合适。太快了飞手看不清,太慢了又起不到提醒作用。我试过200ms闪烁,结果飞手反馈说眼睛累。

5. 常见问题排查与避坑指南

5.1 SPI通信不生效的排查思路

SPI通信不生效是最常见的问题,我遇到过好几次。排查的时候按这个顺序来:先确认CS片选信号有没有正常拉低拉高,用示波器看波形;然后确认SCLK时钟有没有输出,频率对不对;再确认MOSI数据有没有变化。如果这三步都正常,但芯片还是不工作,那大概率是初始化序列有问题。

我遇到过一次,SPI波形完全正常,但芯片就是不出字符。后来发现是RESET引脚一直处于低电平,芯片根本没启动。所以调试的时候,一定要先确认RESET引脚在上电后能正常拉高。还有一个坑:AT7456E的SPI模式必须和MCU配置一致,我试过用模式3配置,结果芯片完全没反应,改成模式0就好了。

现象可能原因解决方法
完全无字符显示RESET未拉高检查复位电路,确保上电后RESET为高
字符乱码SPI速度过快降低SPI时钟到9MHz以下
画面有横纹电源噪声加强电源滤波,模拟数字地隔离
字符位置偏移行场偏移寄存器不对调整0x10和0x11寄存器值
部分字符不显示显示内存地址错误检查地址计算方式,确认行列映射

5.2 视频画面干扰的解决方法

视频画面干扰是另一个高频问题。表现是画面上出现横纹、竖纹、或者亮度异常。我总结了几种常见情况和对应的解决方法:

第一种是电源干扰,表现为横纹滚动。解决方法是加强电源滤波,在芯片的3.3V引脚旁边加一个10uF的钽电容和一个0.1uF的陶瓷电容。如果还不行,就用一个LDO单独给AT7456E供电,和MCU的电源分开。

第二种是地线干扰,表现为竖纹或者画面抖动。解决方法是检查地平面是否完整,模拟地和数字地是否单点连接。我一般会在芯片下方铺一块完整的模拟地,然后用一个0欧姆电阻连接到数字地。

第三种是信号线干扰,表现为画面有细密的噪点。解决方法是缩短视频走线,远离SPI和晶振信号线。如果实在避不开,就在视频线上加一个磁珠或者小电容滤波。

5.3 自定义字符显示异常的调试技巧

自定义字符显示异常一般有三种情况:字符显示成方块、字符显示不全、字符位置错乱。第一种情况通常是点阵数据格式不对,检查一下每个字节的位顺序,AT7456E是高位在前。第二种情况可能是点阵数据长度不对,确认一下是不是54个字节。第三种情况可能是字符索引计算错误,确认一下写入的起始地址是否正确。

我一般会先用预置字符测试,确认硬件和SPI通信没问题之后,再测试自定义字符。这样能快速定位问题出在硬件还是数据上。还有一个技巧:写一个简单的测试图案,比如全亮或者全暗,写入自定义字符内存,看看显示效果。如果全亮能正常显示,说明硬件没问题,问题出在点阵数据上。

6. 性能优化与进阶玩法

6.1 减少SPI占用的批量更新策略

STM32F103的SPI带宽有限,如果OSD数据更新频繁,SPI占用率会很高。我一般用两种策略来优化:第一种是只更新变化的字符,前面已经提到了;第二种是用DMA传输,把SPI数据搬运交给DMA,CPU只负责准备数据。

用DMA的话,需要配置SPI的TX DMA通道,然后把要发送的数据放到一个缓冲区,启动DMA传输。传输完成之后,DMA中断里拉高CS。这样CPU占用率能降到5%以下,对于需要同时处理飞控数据的场景很有用。

void AT7456E_WriteReg_DMA(uint8_t reg, uint8_t *data, uint16_t len) { uint8_t txBuf[len + 1]; txBuf[0] = reg; memcpy(&txBuf[1], data, len); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit_DMA(&hspi1, txBuf, len + 1); } void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi == &hspi1) { HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); } }

注意:DMA传输的时候,txBuf必须是全局变量或者静态变量,不能是局部变量,否则DMA传输过程中栈被释放,数据就乱了。

6.2 多页面显示与菜单系统

AT7456E的显示内存有480个字符位置,足够做多页面显示。我一般会做两个页面:一个飞行页面显示关键数据,一个设置页面用来调整参数。页面切换通过一个按键或者遥控器通道来控制。

实现方式是定义两个显示缓冲区,切换的时候把对应的缓冲区内容写入芯片。设置页面的菜单项可以用反色显示来高亮当前选项,用闪烁来提示可编辑状态。这套逻辑在STM32上实现起来不难,关键是要管理好状态机,避免页面切换的时候数据错乱。

6.3 与飞控系统的数据对接

无人机OSD数据一般来自飞控的串口或者CAN总线。我一般用UART接收飞控的Mavlink数据,解析出需要的字段,然后更新显示缓冲区。Mavlink解析可以用现成的库,也可以自己写一个轻量级的解析器。关键是解析和显示要分开,解析在中断或者任务里做,显示在主循环里做,避免相互阻塞。

如果飞控数据更新频率很高,比如50Hz,那OSD刷新频率可以降到10Hz,人眼看起来已经足够流畅了。我试过50Hz刷新,画面反而因为SPI占用太高出现闪烁。所以刷新频率不是越高越好,10Hz到20Hz是比较合适的范围。

7. 我的实操体会与最后几句

这个项目前前后后折腾了大概两个月,从最开始的点不亮,到后来能稳定显示自定义字符,中间踩了不少坑。最大的体会是:AT7456E这颗芯片本身不复杂,但硬件设计和SPI时序调试需要耐心。尤其是PCB布局,第一版没经验,画出来的板子干扰严重,后来重新布局之后,画面干净了很多。

另一个体会是,不要迷信数据手册上的典型电路。数据手册上的电路是理想情况,实际项目中电源质量、信号完整性、地线处理都会影响芯片工作。我一般会在数据手册电路的基础上,加一些额外的滤波和隔离措施,虽然成本增加了几毛钱,但稳定性提升很明显。

最后分享一个小技巧:调试的时候,先用一个固定的测试图案写入显示内存,确认硬件和SPI通信没问题之后,再接入动态数据。这样能把问题范围缩小,快速定位是硬件问题还是软件问题。这个习惯帮我省了很多时间,希望对你有用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询