Почему статическая переменная моей функции никогда не отличается, несмотря на то, что она увеличивается?

Я пишу функцию обратного вызова на C. Она предназначена для инициализации датчика I2C и вызывается по завершении каждого (раздельного) шага конфигурации; после 9-го звонка аппарат практически готов к работе.

Основная идея функции такова:

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

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

Моя проблема в том, что приведенный выше оператор if никогда не вводится, несмотря на то, что функция вызывается 9 раз.

Код (дез)ассемблера для моей функции кажется разумным (за исключением трюка subi . 0xFF для приращения, несмотря на включение инструкции inc):

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>

Я пишу код для микросхемы Atmel AVR и, таким образом, компилирую с помощью avr-gcc. У меня нет значимых возможностей отладки кода (у меня нет доступа к программатору JTAG, и в любом случае функция асинхронная/расщепленная; печать USART слишком медленная).

Однако у меня есть доступ к логическому анализатору, и я смог определить ряд вещей, поместив while (1) ; операторов в код:

  • вызывается функция - если я помещаю бесконечный цикл в начале функции, микроконтроллер зависает
  • функция должна быть вызвана 9 раз - триггером для функции является связь I2C, и на предыдущем шаге она зависает сразу после первой связи; Я могу наблюдать 9 полных и действительных сообщений I2C
  • вызовы увеличиваются внутри функции - если я добавляю if (calls == 0) { while (1) ; } после увеличения, он не зависает
  • вызовы никогда не бывают ненулевыми в начале функции — если я добавляю if (calls) { while(1) ; } перед приращением, он не зависает

У меня совсем нет идей.

Есть ли у кого-нибудь какие-либо предложения относительно того, что может вызвать это, или даже новые шаги по отладке, которые я мог бы предпринять?


person sapi    schedule 04.09.2013    source источник
comment
почти кажется, что компилятор каким-то образом неправильно обрабатывает статическую переменную. Вы пытались вместо этого использовать глобальную переменную?   -  person Ingo Leonhardt    schedule 04.09.2013
comment
Основываясь на фактах, которые вы предоставляете, единственное, что можно предположить, это то, что вы на самом деле не вызываете обратный вызов 9 раз. Дизассемблирование правильное (как и ожидалось), и вы говорите, что обратный вызов вызывается хотя бы один раз. Однако ни где вы не подтверждаете, что он вызывается 9 раз. Одно предложение: уменьшите счетчик циклов с 9 (до 7, 5 или 2) и посмотрите, в какой момент вы на самом деле вызываете twi_set_register. Другая идея заключается в том, что если у вас есть запасной ввод-вывод, переключайте контакт при входе/выходе в callback() и используйте логический анализатор, чтобы подтвердить, как часто вы в нем находитесь.   -  person Ross    schedule 04.09.2013
comment
@IngoLeonhardt - я пытался использовать глобальное значение с тем же результатом. Я также проверил, что адрес 0x029E находится в .data (он находится во внутренней SDRAM, поэтому должен быть доступен для чтения/записи)   -  person sapi    schedule 04.09.2013
comment
@Ross - моя последняя точка должна была сообщить, что я это пробовал; если я проверю, что calls ever не равно нулю (до оператора приращения), и включу бесконечный цикл, если это так, то этот цикл не сработает.   -  person sapi    schedule 04.09.2013
comment
@Ingo Leonhardt Это выглядит правильно, поскольку Сапи упоминает, что вызовы никогда не обнуляются в начале, что означает, что при вызовах инициализации устанавливается значение 0, как и ожидалось. А разборка показывает, что он просто хранит звонки по адресу 0x029E...   -  person Ross    schedule 04.09.2013
comment
@Sapi Я согласен, что звонки вначале равны нулю. Но, учитывая факты, которые вы представили, я не верю, что обратный вызов вызывается 9 раз. Вероятно, он вызывается более одного раза, но не 9 раз. Поэтому, если вы уменьшите значение чека с 9 до любого другого, вы сможете определить, сколько раз он вызывается; таким образом указывая вам на проблему с кодом (которая, вероятно, не в этом сегменте)...   -  person Ross    schedule 04.09.2013
comment
@Ross - но я говорю, что я делал сброс теста до более одного раза, и он не сработал. Я согласен с тем, что кажется, что он вызывается не более 9 раз, но мне трудно поверить, что он не вызывается более одного раза, когда я знаю, что он работает один раз, каждый раз это один и тот же обратный вызов, и я могу просмотреть триггер несколько раз на анализаторе :/   -  person sapi    schedule 04.09.2013
comment
Другая идея заключается в том, что где-то у вас есть какое-то повреждение памяти, которое перезаписывает адрес calls на 0. Может быть, вы найдете способ проверить это.   -  person Ingo Leonhardt    schedule 04.09.2013
comment
Можете ли вы предоставить код, который запускает обратный вызов? Мне легче поверить, что это не работает, в отличие от повреждений памяти, неправильной обработки статических переменных и т. д.   -  person Ross    schedule 04.09.2013


