View previous topic :: View next topic |
Author |
Message |
JamesW, Kent, England Guest
|
18F4685 USART IRQ Problem |
Posted: Sun Jan 04, 2009 10:02 am |
|
|
Hi Folks,
Why do I always get the weird problems??
I have a 18F4685 that is communicating with a wireless receiver device (also by co-incidence a PIC).
Basically I lower a wake pin on the processor, send a command to the device and receive either
<ACK> or <NAK>
so simple it's untrue!
However after 2 days of head scratching I've got to ask for help.
I have another pc monitoring the receive line via a max232 so I can verify that the processor is getting the data OK.
Using the standard CCS int_rda example I do not receive all the data from the wireless device, my usart buffer contains the first 2 characters and nothing else.
This code does not work
/* TURN ON INTERRUPTS */
enable_interrupts(GLOBAL);
enable_interrupts(INT_RDA);
for(;;)
{
if (COM1_kbhit)
{
printf(lcd_putc,"%c", COM1_bgetc());
}
}
I get about 2 characters
However - if I do not use interrupts. I can receive the entire message
This example works fine..
for(;;)
if (bit_test(USART_PIR1, 5))
{
Response[ResponsePosition++] = getc();
if (ResponsePosition == 5)
{
goto ExitLoop2;
}
}
ExitLoop2
This has got me completely perplexed, I've been thumbing through the microchip datasheet and got the above example working.
Using the ICD-2 debugger I can examine the registers and strangely some do not agree with what I think they should from the datasheet.
(I am running at 20MHZ and need a 9600 baud)
TXSTA = 0xA6
RCSTA = 0x90
BAUDCON = 0x00
SPBRG = 0 (This seems strange!)
Compiler version 4.073
I know that the receive interrupt code example dates back to the 16C74 days, is there something else I should be doing
Thanks
James |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sun Jan 04, 2009 10:38 am |
|
|
I'm unable to recognize what's valid code in your post. Your enabling INT_RDA, but there's no #INT_RDA interrupt service routine. Please post a complete example to make the problem understandable. |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1934 Location: Norman, OK
|
|
Posted: Sun Jan 04, 2009 10:46 am |
|
|
And also use the CODE button for your code to make it readable...
Offhand it sounds like a buffer overrun to me. When we see the code it will be clearer. |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
Re: 18F4685 USART IRQ Problem |
Posted: Sun Jan 04, 2009 11:46 am |
|
|
JamesW, Kent, England wrote: |
Using the standard CCS int_rda example I do not receive all the data from the wireless device, my usart buffer contains the first 2 characters and nothing else. |
This is a pretty good indication that you are getting overrun errors. The UART receive buffer is not being process fast enough. It also indicates you have not included the "errors" directive in the #use RS232 statement
Quote: |
This code does not work
/* TURN ON INTERRUPTS */
enable_interrupts(GLOBAL);
enable_interrupts(INT_RDA);
for(;;)
{
if (COM1_kbhit)
{
printf(lcd_putc,"%c", COM1_bgetc());
}
}
|
Is the for loop inside the int_rda interrup handler? If not then this is your problem, you have enabled interrupts but have no interrupt handler.
If it is in the interrupt handler then this is still a problem - you have used a slow printf ()inside the interrupt handler. If your incoming characters are close together then an overrun error may occur.
Quote: |
BAUDCON = 0x00
SPBRG = 0 (This seems strange!)
|
If you are using a #use RS232 directive after setting these registers then the registers are being set by the #use directive. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
Guest
|
|
Posted: Sun Jan 04, 2009 11:49 am |
|
|
Ahh - that code button, I've been looking for how do to that!!!!
Code: |
/* ------------------------------------------------------------------------- */
/* SERIAL PORT DEFINITIONS */
#define COM1_BUFFER_SIZE 40
BYTE COM1_buffer[COM1_BUFFER_SIZE];
BYTE COM1_next_in = 0;
BYTE COM1_next_out = 0;
unsigned int IrqCount = 0;
/* ------------------------------------------------------------------------- */
#int_rda
void serial_isr()
{
int t;
IrqCount++;
COM1_buffer[COM1_next_in]=getc();
t = COM1_next_in;
COM1_next_in=(COM1_next_in+1) % COM1_BUFFER_SIZE;
if(COM1_next_in==COM1_next_out)
COM1_next_in=t; // Buffer full !!
}
/* ------------------------------------------------------------------------- */
#define COM1_kbhit (COM1_next_in != COM1_next_out)
BYTE COM1_bgetc()
{
BYTE c;
while(!COM1_kbhit) ;
c=COM1_buffer[COM1_next_out];
COM1_next_out=(COM1_next_out+1) % COM1_BUFFER_SIZE;
return(c);
}
/* ------------------------------------------------------------------------- */
/* ROUTINE TO RE-INITIALISE/INITIALISE THE USART */
void COM1_Initialise()
{
unsigned int Junk;
disable_interrupts(INT_RDA);
set_uart_speed(9600);
#ifdef TRY_THIS
bit_clear(USART_RXSTA,4);
/* SET BAUD RATE TO 9600 */
USART_SPBRG = 31;
/* CLEAR SYNC */
bit_clear(USART_TXSTA, 4);
/* CLEAR BRGH */
bit_clear(USART_TXSTA, 2);
/* CLEAR BRG16 */
bit_clear(USART_BAUDCON, 3);
bit_set(USART_RXSTA,4);
#endif
delay_ms(100);
if (COM1_kbhit)
{
Junk = COM1_bgetc();
}
IrqCount = 0;
memset(COM1_buffer, 0, COM1_BUFFER_SIZE);
COM1_next_in = 0;
COM1_next_out = 0;
enable_interrupts(INT_RDA);
}
|
|
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1934 Location: Norman, OK
|
|
Posted: Sun Jan 04, 2009 1:11 pm |
|
|
At first glance your Int_RDA and handling buffer routines look basically OK .
The entire COM1_Initialize routine can be replaced with this line at the beginning of your program:
Code: | #use rs232(baud=9600, xmit=PIN_XX, rcv=PIN_XX, ERRORS) |
(be sure to insert your xmit/recv pin numbers)
Don't enable/disable interrupts anywhere except just before entering your main loop. Let the compiler handle those things for you automatically.
Example: Code: |
void main()
{
IrqCount = 0;
COM1_next_in = 0;
COM1_next_out = 0;
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
while(1)
{ // poll the recv buffer here
// NOTE: Take the "while (!COM1_KBHit)" line out of the COM1_BGetc
// routine. It causes you to hang inside the routine which is
// not a good thing to do
//
if COM1_kbhit
COM1_bgetc();
}
} |
See this topic for more info:
http://www.ccsinfo.com/forum/viewtopic.php?t=36039
Also, you reserve the buffer memory with this:
Code: | BYTE COM1_buffer[COM1_BUFFER_SIZE]; |
So you don't need the memset() line. |
|
|
JamesW, Kent, England Guest
|
|
Posted: Sun Jan 04, 2009 2:23 pm |
|
|
Cheers for the pointers.
I normally use the #USE RS232 line, but when I encountered the problems I started clutching at straws thinking that maybe the initialisation routines were playing up.
The memset is there purely to make it easier to use the debugger to find any data located in the buffer.
I've been delving a little deeper in this.
I have written the routine above that "works" into the code using a counter coming off the timer interrupt to exit the loop. - but this should really be working as an IRQ and seems like a complete bodge.
I have come to the following hypotheses.
1) The usart itself is working fine, as by polling the PIR register for the "I have a character in the RX buffer! I see data and a complete packet.
2) The first interrupt works fine, it is interrupts later on in the data stream that are causing problems or being missed. This at least proves that the compiler is locating the interrupt handler in the right place, and off the right interrupt.
Therefore I "think" that there must be something that needs clearing/reseting in the IRQ service routine that is preventing it handling things correctly. I would normally assume that this is being done automatically by the compiler, but maybe not for the 18F processor.
I've been using the #USE_RS232 and the above interrupt service routine on various processors since about 1997 without a problem.
(Well nothing like this anyway!)
This may well link in with my other post about receive issues when using an 18F device in i2C slave mode.
What should be cleared when servicing an RS232 receive interrupt?
Thanks in advance
James |
|
|
Ttelmah Guest
|
|
Posted: Sun Jan 04, 2009 4:02 pm |
|
|
I'd try changing your buffer size.
I have warned in the past about this.
You buffer size should _either_ be a 'binary' value (16, 32, 64 bytes), or you should re-code the ISR as:
Code: |
#int_rda
void serial_isr()
{
int t;
IrqCount++;
COM1_buffer[COM1_next_in]=getc();
t = COM1_next_in++;
if (COM1_next_in == COM1_BUFFER_SIZE) COM1_next_in=0;
if(COM1_next_in==COM1_next_out)
COM1_next_in=t; // Buffer full !!
}
|
The problem is that using '%', with a size other than a binary value, results in an integer division, and multiplication, being placed inside your interrupt handler, and interrupts being disabled round these same arithmetic routines in the 'main' code.
It may not be the problem, but it will massively reduce the potential performance of the serial code.
The same change should be added to the bgetc, but here it is less important.
There really ought to be a warning in the CCS examples about this....
Best Wishes |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sun Jan 04, 2009 4:09 pm |
|
|
I think, the %40 operation, although time consuming, shouldn't be an issue at 9600 baud. I rather expect, that some considerably long interrupt disable occurs during lcd_putc(). But both assumptions should be checked. |
|
|
JamesW, Kent, England Guest
|
|
Posted: Sun Jan 04, 2009 4:21 pm |
|
|
I will give the buffer size change a try tomorrow morning.
The lcd_putc was put there purely for debug purposes. The original routine consisted of using kbhit to dump the characters in an array until a '>' (end of packet) character was found or a timeout occurred.
I've been reading from the microchip datasheet again. Does the interrupt priority have any effect on this (not that there are any other interrupts enabled at the moment)???
According to the datasheet, reading from the usart clears the IRQ automatically (it seems to be read only)- I'll check that this is clear as well and get back to you.
Cheers
James |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1934 Location: Norman, OK
|
|
Posted: Sun Jan 04, 2009 5:15 pm |
|
|
Using either getc() or reading the UART recv register directly clears the interrupt. Since you are using just one interrupt the priority makes no difference.
The inclusion of the ERRORS at the end of the #USE_RS232 line is designed to take care of any overrun errors that are encountered and is the likely culprit in my opinion. If the data source is slightly off baud rate wise it will definitely cause a problem. |
|
|
JamesW, Kent, England Guest
|
|
Posted: Wed Jan 07, 2009 4:44 am |
|
|
Well I'll damned!
Changing the buffer size to 64 and removing the ERRORS declaration has made it work!
Looking at the datasheet a baud rate of 9600 at 20MHz gives an error of about 1.7%
THANKS CHAPS!!!!!
One more small question, now I am not using the ERRORS directive - should I be checking any bits of the usart for errors?
Thanks again
James |
|
|
Ttelmah Guest
|
|
Posted: Wed Jan 07, 2009 5:06 am |
|
|
If you want to clear errors, use:
Code: |
#bit OERR=0xFAB.1
#bit CREN=0xFAB.4
//At the end of your serial routine
while (OERR) {
CREN=false;
CREN=true;
}
|
Have you tried with leaving ERRORS on though?. Normally the CCS code for this works OK, so it is sounding to me as if the problem may well relate to the buffer arithmetic. This can have unexpectedly 'major' effects, because of the way it leads to interrupts being disabled in the external code....
Best Wishes |
|
|
|