Question

I am writing a callback function in C. It is intended to initialise an I2C sensor, and it called at the conclusion of each (split-phase) configuration step; after the 9th call, the device is almost ready to use.

The basic idea of the function is this:

void callback(void)
{
    static uint8_t calls = 0;

    if (++calls == 9) {
        // Finalise device setup (literally a single line of code)
    }
}

My problem is that the above if statement is never being entered, despite the function being called 9 times.

The (dis)assembly code for my function seems sane (with the exception of the subi . 0xFF trick for an increment, despite the inclusion of an inc instruction):

00000a4c <callback>:
     a4c:   80 91 9e 02     lds r24, 0x029E
     a50:   8f 5f           subi    r24, 0xFF   ; 255
     a52:   80 93 9e 02     sts 0x029E, r24
     a56:   89 30           cpi r24, 0x09   ; 9
     a58:   09 f0           breq    .+2         ; 0xa5c <callback+0x10>
     a5a:   08 95           ret
     a5c:   2e e1           ldi r18, 0x1E   ; 30
     a5e:   35 e0           ldi r19, 0x05   ; 5
     a60:   41 e0           ldi r20, 0x01   ; 1
     a62:   60 e0           ldi r22, 0x00   ; 0
     a64:   84 e7           ldi r24, 0x74   ; 116
     a66:   0c 94 c7 02     jmp 0x58e   ; 0x58e <twi_set_register>

I am writing the code for an Atmel AVR chip, and thus compiling with avr-gcc. I have no meaningful code debugging capabilities (I don't have access to a JTAG programmer, and the function is asynchronous/split-phase in any case; USART printing is too slow).

However, I have access to a logic analyser, and have been able to determine a number of things by placing while (1) ; statements inside the code:

  • the function is called - if I place an infinite loop at the start of the function, the microcontroller hangs
  • the function should be called 9 times - the trigger for the function is an I2C communication, and in the previous step it hangs immediately after the first communication; I can observe 9 complete and valid I2C communications
  • calls is incremented within the function - if I add if (calls == 0) { while (1) ; } after the increment, it does not hang
  • calls is never non-zero at the start of the function - if I add if (calls) { while(1) ; } before the increment, it does not hang

I'm completely at a loss for ideas.

Does anyone have any suggestions as to what could cause this, or even for new debugging steps I could take?

Était-ce utile?

La solution 2

For what you say I can only think of 3 possibilities: 1) your assumption that the function is being called on every I2C communication is incorrect, 2) your program has a bug (maybe a memory leak) in some unrelated function which causes the variable calls to become corrupted. or 3) two or more threads are calling your function simultaneously and calls is being incremented in a different way than you expect, use > instead of ==, if this solves the problem, then you are running in a milti-threaded environment and you didn't konw.

You need an accurate method to know the exact value of calls, if you don't have a debugger and don't have the means to output text either, the only thing you have left to play is time. I don't know you compiler, but I am sure it contains some useful timing functions, so what I would do would be to loop before increment for 10+calls seconds, and after increment again 10+calls seconds, for example:

sleep(1000*(10+calls));
++calls;
sleep(1000*(10+calls));

if(calls>8){
   // your actual code
}

I would (chronometer in hand) expect a delay of (10+0 plus 10+1) = 21 seconds on the first call, 23 seconds on the second call, 25 in the third and so on. That way I could be sure the value of calls started with 0 and then it was progressively increased until 9.

Also, you must test for what you expect not for what you don't expect, so instead of doing this:

++calls;
if (calls==0) while (1);

do this:

++calls;
if (calls==1) while (1);

that way if your program hangs you can be sure the value of calls is exactly 1, and not whatever different from zero. If you count one valid I2C communication and your program hangs then the transition from 0 to 1 was done correctly, so change the hang statement accordingly:

++calls;
if (calls==2) while (1);

Again, if you count 2 valid I2C communications before your program hangs that means that the transition from 1 to 2 was correct, and so on.

Another suggestion, try this:

uint8_t Times(void){
   static uint8_t calls = 0;
   return ++calls;
}


void callback(void){

   if (Times()>8) {
       // Your actual code
   }

}

And this:

void callback(void){
static uint8_t calls = 0;

   if (calls++>7) {
       // some code.
   }
}

Hope this helps.

Autres conseils

I ended up finding the cause of the error; another subsystem was breaking as a side-effect of the first call to the callback function, meaning that no other calls succeeded.

This explains the behaviours I saw:

  • it hung the first time because it was actually being called
  • it didn't hang the second time (or any future time) because it was only being called once
  • the I2C transactions I was observing were occurring, but their callback mechanism was not operating correctly, due to the other subsystem (tasks) breaking

I was able to work this out by using a few GPIO pins as debugging toggles, and thus tracking how the call was progressing through the TWI interface.

Thanks for the help guys. This isn't really an answer to the original question as posed, but it is solved, so that's something :)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top