P89LPC970串口与I2C实战:从双缓冲到多主仲裁的嵌入式通信指南

1. 项目概述与核心价值

在嵌入式开发领域,串行通信是连接微控制器与外部世界的“血管”。无论是向PC端打印调试信息,还是与传感器、显示屏、无线模块对话,UART和I2C这两大经典接口都扮演着不可或缺的角色。很多新手开发者面对数据手册里成片的寄存器描述和时序图时,常常感到无从下手,配置出来的通信链路不是丢数据就是根本不通。今天,我们就以NXP经典的P89LPC970/971/972系列微控制器为例,抛开晦涩的术语,从工程师的视角,彻底讲透其增强型UART和I2C接口,从最基础的比特位如何在线路上跳动,到如何利用双缓冲实现高效连续传输,再到I2C多主仲裁的实战细节。我的目标很简单:让你读完就能在项目里用起来,并且知道每一步配置背后的“为什么”。

P89LPC970/971/972虽然是一款老牌的8位MCU,但其串行通信外设的设计思想非常经典,理解它对于掌握其他更复杂MCU的串口和I2C有直接的帮助。它的UART不仅完全兼容传统的80C51 UART,还增加了独立的波特率发生器、帧错误检测、自动地址识别和双缓冲等增强功能。而它的I2C接口则完整支持多主模式下的时钟同步与仲裁。本文将围绕 UART串口通信 的四种模式、 波特率 精准计算、 双缓冲 机制的应用技巧,以及 I2C总线 的主从收发流程展开,提供可直接“抄作业”的寄存器配置代码和避坑指南。

2. UART核心原理与四种工作模式深度解析

UART通信的本质是一种异步串行协议,双方依靠事先约定好的波特率来同步时序。没有时钟线,所以每一帧数据都必须自带“开始”和“结束”的标记,这就是起始位和停止位。

2.1 模式0:同步移位寄存器模式

这是最特殊的一种模式,常被忽略,但在驱动某些移位寄存器型器件(如74HC595、74HC165)时非常有用。在此模式下,TXD引脚不再发送数据,而是输出移位时钟,RXD引脚则用于数据的输入和输出。

工作原理与配置要点:

  • 数据格式 :固定为8位数据,低位(LSB)在先,没有起始位和停止位。你可以把它理解为一个同步串行接口(SPI的简化版,但只有一根数据线)。
  • 波特率 :固定为CPU时钟频率(CCLK)的1/16。例如,当CCLK为12MHz时,波特率固定为750Kbps。这个速率很高,但不可调。
  • 操作流程
    1. 设置SCON寄存器,将SM0和SM1位设为00,进入模式0。
    2. 将REN(SCON.4)置1,使能接收。
    3. 发送 :直接写数据到SBUF寄存器,硬件即开始通过RXD引脚移出数据,同时TXD输出移位时钟。发送完成后,TI(SCON.1)标志置1,需软件清零。
    4. 接收 :必须先 手动清零RI标志(SCON.0) ,然后硬件才会开始采样RXD上的数据,在TXD时钟的驱动下移入。接收完成后,RI置1,数据在SBUF中。

重要提示 :模式0下, 必须禁用双缓冲 (即SSTAT寄存器的DBMOD位必须为0)。因为其工作方式与双缓冲机制不兼容,强行使能可能导致数据错乱。

2.2 模式1:标准8位UART模式

这是最常用、最经典的异步串行通信模式,我们常说的“串口”默认指的就是它。

工作原理与配置要点:

  • 数据格式 :1位起始位(逻辑0) + 8位数据位(LSB在先) + 1位停止位(逻辑1)。共10位。
  • 波特率 :可变。可由定时器1(Timer 1)溢出率或独立的波特率发生器(BRG)产生。这是配置的关键和易错点。
  • 数据缓冲 :接收时,停止位会存入RB8(SCON.2);发送时,TB8(SCON.3)未使用。
  • 中断 :接收完成(RI)和发送完成(TI)共用一个中断向量(0023h),也可以通过设置SSTAT.CIDIS位将它们分开。

