You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PIC USART模块接收异常:输入$后的首个字符数值始终偏高2的问题求助

问题:PIC18 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 12:17:30