Полная версия этой страницы:
Повторный вызов функции
Георгий
Dec 1 2005, 06:40
Вопрос по повторному вызову функций. Есть такой алгоритм:
читается с линии байт и по результатам чтения устанавливается ПРИЗНАК_А и РЕЗУЛЬТАТ_А. В зависимости от ПРИЗНАК_А производятся разные действия, одно из которых теперь уже в зависимости от РЕЗУЛЬТАТ_А может потребовать прием второго байта с линии. Так вот, когда второй байт принимаю, он всегда равен первому принятому байту. Я уже продублировал функцию приема с линии и для приема второго байта использую другую функцию с другими переменными. Но результат тот же. Это же алгоритм отлично работает на ассемблере. Причем когда идут однобайтовые команды, они принимаются нормально, при двухбайтовых происходит этот глюк. Код, к сожалению, выложить не могу, привожу условно. Хотелось бы теоретических рекомендаций, где я криво написал.
Код
РЕЗУЛЬТАТ_А = func_receive(); // тут же формируется через глобальную переменную ПРИЗНАК_А
switch (ПРИЗНАК_А)
{
case 1: func1;
break;
case 2: func2(РЕЗУЛЬТАТ_А);
break;
default: func3;
break;
}
void func2(unsigned char РЕЗУЛЬТАТ_А)
{
switch (РЕЗУЛЬТАТ_А)
{
case 1: func1_1;
break;
case 2: РЕЗУЛЬТАТ_Б = func_receive_2(); // РЕЗУЛЬТАТ_Б = РЕЗУЛЬТАТ_А всегда!
if ((РЕЗУЛЬТАТ_Б & 2) == 0)
PORTD &= 0xBF;
else PORTD |= 0x40;
break;
}
}
Если это не дурацкая ошибка в строке if (РЕЗУЛЬТАТ_Б & 2) - здесь же не сравнение со значением 2, а проверка бита, то все остальное правильно. Варианты ошибки могут быть или в работе оптимиизатора IAR - он очень любит выбросить половину компилируемого текста, или в функции func_receive() - по какой-то причине она не дожидается приема второго байта и читает из регистра приемника еще раз предыдущий.
Первая рекомендация - выключить напрочь оптимизатор.
Георгий
Dec 1 2005, 10:32
Это была не ошибка, это я не дописал, сейчас исправил, проверяю я именно это бит, из-за этого злополучного бита и вся бодяга. А оптимизатор я отключал напрочь, не помогло. Регистра приемника нет, функция func_receive читает прямо с линии и выдает результат, только когда чтение упешно прошло. Я проверял, ошибки при приеме второго байта не возникает, значит байт принимается и где-то в недрах компилятора подменивается на предыдущий результат.
Посмотрел еще раз листинги, возникло такое предположение - проверка условий на Си занимает больше времени, чем на Ассемблере, и программа просто не успевает вовремя дать подтверждение приема, источник не получив подтверждения еще раз высылает первый байт команды, который я благополучно принимаю за второй.
Вечером проверю.
rezident
Dec 1 2005, 16:46
А func_receive_2() как-то опрашивает готовность приемника? А то может второй байт вовсе и не принят еще к тому времени как его читает func_receive_2().
Георгий
Dec 2 2005, 05:33
Если бы вы внимательно прочитали, то увидели, у меня нет приемника, или регистра приемник, процедуры func_receive и и ее копия func_receive2 читают прямо с линии. Линия естественно проверяется на старт стоп паузу и т .п. Пока вечером копался, накопал еще хуже.
1. Результат, возвращаемый функцией, зависит от начального состояния переменной, которая загоняется в return.
2. Если принудительно возвращать нужный результат в return, программа все равно после отработки либо погасит, либо включит светодиод, в зависимости от начального состояния переменной РЕЗУЛЬТАТ_Б, даже если в самой функции эта переменная изменяется.
Чем лучше дизассемблировать результатный HEX, чтобы посмотреть, что на самом деле наворачивает компилятор?
Георгий
Dec 2 2005, 10:10
Нашел глюк компилятор, взгляните на куски листинга:
(в процедуре приема func_receive)
Код
56 if ((PIND & 4) > 0) Paritet++;
\ 0000003E 9982 SBIC 0x10, 0x02
\ 00000040 9513 INC R17
57 delay40mks;
\ ??inbyte_4:
\ 00000042 EA20 LDI R18, 160
\ 00000044 952A DEC R18
\ 00000046 F7F1 BRNE $-2
(в процедуре приема func_receive2)
Код
128 if ((PIND & 4) > 0) Paritet2++;
\ 00000038 B300 IN R16, 0x10
129 delay40mks;
\ 0000003A EA00 LDI R16, 160
\ 0000003C 950A DEC R16
\ 0000003E F7F1 BRNE $-2
Поэтому второй байт всегда принимается с ошибкой. Чем победить, пока не знаю. Упорно ставит IN
Георгий
Dec 5 2005, 06:55
Разнес процедуры в разные модули - глюк исчез. А разочарование осталось. Решил продолжать писать на ассемблере, по крайней мере полный контроль над процессом. С глюками Си быстрее не получается.
Попробуйте к критичным переменным применить идентификатор volatile
Георгий
Dec 6 2005, 12:17
А какую роль играет модификатор volatile?
Пробую еще перестроить программу. Не оставляет мысль, что я где-то виноват. Практика показывает, что в 90% виноват человеческий фактор, и только в остальном глюки, ошибки системы и т. п.
IgorKossak
Dec 6 2005, 12:55
Георгий , может вместо
Код
if ((PIND & 4) > 0) и т. д.
лучше попробовать
Код
if (PIND_Bit2) и т. д.
?
Я допускаю, что Вы работаете в среде IAR v4.11.
Георгий
Dec 6 2005, 13:08
Так, конечно, понятнее и красивее, но конечный код не изменился. Я все-таки склонен думать, что где-то я плюшку допускаю, переводя с ассемблера. Сижу сейчас, копаю глубоко.
Кстати, в программе так и написано?
case 1: func1;
Это к тому, что в Си идентификатор функции без скобок означает просто её адрес. Чтобы вызвать функцию, обязательно добавлять скобки, даже если параметров у неё нет. В отличие от того же Паскаля.
Георгий
Dec 6 2005, 13:25
case 1: func1();
break;
Про скобки я помню, для простоты не указал сразу.
идентификатор volatile сообщает компилятору, что эту переменную нельзя оптимизировать. Такой переменной гарантировано размещение в памяти, а не в регистре(размещение переменных в регистрах является одним из видов оптимизации). В основном этот идентификатор применяют к переменным, которые используются в обработчиках прерываний и в тоже время могут изменяться в основной программе.
Лучше конечно поглядеть на исходный текст программы, может что и броситься сразу в глаза.
Георгий
Dec 15 2005, 05:50
Посмотрел осцилограммы и убедился, что глюк у меня был от недооценки Cи. Я решил, что после Си код будет медленней работать и уменьшал задержки. В результате код ответа практически накатывался на код приема первого байта и передатчик его просто не воспринимал. А не получив ответ, передатчик посылал снова код первого байта и так несколько раз. А я грешил, что не успеваю ответить.
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.