模式1的接收启动机制值得细说 :接收器持续监测RXD线。当检测到一个从高到低的跳变(起始位前沿)时,启动一个内部计数器。这个计数器以16倍于波特率的频率采样。为了抗干扰,它在每个比特位的中间时段(第7、8、9个采样点)进行三次采样,取至少出现两次的值作为最终判定。如果起始位验证有效(采样值为0),则正式开始接收数据帧。

2.3 模式2与模式3:9位UART与多机通信基石

这两种模式在8位数据的基础上,增加了一个可编程的第9位数据,为多处理器通信和地址自动识别提供了硬件支持。

  • 数据格式 :1位起始位 + 8位数据位 + 1位可编程第9位 + 1位停止位。共11位。
  • 第9位的用途
    • 发送 :第9位数据来自TB8(SCON.3)寄存器。你可以手动设置它为0或1,也可以将程序状态字PSW中的奇偶校验位P赋值给它,实现简单的硬件奇偶校验。
    • 接收 :接收到的第9位数据存入RB8(SCON.2),停止位不保存。
  • 模式2与模式3的唯一区别 波特率源
    • 模式2 :波特率固定为 CCLK / 32 CCLK / 64 ,具体由PCON寄存器中的SMOD1位决定(SMOD1=1时为/32,SMOD1=0时为/64)。不可变,但非常稳定。
    • 模式3 :波特率可变,与模式1相同,可由定时器1或波特率发生器产生。灵活性高。

多机通信(Multiprocessor Communication)机制 : 这是模式2/3的杀手级应用。通过巧妙利用第9位,可以实现一主多从的硬件筛选。

  1. 主机寻址 :主机发送地址帧时,将第9位(TB8)设置为1;发送数据帧时,设置为0。
  2. 从机配置 :所有从机初始化时,设置SM2(SCON.5)=1。在此状态下,从机UART只有在接收到的第9位(RB8)为1时,才会产生接收中断(RI置位)。
  3. 寻址过程 :主机广播地址帧(第9位=1),所有从机都会中断,读取SBUF中的地址字节,并与自身地址比较。匹配的从机清除自己的SM2位(设为0)。不匹配的从机保持SM2=1。
  4. 数据传输 :主机随后发送数据帧(第9位=0)。此时,只有SM2=0的(即被寻址的)从机会产生中断并接收数据,其他从机完全忽略这些数据帧。传输结束后,被寻址的从机应重新置位SM2,等待下一次寻址。

这种机制极大地减轻了CPU的负担,避免了软件轮询地址的 overhead。

3. 波特率生成:精度与稳定的关键

不准确的波特率是串口通信失败的头号元凶。P89LPC970提供了两种灵活的波特率生成方案。

3.1 方案一:使用定时器1(Timer 1)

这是传统80C51的方式。波特率公式为: 波特率 = (2^SMOD1 / 32) * (Fosc / (12 * (256 - TH1))) 其中,SMOD1是PCON.7位,Fosc是振荡器频率,TH1是定时器1的重装值。

配置步骤与计算示例: 假设我们使用11.0592MHz晶振(这是一个经典值,因为它能使很多常用波特率产生整数重装值,减少误差),目标波特率为9600 bps,SMOD1设为0。

  1. 定时器1工作于模式2(8位自动重装),此时定时器溢出率 = Fosc / (12 * (256 - TH1))。
  2. 代入公式:9600 = (1/32) * (11059200 / (12 * (256 - TH1)))
  3. 解得:TH1 = 256 - 11059200/(32 12 9600) = 256 - 3 = 253 (0xFD)。

注意事项

  • 定时器1的中断要关闭,我们只利用其溢出率。
  • 这种方式会占用一个定时器资源。

3.2 方案二:使用独立波特率发生器(BRG)

这是P89LPC970的增强功能,不占用定时器1,且精度更高,因为其时钟源是CCLK(CPU时钟,可能经过分频),而非固定的Fosc/12。

波特率计算公式 波特率 = CCLK / (16 * (BRGR + 1)) 其中,BRGR是一个16位的值,由BRGR1(高字节)和BRGR0(低字节)寄存器组成。

