Почему статическая переменная моей функции никогда не меняется, несмотря на увеличение?
Я пишу функцию обратного вызова в C. Она предназначена для инициализации датчика I2C, и она вызывается в конце каждого шага конфигурации (расщепления фазы); после 9-го вызова устройство почти готово к использованию.
Основная идея функции такова: {[8]]}void callback(void)
{
static uint8_t calls = 0;
if (++calls == 9) {
// Finalise device setup (literally a single line of code)
}
}
Моя проблема заключается в том, что приведенный выше оператор if никогда не вводится, несмотря на то, что функция вызывается 9 раз.
Ассемблерный код (dis)для моей функции кажется нормальным (за исключением 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) ; }
перед приращением он не висит
Я полностью в недоумении для идей.
Есть ли у кого-нибудь предложения относительно того, что может вызвать это, или даже для новых шагов отладки, которые я мог бы предпринять?2 ответа:
Для того, что вы говорите, Я могу думать только о 3 возможностях: 1) ваше предположение, что функция вызывается при каждом сообщении I2C, неверно, 2) в вашей программе есть ошибка (возможно, утечка памяти) в какой-то несвязанной функции, которая вызывает повреждение переменной вызовов. или 3) два или более потоков вызывают вашу функцию одновременно и вызовы увеличиваются другим способом, чем вы ожидаете, используйте > вместо того, чтобы ==, если это решит проблему ... проблема, значит, вы работаете в многопараметрических-многопоточной среде и не знать.
Вам нужен точный метод, чтобы знать точное значение вызовов, Если у вас нет отладчика и нет средств для вывода текста, единственное, что вам остается играть, - это время. Я не знаю, что вы компилятор, но я уверен, что он содержит некоторые полезные функции синхронизации, поэтому я бы сделал цикл перед инкрементом в течение 10+секунд вызовов, а после инкремента снова 10 + секунд вызовов, для пример:
Я бы (хронометр в руке) ожидал задержки (10+0 плюс 10+1) = 21 секунда на первый вызов, 23 секунды на второй вызов, 25 на третий и так далее. Таким образом, я мог быть уверен, что значение вызовов начиналось с 0, а затем постепенно увеличивалось до 9. Кроме того, вы должны проверить то, что вы ожидаете, а не то, чего вы не ожидаете, поэтому вместо этого:sleep(1000*(10+calls)); ++calls; sleep(1000*(10+calls)); if(calls>8){ // your actual code }
++calls; if (calls==0) while (1);
Сделайте так:
++calls; if (calls==1) while (1);
Таким образом, если ваша программа зависает, вы можете быть конечно, значение вызовов точно равно 1 и не отличается от нуля. Если вы посчитали одно допустимое сообщение I2C и ваша программа зависает, то переход от 0 к 1 был сделан правильно, поэтому измените оператор hang соответственно:
++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. } }
Надежда эта помощь.
В конечном итоге я нашел причину ошибки; другая подсистема ломалась как побочный эффект первого вызова функции обратного вызова, что означает, что никакие другие вызовы не увенчались успехом.
Это объясняет поведение, которое я видел:
- он висел в первый раз, потому что его действительно называли
- он не зависал во второй раз (или в будущем), потому что его вызывали только один раз
- транзакции I2C, которые я наблюдал, происходили, но их механизм обратного вызова не работал корректно, из-за поломки другой подсистемы (задачи)
Я смог решить эту задачу, используя несколько выводов GPIO в качестве переключателей отладки и, таким образом, отслеживая, как происходит вызов через интерфейс TWI.
Спасибо за помощь, ребята. На самом деле это не ответ на первоначальный вопрос, но он решен, так что это что-то :)