1. AM1806 LCD控制器:嵌入式显示系统的核心引擎
在嵌入式系统开发中,尤其是那些需要人机交互界面的设备,LCD控制器是一个绕不开的核心外设。它就像一位尽职尽责的“放映员”,负责将内存中绘制好的图像,按照屏幕能理解的“语言”和“节奏”,一帧一帧地播放出来。我接触过不少基于TI Sitara系列处理器的项目,AM1806因其均衡的性能和丰富的外设,在工业HMI、便携式医疗设备等场景中很常见。它的LCD控制器模块设计得相当经典和实用,理解其工作原理,对于解决显示闪烁、撕裂、时序不匹配等问题至关重要。今天,我就结合手册和实际调试经验,把AM1806的LCD控制器,特别是其双模架构——Raster与LIDD,以及最让人头疼的寄存器配置和时序计算,掰开揉碎了讲清楚。无论你是正在驱动一块新屏幕,还是在优化现有显示的流畅度,相信这些细节都能帮到你。
AM1806的LCD控制器是一个高度集成的模块,其最大支持分辨率是1024x1024像素,这决定了它能应对从小型单色屏到中型彩色屏的多种需求。它的核心价值在于“解放CPU”:通过内置的DMA引擎自动从帧缓冲区搬运图像数据,CPU只需要在需要更新画面时修改帧缓冲区的内容即可,无需干预每一行、每一个像素的发送过程,极大地节省了计算资源。控制器内部包含两套独立的“放映机制”:Raster控制器和LIDD控制器。它们就像剧院里的两套不同的放映设备,一次只能使用一套,但可以针对不同类型的“屏幕”(显示面板)进行切换。选择哪种模式,根本上取决于你手头屏幕的接口类型。
2. 双模架构解析:Raster与LIDD的应用分野
2.1 Raster模式:驱动现代TFT/STN屏的同步接口
Raster模式,也称为“光栅扫描模式”,是我们最常接触的模式,用于驱动具有同步时钟接口的显示面板,比如常见的TFT-LCD或彩色STN屏。你可以把它想象成一套非常精确的“行场扫描协议”。
它的工作流程非常标准化:控制器会依次生成VSYNC(垂直同步)、HSYNC(水平同步)和PCLK(像素时钟)信号。VSYNC信号宣告新的一帧图像开始,HSYNC信号宣告新的一行像素开始,而PCLK的每个上升沿(或下降沿)则对应一个像素数据的输出。数据线(LCD_D[15:0])则随着PCLK的节奏,将帧缓冲区中的像素值依次送出。这种模式是“主动式”的,控制器完全掌控了刷新的节奏,屏幕被动地接收时序和数据。
Raster模式的关键优势在于其高效和标准化。由于时序是周期性的、可精确编程的,DMA可以以最高的效率连续不断地输送数据。它支持多种像素格式(通过调色板和灰度缩放模块),并且可以处理双帧缓冲,这对于实现流畅的动画或避免撕裂效应非常有用。在配置时,我们需要根据屏幕数据手册,精确计算并设置一系列时序参数,如行前肩、同步脉宽、行后肩等,这部分我们会在后面详细展开。
2.2 LIDD模式:驱动字符型或旧式图形屏的异步接口
LIDD模式,全称LCD接口显示驱动模式,则用于驱动异步接口的显示设备。这类设备没有统一的像素时钟,其通信更像是对一个存储器(RAM)进行读写操作,典型代表是HD44780字符液晶控制器、以及采用6800或8080并行总线协议的图形模块。
在这种模式下,控制器不再产生固定的VSYNC/HSYNC/PCLK时序,而是模拟一个“主机”去访问一个“从设备”。它通过一组可编程的控制信号线,如片选(CS)、写使能(WE)、读使能(OE)、地址锁存使能(ALE)等,来发起读写操作。数据在控制信号的协调下进行传输。例如,要向屏幕的某个坐标写一个像素,你可能需要先通过地址线发送坐标,再通过数据线发送颜色值,整个过程由多个独立的读写周期组成。
LIDD模式的核心是灵活性。它几乎可以模拟任何异步并行总线的时序,通过配置寄存器,你可以定义每个控制信号的有效极性、建立时间、保持时间和选通脉冲的宽度,从而适配千奇百怪的旧式显示模块。但它的缺点也很明显:效率通常低于Raster模式,因为每个像素或指令的传输都需要多个总线周期,并且需要CPU或DMA更频繁地干预控制流程。它更适合驱动分辨率不高、更新率要求不高的段码式或字符式液晶。
注意:模式选择是硬件设计阶段就必须确定的。Raster和LIDD模式使用相同的物理引脚,但这些引脚的功能定义在不同模式下完全不同。例如,在Raster模式下用作VSYNC的引脚,在LIDD模式下可能用作某个片选信号。因此,在原理图设计和PCB布线时,就必须明确目标屏幕的接口类型,并确保硬件连接与软件将要配置的模式一致。模式的选择通过
LCD_CTRL寄存器中的MODE位域进行切换。
3. 核心寄存器详解:从全局控制到时序微调
AM1806的LCD控制器寄存器映射在地址0x01E1 3000开始的空间。理解每个寄存器的功能是进行驱动开发的基石。下面我将这些寄存器分组进行解读,并穿插一些配置时的实际考量。
3.1 全局控制与状态寄存器
这三个寄存器是控制器的“大脑”和“仪表盘”。
- REVID (0x01E1 3000): 修订标识寄存器。只读,用于识别控制器模块的硅版本。在驱动中,通常用于版本兼容性检查,但对于基本功能配置,一般不需要操作它。
- LCD_CTRL (0x01E1 3004):LCD控制寄存器,这是最重要的寄存器之一。
LCD_ENABLE: 整个LCD控制器的总开关。必须在配置好所有其他参数后,最后才置位此位来开启输出。MODE: 模式选择位。0代表LIDD模式,1代表Raster模式。在切换模式前,务必先禁用LCD (LCD_ENABLE=0)。CLK_DIV: 像素时钟分频器。像素时钟(PCLK)来源于外设的输入时钟(如PLL0_SYSCLK),通过此分频值产生。PCLK = 输入时钟 / (CLK_DIV + 1)。这是决定刷新率的关键参数之一。PALETTE_BYPASS: 调色板旁路。如果使能,则直接输出帧缓冲区中的原始数据;如果禁用,则数据会经过内部256x16位的调色板RAM进行颜色映射。对于真彩色显示(如RGB565),通常需要旁路调色板。
- LCD_STAT (0x01E1 3008): LCD状态寄存器。主要包含
FRAME_DONE(帧完成)和SYNC_LOST(同步丢失)等状态位。可以通过查询或中断方式,获知一帧图像是否发送完毕,这对于实现与垂直同步相关的操作(如垂直同步下的帧缓冲切换)非常有用。
3.2 LIDD模式专用寄存器组
当工作在LIDD模式时,以下寄存器用于配置异步访问的时序。TI手册中提供了针对HD44780、6800、8080等标准接口的时序图,我们的任务就是将图中的时间要求,转化为寄存器的参数。
LIDD_CTRL (0x01E1 300C): LIDD控制寄存器。选择使用CS0还是CS1片选,以及基本的通信模式。
LIDD_CS0_CONF (0x01E1 3010) / LIDD_CS1_CONF (0x01E1 301C): 片选配置寄存器。这是配置LIDD时序的核心。它包含多个位域,分别对应读写周期中各个阶段的时钟周期数:
W_SU/R_SU: 写/读建立时间。指地址/控制信号有效后,到写使能/读使能有效之间的延迟周期数。W_STROBE/R_STROBE: 写/读选通脉冲宽度。即WE或OE信号保持有效的周期数。W_HOLD/R_HOLD: 写/读保持时间。指写使能/读使能无效后,地址/控制信号继续保持有效的周期数。CS_DELAY: 片选延迟。在两个连续的访问周期之间,片选信号保持无效的周期数。- 如何计算?这些参数的单位是
LCD_MCLK的周期。LCD_MCLK的频率由系统时钟分频而来。例如,如果LCD_MCLK周期为20ns,而屏幕数据手册要求t_{AS}(地址建立时间)最小为40ns,那么W_SU至少应设置为ceil(40ns / 20ns) = 2。
LIDD_CS0_ADDR/DATA (0x01E1 3014/3018): 当CPU需要直接通过LIDD接口访问外设时,向这些寄存器写入地址或数据,控制器就会自动产生符合
LIDD_CS0_CONF所定义时序的访问周期。这为CPU提供了一种便捷的“内存映射”式访问方式。
3.3 Raster模式专用寄存器组
Raster模式的配置相对更复杂,因为它要生成一整套连续的时序信号。
- RASTER_CTRL (0x01E1 3028): Raster控制寄存器。
PALETTE_LOAD: 加载调色板。如果使用调色板模式,在配置好调色板RAM后,需要置位此位来生效。TFT_MODE: 选择TFT(主动矩阵)或STN(被动矩阵)面板类型。这会影响某些控制信号(如LCD_AC_ENB_CS)的行为。MONO_8BIT: 选择8位单色模式。在单色模式下,可以使用8位数据线输出灰度或帧缓冲数据。RGB_565: 选择RGB565色彩格式。这是最常用的16位真彩色格式,帧缓冲区中每两个字节表示一个像素(R[4:0], G[5:0], B[4:0])。
- RASTER_TIMING_0 (0x01E1 302C):水平时序寄存器。
PPL: 每行像素数。此值 = 屏幕有效宽度 - 1。例如,对于800x480的屏幕,PPL应设置为799。HSW: 水平同步脉宽。即HSYNC信号有效的像素时钟周期数。HFP: 水平前肩。一行有效数据结束到HSYNC信号开始之间的像素时钟周期数。HBP: 水平后肩。HSYNC信号结束到下一行有效数据开始之间的像素时钟周期数。
- RASTER_TIMING_1 (0x01E1 3030):垂直时序寄存器。
LPP: 每帧行数。此值 = 屏幕有效高度 - 1。对于800x480的屏幕,LPP为479。VSW: 垂直同步脉宽。即VSYNC信号有效的行数(以行为单位)。VFP: 垂直前肩。一帧有效数据结束到VSYNC信号开始之间的行数。VBP: 垂直后肩。VSYNC信号结束到下一帧有效数据开始之间的行数。
- RASTER_TIMING_2 (0x01E1 3034): 主要用于被动矩阵(STN)屏幕,配置
AC_BIAS(交流偏置频率)等参数。对于TFT屏,通常保持默认或设置为0。 - RASTER_SUBPANEL (0x01E1 3038): 用于配置双扫描面板,可以将一帧数据分上下两个子面板输出。对于普通单扫描屏幕,无需配置。
3.4 DMA与帧缓冲区寄存器组
这是实现“解放CPU”功能的关键。
- LCDDMA_CTRL (0x01E1 3040): DMA控制寄存器。
BURST_SIZE: DMA突发传输大小。建议设置为与系统内存总线宽度匹配的值(如8个字),以提高传输效率。FB0/1_BASE_ADDR: 帧缓冲区基地址。必须确保该地址是内存对齐的(通常是32字节对齐)。
- LCDDMA_FB0_BASE/CEILING (0x01E1 3044/3048): 帧缓冲区0的基地址和天花板地址。DMA会在
BASE和CEILING定义的地址范围内循环取数据。CEILING地址通常指向缓冲区末尾的下一个字节。 - LCDDMA_FB1_BASE/CEILING (0x01E1 304C/3050): 帧缓冲区1的地址。双缓冲机制:当控制器正在从FB0读取数据显示当前帧时,CPU可以渲染下一帧到FB1。在一帧结束时(通过
LCD_STAT的FRAME_DONE判断),通过软件切换LCDDMA_CTRL中的基地址指针到FB1,即可实现无撕裂的帧切换。FB1的配置不是必须的,单缓冲时只需配置FB0。
4. 时序参数计算与配置实战:以800x480 TFT为例
理论说了这么多,我们来点实际的。假设我们要驱动一块常见的7寸800x480 RGB TFT屏幕,其数据手册给出了如下时序要求(典型值):
- 像素时钟(DCLK):约33.3 MHz
- 水平时序(单位:像素时钟周期):
HBP=46,HFP=210,HSW=1,有效宽度=800 - 垂直时序(单位:行数):
VBP=23,VFP=22,VSW=1,有效高度=480
我们的系统为AM1806提供的外设时钟(例如来自PLL0_SYSCLK)为100MHz。现在我们来一步步计算并配置寄存器。
4.1 计算像素时钟分频(CLK_DIV)
目标像素时钟PCLK_target = 33.3 MHz。 输入时钟CLK_in = 100 MHz。 分频系数DIV = CLK_in / PCLK_target = 100 / 33.3 ≈ 3.003。 根据公式PCLK = CLK_in / (CLK_DIV + 1),可得CLK_DIV = DIV - 1 ≈ 2.003。 我们取整数值CLK_DIV = 2。 则实际像素时钟PCLK_actual = 100 / (2+1) ≈ 33.33 MHz,满足要求。 因此,LCD_CTRL.CLK_DIV = 2。
4.2 配置Raster水平时序寄存器(RASTER_TIMING_0)
PPL(每行像素数):800 - 1 = 799。HSW(水平同步脉宽): 数据手册给出HSW=1。注意,有些手册定义HSYNC脉冲宽度包含前后沿,这里我们按标准理解,HSW就是HSYNC信号本身有效的周期数,设置为1。HFP(水平前肩):210。HBP(水平后肩):46。 所以,RASTER_TIMING_0 = (HBP << 24) | (HFP << 16) | (HSW << 8) | PPL。计算后填入即可。
4.3 配置Raster垂直时序寄存器(RASTER_TIMING_1)
LPP(每帧行数):480 - 1 = 479。VSW(垂直同步脉宽):1。VFP(垂直前肩):22。VBP(垂直后肩):23。 所以,RASTER_TIMING_1 = (VBP << 24) | (VFP << 16) | (VSW << 8) | LPP。
4.4 配置其他关键寄存器
RASTER_CTRL: 设置为TFT模式 (TFT_MODE=1),RGB565格式 (RGB_565=1),旁路调色板 (PALETTE_LOAD先置0,若不用调色板则相关位不使能)。LCDDMA_CTRL: 设置BURST_SIZE=8(假设总线宽度支持),并配置帧缓冲区基地址(稍后设置)。LCDDMA_FB0_BASE: 指向我们分配好的帧缓冲区内存地址。这个缓冲区的大小为:宽度 * 高度 * 每像素字节数。对于800x480 RGB565,大小为800 * 480 * 2 = 768,000 字节。必须确保这块内存是物理连续的,并且地址对齐。通常会在DDR内存中预留一块区域。LCDDMA_FB0_CEILING: 设置为FB0_BASE + 缓冲区大小。
4.5 初始化与启动流程
- 时钟与引脚复用配置:首先确保给LCD控制器模块的时钟已经使能(通过PSC模块)。然后,将对应的引脚功能复用到LCD模式(通过PINMUX寄存器)。
- 关闭控制器:向
LCD_CTRL寄存器写入,确保LCD_ENABLE=0。 - 配置模式与基本参数:写入
LCD_CTRL,设置MODE=1(Raster),CLK_DIV=2,但先不使能。 - 配置时序参数:依次写入
RASTER_TIMING_0,RASTER_TIMING_1,RASTER_TIMING_2(TFT屏可设为0),RASTER_CTRL。 - 配置DMA与帧缓冲区:写入
LCDDMA_CTRL,LCDDMA_FB0_BASE,LCDDMA_FB0_CEILING。如果使用双缓冲,同样配置FB1。 - 初始化帧缓冲区:在内存中分配的帧缓冲区,需要填充初始图像(如全黑、全白或启动logo)。
- 最后使能控制器:向
LCD_CTRL寄存器写入,置位LCD_ENABLE。此时,控制器开始按照配置的时序,从帧缓冲区读取数据并输出到屏幕。
实操心得:规避上电闪屏。一个常见的现象是,在系统启动、软件尚未初始化LCD控制器时,LCD引脚可能处于不确定状态,导致屏幕出现乱码或闪烁。解决方法有两种:一是在初始化引脚复用后、使能控制器前,将LCD数据线引脚配置为GPIO输出并拉低;二是在屏幕的背光或电源控制电路上做文章,确保只有在LCD控制器初始化完成后,才给屏幕供电或开启背光。
5. 深度调试与常见问题排查实录
即使按照手册计算配置,第一次点亮屏幕也常常会遇到问题。下面是我总结的几个典型故障场景和排查思路。
5.1 问题一:屏幕白屏或全黑,无任何显示
- 排查思路:
- 电源与背光:首先用万用表测量屏幕的供电电压(VCC、VDDIO等)和背光电压是否正常。这是最基础也最容易被忽略的一步。
- 信号探测:使用示波器或逻辑分析仪,测量
LCD_PCLK、LCD_HSYNC、LCD_VSYNC三个关键信号。如果这三个信号完全没有,说明控制器没有工作。- 检查
LCD_CTRL的LCD_ENABLE位是否已置位。 - 检查系统时钟和模块时钟是否使能。
- 检查
- 时序参数:如果有时钟和同步信号,但数据线
LCD_D[15:0]没有波形,检查RASTER_CTRL的色彩格式设置是否正确(如RGB565),以及帧缓冲区地址LCDDMA_FB0_BASE是否指向了有效的、已初始化的内存区域。可以尝试向帧缓冲区填充一个简单的图案(如交替的色条),再看数据线是否有对应的变化。 - 数据线电平:如果数据线有波形,但屏幕不亮,检查数据线的电压幅值是否符合屏幕要求(通常是3.3V)。AM1806的I/O电压是否与屏幕匹配?
5.2 问题二:图像显示错位、撕裂或滚动
- 排查思路:
- 时序不匹配:这是最常见的原因。现象可能是图像在水平方向偏移、出现鬼影、或垂直方向滚动。重点检查
PPL、LPP、HFP、HBP、VFP、VBP这几个参数。务必与屏幕数据手册中的“时序图”而非“推荐值”表格进行核对。有时手册给的“宽度”包含了消隐区,而PPL仅指有效像素,这里容易出错。 - 帧缓冲区大小与地址错误:如果
CEILING地址设置错误,DMA可能访问到非法内存区域,导致数据错乱。确保CEILING = BASE + (width * height * bpp / 8)。 - 像素格式错误:如果配置为RGB565,但帧缓冲区里存储的是ARGB8888的数据,会导致颜色完全错误。同样,如果屏幕是BGR顺序,而软件按RGB顺序填充,也会出现红蓝色互换。
- 时序不匹配:这是最常见的原因。现象可能是图像在水平方向偏移、出现鬼影、或垂直方向滚动。重点检查
5.3 问题三:图像闪烁或抖动
- 排查思路:
- 像素时钟不稳定:检查
CLK_DIV分频后的PCLK是否稳定。可以用示波器测量LCD_PCLK的周期是否恒定。系统主频是否因功耗管理而动态变化?确保LCD时钟源是稳定的。 - 内存带宽瓶颈:如果帧缓冲区位于SDRAM中,而系统总线非常繁忙,DMA可能无法及时读到数据,导致显示撕裂或闪烁。可以尝试:
- 增加DMA的
BURST_SIZE。 - 优化SDRAM控制器设置,提高带宽。
- 使用双缓冲,并确保在垂直消隐期间(通过
FRAME_DONE中断判断)切换缓冲区,避免在行扫描中间切换。
- 增加DMA的
- 电源噪声:模拟电源(AVDD)噪声过大可能会干扰LCD的模拟电路,导致显示抖动。确保电源滤波良好。
- 像素时钟不稳定:检查
5.4 问题四:LIDD模式无法与屏幕通信
- 排查思路:
- 控制信号极性:检查
LIDD_CS0_CONF等寄存器中,关于信号有效电平的配置位,是否与屏幕要求一致(高有效还是低有效)。 - 时序参数过紧或过松:用逻辑分析仪抓取
LCD_MCLK、片选、读写、地址、数据线的波形。对照屏幕数据手册的时序图,测量t_{SU},t_{H},t_{PW}等时间参数。调整W_SU/R_SU、W_STROBE/R_STROBE、W_HOLD/R_HOLD等参数,使生成的波形满足屏幕要求。通常可以先设置较宽松的时序(较大的值),确保通信建立,再逐步收紧以优化速度。 - 访问序列错误:对于某些屏幕,初始化或访问需要特定的命令序列。确保你的驱动代码是按照屏幕手册的流程,通过写入
LIDD_CS0_ADDR和LIDD_CS0_DATA寄存器来模拟正确的总线操作。
- 控制信号极性:检查
调试工具推荐:一块支持多通道的数字示波器或逻辑分析仪是调试LCD接口的利器。我习惯将PCLK、HSYNC、VSYNC和一根数据线(如LCD_D[0])同时抓取,这样可以非常直观地看到行、场时序关系以及数据是否同步。逻辑分析仪则更适合解码LIDD模式下的复杂总线协议。
6. 性能优化与高级应用技巧
当基本显示功能实现后,我们通常会追求更流畅的体验和更低的功耗。
6.1 实现流畅动画与避免撕裂:双缓冲与垂直同步
单缓冲模式下,CPU在修改帧缓冲区时,DMA可能正在读取该缓冲区发送到屏幕,这会导致屏幕上同时显示两帧不同的内容,即“撕裂”。双缓冲是解决此问题的标准方案。
- 分配两个帧缓冲区:FB0和FB1,大小相同。
- 初始化:将FB0设为当前显示缓冲区(
LCDDMA_FB0_BASE指向FB0),FB1作为后台渲染缓冲区。 - 渲染循环:
- CPU在FB1中绘制下一帧图像。
- 等待
LCD_STAT寄存器中的FRAME_DONE标志置位(或使能该中断)。这表示上一帧(FB0)的扫描已经完成,即将开始新的一帧。 - 在
FRAME_DONE发生后,立即将LCDDMA_FB0_BASE更新为FB1的地址。由于切换发生在垂直消隐期,屏幕不会显示撕裂。 - 交换角色:将FB0作为新的后台渲染缓冲区,FB1作为新的显示缓冲区,循环往复。
6.2 降低系统功耗:动态调整刷新率
对于电池供电的设备,LCD控制器及其驱动的屏幕往往是耗电大户。在显示静态内容时,降低刷新率可以显著省电。
实现方法:动态修改LCD_CTRL.CLK_DIV和RASTER_TIMING寄存器。例如,正常模式下使用33MHz的PCLK,在进入待机界面时,可以增大CLK_DIV,降低PCLK频率,同时等比例增加HFP、HBP等参数,以维持相同的总行/场时间,从而降低刷新率。需要注意的是,修改这些寄存器前必须先禁用LCD (LCD_ENABLE=0),修改完成后再重新使能。屏幕可能需要一个短暂的复位或重新初始化过程来适应新的时序。
6.3 使用调色板实现颜色映射与节省内存
对于颜色数量有限的显示(如256色),可以使用控制器的内部256x16位调色板RAM。
- 配置:在
LCD_CTRL中禁用调色板旁路。 - 填充调色板:调色板RAM可以看作一个256项的查找表。帧缓冲区中存储的每个像素值(0-255)作为索引,从调色板中读出对应的16位RGB565颜色值输出。你需要根据你的颜色需求,预先计算并填充这个查找表。
- 优势:
- 节省内存:帧缓冲区大小从
宽*高*2字节减少到宽*高*1字节。 - 快速全局换色:要改变整个画面的色调,只需修改调色板中的几项,无需重绘整个帧缓冲区,速度极快。
- 节省内存:帧缓冲区大小从
6.4 与图形库的整合要点
如果你使用如emWin、Qt for MCU、LVGL等嵌入式图形库,驱动适配的关键在于实现好底层的“打点”(set_pixel)和“填充”(fill_buffer)函数,以及提供帧缓冲区指针。
- 帧缓冲区对接:图形库通常需要一块连续的内存作为画布。直接将我们分配的物理帧缓冲区地址提供给图形库是最直接高效的方式。
- 实现刷新函数:图形库在完成一帧绘制后,会调用一个“刷新”或“同步”函数。在这个函数里,如果你用的是双缓冲,就需要执行缓冲区交换操作;如果是单缓冲,可能只需要等待一个垂直同步周期,以确保图形库不会在屏幕扫描过程中修改缓冲区。
- 处理输入:如果屏幕带触摸,还需要将触摸控制器(通常通过SPI或I2C连接)的坐标数据,转换成图形库能接受的输入事件。
最后,关于AM1806的LCD控制器,我个人最深刻的体会是:数据手册中的时序图永远比参数表格更可靠。遇到显示问题,第一件事就是拿出示波器,对照手册的波形图,逐个信号、逐个边沿地去核对。那些t_{SU},t_H的时间要求,最终都体现在W_SU、HBP这些寄存器值上。把抽象的时序参数和屏幕上具体的现象(偏移、闪烁、花屏)联系起来,是嵌入式显示驱动调试的必修课。另一个小技巧是,在初始化代码中,可以将所有LCD相关寄存器在配置完成后,再读回来打印到串口,确保写入的值是正确的,这能排除很多因内存访问或位域操作错误导致的诡异问题。