配置步骤与计算示例: 假设CCLK = 12MHz,目标波特率仍为9600。

  1. 计算BRGR值:BRGR = CCLK / (16 * 波特率) - 1 = 12000000 / (16 * 9600) - 1 = 78.125 - 1 ≈ 77.125。
  2. 取整:BRGR = 77 (0x4D)。此时实际波特率 = 12000000 / (16 * 78) ≈ 9615.38 bps,误差约为0.16%,在可接受范围内(通常要求<2%)。
  3. 寄存器操作( 关键步骤! ):
    // 1. 确保波特率发生器禁用
    BRGCON &= ~(1 << 0); // 清除BRGEN位
    // 2. 写入波特率重装值
    BRGR0 = 77;          // 低字节
    BRGR1 = 0;           // 高字节
    // 3. 选择波特率发生器作为源(针对模式1和3)
    BRGCON |= (1 << 1);  // 置位SBRGS位
    // 4. 最后,使能波特率发生器
    BRGCON |= (1 << 0);  // 置位BRGEN位
    

    严重警告 :必须在 BRGEN=0 时才能写入BRGR1和BRGR0!如果在使能状态下写入,会产生不可预测的波特率,导致通信彻底失败。这是一个非常容易踩的坑。

方案选择建议

  • 对波特率精度要求高,且不想占用定时器资源时, 优先选择独立波特率发生器(BRG)
  • 需要与旧有80C51代码兼容,或CCLK频率不稳定时,可使用定时器1方案。
  • 模式0和模式2的固定波特率方案,适用于对速率要求固定且简单的场景。

4. 双缓冲机制:提升连续发送效率的利器

想象一下,你要通过串口快速发送一长串数据。在普通单缓冲模式下,你必须等一个字节完全发送完毕(TI标志置位),才能写入下一个字节到SBUF。在字节之间,会有一个停止位的时间“空隙”。双缓冲就是为了填满这个空隙。

4.1 双缓冲工作原理

启用双缓冲(设置SSTAT.7,DBMOD=1)后,UART内部实际上有两个缓冲区:一个 发送移位寄存器 (正在向外发送数据)和一个 发送缓冲寄存器 (用于预存下一个要发送的字节)。

工作流程如下:

  1. 当你写入第一个字节到SBUF时,数据被直接加载到移位寄存器,并立即开始发送。同时,产生一个发送中断(TI)。
  2. 在第一个字节的发送过程中(比如在发送其数据位时) ,你就可以将第二个字节写入SBUF。这个字节会被存入发送缓冲寄存器,而不会影响当前正在进行的发送。
  3. 当第一个字节的停止位开始发送时,硬件自动将缓冲寄存器中的第二个字节加载到移位寄存器,紧接着第一个字节的停止位之后开始发送第二个字节的起始位。 两个字节之间没有额外的空闲时间
  4. 以此类推,只要你能在下一个字节的“加载时间窗口”内准备好数据,就能实现无间隔的连续发送。

4.2 相关寄存器配置与中断策略

双缓冲的灵活性与以下几个状态寄存器位密切相关:

  • INTLO (SSTAT.6) :发送中断位置选择。
    • INTLO=0 :中断在 停止位开始 时产生(早期中断)。这给了你更多时间准备下一个数据。
    • INTLO=1 :中断在 停止位结束 时产生(晚期中断)。兼容传统模式。
  • DBISEL (SSTAT.4) :双缓冲发送中断选择(仅在DBMOD=1时有效)。
    • DBISEL=0 :每写入一个字符到SBUF,就产生一次发送中断。这是最直观的方式。
    • DBISEL=1 :除了每个字符的中断,在 最后一个字符 发送的停止位期间(根据INTLO决定是开始还是结束)还会产生一个额外的“发送完成”中断。这个中断可以用来通知CPU,一串数据已全部发送完毕,可以开始新的任务。

配置示例与代码片段 : 假设我们要启用双缓冲,并希望在停止位开始时产生中断,同时需要“发送完成”中断。

