尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PIC18F45K22 UART实战:寄存器配置与MPLAB X调试

PIC18F45K22 UART实战:寄存器配置与MPLAB X调试 简介这是一个面向PIC18F45K22微控制器的UART串口通信示例工程用于演示基于RS485协议的字符串收发适合学习MPLAB X IDE开发流程和嵌入式串行通信的工程师与学生。压缩包共18个文件体积约25KB包含C源码、Hex固件、Makefile构建脚本、MPLAB X工程配置及属性文件等主程序与编译输出文件可帮助用户直接查看代码逻辑并烧录验证。已有169人学习下载。资源核心价值在于通过一个可运行的UART_TEST工程直观呈现UART初始化、波特率配置、RS485方向控制及收发字符串的完整代码路径结合nbproject目录下的项目和Makefile配置还能了解MPLAB X下PIC18系列项目的组织方式适合作为RS485多机通信开发的入门参考。1. 用MPLAB X把PIC18F45K22的UART_TEST跑起来先理清串口调试链路UART_TEST.zip这个命名把目标写得很直白一个针对PIC18F45K22的UART收发测试固件包。配合mplab x它就是在MPLAB X里打开、编译、烧录并验证串口通断的最小工程。PIC18F45K22是Microchip 8位产品线里带增强型EUSART外设的型号相比老PIC18F4520多了发送缓冲、PPS引脚重映射和自动波特率检测MPLAB X从v5.30开始也能用MCC把这套初始化代码直接生成出来。目标读者有两类从Arduino或STM32转过来、初次接触MPLAB X的人想知道工程建在哪、为什么串口助手全是乱码以及在PIC上写过项目、想确认EUSART寄存器顺序的工程师。下面从寄存器结构与波特率计算讲起落到建工程步骤和收发代码最后收在调试验证上。2. PIC18F45K22的EUSART寄存器与波特率计算写UART代码前的必修课2.1 增强型EUSART与普通UART的差异在哪里PIC18F45K22的串口模块在Microchip内部标识是EUSART与早期型号的USART模块相比增强点集中在三处发送端多一级缓冲可以让CPU提前准备下一字节写入减少字节间隙接收端支持硬件自动波特率检测利用同步字符的跳变沿估算波特率适合批量板卡测试另外增加了面向LIN总线协议的功能比如Break字符检测和帧错误标志FERR的硬件管理。落到UART_TEST这个场景最需要记住的是引脚选项。PIC18F45K22默认把EUSART放在RC6/TX与RC7/RX如果使用PPS重映射也能把这两个信号搬到其他端口。重映射需要同时配置两个方向的寄存器输入方向选择寄存器和输出方向引脚选择寄存器只配一个方向就会出现发送有波形、接收没数据的问题。功能默认引脚PPS重映射示例重映射时需要确认的寄存器TX/CKRC6RB7输出方向选择寄存器RPORRX/DTRC7RB5输入方向选择寄存器RPINR电平标准3.3V/5V TTL与默认一致无需额外配置PPS重映射在带外设引脚选择功能的型号上要小心因为重映射后原引脚的模拟输入和数字IO方向会自动切换问题常常出现在“配置了PPS但没关闭原引脚功能”的情况。2.2 波特率计算公式与SPBRG配置速查异步模式下的波特率公式由BRGH位决定分频方式当BRGH 0低速波特率 FOSC /64 ×SPBRGH:SPBRG 1当BRGH 1高速波特率 FOSC /16 ×SPBRGH:SPBRG 1反推写入值SPBRGH:SPBRG FOSC /16 × 目标波特率- 1这里BRGH取1。SPBRGH和SPBRG合起来是一个16位值。假设FOSC为8MHz目标9600波特率则计数 8000000 / 153600 - 1 51.08取整后SPBRG 51、SPBRGH 0实际波特率9615.38误差只有0.16%。下表按FOSC 8MHz、BRGH 1给出常用设置值目标波特率SPBRG写入值实际波特率误差是否推荐12004161199.04-0.08%推荐24002072403.850.16%推荐48001034807.690.16%推荐9600519615.380.16%推荐192002519230.770.16%推荐384001238461.540.16%推荐57600855555.56-3.55%不推荐115200-100000-13.2%不可用可以看出PIC18F45K22在8MHz系统时钟下稳定波特率上限到38400。要跑57600或115200需要把FOSC提到16MHz以上或者接受误差后在上位机侧按实际波特率重新配置。多数工程师的做法是改用外部晶振或打开内部PLL提高系统时钟。提示上表基于FOSC 8MHz且BRGH 1。波特率误差超过2%时长帧数据容易出现字节错位回环测试能明显看到这个问题。2.3 振荡器频率忘记改串口输出全是乱码PIC18F45K22波特率公式里的FOSC指系统时钟频率不是指令周期。系统时钟最终来源由配置位和OSCCON寄存器共同决定。很多UART_TEST工程出现“发出去一开始是乱的后面完全不对”就是因为初始化代码里没有显式设置内部振荡器频率。常见做法是main函数开头先完成振荡器切换void osc_init(void) { OSCCONbits.IRCF 0b111; // 内部振荡器输出8MHz OSCCONbits.SCS 0b00; // 时钟源由配置位决定 while (!OSCCONbits.HFIOFS); // 等待高频振荡器稳定 }参数说明IRCF是OSCCON的bit6~bit40b111对应8MHzSCS的0b00表示系统时钟来源跟随配置字不在这里强行切换HFIOFS是高频内部振荡器稳定标志刚上电或改变IRCF后需要等它置位否则接着初始化UART时波特率发生器的时钟可能还没切换完成。如果在MPLAB X里使用图形化配置位窗口内部振荡器频率选项会直接写进配置字。手动代码里坚持用OSCCONbits配合HFIOFS等待比直接写OSCCON整个寄存器更不容易出错因为不用记住每个保留位的复位值。3. 在MPLAB X中搭PIC18F45K22最小UART工程寄存器初始化和MCC两条路线3.1 手动初始化EUSART的推荐寄存器序列在MPLAB X里新建工程后先确认Project Properties中选中的是PIC18F45K22编译器是XC8。然后写初始化函数UART_TEST里最常用的EUSART初始化序列如下void uart_init(unsigned long baud) { unsigned int spbrg_value; unsigned char dummy; spbrg_value (unsigned int)(8000000UL / (16UL * baud) - 1); TXSTA 0x24; // TXEN1, BRGH1, 异步8位 RCSTA 0x90; // SPEN1, CREN1 SPBRGH (unsigned char)(spbrg_value 8); SPBRG (unsigned char)(spbrg_value 0xFF); dummy RCREG; // 清掉残留数据 (void)dummy; RCSTAbits.CREN 0; // 先关接收 RCSTAbits.CREN 1; // 重开清OERR PIE1bits.RCIE 0; // 中断接收时再置1 }代码说明TXSTA的bit2即BRGH位置1后按16分频误差比64分频小一个数量级。RCSTA的bit7是SPEN相当于EUSART总开关bit4是CREN连续接收使能清零再置位可以清除溢出错误OERR。SPBRGH和SPBRG必须分开写入高、低字节顺序无所谓但要在TXSTA配置之前完成避免波特率发生器按旧值先跑起来。手动写初始化的好处是每个位都看得见对照数据手册容易排查。缺点是换时钟或换波特率时需要重新计算。我一般把FOSC定义成宏把初始化函数封装成接收baud参数的形式这样改时钟频率时只动一处代码。3.2 用MCC配置EUSART的步骤与生成代码的裁剪MPLAB X的MCC使用教程一直是热门搜索词。在v5.30中通过Tools → Embedded → MPLAB Code Configurator可以启动MCC。对UART_TEST这类工程配置路径如下在Device Resources中双击EUSART将其加入工程。在EUSART设置页勾选Enable Receiver和Enable TransmitterBaud Rate选9600。在Pin模块中确认TX RC6、RX RC7与目标板接线一致。在System Module中把时钟源设为内部振荡器并明确选择8MHz。点击Generate生成代码。配置项推荐值说明ReceiverEnable生成接收相关代码TransmitterEnable生成发送相关代码Baud Rate9600与上位机串口助手严格一致Stop Bits1标准异步帧格式Interrupt DrivenDisable先跑通轮询模式再开中断MCC生成的代码里我一般保留uart_initialize之类的初始化接口不直接用它的读写函数。原因是MCC的Read/Write带有缓冲区管理和超时逻辑对UART_TEST这种想精确控制收发行为的场景反而增加排查难度。把初始化代码和自己的收发函数搭配使用是最稳妥的落地方式。3.3 编译器路径与烧录器选择的三个注意点从Microchip官网下载MPLAB X v5.30安装后还需要单独安装与版本匹配的XC8编译器。装完后在Tools → Options → Embedded → Build Tools里指定编译器目录否则工程会报Compiler not found。烧录器方面PICkit 3和ICD4都可以通过ICSP口烧录PIC18F45K22。在Project Properties的Hardware Tool里选择对应调试器然后确认MCLR Enabled勾选因为UART_TEST调试期间依赖MCLR复位。烧录时报Device ID mismatch先量VDD是否正常。PICkit 3对目标板供电很敏感如果开发板用外部5V电源USB转串口模块同时接3.3V两套供电之间有电位差就会烧不进。把两个模块共地或者拔掉外部电源改用PICkit 3供电通常马上解决。4. UART收发实战让PIC18F45K22的测试数据进入串口助手4.1 轮询方式发送字符串为什么不建议只查TXIF发送一个字符串的代码很简单内部有一个细节容易踩判断发送完成的标准要选对。void uart_send_byte(unsigned char data) { while (!TXSTAbits.TRMT); // 等待移位寄存器空 TXREG data; } void uart_send_string(const char *str) { while (*str) { uart_send_byte((unsigned char)*str); } }代码说明TXSTAbits.TRMT是只读的移位寄存器空标志表示TSR已经完成发送下一字节可以安全写入TXREG。如果改用TXIF虽然TXIF代表TXREG为空但TSR可能仍在发送上一字节此时连写两个字节会覆盖正在发送的数据出现丢字节或错位。用TRMT判断能保证每个字节真正发送结束再写下一字节这是UART_TEST里最稳的发送方式。收发方式适用场景需要注意的点轮询发送日志输出、测试指令连续发送勿用TXIF判断中断接收回显短帧交互、回环验证回显会阻塞中断长帧不适用中断接收环形缓冲区协议数据、命令帧缓冲区满时选择丢弃或覆盖轮询发送会让CPU一直等在循环里在集中发日志时问题不大。如果主循环还要处理按键或ADC采样就需要把发送放到调度里或者改用中断。4.2 中断方式接收读取RCREG前的标志处理实时性要求高时用中断接收。PIC18的中断入口统一在0x08地址ISR中通过PIR1的RCIF判断是否为EUSART接收中断。void __interrupt() isr(void) { unsigned char rx_data; if (PIR1bits.RCIF PIE1bits.RCIE) { if (RCSTAbits.OERR) { RCSTAbits.CREN 0; // 溢出后先关接收 RCSTAbits.CREN 1; // 重开清除OERR } if (!RCSTAbits.FERR) { rx_data RCREG; // 正常数据 uart_send_byte(rx_data); // 回显一个字节 } else { unsigned char dummy RCREG; // 帧错误也读走数据 (void)dummy; } } }逻辑说明RCIF置位表示RCREG已经有可用数据OERR处理必须在读RCREG之前完成否则接收器一直停摆FERR是帧错误标志接收到错误停止位时置位此时读出的数据虽然没有意义但仍然要读走避免留在缓冲器里影响后续判断。注意main中需要打开INTCON的GIE和PEIE以及PIE1的RCIE。这段ISR里调用了uart_send_byte回显在波特率9600下不会丢数据。如果波特率更高或回显帧较长不要在中断里等待发送而是把数据交给缓冲区。4.3 一个环形缓冲区把中断接收的数据转交主循环中断接收的数据最终要给主循环用环形缓冲区是最简单的结构。它解决两个问题ISR执行时间短数据不会丢主循环有数据时拿走没有数据时继续干别的。#define UART_RX_BUF_SIZE 64 volatile unsigned char uart_rx_buf[UART_RX_BUF_SIZE]; volatile unsigned int uart_rx_head 0; volatile unsigned int uart_rx_tail 0; void uart_rx_put(unsigned char data) { unsigned int next (uart_rx_head 1) % UART_RX_BUF_SIZE; if (next ! uart_rx_tail) { // 缓冲区不满 uart_rx_buf[uart_rx_head] data; uart_rx_head next; } } unsigned char uart_rx_get(void) { unsigned char data; data uart_rx_buf[uart_rx_tail]; uart_rx_tail (uart_rx_tail 1) % UART_RX_BUF_SIZE; return data; }代码说明head指向下一个写入位置tail指向下一个读取位置两者相等时缓冲区为空。代码刻意用next tail作为满条件tail永远慢head一步满和空可以区分。get操作只改tailput只改head中断和主循环各写各的索引不会有竞争问题。在ISR中把RCREG的数据交给uart_rx_put主循环里检查head ! tail后调用uart_rx_get即可。5. UART_TEST联调的排错顺序PIC18F45K22的时钟、引脚与回环验证5.1 时钟与引脚复用先排除模拟功能和配置位干扰串口助手完全收不到数据时第一步量TX引脚有没有波形。没有波形按三个点查系统时钟有没有起来、TXSTA的TXEN是否置1、RC6是否被配置成模拟功能或第二功能。PIC18F45K22的模拟选择寄存器ANCON0和ANCON1决定多个引脚是数字还是模拟状态。如果工程之前开过ADC但没有关闭对应通道RX/TX又恰好重映射到带模拟功能的引脚接收就会失效。保险的写法是在初始化开头执行ANCON0 0xFF; // 关闭所有模拟输入 ANCON1 0xFF; // AN8~AN15全部设为数字功能注意这条命令会把与ADC、比较器相关的引脚全部切回数字功能。如果工程里同时要用ADC就不要全写0xFF按数据手册只关用不到的通道位。5.2 用逻辑分析仪验证波特率偏差TX有波形但PC端收乱码用逻辑分析仪抓TX波形比反复猜参数更高效。发送固定值0x55二进制是01010101示波器上应看到占空比稳定的方波。测量一个bit的宽度9600波特率对应的周期是104微秒左右如果测出112微秒说明实际波特率偏低优先检查FOSC是不是真的8MHz再考虑SPBRG取整误差和内部振荡器漂移。5.3 回环测试把PIC18F45K22的发送直接接到接收排错最后一步推荐回环测试把TX引脚直接用杜邦线短接到RX引脚运行下面这段代码void loopback_test(void) { const unsigned char test_data[] UART_TEST_OK\r\n; unsigned int i; unsigned char received; for (i 0; i sizeof(test_data) - 1; i) { uart_send_byte(test_data[i]); while (!PIR1bits.RCIF); // 等待接收完成 received RCREG; if (received ! test_data[i]) { LATBbits.LATB0 1; // 不匹配时点亮LED while (1); } } }逻辑说明逐字节发送、逐字节接收并比较。接收侧用RCIF等待并读取RCREG任何一个字节不匹配就停在死循环并点灯失败位置可以直接看到。回环测试全部通过说明EUSART的发送路径、接收路径和接线都正常剩下的问题只可能是外部电平转换或USB转串口模块。这套顺序同样适用于从UART_TEST迁移到实际产品代码后的串口联调把test_data换成带设备地址和校验字节的帧格式回环测试就能直接验证协议层的字节序。本文还有配套的精品资源点击获取
返回列表