Menu

External interrupt problem

Jason
2015-02-14
2015-02-15
  • Jason

    Jason - 2015-02-14

    Hello, first I want to say thank you for posting this FreeRTOS port for Mega2560, I have been using it for the past several months and it's always worked great. It's saved me a ton of work. Thank you.

    This said I was wondering if anyone could help me get external interrupts working. Here is the background. I am adding an I2C device to my system, a UART expander based on the NXP SC16IS762 chip (my application needs to talk to several serial devices & even all 4 UARTS on the 2560 is not enough).

    I am able to talk to this I2C device fine, I can write to it, and I can read from it in a polled mode. However the device can generate an interrupt when data is available in its FIFO and I would like to use this interrupt capability instead of always polling the device, to cut down on the overhead.

    Here is my problem. When I enable any external interrupt in my code, the entire application freezes up. For example when am trying to use the INT4 input trigger on the falling edge of the signal. I have added just the following lines, nothing else, to my previously working program, and this causes it to freeze as soon as it hits the line where I set EIMSK:

    ~~~~~~~~~~~~~~~~~
    :::c

    include <avr interrupt.h="">

    EICRB = 0x02;
    EIMSK = 0x16;

    ...

    ISR( INT4_vect)
    {
    }
    ~~~~~~~~~~~~~~~~~~

    Can anyone tell me why this is happening? I have tried the following things:
    - Looping the INT4 pin back to VDD +5V so that the interrupt can't be triggered
    - Adding critical section using portENTER_CRITICAL()/portEXIT_CRITICAL while I am setting the registers from within my task
    - Setting the registers before calling vTaskStartScheduler(). This causes the program to run for what appears to be a small number of ticks (maybe only 1), then freeze
    - Adding taskYIELD() to my empty ISR, even though there is no chance that a context switch should occur since the ISR contains no code

    None of these things seem to work. By the way I am not simultaneously controlling the interrupt pin as PWM by mistake (I even hooked up to a scope to verify that there is nothing going on on that interrupt pin).

    Can anyone tell me why this is happening? Thanks in advance.

     

    Last edit: Phillip Stevens 2015-02-15
  • Phillip Stevens

    Phillip Stevens - 2015-02-15

    You are setting EIMSK incorrectly.

    You should set it to be:

    EIMSK |= _BV(INT4);  // 0x10;
    

    which turns on INT4 and doesn't affect the other interrupts in play.

    But you are setting it to this:

    EIMSK = _BV(INT4) | _BV(INT2) | _BV(INT1);  // 0x16;
    

    which does any number of bad things, that you may not want.

    Try this code

    ~~~~~~~~~~~~~~
    :::c

    include <avr interrupt.h="">

    // within your main() function

    DDRH |= _BV(DDH6); // define Pin H6 as an output pin for the test. We'll flash a LED on the H6 pin.

    DDRE &= ~_BV(DDE4); // Set Pin E4 as an input pin for our test (though it doesn't need to be, it can be an output and it will work too).
    PORTE |= _BV(PE4); // Turn on the pull ups on the E4 pin, to stop it floating around.
    EICRB |= _BV(ISC41); // 0x02; set the falling edge of E4 (Arduino Mega Pin 2) to generate an interrupt.
    EIMSK |= _BV(INT4); // Turn on the interrupt for INT4. Don't touch the interrupts for INT2 or INT1. _BV(INT2) | _BV(INT1); // 0x16;

    // outside the main function

    ISR( INT4_vect)
    {
    PINH |= _BV(PINH6); // Setting PINH6 to one, toggles the H6 pin each time there's an interrupt.
    }
    ~~~~~~~~~~~~~~~

     

    Last edit: Phillip Stevens 2015-02-15
  • Jason

    Jason - 2015-02-15

    My goodness, you are right, to figure out the correct EIMSK register setting I had done the conversion from binary (00010000) to decimal (16), forgetting to then convert decimal 16 to hex (0x10). And here I thought I had covered all possible "you're doing it wrong, dumbass" scenarios.

    I have set the correct interrupt enable mask now and it works correctly. I suppose this is what I get for being lazy & not bothering to look up the predefined bitmasks.

    One thing I don't understand is why it didn't work even with INT1 and INT2 also mistakenly enabled. As part of my debug, as a precaution, I added in empty ISR routines for all external interrupt vectors INT0_vect through INT7_vect. This was to guard against a situation where I was mistakenly raising the wrong interrupt flag in EIFR with no corresponding ISR available to be called in the code, as I suspected no ISR would cause the application to freeze since nothing would ever clear the bit in EIFR.

    Anyway in my mind enabling those two bits in the EIMSK as I did should have resulted in a lot of extra ISR calls to my empty routines, but not in the application completely freezing as it did. (These pins are are the UART1 RX and the I2C data line, both in active use on my project, and so would have seen tons and tons of interrupt events). I could see the app running choppy and slow but not completely freezing. Maybe this was a stack overflow situation as you previously suggested before editing your e-mail with the real solution above?

    So much thanks for the help in pointing out the obvious.

    I do have one other thing I wanted to ask. In the I2C drivers included with the project (I2CMultiMaster.c/.h) the driver is setup to perform simple unaddressed slave read operations while configured as a Master, but in my experience many/most I2C slave devices require a register address byte to be transmitted + a repeated start in order to receive a response from the slave device. For example:

    <START>,<ADDR+W>,<REG_ADDR>,<REP_START>,<ADDR+R> -> slave response begins..

    The current drivers only support the following:

    <START>,<ADDR+R> -> slave response will never start w/o register address byte

    I have added the needed capability to my drivers, is this something you are interested in adding to your project here on SourceForge? Let me know and I can send it to you.

     
  • Phillip Stevens

    Phillip Stevens - 2015-02-15

    Your code also over-wrote the INT0 through INT7 settings too. I'm only guessing, but this probably affected the Timer 3 (or Timer 1 or Timer 2 depending on what you are using) interrupt used for scheduling. Also all the pins didn't have pull-ups activated so they may have flooded the MCU with too many interrupts, as they floated around.

    I'm now careful to either OR or AND any register settings I make (read, modify, set), so that I don't stomp on something else in another piece of code.

    Yes, my I2C code is very basic. If you've read the AVR data sheet on how to use the TWI interface, you can see that it is all directly extracted (and merged) from their Master and Slave advisories. I didn't even make the Slave code work until I had occasion to use it in the Peggy Display where I had two MCU communicating over I2C, with the Peggy Display acting as the Slave. The MultiMaster code is still untested. If you've found issues, it would be good to know. I'm typically only using the Master code for reading an I2C RTC, or 9DOF IMU, or similar.

    If you have some nice code for I2C, I'd like to have a look so that I can put it into the repository. Thanks.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.