// 假设UART已初始化为模式1或3,波特率已配置好
SSTAT = 0x00; // 先清零
SSTAT |= (1 << 7); // DBMOD=1, 使能双缓冲
SSTAT |= (1 << 4); // DBISEL=1, 使能结束中断
SSTAT &= ~(1 << 6); // INTLO=0, 停止位开始时产生中断
// 同时,如果需要分开的Tx/Rx中断,可以设置
SSTAT |= (1 << 5); // CIDIS=1, 分离Tx和Rx中断
ES = 1; // 使能串口总中断
EA = 1; // 开启全局中断

// 中断服务函数示例 (框架)
void UART_ISR() interrupt 4
{
    if (TI) {
        TI = 0; // 必须软件清零
        // 检查是否是“发送完成”中断?可以通过一个发送数据队列的索引或状态标志来判断
        // 如果不是最后一个数据,则从队列加载下一个数据到SBUF
        // 如果是最后一个数据(由DBISEL和INTLO触发的那个额外中断),则进行后续处理,如置位发送完成标志
    }
    if (RI) {
        RI = 0;
        // 处理接收到的数据
    }
}

4.3 双缓冲使用中的陷阱与技巧

  1. 模式0禁用 :切记,双缓冲在模式0下必须禁用。
  2. 第9位数据(TB8)的写入时机 :在双缓冲使能时, 必须先写TB8,再写SBUF 。因为TB8会和SBUF中的数据一起被存入双缓冲。顺序反了,会导致第9位数据错误。
  3. 中断响应时间 :虽然双缓冲提供了更宽松的数据写入窗口,但如果你的中断服务程序执行时间过长,仍然可能错过加载下一个字节的时机,导致发送出现间隙。对于高速连续发送,优化中断服务程序或使用DMA(如果MCU支持)是更好的选择。
  4. “发送完成”中断的歧义 :当 DBISEL=1 时,如果CPU在最后一个字节的停止位期间又写入了新的SBUF,UART硬件可能无法判断这是否真的是“最后一个”字节,从而导致“发送完成”中断逻辑出现不确定性。稳妥的做法是,在软件层面维护一个发送队列和状态机,而不是完全依赖这个硬件标志。

5. 高级功能与错误处理

5.1 自动地址识别(Automatic Address Recognition)

这是模式2/3下,在硬件层面简化多机通信的另一个强大功能。它通过两个特殊功能寄存器SADDR(从机地址)和SADEN(地址掩码)来实现。

工作原理

  • 给定地址(Given Address) SADDR & SADEN 的结果。主机发送的地址字节必须与此匹配,从机才会响应。
  • 广播地址(Broadcast Address) SADDR | SADEN 的结果。主机发送此地址,所有从机都会响应。
  • 掩码(SADEN) :SADEN中为0的位,在地址比较时是“不关心”位(Don‘t Care)。这允许一个从机响应多个地址,或者多个从机共享部分地址位。

实战配置示例 : 假设一个系统有三个从机:

  • 从机A:只响应地址 0xA2 (1010 0010)。
  • 从机B:只响应地址 0xB4 (1011 0100)。
  • 从机C:响应地址高5位为 0xC (1100) 的任何地址(即 0xC0 ~ 0xCF )。

我们可以这样设置:

// 从机A配置
SADDR = 0xA2; // 地址
SADEN = 0xFF; // 全掩码,要求8位全匹配
// Given Address = 0xA2, Broadcast Address = 0xFF

// 从机B配置
SADDR = 0xB4; // 地址
SADEN = 0xFF; // 全掩码
// Given Address = 0xB4, Broadcast Address = 0xFF

// 从机C配置
SADDR = 0xC0; // 地址基础值
SADEN = 0xF0; // 高4位掩码为1111(必须匹配),低4位掩码为0000(不关心)
// Given Address = 0xCx (x为任意值), Broadcast Address = 0xFF

这样,主机发送 0xA2 只能唤醒从机A,发送 0xB4 只能唤醒从机B,发送 0xC0 0xCF 之间的任意地址都能唤醒从机C。发送 0xFF 则可以广播给所有从机。

5.2 错误检测:帧错误、溢出错误与间隔检测