Ответы (2)


Для того, что вы говорите, я могу думать только о 3 возможностях: 1) ваше предположение о том, что функция вызывается при каждой связи I2C, неверно, 2) в вашей программе есть ошибка (возможно, утечка памяти) в какой-то несвязанной функции, которая вызывает переменную вызовы становятся поврежденными. или 3) два или более потока вызывают вашу функцию одновременно, и вызовы увеличиваются не так, как вы ожидаете, используйте > вместо ==, если это решит проблему, то вы работаете в многопоточной среде и не знаете.

Вам нужен точный метод, чтобы узнать точное значение вызовов, если у вас нет отладчика и средств для вывода текста, единственное, что у вас осталось, это время. . Я не знаю вашего компилятора, но я уверен, что он содержит некоторые полезные функции синхронизации, поэтому я бы сделал цикл перед увеличением в течение 10+ вызовов секунд и после увеличения снова 10+ вызовов секунд, например:

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

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

Я бы (хронометр в руке) ожидал задержку (10 + 0 плюс 10 + 1) = 21 секунда при первом звонке, 23 секунды при втором звонке, 25 секунд при третьем и так далее. Таким образом, я мог быть уверен, что значение вызовов начинается с 0, а затем постепенно увеличивается до 9.

Кроме того, вы должны проверить то, что вы ожидаете, а не то, чего вы не ожидаете, поэтому вместо этого:

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

сделай это:

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

таким образом, если ваша программа зависнет, вы можете быть уверены, что значение вызовов точно равно 1 и ничем не отличается от нуля. Если вы насчитали одно действительное соединение I2C и ваша программа зависла, значит, переход от 0 к 1 был выполнен правильно, поэтому соответствующим образом измените оператор зависания:

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

Опять же, если вы считаете 2 действительных связи I2C до того, как ваша программа зависнет, это означает, что переход от 1 к 2 был правильным и так далее.

Еще одно предложение, попробуйте следующее:

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


void callback(void){

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

}

И это:

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

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

Надеюсь это поможет.

person Demetrius Amadeus    schedule 04.09.2013

В итоге я нашел причину ошибки; другая подсистема ломалась как побочный эффект первого вызова функции обратного вызова, что означало, что никакие другие вызовы не увенчались успехом.

Это объясняет поведение, которое я видел:

  • он завис в первый раз, потому что его на самом деле вызывали
  • он не зависал во второй раз (или в любой другой раз в будущем), потому что он вызывался только один раз
  • транзакции I2C, которые я наблюдал, происходили, но их механизм обратного вызова не работал должным образом из-за нарушения другой подсистемы (задач).

Я смог решить это, используя несколько контактов GPIO в качестве переключателей отладки и, таким образом, отслеживая, как вызов проходит через интерфейс TWI.

Спасибо за помощь, ребята! На самом деле это не ответ на исходный вопрос в том виде, в каком он был задан, но он решен, так что это что-то :)

person sapi    schedule 05.09.2013