我正在使用PIC18F87J11和RN42 Bluetooth module,波特率为38400。我制作了一个连接蓝牙模块的Android应用程序,并将一些数据从我的手机发送到Micrcontroller。一切都很好但我注意到有时候我会丢失一些数据。 PIC18预计大约100个字符,如果应用程序发送这100个字符没有问题,但有时它发送大约98个字符。然后PIC18继续等待2个以上的字符,但下次应用程序发送长度为100个字符的内容。
这导致应用程序与微控制器不同步,因为第一个字节代表一个命令。我发送的每个字符之间有大约20毫秒的延迟但是没有解决问题。这并不是一直发生的,但一旦发生,它就会弄乱一切。我尝试增加时间延迟似乎很有帮助,但我不希望它太慢。
导致部分数据丢失的原因是什么?如何解决?
UART代码和中断
#define CLOCK_FREQ 8000000
#define BAUD_RATE 38400
#define SPBRG_VAL ( ((CLOCK_FREQ/BAUD_RATE)/16) - 1)
void ConsoleInit(void)
{
TRISCbits.TRISC7 = 1;
TRISCbits.TRISC6 = 0;
TXSTA = 0x24;
RCSTA = 0x90; // 0b10010000;
SPBRG = SPBRG_VAL;
}
BYTE ConsoleGet(void)
{
if(RCSTAbits.OERR)
{
RCSTAbits.CREN = 0; // Disable UART receiver
RCSTAbits.CREN = 1; // Enable UART receiver
}
return RCREG;
}
void timerInit (void)
{
// Time Period Achieved : 0.001s
T0CONbits.T08BIT = 0;
T0CONbits.T0CS = 0;
T0CONbits.PSA = 1;
TMR0H = 0xF8;
TMR0L = 0x30;
T0CONbits.TMR0ON = 1;
}
中断例程
if (TMR_IF) {
TMR0H = 0xF8;
TMR0L = 0x30;
if (PIR1bits.RCIF )
{
ProcessMenu(); // In this function I call ConsoleGet() and have switch statment
}
if (INTCONbits.TMR0IE) {
/* there was a timer overflow */
PIR1bits.RCIF = 0;
}
}
谢谢!
答案 0 :(得分:2)
当接收程序丢失通过UART接收的字节时,通常意味着接收程序没有足够快地为UART的接收中断服务。 UART只能保存有限数量的接收字节,可能只有一个。因此,在UART接收下一个字节之前,程序必须从UART读取接收到的字节。如果在下一个字节到达之前没有从UART读取字符,那么UART将丢弃一个字节,程序将丢失它。
如果您正在使用ISR来处理UART的接收中断,那么我可以想到两个可能的原因,即您的程序可能无法足够快地为中断提供服务。一个原因是程序可能花费太多时间来处理更高优先级的中断。然后,较低优先级的UART接收中断可能会被推迟太长时间,UART将丢弃字节。您是否有更高优先级的ISR执行了几毫秒?
第二个原因是你的程序可能在接收中断本身花费了太多时间。你的ISR调用似乎可疑的ProcessMenu()。如果ProcessMenu()执行大量处理并且花费的时间超过串行接口的字符间延迟,则程序将无法足够快地为接收中断提供服务,并且UART将丢弃字节。 ProcessMenu()的最长执行时间是多少?
这些原因是两个例子,说明为什么有一般的经验法则可以保持ISR的简短。
如果ProcessMenu()确实花了太长时间,那么修复就是,不要在ISR中调用ProcessMenu()。 ISR应该只读取UART中的字节并将其复制到RAM中的循环接收缓冲区。通过这种方式,ISR可以非常快速地完成,并在UART接收到另一个字节之前再次准备就绪。然后,在ISR之外,主编程循环应轮询接收缓冲区并在有可用字节时调用ProcessMenu()。
答案 1 :(得分:1)
PIC18F87J11有2个不同的UART模块,UART1和UART2。您必须在写入寄存器时指定您正在使用的那个。您既不使用BAUDCON来指定波特率值是8还是16位,
TXSTA1 = 0b00100100; // TXEN, BRGH
RCSTA1 = 0b10010000; // SPEN, CREN (ASYNC assume)
BAUDCON1 = 0b00000000; //8 bit baudrate at 38400.
SPBRGL = .12; //ecimal value for 38400 BS, 0.16% error
这是UART1初始化。输入/输出由uart模块直接驱动,如数据表中所述 EUSARTx控制将自动执行 将引脚从输入重新配置为输出 需要的。
然后,在你的中断例程中,假设你已经设置了必要的位,你就搞砸了一些东西:
PIE.RC1IE = 1; //Enable receive interrupt
IPEN = 0; //All masked interrupt enabled
GIE = 1; //
PEIE = 1; //Enable peripheral interrupts (RC1IE)
然后,在中断例程的括号内
INTCON.GIE = 0; //Disable all interrupts
//Series of IF to gather interrupt source
if (PIR1.RC1IF) //Receive serial flag up?
{
if (ucTrackBuffer >= BufferSize){ucTrackBuffer= 0;}
//if ucTrackBuffer >= BufferSize, reset 0.
ucTabRx232[ucIndiceTampon] = RCREG1;//Receive uart to buffer
ucTrackBuffer++; //increm buffer tracking
}
INTCON.GIE = 1; //Re-start interrupts
PIR1.RCIF = 0; //Lower flag to permit a second interrupt
所有这些代码都可以帮助您。注意你的ISR中的括号,再也不要在ISR中调用函数,因为你可以跳过宝贵的指令周期(跳过一个字符!)。
中断服务
if (PIR1.RC1IF)
{
//Code for serial reception
}
if (TMR1_IF)
{
//Code for timer1 overflow
}
希望有所帮助,如果您还有其他需要,请告诉我。