可靠的通信必须能发现错误。P89LPC970的UART提供了三种错误标志,位于SSTAT寄存器中。

  • 帧错误(FE, SSTAT.3) :当接收器在预期的停止位位置检测到逻辑0(即不是预期的停止位)时,此位置1。可能的原因包括:波特率不匹配、线路干扰、发送方未发送停止位。
  • 溢出错误(OE, SSTAT.1) :当一个新的字节已经接收完成(第8或第9位已移入),而前一个字节还未被软件从SBUF读取(RI标志仍为1)时,此位置1。这意味着数据丢失了。 这是中断服务程序处理过慢的典型标志
  • 间隔检测(BR, SSTAT.2) :当RXD线上连续检测到11个位时间(对于不同模式,包含的位不同)的低电平时,此位置1。这通常表示发送方发送了一个“Break”信号,常用于协议复位或唤醒。 一个重要的特性是,如果使能了AUXR1.6(EBRR)位,检测到Break信号可以触发芯片复位并进入ISP编程模式,这在远程固件升级场景中非常有用。

错误处理流程 : 在中断服务程序中,除了检查RI和TI,还应检查这些错误位。

void UART_ISR() interrupt 4
{
    if (RI) {
        RI = 0;
        // 正常处理数据
    }
    if (TI) {
        TI = 0;
        // 发送处理
    }
    // 检查状态中断(如果STINT使能了)
    if (SSTAT & 0x0E) { // 检查FE, BR, OE位
        if (SSTAT & (1<<3)) { // FE
            // 处理帧错误,如重置接收状态、记录日志
            SSTAT &= ~(1<<3); // 清除FE标志
        }
        if (SSTAT & (1<<2)) { // BR
            // 处理间隔信号
            SSTAT &= ~(1<<2); // 清除BR标志
        }
        if (SSTAT & (1<<1)) { // OE
            // 数据溢出!检查代码效率,可能需要增加缓冲区
            SSTAT &= ~(1<<1); // 清除OE标志
        }
    }
}

6. I2C总线接口原理与多主仲裁

I2C是一种仅需两根线(SDA数据线、SCL时钟线)的同步、半双工、多主从串行总线。P89LPC970的I2C接口是一个字节型接口,支持主发、主收、从发、从收四种模式。

6.1 I2C总线基本时序

一次完整的I2C传输始于 起始条件(S) ,终于 停止条件(P)

  • 起始条件(S) :SCL为高时,SDA产生一个下降沿。
  • 停止条件(P) :SCL为高时,SDA产生一个上升沿。
  • 数据有效性 :在SCL高电平期间,SDA必须保持稳定。数据只能在SCL低电平期间改变。
  • 应答(ACK) :每传输完一个字节(8位),接收方必须在第9个时钟脉冲期间拉低SDA作为应答(ACK)。若保持高电平,则为非应答(NACK)。

6.2 多主仲裁与时钟同步

这是I2C的精髓所在,也是容易出问题的地方。

  • 时钟同步 :所有主设备都将自己的SCL输出为开漏模式,并连接到总线上。SCL线是线与关系。任何一个主设备在输出低电平时,都会将总线拉低。只有当所有主设备都释放SCL(输出高)时,总线才变高。这样,慢速设备的低电平周期会延长整个总线的低电平周期,实现时钟同步。
  • 仲裁 :发生在SDA线上。当多个主设备同时开始传输时,它们会一边发送数据,一边检测SDA线上的实际电平。如果某个主设备发送了高电平(释放总线),但检测到SDA线是低电平(被其他设备拉低),那么它就仲裁失败,立即切换到从接收模式,并停止驱动SDA。获胜的主设备继续传输。 仲裁的过程不会破坏获胜方的数据

实战要点

  1. 开漏输出 :MCU的I2C引脚必须配置为开漏输出模式,并外接上拉电阻(通常4.7kΩ)。这是实现“线与”和仲裁的基础。
  2. 总线忙判断 :在尝试发起起始条件前,必须检测总线是否空闲(即检测到停止条件后的一段时间)。P89LPC970的I2C模块有状态寄存器可以指示总线状态。
  3. 仲裁失败处理 :在程序中,必须处理仲裁失败的情况。通常,仲裁失败后,硬件会置位一个标志位,并可能产生中断。软件需要识别这个状态,释放总线控制权,并可能等待随机时间后重试。

