PIC16F690 UART中RCREG寄存器清除及长数据包处理问题
UART接收21字节后停止并清除RCREG的解决方案
问题背景
基于PIC16F690、MPLAB IDE v8.84(非X IDE)及HiTech-C开发UART收发程序,接收长度≤21字节的数据包时工作正常,但接收超过21字节的数据包时,RCREG寄存器会残留未处理数据,导致程序阻塞直至读完所有残留数据。需求为:接收满21字节后停止接收,并彻底清除RCREG寄存器内的残留数据。
问题分析
当前代码中CREN位始终处于开启状态,UART会持续接收数据并填充RCREG;同时代码缺乏主动停止接收或清空残留数据的逻辑,后续调用uart_getch()时会一直等待RCIF置位,最终造成程序阻塞。
修改方案
- 接收满21字节后,关闭
CREN位以停止UART接收 - 新增缓存清空函数,主动读取RCREG直至所有残留数据被处理
- 优化主循环逻辑,避免状态变量混乱导致的异常
修改后的完整代码
__CONFIG(FOSC_INTRCIO & WDTE_OFF & PWRTE_OFF & MCLRE_ON & BOREN_OFF & CP_ON & CPD_ON); #define _XTAL_FREQ 8000000 // 8MHz晶振频率 #define Fcyc _XTAL_FREQ / 4 // 2MHz #define Tcyc 1 / Fcyc // 500ns = 0.5ms #define BAUD_RATE 9600 #define BRG_VALUE (_XTAL_FREQ/(16*BAUD_RATE)) -1 // (8MHZ/(16*9600))-1 = 51.083333->51 unsigned int count = 0; unsigned int header = 0; unsigned int tail = 20; unsigned char received_data[21] = {0}; void init_ports(void){ OSCCON = 0b01110000; //8MHz CM1CON0 = 0x00; CM2CON0 = 0x00; ADCON0 = 0x00; ANSEL = 0x00; ANSELH = 0x00; TRISA = 0b00000000; TRISB = 0b00100000; // TRISB5 : RX | TRISB7 : TX TRISC = 0b00000011; // 1 : input / 0 : output } void init_uart(void){ SPBRG = 51; TX9 = 0; TXEN = 1; SYNC = 0; BRGH = 1; SPEN = 1; RX9 = 0; CREN = 1; RCIF = 0; RCIE = 0; TXIF = 0; TXIE = 0; } void init_timer2(void){ GIE = 1; PEIE = 1; TMR2IE = 1; TMR2IF = 0; T2CON = 0b00001000; // 1:2 postscaler, TIMER2 OFF, 1:1 prescaler TMR2 = 0; PR2 = 99; // Tcyc X pre X post X PR2 = 500ns X 2 X 1 X 100(0~99) = 100us TMR2ON = 1; } unsigned char uart_getch(void) { while(!RCIF); RCIF = 0; return RCREG; } void uart_putch(unsigned char data) { TXREG = data; while(!TRMT); } // 新增:清空RCREG残留数据 void uart_flush_rcbuf(void) { unsigned char dummy; // 循环读取RCREG直到RCIF位清零,彻底清空缓存 while(RCIF) { dummy = RCREG; RCIF = 0; } } void main(void){ init_ports(); init_uart(); init_timer2(); while(1){ if(RCIF){ int i; header = 0; // 接收21字节数据 for(i=0; i<21; i++){ count = 0; while(!RCIF){ if(count >= 10){ break; } count ++; __delay_ms(1); } if(count >= 10){ break; } received_data[i] = uart_getch(); header = i; } // 停止接收并清空残留数据 CREN = 0; uart_flush_rcbuf(); // 收到完整21字节则回发 if(header == tail){ for(int i = 0; i<21; i++){ RB4=1; uart_putch(received_data[i]); RB4=0; } } // 重新开启接收,准备下一次数据包 CREN = 1; } } }
关键修改说明
- 新增
uart_flush_rcbuf()函数:通过循环读取RCREG寄存器,确保所有残留数据被处理,RCIF位清零,从根源避免阻塞 - 接收满21字节后先关闭
CREN位停止接收,再清空缓存,防止新数据持续写入 - 移除冗余的
flag和overflow_bit变量,简化状态逻辑,避免异常触发 - 处理完当前数据包后重新开启
CREN位,保证后续接收正常
内容的提问来源于stack exchange,提问作者user21998678
相关产品推荐
相关产品推荐

