View previous topic :: View next topic |
Author |
Message |
ob5erver
Joined: 17 Oct 2007 Posts: 12
|
RS232 UART Interrupts |
Posted: Tue Apr 01, 2008 12:32 pm |
|
|
Hello,
I am having very strange problems with serial interrupts.
Let me try to explain the situation:
The device is PIC18F4620. The RX pin is connected to TX pin.
RS232 was set up with this
Code: |
#define RS232_TX_PIN PIN_C6
#define RS232_RX_PIN PIN_C7
#use rs232(baud=9600, xmit=RS232_TX_PIN, rcv=RS232_RX_PIN, bits=8, PARITY=N, STOP=1, stream=MYSTREAM)
|
INT_RDA is enabled and INT_TBE is disabled when the pic is powered on.
When character comes from outside the interrupt routine that handles INT_RDA reads that char by direct reading of RCREG then immediatelysends char by TXREG and sets the flag that will block first incoming character (to block the echo). The device have two more timer interrupts but they are used only to increment some counters.
All this works fine for a while, but after random time interval the device starts to behave strange (Receives corrupted chars and sets my error variable (int8) to zero no matter that it was set to zero only one time - after power on reset) i.e I think that this is stack corruption. I have tried 3-4 different approaches but when I send chars in interrupts with TXREG this problem remains. If I use fputc it works but blocks my interrupt until the char is sent. When the same routine is realized only with INT_RDA and fputc is in my main loop it works too.
I also had one version with TXREG in INT_TIMER interrupt handler without success too. I even have tried the Ttelmah function (http://www.ccsinfo.com/forum/viewtopic.php?t=24503) for send char...the result was even worse - device restarts avter a while.
So I am out of ideas, so ideas are welcome...thanks in advice! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Apr 01, 2008 12:39 pm |
|
|
Quote: | the flag that will block first incoming character (to block the echo). |
You must read the characters from the UART receiver. If you don't
do this, you could get an overrun error and the receiver will lock up.
Even if you don't want to use the character, you must still get it
from the UART. (This is if you don't use ERRORS parameter).
To automatically clear a hardware UART receiver lock-up, add the
ERRORS parameter to your #use rs232() statement. |
|
|
ob5erver
Joined: 17 Oct 2007 Posts: 12
|
|
Posted: Tue Apr 01, 2008 12:55 pm |
|
|
PCM programmer wrote: | Quote: | the flag that will block first incoming character (to block the echo). |
You must read the characters from the UART receiver. If you don't
do this, you could get an overrun error and the receiver will lock up.
Even if you don't want to use the character, you must still get it
from the UART. (This is if you don't use ERRORS parameter).
To automatically clear a hardware UART receiver lock-up, add the
ERRORS parameter to your #use rs232() statement. |
Thanks for your fast reply.
I have read the character and then return from interrupt if my flag is set from transmit routine. Also I have version that I block the receiver until the character was sent either by polling TXSTA_TRMT, ether with INT_TBE...the result was the same :( |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Apr 01, 2008 1:02 pm |
|
|
Post a very short program that demonstrates the problem. Don't post
200 lines of code. The program must be complete, and compilable
with no errors. Test the program before you post it.
Look at this thread to see an example of a test program. Look at the
complicated test programs that are posted, and then look at the little
program that I have posted at the end. Post a program that is about
that length, or maybe a little longer.
http://www.ccsinfo.com/forum/viewtopic.php?t=34110 |
|
|
ob5erver
Joined: 17 Oct 2007 Posts: 12
|
|
Posted: Tue Apr 01, 2008 1:26 pm |
|
|
PCM programmer wrote: | Post a very short program that demonstrates the problem. Don't post
200 lines of code. The program must be complete, and compilable
with no errors. Test the program before you post it.
Look at this thread to see an example of a test program. Look at the
complicated test programs that are posted, and then look at the little
program that I have posted at the end. Post a program that is about
that length, or maybe a little longer.
http://www.ccsinfo.com/forum/viewtopic.php?t=34110 |
I will try to reproduce the problem with smallest code size. My current program is more than 10 000 lines long. I have 15 years coding experience and I work as a lead programmer in big software company, so I don't think that the problem is within the rest of the code and you can trust me. I just want some clue to solve this mess... |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: RS232 UART Interrupts |
Posted: Tue Apr 01, 2008 1:35 pm |
|
|
What you are doing sounds very confusing. You say that the RX pin is connected to the TX pin. So TX is driving RX pin on the same PIC, so the PIC can talk to itself. But then you say that a character comes from the outside. How can this be? Do you have something else connected to the RX pin besides the TX pin? If so, then this something else is also trying to drive the RX pin. You can't have two different sources drive the same pin. They will fight each other.
Maybe you are thinking of a bus. RS-232 is not a bus. The TX pin drives all the time. It drives both high and low. You can't connect two transmitters to one receiver. There are ways to implement a half-duplex serial communication that shares a common wire, but it involves open collector outputs, diodes, or something besides just "connecting TX and RX". Whatever this is, that might be the source of your problem. Can you describe your connections more fully?
Robert Scott
Real-Time Specialties |
|
|
ob5erver
Joined: 17 Oct 2007 Posts: 12
|
Re: RS232 UART Interrupts |
Posted: Tue Apr 01, 2008 1:50 pm |
|
|
RLScott wrote: | What you are doing sounds very confusing. You say that the RX pin is connected to the TX pin. So TX is driving RX pin on the same PIC, so the PIC can talk to itself. But then you say that a character comes from the outside. How can this be? Do you have something else connected to the RX pin besides the TX pin? If so, then this something else is also trying to drive the RX pin. You can't have two different sources drive the same pin. They will fight each other.
Maybe you are thinking of a bus. RS-232 is not a bus. The TX pin drives all the time. It drives both high and low. You can't connect two transmitters to one receiver. There are ways to implement a half-duplex serial communication that shares a common wire, but it involves open collector outputs, diodes, or something besides just "connecting TX and RX". Whatever this is, that might be the source of your problem. Can you describe your connections more fully?
Robert Scott
Real-Time Specialties |
This is simple one wire asynchronous serial interface. The other receiver/transmitter will not send characters until he gets one character from me. When I send character, then I am waiting for reply from the other receiver and then he waits for my reply and so on... |
|
|
ob5erver
Joined: 17 Oct 2007 Posts: 12
|
|
Posted: Tue Apr 01, 2008 2:01 pm |
|
|
Here is my snippet.
Code: | #include <18F4620.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz)
#FUSES PROTECT //Code protected from reading
#FUSES BROWNOUT //Brown out reset when brownout detected
#FUSES BORV28
#FUSES NOPUT //No Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No debug mode for use with ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES NOPBADEN //PORTB pins are configured as digital I/O on RESET
#FUSES WRTC //configuration registers write protected
#FUSES NOWRTB //Boot not block not write protected
#FUSES NOEBTR //Memory nto protected from table reads
#FUSES NOEBTRB //Boot not block protected from table reads
#FUSES NOCPB //No Boot Block code protection
#FUSES MCLR //Master Clear pin enabled
#use delay(clock=20000000)
#BIT RDPU=0xF84.7
#BYTE TXREG = 0xFAD //Transmit char
#BYTE RCREG = 0xFAE //Receive char
#BIT TXSTA_TRMT = 0xFAC.1 //Transmit buffer empty
#BIT TXSTA_TXEN = 0xFAC.5 //TX Enable
#BIT RCSTA_CREN = 0xFAB.4 //RX Enable
#BIT RCSTA_SREN = 0xFAB.5 //Single receive enable
#priority INT_TIMER1, INT_TIMER2, INT_RDA
int16 g_iTimer1, g_iTimer2;
int1 g_bSendRequest;
#INT_TIMER1
void TimeClock()
{
g_iTimer1 ++;
if (g_iTimer1 >= 10000)
g_iTimer1 = 0;
}
#INT_TIMER2
void DisplayClock()
{
g_iTimer2 ++;
if (g_iTimer2)
output_high(PIN_A5);
else
output_low(PIN_A5);
}
void SendChar(int8 value)
{
g_bSendRequest = true;
TXREG = value;
}
#INT_RDA
void SerialDataReceive()
{
int8 rxData;
rxData = RCREG;
if (g_bSendRequest)
{
g_bSendRequest = false;
return;
}
SendChar(0xFF - rxData);
}
void DoSomeStuff()
{
delay_ms(50);
}
void main()
{
g_iTimer1 = 0;
g_iTimer2 = 0;
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(SPI_SS_DISABLED);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_OFF);
setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1 | T1_CLK_OUT);
setup_timer_2(T2_DIV_BY_16, 60, 1);
setup_timer_3(T3_DISABLED);
set_timer1(57344);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
enable_interrupts(int_timer1);
enable_interrupts(int_timer2);
enable_interrupts(global);
set_timer1(0);
enable_interrupts(INT_RDA);
while(1)
{
DoSomeStuff();
delay_ms(1);
}
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Apr 01, 2008 2:47 pm |
|
|
1. The program is missing the #use rs232() statement.
2. Tell us what this program is supposed to do.
3. Did you test the program and confirm that it fails ? Describe
the failure. Tell us what it's supposed to do, and what it didn't do.
Describe the test conditions in detail.
4. Describe the external hardware connections.
5. Your SendChar() routine does not check to see if the TXREG is
empty before it writes a byte to it. The CCS putc() function does do this.
Why did you leave off that test ? |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Tue Apr 01, 2008 6:07 pm |
|
|
Here is a possible problem. According to the timing diagram in the 18F series datasheet, the RCIF interrupt flag is set at what looks like the middle of the stop bit of the received character. That causes an immediate INT_RDA, which invokes your SerialDataReceive() interrupt service routine, which immediately loads TXREG (if this was not an echo). Loading TXREG immediately causes the beginning of a start bit out of the TX pin. Since the TX and RX pins share a common one wire, the start bit out of TX would begin before the stop bit of the previously received character was allowed to finish. This minor collision could make the character unreceivable at the other end, since the receiving UART might not recognize the start bit coming so close after an aborted stop bit. For that matter it could even throw off the reception of the echo, delaying the recognition of the start bit until some data bit later in the character.
I suggest puting a one bit (100 microsecond) time delay between the INT_RDA and the loading of TXREG to give the received stop bit time to finish. This delay will problably have to be done inside your RDA interrupt service routine. Normally it is bad practice to engage in any busy-waiting inside an interrupt service routine, but for such a small delay it should not be too bad. I don't know what library functions may be available for so short a delay, but you could accomplish the same thing with a homemade downcounter loop. You could even count instructions to adjust the length of the delay. Try it and see if the reliability of the communication improves.
Robert Scott
Real-Time Specialties |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Apr 01, 2008 8:55 pm |
|
|
Quote: | I don't know what library functions may be available for so short a delay | delay_us() ... |
|
|
ob5erver
Joined: 17 Oct 2007 Posts: 12
|
|
Posted: Tue Apr 01, 2008 11:37 pm |
|
|
PCM programmer wrote: | 1. The program is missing the #use rs232() statement.
2. Tell us what this program is supposed to do.
3. Did you test the program and confirm that it fails ? Describe
the failure. Tell us what it's supposed to do, and what it didn't do.
Describe the test conditions in detail.
4. Describe the external hardware connections.
5. Your SendChar() routine does not check to see if the TXREG is
empty before it writes a byte to it. The CCS putc() function does do this.
Why did you leave off that test ? |
1. My #use rs232 is in my first post.
2. The program is very complicated, i send packets and receive packets, they have check sums and occasionally my checksum falls or the packets are not full and the PIC behaves strange. I monitor the communication with serial port sniffer and the only problem I can see is one repeating char after every packet which does not persist in my non interrupt version.
3. The test program could not reproduce the problem because there is no packet logic and my external receiver/transmitter waits for full packet in order to answer.
5. I used to check the TXREG, I have tried to use fputc in SendChar and if there in #use rs232 state ENABLE is not used the function does not block my interrupt but the problem remains. |
|
|
ob5erver
Joined: 17 Oct 2007 Posts: 12
|
|
Posted: Tue Apr 01, 2008 11:42 pm |
|
|
RLScott wrote: | Here is a possible problem. According to the timing diagram in the 18F series datasheet, the RCIF interrupt flag is set at what looks like the middle of the stop bit of the received character. That causes an immediate INT_RDA, which invokes your SerialDataReceive() interrupt service routine, which immediately loads TXREG (if this was not an echo). Loading TXREG immediately causes the beginning of a start bit out of the TX pin. Since the TX and RX pins share a common one wire, the start bit out of TX would begin before the stop bit of the previously received character was allowed to finish. This minor collision could make the character unreceivable at the other end, since the receiving UART might not recognize the start bit coming so close after an aborted stop bit. For that matter it could even throw off the reception of the echo, delaying the recognition of the start bit until some data bit later in the character.
I suggest puting a one bit (100 microsecond) time delay between the INT_RDA and the loading of TXREG to give the received stop bit time to finish. This delay will problably have to be done inside your RDA interrupt service routine. Normally it is bad practice to engage in any busy-waiting inside an interrupt service routine, but for such a small delay it should not be too bad. I don't know what library functions may be available for so short a delay, but you could accomplish the same thing with a homemade downcounter loop. You could even count instructions to adjust the length of the delay. Try it and see if the reliability of the communication improves.
Robert Scott
Real-Time Specialties |
Nice suggestion, but as I already said, I had a version that sets one flag in INT_RDA when I need to send a char and then after 50 cycles of Timer2 interrupt I set the TXREG register if it is empty. This does not helped too :(
The only way that this works is by calling fputs in main loop of my program.
I will try to play with delays to see is there will be any changes. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Wed Apr 02, 2008 5:57 am |
|
|
ob5erver wrote: | ...I had a version that sets one flag in INT_RDA when I need to send a char and then after 50 cycles of Timer2 interrupt I set the TXREG register if it is empty. This does not helped too ... |
Then maybe you have two problems. The fact that using the Timer 2 delay didn't fix everything does not change the fact that the delay is still needed. You can't go starting new characters after only half a stop bit.
Maybe the other end of the one wire protocol is also at fault, transmitting before it receives a character.
Robert Scott
Real-Time Specialties |
|
|
ob5erver
Joined: 17 Oct 2007 Posts: 12
|
|
Posted: Wed Apr 02, 2008 7:12 am |
|
|
RLScott wrote: | ob5erver wrote: | ...I had a version that sets one flag in INT_RDA when I need to send a char and then after 50 cycles of Timer2 interrupt I set the TXREG register if it is empty. This does not helped too ... |
Then maybe you have two problems. The fact that using the Timer 2 delay didn't fix everything does not change the fact that the delay is still needed. You can't go starting new characters after only half a stop bit.
Maybe the other end of the one wire protocol is also at fault, transmitting before it receives a character.
Robert Scott
Real-Time Specialties |
I have added 100us delay before and after I send a char without any success. The other end does not fault, when I does not use send char within the interrupts it works fine... |
|
|
|