6.3 P89LPC970 I2C操作流程简述

虽然数据手册提供了详细的寄存器描述,但操作I2C通常遵循以下状态机(以主模式为例):

  1. 初始化 :配置I2C时钟频率(通过设置I2C分频寄存器),使能I2C模块。
  2. 主发送模式 : a. 等待总线空闲。 b. 发送起始条件(S)。 c. 发送从机地址(7位地址 + 写方向位0)。 d. 检查从机是否应答(ACK)。 e. 发送数据字节,并检查每个字节后的ACK。 f. 发送停止条件(P)结束,或发送重复起始条件(Sr)以切换读写方向。
  3. 主接收模式 : a. 发送起始条件(S)。 b. 发送从机地址(7位地址 + 读方向位1)。 c. 接收数据字节。每接收一个字节后,主设备需发送ACK(最后一个字节前)或NACK(最后一个字节后)。 d. 发送停止条件(P)。

代码结构示例(主发送):

void I2C_Master_WriteByte(uint8_t slaveAddr, uint8_t data)
{
    I2CONSET = 0x20; // 设置I2EN,使能I2C
    I2CONSET = 0x40; // 设置STA,发送起始条件
    while (!(I2STAT == 0x08)); // 等待START条件已发送状态

    I2DAT = (slaveAddr << 1) | 0x00; // 发送从机地址+写位
    I2CONCLR = 0x28; // 清除STA和SI标志
    while (!(I2STAT == 0x18)); // 等待SLA+W已发送,收到ACK

    I2DAT = data; // 发送数据字节
    I2CONCLR = 0x08; // 清除SI标志
    while (!(I2STAT == 0x28)); // 等待数据已发送,收到ACK

    I2CONSET = 0x10; // 设置STO,发送停止条件
    I2CONCLR = 0x08; // 清除SI标志
    while (I2CON & 0x10); // 等待STO标志被清除(停止条件完成)
}

注意 :上述代码是高度简化的流程,实际应用中必须包含超时判断和错误状态(如NACK、仲裁丢失)的处理。I2C状态码(I2STAT)非常丰富,需要根据不同的状态码执行相应的操作。

7. 常见问题排查与调试心得

在多年与P89LPC970这类MCU打交道的过程中,我总结了一些串口和I2C调试的“血泪教训”。

7.1 UART通信不通?按这个清单逐项检查

  1. 电平与硬件

    • 首先用示波器或逻辑分析仪看波形! 这是最直接有效的方法。看TXD脚有没有信号?波特率对不对?起始位、停止位对不对?
    • 如果是TTL电平(3.3V/5V)对接,确保共地。
    • 如果是通过RS-232/RS-485转换芯片,检查芯片供电和使能端。
    • 检查RX/TX线是否接反了?这个错误极其常见。
  2. 软件配置

    • 波特率计算 :反复核对计算过程。特别是使用定时器1时,确认定时器的工作模式(必须是8位自动重装模式2)和SMOD1位的值。使用BRG时,确认CCLK频率是否正确,以及是否在 BRGEN=0 时写的BRGR值。
    • 中断与轮询 :如果使用中断,中断服务函数是否清除了TI/RI标志?中断是否已全局使能(EA=1)和串口使能(ES=1)?如果使用轮询,你的主循环是否足够快,能在下一个字节到来前读取SBUF?
    • 双缓冲配置 :如果使能了双缓冲,是否遵循了正确的TB8写入顺序?是否在模式0下错误地使能了它?
    • 引脚功能 :确认使用的RX/TX引脚是否已通过相关的引脚功能选择寄存器配置为UART功能,而不是普通的GPIO。

