资讯详情

资讯详情

LPC1778串口UART2调试教程:从寄存器配置到CH340驱动排障

简介基于ARM Cortex-M3内核的LPC1778开发板资料包面向嵌入式工程师、电子爱好者及高校学生适合学习LPC1778芯片驱动开发、外设配置与Cortex-M3编程。压缩包内含887个文件约76.56MB以C源码356个.c、319个.h、工程文件32个.uvproj、编译脚本29个.s、29个.bin、PDF文档26个为主并辅以调试工具、链接库及说明文档可满足从原理图查看到固件编译、烧录调试的完整开发流程。资源中还包含大量UART、GPIO、定时器、ADC、DMA等外设例程以及OS移植相关的汇编与备份文件便于对照学习。目前已有680人学习下载。通过这套资料开发者可获得LPC1778数据手册、开发板原理图、使用指南和可直接导入的工程模板快速搭建开发环境理解Cortex-M3内核的寄存器操作与中断机制尤其适合需要从零开始做嵌入式项目或准备相关课程设计的读者。1. 别急着给2015年的LPC1778开发板判死刑拿到一份名叫 LPC1778开发板(2015-10-27).zip 的资料包第一反应很可能是这板子已经过时。真正把里面的 LPC1778 例程读一遍后会发现芯片本身到今天仍大量出现在产线设备里厂家停产不代表现场退役。开发板的价值不在“新”而在于它把 Cortex-M3 的串口通信引脚、时钟树和外设电源管理全部暴露在排针上调通一个 UART2等于把 Keil MDK、ISP 下载、USB 转串口驱动和寄存器初始化这几样基本功完整过了一遍。这篇文章以 lpc1778 的串口2为切入点先理清寄存器模型和波特率计算再给出最小可复现代码最后把串口烧写失败、CH340 驱动、虚拟串口丢数据这类高频问题一次性讲透。内容适合手里正握着这种老开发板的工程师也适合需要维护同类工业板的嵌入式维护人员。2. LPC1778 串口外设结构先看懂时钟、引脚和寄存器模型2.1 LPC1778 的 UART 模块和串口2默认引脚LPC1778 包含 UART0 到 UART3 四个异步串口UART1 带完整的调制解调器接口UART2 和 UART3 则带增强 FIFO。标题里的“LPC1778串口%2”其实就是在说 UART2资料包里的例程喜欢拿 UART2 下手是因为它没有 UART1 那堆 modem 控制线初始化序列最干净适合当寄存器教学模板。串口2在芯片默认引脚上是 P0.10 作为 U2_TXD、P0.11 作为 U2_RXDIOCON 功能代码为 0x01。但 2015 年的开发板不一定把这组引脚直接引到排针有些板子把 UART0 接到板级 USB 转串口芯片有些干脆把 UART2 和 SD 卡复用脚混在一起。所以拿到例程第一件事不是编译而是打开开发板原理图确认 U2_TXD/U2_RXD 接在哪个排针、中间是否有电平转换芯片。串口信号芯片引脚IOCON 功能值U2_TXDP0.100x01U2_RXDP0.110x01如果引脚对不上后面代码里两行 IOCON 赋值就要改成你板卡对应的引脚。这是“LPC1778 例程烧了没反应”的第一大原因甚至比时钟配置更容易被忽略。2.2 UART2 初始化寄存器序列LPC1778 的外设不是默认全部供电。UART2 在 PCONP 的 bit24要先把这一位置 1否则寄存器可以写但不会工作。接着才是波特率。这里有个常见误区外设时钟和 CPU 主频是两回事很多人拿 120MHz 主频去除而 UART2 的实际 PCLK 可能是主频的四分之一甚至更低。初始化函数里必须有一个 UART2_PCLK 常量且和板级时钟初始化严格对应。#include LPC177x_8x.h #define UART2_PCLK 12000000UL /* UART2 外设时钟按实际时钟树修改 */ void UART2_Init(uint32_t baud) { uint32_t div; LPC_SC-PCONP | (1 24); /* 打开 UART2 外设电源 */ LPC_IOCON-PIO0_10 0x01; /* P0.10 复用为 U2_TXD */ LPC_IOCON-PIO0_11 0x01; /* P0.11 复用为 U2_RXD */ LPC_UART2-LCR 0x83; /* 8位数据1停止位无校验DLAB1 */ div (UART2_PCLK / 16) / baud; /* 16倍过采样 */ LPC_UART2-DLL div 0xFF; LPC_UART2-DLM (div 8) 0xFF; LPC_UART2-FDR 0x10; /* 先关闭分数分频见 2.3 节 */ LPC_UART2-LCR 0x03; /* 关闭 DLAB恢复 THR/RBR 通路 */ LPC_UART2-FCR 0x07; /* 使能 FIFO清空收发 FIFO */ }这段初始化看起来不长但每一行都有明确目的。PCONP | (1 24)解决外设供电LCR 0x83同时完成数据帧格式和分频寄存器访问权限的切换div由外设时钟除以 16 倍过采样再除以目标波特率得到写入 DLL/DLMFDR 0x10表示 MulVal1、DivAddVal0即分数分频关闭。最后LCR 0x03把 DLAB 拉回 0恢复 THR/RBR 的正常读写通路。如果初始化后发现回环字符乱码优先检查的不是波特率宏而是PCLKSEL0/1里的 UART2 分频位。常见做法是在系统初始化阶段保留一个UART2_PCLK宏所有调试以此为中心展开。2.3 波特率分解DLL/DLM 和 FDR 分数分频UART2 的波特率由 DLL/DLM 决定整数分频FDR 决定分数分频关系式是baud UART_PCLK / (16 * (DLL 256 * DLM) * (1 DivAddVal / MulVal))只写整数分频时外设时钟不是目标波特率的整数倍误差很容易超过 2% 的容限。以 12MHz 外设时钟为例9600 波特率对应的整数分频是 78.125取 78 时实际波特率 9615误差 0.16%可以不用 FDR。115200 对应的整数分频是 6.51直接取 6 误差超过 8%必须用分数分频拉回来。波特率DLLDLMFDR 值实际波特率误差96000x4E0x000x1096150.16%1152000x060x000xD1约1160710.76%FDR 的 0xD1 表示 DivAddVal1、MulVal13等于给整数分频额外乘 14/13把 6.51 的差距修到 UART 容限以内。FDR 有两个约束必须记住DivAddVal 必须小于等于 MulValMulVal 最大 15。表里这两组值只在 UART2 外设时钟恰好是 12MHz 时成立换了时钟树必须重算。我一般会在调试器里直接读PCLKSEL0/1确认外设时钟后再决定用哪一组 FDR而不是凭感觉改波特率宏。3. 在 LPC1778 开发板上编译并烧录 UART2 例程3.1 工具链MDK5 器件包、CMSIS 和下载算法LPC1778 是老芯片但 MDK5 下编译并不麻烦。选器件时填 LPC1778Pack 会自动带入 CMSIS 头文件和启动文件如果手里的 ip 包是 2015 年那批头文件多半是旧版LPC177x_8x.h功能上仍然是官方寄存器和位定义可以直接用。烧录器用 J-Link 或板载调试器都行Flash Download 算法要选对应 Flash 容量的型号选错会报校验失败。资料包里通常有 lpc1778 外设库源码但不用全编进工程。我一般只留system_LPC177x_8x.c、启动文件和 UART2 相关文件。特别注意SystemInit里的外部晶振值很多乱码不是串口代码问题而是默认按 12MHz 外部晶振初始化你的开发板实际用的是 8MHz最后 UART2_PCLK 和代码里的常量对不上。3.2 UART2 回环 main.c 代码把第 2.2 节的UART2_Init原样放进工程再补上发送和接收两个函数就是一个能验证物理链路的最小回环程序。#include LPC177x_8x.h void UART2_Init(uint32_t baud); /* 见 2.2 节实现 */ void UART2_SendByte(uint8_t ch) { while ((LPC_UART2-LSR (1 5)) 0); /* 等待 THR 空 */ LPC_UART2-THR ch; } uint8_t UART2_RecvByte(void) { while ((LPC_UART2-LSR 0x01) 0); /* 等待 RBR 有数据 */ return (uint8_t)(LPC_UART2-RBR); /* 读 RBR 自动清标志 */ } int main(void) { uint8_t byte; UART2_Init(115200); while (1) { byte UART2_RecvByte(); UART2_SendByte(byte); /* 回环收到什么发回什么 */ } }UART2_SendByte等待的是 LSR 的 bit5THRE表示 THR 已把数据交给移位寄存器UART2_RecvByte等待 bit0RDR。轮询方式不依赖任何库函数新手可以用调试器单步看寄存器变化熟手则会直接把它当稳定的调试桩。回环逻辑的唯一目的就是先确认链路电脑发什么开发板就回什么。3.3 用串口调试助手做点对点验证编译烧录完成后打开串口调试助手。串口参数必须和初始化保持一致波特率 1152008 位数据1 停止位无校验无流控。先用十六进制发送 55 AA能看到 55 AA 回显说明 TX/RX 通路基本没问题。再做一次更严格的丢帧测试发送 00 01 02 03 一直递增到 FF看接收区是否一个不差。验证项预期结果意义十六进制发 55 AA原样回显物理通路正常连续发 0x00-0xFF无丢字节FIFO 和波特率匹配发字符串 uart逐字节回显THR 空判断正确这里有一个经常踩的坑串口调试助手如果同时勾选“发送新行”会在数据尾巴追加 0D 0A回环代码会原样送回别把这两个字节当成异常。驱动装好后设备管理器里应能看到USB-SERIAL CH340或类似名称的 COM 口选错口会直接打不开。4. LPC1778 串口烧写失败和连接异常怎么定位4.1 先解决串口烧写失败CH340 驱动、ISP 引脚和 boot 模式串口烧写失败是 LPC1778 开发板出现频次最高的问题大半和 UART 例程无关。先看 USB 转串口芯片2015 年左右的板子以 CH340、PL2303、FTDI 三种为主。系统不识别 COM 口时优先换驱动Win10 下 CH340 要用新版 CH340/CH341 驱动老 PL2303 驱动会报信息证失败FTDI 用 VCP 驱动。转换芯片驱动关键字设备管理器名称Linux 节点CH340CH340/CH341USB-SERIAL CH340/dev/ttyUSB0PL2303Prolific PL2303USB to Serial/dev/ttyUSB0FTDIFTDI VCPUSB Serial Port/dev/ttyUSB0驱动之后看 ISP 引导条件。LPC1778 串口下载要求在复位瞬间把 P2.10 拉低进入片内 ROM 的串口引导程序。常见做法是按住开发板 ISP 键再按复位松开 ISP 后立刻点下载顺序反了就会出现同步同步失败。最后是波特率ROM 引导对高频波特率的容错更小下载失败时把波特率降到 9600 再试成功率会明显提高。4.2 UART 能下载但回环不通查哪里能进入 ISP 说明串口物理链路没有大问题回环不通大多出在初始化代码。按顺序检查 IOCON 功能值、PCONP 的 bit24、PCLKSEL 分频位、波特率宏和外设时钟是否一致。示波器或逻辑分析仪看 U2_TXD 引脚在发送瞬间有没有电平翻转这是最直接的判断。没有示波器时可以用串口数据记录仪的思路做间接验证在接收端抓一段波形数一帧的位宽。115200 波特率下每位约 8.68us一帧 10 位约 86.8us如果测出来偏差超过 10%基本就是 FDR 算错或者 PCLK 和宏不一致。用位宽反推实际波特率比反复改配置快得多。代码层面的常见误用是死等 RDR 前没有清 FIFO 相关标志。调试中设备收到半包数据后停止回环把FCR 0x07的清除操作放到每次启动时执行而不是只在初始化时做一次。另一个低级错误是 TX/RX 接反USB 转串口的 TXD 必须接板子的 RXDRXD 接板子的 TXD这个错误占回环失败的三成左右。4.3 Ubuntu 下 CH340 虚拟串口丢数据LPC1778 开发板挂载到 Ubuntu 后会出现 Windows 下没有的权限和抢占问题。ttyUSB 设备属于 dialout 组普通用户 open 会报 Permission deniedModemManager 还会抢占串口导致数据读一半就断开。从 Linux 从串口接收数据丢失这个现象看多半不是驱动丢数据而是没设置 raw 模式或者 buffer 读得太慢。dmesg | grep ttyUSB sudo usermod -aG dialout $USER sudo systemctl stop ModemManager stty -F /dev/ttyUSB0 115200 cs8 -parenb -cstopb raw -echo cat /dev/ttyUSB0 | xxddmesg先确认设备节点和驱动加载情况usermod把当前用户加入 dialout 组重新登录后生效systemctl stop ModemManager解决系统服务抢串口stty设置 8 数据位、无校验、raw 模式避免内核行规程把 0D 0A 当行尾处理。最后用cat | xxd直接看十六进制比cat输出乱码直观很多。这套命令在树莓派或其它 ARM 板的 USB 转串口场景里同样通用。5. 把 LPC1778 串口例程改造成带中断的串口数据记录仪5.1 用接收 FIFO 中断代替轮询轮询回环能验证链路但主循环会被UART2_RecvByte阻塞。改成中断接收后数据自动进入 FIFO到达触发阈值时进入 UART2_IRQHandler主循环只消费缓冲区UART 通信和业务逻辑互不干扰。volatile uint8_t rx_fifo[256]; volatile uint16_t rx_head 0, rx_tail 0; void UART2_IRQHandler(void) { while (LPC_UART2-LSR 0x01) /* RDR1 表示 RBR 有数据 */ { rx_fifo[rx_head] (uint8_t)(LPC_UART2-RBR); rx_head (rx_head 1) % 256; } }初始化时打开接收中断启动文件里的中断服务函数名必须写成UART2_IRQHandler否则会卡在默认死循环。LPC_UART2-IER | 0x01; /* 使能 RBR 接收中断 */ NVIC_EnableIRQ(UART2_IRQn); /* 打开 NVIC 通道 */ NVIC_SetPriority(UART2_IRQn, 3);FIFO 触发阈值在 FCR 的 bit7:6 配置。阈值设 1 时每个字节都触发一次中断CPU 开销大设 4 时小包数据稍有延迟。数据记录场景我一般设 4中断里一直把 RBR 读到空再配合 256 字节环形缓冲115200 波特率下丢数据概率明显低于轮询。5.2 PC 端 Python 脚本把数据落盘开发板侧持续上报数据时PC 侧需要一个稳定的接收端。下面这段 Python 就是最简单的串口数据记录仪使用方式import serial, time ser serial.Serial(/dev/ttyUSB0, 115200, timeout0.5) with open(lpc1778_uart.log, ab) as f: while True: data ser.read(64) if data: f.write(data) f.flush()read(64)一次读一批字节因为 USB 转串口驱动的批量端点通常能一次携带几十字节ab模式追加写入断电不会丢已落盘内容。要加时间戳在f.write之前加一行time.strftime([%Y-%m-%d %H:%M:%S] )。长时间抓数时flush每包调用一次即可不要在主循环里加 sleep 限制 LPC1778 的发送频率。如果数据仍然断流回头用串口调试助手固定接收 0x55 包统计每秒数据量是否与理论值一致误差超过 0.5% 就要重新核对 UART2_PCLK 和 FDR 分数分频而不是加大缓冲区掩耳盗铃。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →