|
|
View previous topic :: View next topic |
Author |
Message |
Bill24
Joined: 30 Jun 2012 Posts: 45
|
Interrupt nesting |
Posted: Mon Aug 06, 2012 2:08 am |
|
|
Using a PIC18f450 at 4Mhz and CCS 4.119
There seems to be a problem with our serial interrupt routine. RS232 uses a baud rate of 19200.
When a stream of 4 characters arrive with an inter byte gap of 50uS the interrupt routine appears to interrupt itself as shown by toggling an output pin.
The routine is very short. I believe there is approx 500uS before each character arrives. It is a bit strange to me as it uses inter packet time as a delimiter rather than the usual strat/end character. This format has been imposed by a customer. But the customer sends a 4 byte packet back to back.
Am I correct in my thought that the problem is interrupt nesting causing problems.
Code: |
#int_RDA
void incomming_rs485(void)
{
// Definitions for State machine
#define ADDRESS_ST 0
#define COMMAND_ST 1
#define LENGTH_ST 2
#define DATA_ST 3
#define CHECKSUM_ST 4
unsigned int8 b, timer_count;
// Statics used to retain their vale between function calls
static unsigned int8 checksum ,state = ADDRESS_ST,length;
// Timer 0 ticks is set to tick every 256us ( Rolls over at 65ms).
// If the timer > 5ms (20 ticks) this byte is in the current frame but delayed.
// If the timer > 10ms (40 ticks ) then this is a new frame.
timer_count = get_timer0();
// Ensure incoming message does not exceed buffer bounds.
// and also byte received on time.
if ( ( next_in > RS485_RX_BUFFER_SIZE ) || (timer_count > 20 ))
{
// Ignore it and restart reading at buffer[0]
message_received = 0;
state = ADDRESS_ST;
}
// Prevent buffer overflow if main loop is not processing messages
if ( 0 == message_received )
{
if ( timer_count >= 40 )
{
// This is definitely a new message.
// Re sync state machine.
state = ADDRESS_ST;
}
// Read input byte
b=fgetc(RS485);
switch(state)
{
case ADDRESS_ST: // Get address
next_in = 0;
checksum = b;
rs485_buffer[next_in] = b;
if ( b != this_module_address) //or broadcast address
{
// No need to continue
// Leave state in ADDRESS_ST and bail out.
message_received = 0;
}
else
{
next_in += 1;
state = COMMAND_ST;
}
break;
case COMMAND_ST: // Get command
checksum ^= b;
rs485_buffer[next_in] = b;
next_in += 1;
state = LENGTH_ST;
break;
case LENGTH_ST: // Get length
if (b == 0)
{
state = CHECKSUM_ST;
}
else
{
length = b;
checksum ^= b;
rs485_buffer[next_in] = b;
next_in += 1;
state = DATA_ST;
}
break;
case DATA_ST: // Get variable length packet
checksum ^= b;
rs485_buffer[next_in] = b;
next_in += 1;
--length;
if (!length)
{
state = CHECKSUM_ST;
}
break;
case CHECKSUM_ST: // Get checksum
// Add to buffer
rs485_buffer[next_in] = b;
if ( b ==checksum ) // verify calculated with received checksum
{
// Signal Main Loop that message has been received
message_received = 1;
}
state = ADDRESS_ST;
break;
default:
state = ADDRESS_ST;
break;
}
}// if ( 0 == message_received )
// Restart counter timer
set_timer0(0);
} |
|
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Mon Aug 06, 2012 3:32 am |
|
|
No, it can't be nested interrupts as you have done nothing (as far as the code you've shown us) that re-enables interrupts inside your ISR. By default, interrupts are disabled while ISRs are executing. So, no nested interrupts.
At 19200 characters can indeed only come in at no less than 520.8us intervals. I don't know how the 50us inter-character gap is implemented on the master, but a naive implementation may well mean there is actually no inter-character gap; something along the lines of;
Code: |
puts(character);
delay-us(50);
puts(another_character);
|
The delay has to be after the character is sent, not as above while the character is being sent, which at this baud rate is ten times longer than the inter-character gap.
Delays were sometimes favoured when the implementation of the protocol was in SSI/MSI logic hardware. It was simpler to trigger a monostable by the the end of a character than it was to compare the character rs on receive with a number of patterns, i.e. STX/ETX etc. Software does such comparisons easily, it was trickier in 1970's style hard-wired logic. MODBUS RTU is a protocol, commonly used over RS485, that uses delays to delimit packets/messages. It dates from the late 70's and is clearly designed for hardware implementation. The more recent MODBUS ASCII variant uses character delimiters and is more suited to soft implementation. Libraries are available for both are available for the PIC.
So what is the problem? I'm not sure, but you must always read the character from the serial port in a receive interrupt. You are NOT reading it if your buffer is full. If you don't read it the interrupt is not cleared and will re-interrupt immediately the ISR exits.
Also with buffered serial ports such as most on the PICs, its usually better to read and save (or ignore if your buffer is full) ALL available characters. To do this you need a loop on kbhit(). Remember to always read all characters regardless of whether you use them or not.
On a stylistic note, there appears to be a fair bit of repetition and additional logic in the state machine. A reorganisation may help to simplify and rationalise it. Even seeing += 1 used to increment is something that reduces readability as its just not what most folks expect to see. Also "if ( 0 == message_received )", I'd expect to see "if ( !message_received )" as I read it as "if no message received". You are clearly comfortable with:
Code: |
--length;
if (!length)
{
|
All this is very minor and purely about style and readability though.
I can't see anything else, though I'd be very careful, buffer operations have a nasty habit of overflowing and trashing other variables...
RF Developer |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Mon Aug 06, 2012 3:38 am |
|
|
Are you absolutely certain that you've got a nesting problem.
You're not showing me enough to tell either way.
I realise you may have confidentiality issues.
However, it would help if you could post a minimum version which is complete & compilable and also exhibits the problem. Then we can test your hypothesis.
Mike |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19569
|
|
Posted: Mon Aug 06, 2012 7:22 am |
|
|
One thing here could cause a problem.
You only call fgetc, if 'message_received' is zero.
Problem is that INT_RDA, _must_ always call getc. If it doesn't, it won't be allowed to exit (the interrupt can't be cleared - it'll keep resetting itself). This variable gets set to '1' at the end of the state machine, and if a second character arrives, before the main code clears it, then you will get hung calling the routine...
fgetc, _must_ be called whenever the interrupt occurs. If you don't want the character, throw it away, but make this call before your conditional tests.
Best Wishes |
|
|
Bill24
Joined: 30 Jun 2012 Posts: 45
|
|
Posted: Tue Aug 07, 2012 1:33 am |
|
|
Thanks for all of your replies. I did not know by default, interrupts are disabled while ISRs are executing. The code does not entirely belong to us so I will pass the information along about always calling fgetc().
It seems to me that RS232 is old technology. Is there a general preference for new designs e.g CAN bus. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|