Main Site Documentation

Setting a variable in a interrupt


#1

Hi all,
I’m reading a quadrature rotary encoder from a motor. It’s pretty simple, it just change a variable in a interrupt. But I got a problem: I need to read it 100 times per second in a PID loop, but then it misses some pulses.

The code looks like this:


        static void LeftEncoderA_OnInterrupt(uint port, uint state, DateTime time)
        {
            bool portState;
            if (state == 1)
                portState = true;
            else
                portState = false;

            if (portState == LeftEncoderB.Read())
                leftCounter--;
            else
                leftCounter++;  
        }
        static void LeftEncoderB_OnInterrupt(uint port, uint state, DateTime time)
        {
            bool portState;
            if (state == 1)
                portState = true;
            else
                portState = false;

            if (portState == LeftEncoderA.Read())
                leftCounter++;
            else
                leftCounter--;  
        }

It works fine when I only read it in a while loop 10 times a second like this:


            while (true)
            {                                
                Debug.Print("Counter: " + counter);
                Thread.Sleep(100);
            }

But if I only change the delay to 10, then it misses some pulses.

Normally I would just set it to a volatile variable. My first thought was to do like so:


static volatile int leftCounter;

But then it can’t compile. It writes the following messages:

Predefined type 'System.Runtime.CompilerServices.IsVolatile' is not defined or imported

I have tried to add:


using System.Runtime.CompilerServices;

But that doesn’t work.

How would you do this in NETMF? Do I need a reference or is it not supported in NETMF?


#2

thread.sleep is not deterministic, ie you can’t rely on it being able to stay on a fixed schedule.

debug.print creates strings, and destroy them, meaning you are asking the GC to do stuff, which takes time. If you’re using debug.print and thread.sleep(10) you’re probably seeing exactly what I would expect, and the approach I’d take is to try your actual work you need to do and see if that is any different?


#3

They are different, that the whole point!! Normally you would assign the variable as volatile. This is a example using the mbed: http://mbed.org/handbook/InterruptIn

The problem is that it tries to set a variable while is being read, which corrupts the variable. When the motors is on the following error occurs:


Failed allocation for 13 blocks, 156 bytes


#4

The sleep lets the main thread sleep but the interrupts should still be handled.

The main problem is more that the time at which the interrupt routine is scheduled might be out of phase with the encoders: OnInterrupt might be handled 20 ms after the actual interrupt. Inside the handler you are comparing e.g. the value of A when the interrupt happened and comparing it to the current value of B. This will fail from time to time.


#5

So you’re saying you know why the code appears to miss values? Great, now deal with it.

“Volatile” is a compiler construct to tell the compiler that this variable may get adjusted in ways that the compiler may not understand, so don’t try to optimise the code; this isn’t an issue that I have ever seen discussed in NETMF.

Actually, why don’t you explain what you mean by “but it misses some pulses”. Is the problem that your interrups miss pulses, or that your PID loop doesn’t get to process each of the pulses as they come in? As I said, thread.sleep is not guaranteed to be a precise time interval; so a loop with a thread.sleep(100) in it won’t get processed 10 times a second, and if it was thread.sleep(10) it won’t get processed 100 times a second either.


#6

Okay, I will try to reexplain my problem. The problem is that you can’t write to a variable while is being read. That is not a problem if you only read it approximately 10 times a second. But if you have to read it approximately 100 times a second (it has to feed a PID controller for a segway - it balances very well, but I would like to enable the encoders as well) it fails to read the encoders correctly - it misses the some of the pulses sent from the encoder.
It is a total of 1856 counts per revolution. I can count all of them when reading only 10 times a second, but when reading 100 times, it only counts something like 300-500, and it changes a lot.
So I was really asking if there were a similar function in NETMF, as you would normal just set the variable as volatile.
Hope that explained it better :slight_smile:


#7

I think a better understanding of how interrupts are handled is necessary. :slight_smile:

When an interrupt occurs, an internal queue entry is created. The runtime then schedules the interrupt handlers. There can be a few millisecond delay between when the interrupt actually occurs and when the handler gets the interrupt. That is why there is a timestamp parameter.

In your code, you are handling an interrupt, which occurred in the past, and contains the state at the past point, and you reading the current value of the other phase. You have created a time warp.

Interrupts are handle in chronological order and one at a time. You should have variables which contains the state of the A and B. When handling A you should use the B value that existed in the last B interrupt, not the current value of B.

Since interrupts are queued in memory, if you don’t handle interrupt fast enough you will run out of memory and get an exception.

BTW, I have just finished writing code for an encoder with 512 changes per rotation. If I put Debug.Print statements into the interrupt handlers I often ran out of memory.


#8

Ahh, I don’t know why I didn’t thought of that :slight_smile: Thank you very much, that did the trick.
If anybody is interested this is the modified code:


        static void LeftEncoderA_OnInterrupt(uint port, uint state, DateTime time)
        {            
            leftEncoderAState = state

            if (leftEncoderAState == leftEncoderBState)
                leftCounterA++;
            else
                leftCounterA--;  
        }
        static void LeftEncoderB_OnInterrupt(uint port, uint state, DateTime time)
        {
            leftEncoderBState = state;

            if (leftEncoderBState == leftEncoderAState)
                leftCounterB--;
            else
                leftCounterB++;  
        }           

Again thank you all for the support!! :smiley:


#9

Hmm, it still throws an exception, if the motor spins to fast - even if I don’t do anything in the main thread. Might have to use RLP or set up the interrupt using the registers, or do anyone have any suggestions?

Regards
Lauszus


#10

Typically, 1500-1700 interrupts per seconds with no interrupt processing is the maximum the processor can handle in managed code.

You might be able to use an external divider chip and reduce the interrupt rate.


#11

I will try to use only one interrupt for each motor and see how it works. I don’t need 1856 counts per revolution anyway.