PIC USART模块接收异常:输入$后的首个字符数值始终偏高2的问题求助
我正在使用MPLAB XC8开发PIC18单片机的USART模块,但遇到了异常问题。以下是我的代码:
#include <xc.h> #include "conbits.h" #include <stdbool.h> #include <stdint.h> #include <string.h> #include <stdio.h> #define _XTAL_FREQ 8000000 void UART_RX_INIT(void); void UART_TX(uint8_t *s); void usart_send_str(uint8_t s[]); uint8_t rx_buffer[]; uint8_t UART_Buffer = 0; int rx_index=0; bool rx_reg_ok = false; uint8_t data; void main(void) { OSCCONbits.IRCF = 0X07; OSCCONbits.SCS = 0X03; while(OSCCONbits.HFIOFS !=1); RCONbits.IPEN = 1; ei(); UART_RX_INIT(); TRISD = 0X00; LATD = 0X00; while(1) { } } void UART_RX_INIT(){ TRISCbits.TRISC7 = 1; TRISCbits.TRISC6 = 0; SPBRG = 51; RCSTAbits.CREN = 1; RCSTAbits.SPEN = 1; BAUDCON1bits.BRG16 = 0; TXSTA1bits.SYNC = 0; TXSTA1bits.BRGH = 1; TXSTAbits.TXEN = 1; IPR1bits.RCIP = 1; PIE1bits.RCIE = 1; IPR1bits.TXIP = 0; PIE1bits.TXIE = 1; } void __interrupt(high_priority) ISR(void){ if(PIR1bits.RC1IF==1){ UART_Buffer = RCREG1; if(UART_Buffer == 36 && rx_index==0) { memset(rx_buffer, 0, strlen(rx_buffer)); rx_buffer[0] = UART_Buffer; rx_index++; rx_reg_ok=true; } else if(rx_reg_ok && UART_Buffer != 36) { rx_buffer[rx_index] = UART_Buffer; usart_send_str(rx_buffer); usart_send_str("\r\n"); rx_index++; } else { usart_send_str("\r\nNot Registered\r\n"); } if(UART_Buffer == 45) { usart_send_str("\r\n-----"); usart_send_str(rx_buffer); usart_send_str("-----\r\n"); } PIR1bits.RC1IF = 0; } } void __interrupt(low_priority) low_isr(void){ INTCONbits.GIEH = 0; if(PIR1bits.TXIF){ PIR1bits.TXIF=0; } INTCONbits.GIEH = 1; } ///////////////////////////TRANSMIT void UART_TX(uint8_t *s){ TXREG = *s; while(TXSTA1bits.TRMT==0); } void usart_send_str(uint8_t s[]){ int i=0; while(s[i]!='\0') { UART_TX(&s[i]); i++; } }
问题详情:
请重点关注高优先级中断函数(ISR)部分。初始状态下,若未发送$字符(ASCII码36),PIC18会返回"Not Registered";发送$字符后,后续输入的字符会被存入rx_buffer数组并回显。但此时出现异常:发送$后的首个字符数值始终比实际输入高2,例如输入'a'会被识别为'c',输入'1'会被识别为'3',且该问题仅在发送$后出现。
模拟操作示例:先后输入'1'-'2'-'$'-'a'-'b'-'c'-'-',输入'-'(ASCII码45)时,设备会显示rx_buffer内容,可观察到上述异常。请问该如何解决这个问题?
我帮你排查了代码里的几个关键问题,这个字符值偏高2的异常,核心原因是USART1的寄存器混用导致波特率配置错误,再加上一些其他的代码隐患,具体解决步骤如下:
1. 修复USART寄存器混用问题(核心)
你在代码里混用了USART0和USART1的寄存器:中断里用的是USART1的RC1IF、RCREG1,但初始化时却用了USART0的SPBRG、RCSTA、TXSTA,导致USART1的波特率完全没有被正确配置,接收采样错误,所以字符值出现固定偏移。
修改UART_RX_INIT函数,确保所有USART1的寄存器都使用带1的后缀:
void UART_RX_INIT(){ TRISCbits.TRISC7 = 1; // USART1 RX引脚 TRISCbits.TRISC6 = 0; // USART1 TX引脚 SPBRG1 = 12; // 8MHz晶振,BRGH=1,BRG16=0时,9600波特率对应的SPBRG值 RCSTA1bits.CREN = 1; // 开启USART1连续接收 RCSTA1bits.SPEN = 1; // 使能USART1 BAUDCON1bits.BRG16 = 0; // 关闭16位波特率发生器 TXSTA1bits.SYNC = 0; // 异步模式 TXSTA1bits.BRGH = 1; // 高速波特率模式 TXSTA1bits.TXEN = 1; // 开启USART1发送 IPR1bits.RCIP = 1; // USART1接收中断设为高优先级 PIE1bits.RCIE = 1; // 使能USART1接收中断 IPR1bits.TXIP = 0; // USART1发送中断设为低优先级 PIE1bits.TXIE = 1; // 使能USART1发送中断 }
这里把SPBRG改成SPBRG1,RCSTA改成RCSTA1,TXSTA改成TXSTA1,同时修正了SPBRG1的值:8MHz晶振、高速模式、16位波特率关闭时,9600波特率对应的SPBRG1是12(计算式:(FOSC/(64*波特率)) -1 = (8000000/(64*9600)) -1 ≈12)。
2. 修复rx_buffer的未定义大小问题
全局数组uint8_t rx_buffer[];没有指定大小,属于不完整类型,会导致memset、strlen等操作出现未定义行为,比如错误清除其他变量的内存。改成指定大小的数组:
#define RX_BUFFER_SIZE 64 uint8_t rx_buffer[RX_BUFFER_SIZE];
3. 修正memset的调用参数
之前用strlen(rx_buffer)作为清除长度是错误的,因为rx_buffer初始没有终止符\0,strlen会返回随机值。改成用数组的实际大小:
memset(rx_buffer, 0, sizeof(rx_buffer)); // 或者用定义的宏:memset(rx_buffer, 0, RX_BUFFER_SIZE);
4. 在中断中添加错误处理逻辑
PIC18的USART出现溢出错误(OERR)或帧错误(FERR)时,会导致后续接收数据错误,需要在中断里检查并清除这些错误:
void __interrupt(high_priority) ISR(void){ if(PIR1bits.RC1IF==1){ // 先检查并清除USART错误 if(RCSTA1bits.OERR || RCSTA1bits.FERR){ RCSTA1bits.CREN = 0; // 关闭接收 RCSTA1bits.CREN = 1; // 重新开启接收,清除错误标志 PIR1bits.RC1IF = 0; return; } UART_Buffer = RCREG1; // 你的原有处理逻辑... if(UART_Buffer == 36 && rx_index==0) { memset(rx_buffer, 0, sizeof(rx_buffer)); rx_buffer[0] = UART_Buffer; rx_index++; rx_reg_ok=true; } else if(rx_reg_ok && UART_Buffer != 36) { // 注意:这里要先判断rx_index是否超过缓冲区大小,避免数组越界 if(rx_index < RX_BUFFER_SIZE -1){ rx_buffer[rx_index] = UART_Buffer; usart_send_str(rx_buffer); usart_send_str("\r\n"); rx_index++; } } else { usart_send_str("\r\nNot Registered\r\n"); } if(UART_Buffer == 45) { usart_send_str("\r\n-----"); usart_send_str(rx_buffer); usart_send_str("-----\r\n"); } PIR1bits.RC1IF = 0; } }
另外还添加了数组越界检查,避免rx_index超过rx_buffer的大小导致内存溢出。
5. 修正发送函数的寄存器混用
发送函数里的TXREG也要改成TXREG1,因为你用的是USART1:
void UART_TX(uint8_t *s){ TXREG1 = *s; while(TXSTA1bits.TRMT==0); }
把这些修改完成后,重新编译烧录,应该就能解决字符值偏高2的问题了。
内容的提问来源于stack exchange,提问作者batgre

