CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Interrupt nesting

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Bill24



Joined: 30 Jun 2012
Posts: 45

View user's profile Send private message

Interrupt nesting
PostPosted: Mon Aug 06, 2012 2:08 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Aug 06, 2012 3:32 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Aug 06, 2012 3:38 am     Reply with quote

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: 19448

View user's profile Send private message

PostPosted: Mon Aug 06, 2012 7:22 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Aug 07, 2012 1:33 am     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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