7.2 I2C总线锁死或数据错误?

  1. 总线锁死(SCL被拉低)

    • 最常见原因 :从设备在传输中(例如正在处理内部操作)拉低了SCL(时钟延长)。主设备程序没有处理这种情况,超时后强行复位,但未产生停止条件,导致从设备一直等待时钟。
    • 解决办法 :主设备程序应增加超时机制。如果超时,可以尝试连续发送几个(比如9个)SCL时钟脉冲,同时确保SDA为高,试图“喂”完从设备需要的时钟,然后发送一个停止条件来复位总线状态。更粗暴但有效的方法是在硬件上短暂切断I2C总线的上拉(如果设计允许)。
  2. 从设备无应答(NACK)

    • 检查从机地址是否正确(7位地址 vs 8位地址,注意左移一位)。
    • 检查从设备是否上电、初始化完成。
    • 从设备的内部寄存器地址是否正确?很多I2C设备需要先写一个寄存器地址指针。
  3. 波形畸变或通信不稳定

    • 上拉电阻 :阻值是否合适?线缆过长、负载过多时,4.7kΩ可能偏大,导致上升沿太慢,可以尝试减小到2.2kΩ或1kΩ(需考虑驱动能力)。
    • 电源噪声 :在VCC和GND之间靠近I2C芯片处增加一个0.1uF的退耦电容。
    • 总线电容 :过长的走线或连接过多设备会增加总线电容,降低信号质量。尽量缩短走线,并减少挂载设备。

7.3 调试工具与技巧

  • 逻辑分析仪 :对于数字通信调试,一个哪怕是最基础的逻辑分析仪也比万用表强一万倍。它能清晰展示每一位的时序、起始停止位、地址和数据字节,是分析波特率误差、协议错误的终极武器。
  • 软件模拟 :在硬件调试之前,可以先用软件模拟UART的TX功能(位翻转延时),发送固定的数据帧,用逻辑分析仪验证基本的GPIO控制和时序是否正确。
  • 打印调试法 :如果UART本身是调试对象,可以暂时用另一个已知好的UART端口(如果MCU有多个)或者用GPIO翻转来指示程序运行到了哪一步(例如,进入中断时拉高一个引脚,退出时拉低)。
  • I2C总线监控器 :有些高级的逻辑分析仪或专门的USB-I2C适配器带有协议分析功能,可以非侵入式地监听I2C总线上的所有对话,对于分析多主设备交互和从设备行为至关重要。

最后,保持耐心,从最简单的收发一个字节开始验证。嵌入式通信调试往往就是这样一个从电源、时钟、引脚配置,到软件初始化、中断处理,层层递进、逐项排除的过程。把本文提到的寄存器配置清单和排查要点放在手边,能帮你节省大量漫无目的的调试时间。

开发一个基于PHP + MySQL技术的小型web系统,通过用户的登录和注册来要实现讯息系统的发布、查看、删除和退出登录功能。 1) 登录设计:通过连接 MySQL 数据库来判断当前用户和密码是否在数据库中存在如存在则把账户名写入到session中然后进入系统,否则登录失败。 2) 注册设计:填写需要注册的账户和密码以及确认密码,如数据库中有账户一样的记录,则注册失败;如密码和确认密码不一样,则同样注册失败;否则注册成功跳转到登录页面。 3) 面向对象:对数据库的一些操作封装成一个类。 4) 发布讯息:用户填写好昵称,题,新鲜事即可点击发布。然后通过后端程序传入到数据库中保存,如昵称和题没有填写,否则发布失败。 5) 查看讯息:用户发布成功后自动跳转到查看讯息页面。然后读取页面session信息来显示登录者的名字。通过后端程序读取数据库的讯息并显示到查看页面。也可点击查看讯息按钮来查看讯息。 6) 删除讯息:通过读取页面的session 来获取当前的登录者的账号,当用户点击删除时,需要判断删除的讯息是否为当前登录者发布的,如是则删除成功,否则删除失败。 7) 退出登录:当用户点击退出登录时跳转到登录页面并使当前session失效。 8) 天气预报日期显示:通过网络在前端页面中即可显示出来。 9) 文件上传:可以在注册时上传用户的头像,登录时在前端页面显示出来。 10) Session设置:在登录页面设置session,其他页面需要时可以读取。 运行环境NetBeans+WampServer
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值