реклама на сайте
подробности

 
 
2 страниц V  < 1 2  
Reply to this topicStart new topic
> Пару вопросов по Си для MCS51, от начинающего...
AndreyS
сообщение Dec 10 2008, 10:57
Сообщение #16


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Люди!!! Помогите. Пока перебросил переменные одной процедуры в XDATA, чтобы они ушли из перекрестка.

Вот настройки проекта:
Эскизы прикрепленных изображений
Прикрепленное изображение
Прикрепленное изображение
Прикрепленное изображение
Прикрепленное изображение


Прикрепленное изображение
Прикрепленное изображение
 


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
barabek
сообщение Dec 10 2008, 14:18
Сообщение #17


Знающий
****

Группа: Свой
Сообщений: 540
Регистрация: 16-08-07
Из: Владивосток
Пользователь №: 29 831



Цитата(AndreyS @ Dec 10 2008, 18:56) *
Суть проблеммы
В рабочем режиме иногда прошивка виснет. Выяснил, что зависает (зацикливается) она по причине того что локальные переменные процедуры кто-то запорол.
Нашел в карте памяти что данные ячейки относятся еще к двум процедурам (причем там эти переменные тоже локальные). Одна из которых вызывается в прерывании.
Как мне теперь заставить Keil убрать из перекрестных ссылок эти переменные (чтобы они лежали в разных адресах DATA)?
Делать их глобальными внутри одного файла желания нет.

Пока я проверил только локальные переменные в области DATA, быть может такая петрушка есть еще с переменными в области XDATA. Почему Keil их так расположил? Ведь при компиляции он знает какие процедуры попадают в прерывание и по идее должен этим процедурам для локальных переменных выдилять свои адреса. Может нужно что-то указать??

Спасибо.

Как я понял, у Вас есть функция которая вызывается из функции обработки прерывания? Если так, то это очень не гуд smile.gif. Для этого нужны очень веские основания. Только в том случае, если Вы эту функцию вызываете еще из другого места. Но эту ситуацию линкер возможно и не отслеживает и нужно в этой функции переменные объявить static, хотя это равносильно глобальной переменной. Да и тем более возможен повторный вход в функцию со всеми вытекающими. Этот вариант отметаем sad.gif
Если же вызов только из прерывания, тогда лучше вобще обойтись без дополнительной функции, все стащить в одно место, в функцию-обработчик. Вы же наверняка пользуетесь using-ами, может быть линкер эти переменные через регистры протащит, будет маленькое счастье smile.gif. А нельзя вобще обойтись в прерывании без обработки, выставив соответствующий флажок и обработать в другом месте?
Go to the top of the page
 
+Quote Post
AndreyS
сообщение Dec 10 2008, 16:08
Сообщение #18


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Цитата(barabek @ Dec 10 2008, 17:18) *
Как я понял, у Вас есть функция которая вызывается из функции обработки прерывания? Если так, то это очень не гуд smile.gif. Для этого нужны очень веские основания. Только в том случае, если Вы эту функцию вызываете еще из другого места. Но эту ситуацию линкер возможно и не отслеживает и нужно в этой функции переменные объявить static, хотя это равносильно глобальной переменной. Да и тем более возможен повторный вход в функцию со всеми вытекающими. Этот вариант отметаем sad.gif
Если же вызов только из прерывания, тогда лучше вобще обойтись без дополнительной функции, все стащить в одно место, в функцию-обработчик. Вы же наверняка пользуетесь using-ами, может быть линкер эти переменные через регистры протащит, будет маленькое счастье smile.gif. А нельзя вобще обойтись в прерывании без обработки, выставив соответствующий флажок и обработать в другом месте?


Огромное спасибо за содержательный ответ.

В прерывании использую процедуру которая вызывается еще и по коду. Суть процедуры сформировать (подготовить запрос на SPI шину и поставить его в очередь). Т.е. по сути - это и флажок и короткий обработчик этого флажка.

Да. Переосмыслил сейчас еще раз свой код и чую буду переделывать обработчик прерывания. smile.gif Да, тут я упустил из внимания возможность повторного вызова процедуры в неблагоприятный момент. smile.gif

Вы подняли мне веки smile.gif

Правда это не отменяет объединения (отображения части переменных по одним и тем же адресам) локальных переменных двух разных процедур (я пока просто перебросил переменные в одной процедуре в XRAM и таким образом развязал эти процедуры).

Буду переписывать обработчик.

Сообщение отредактировал AndreyS - Dec 10 2008, 16:21


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
Axel
сообщение Dec 10 2008, 17:37
Сообщение #19


Местный
***

Группа: Свой
Сообщений: 480
Регистрация: 21-11-04
Пользователь №: 1 188



Если функция не написана, как реентерабельная (слово не я придумал!) и при этом может вызываться из основной петли и из прерываний, то это, вобщем, конкретные грабли.
Go to the top of the page
 
+Quote Post
barabek
сообщение Dec 11 2008, 00:31
Сообщение #20


Знающий
****

Группа: Свой
Сообщений: 540
Регистрация: 16-08-07
Из: Владивосток
Пользователь №: 29 831



Цитата(AndreyS @ Dec 11 2008, 02:08) *
Правда это не отменяет объединения (отображения части переменных по одним и тем же адресам) локальных переменных двух разных процедур (я пока просто перебросил переменные в одной процедуре в XRAM и таким образом развязал эти процедуры).

Если две функции не могут вызываться одновременно (случай с прерыванием) - то конечно же пусть они используют одни и те же адреса для локальных переменных. В этом и соль этих переменных в отличии от глобальных. Если посмотреть на листинг то почти в каждой функции для локальных переменных используются регистры. И это очень хорошо, это только плюс компилятору-линкеру. Ведь памяти очень немного smile.gif
Go to the top of the page
 
+Quote Post
tag
сообщение Dec 11 2008, 10:05
Сообщение #21


Частый гость
**

Группа: Свой
Сообщений: 151
Регистрация: 21-02-06
Пользователь №: 14 561



Цитата(barabek @ Dec 11 2008, 04:31) *
В этом и соль этих переменных в отличии от глобальных.


Уверяю вас соль не в этом. Как известно памяти у 51 немного, плюс компилятор разработан так что локальные переменные сохраняет не на стеке, а либо в регистрах, либо в памяти (в этом вы можете убедится просмотрев руководство). В случае когда переменные располагаются в памяти линковщик может перекрывать область памяти локальных переменных одной подпрограммы и такую же область другой подпрограммы. Видимо так сделано с целью экономии памяти. Тогда, конечно подразумевается что эти фукции к примеру, не могут вызывать друг друга из своего тела. Так линковщик работает если ему не указать опцию nooverlay. Когда данная опция указана области локальных переменных подпрограмм не пересекаются и следовательно проблема порчи локальных переменных исчезает.
Прикрепленные файлы
Прикрепленный файл  nooverlay.bmp ( 868.45 килобайт ) Кол-во скачиваний: 18
 
Go to the top of the page
 
+Quote Post
barabek
сообщение Dec 11 2008, 10:53
Сообщение #22


Знающий
****

Группа: Свой
Сообщений: 540
Регистрация: 16-08-07
Из: Владивосток
Пользователь №: 29 831



Цитата(tag @ Dec 11 2008, 20:05) *
Уверяю вас соль не в этом. Как известно памяти у 51 немного, плюс компилятор разработан так что локальные переменные сохраняет не на стеке, а либо в регистрах, либо в памяти (в этом вы можете убедится просмотрев руководство). В случае когда переменные располагаются в памяти линковщик может перекрывать область памяти локальных переменных одной подпрограммы и такую же область другой подпрограммы...

А я разве сказал не тоже самое?:-D
Go to the top of the page
 
+Quote Post
AndreyS
сообщение Dec 11 2008, 12:00
Сообщение #23


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Цитата(barabek @ Dec 11 2008, 03:31) *
Если две функции не могут вызываться одновременно (случай с прерыванием) - то конечно же пусть они используют одни и те же адреса для локальных переменных. В этом и соль этих переменных в отличии от глобальных. Если посмотреть на листинг то почти в каждой функции для локальных переменных используются регистры. И это очень хорошо, это только плюс компилятору-линкеру. Ведь памяти очень немного smile.gif


Добрый день.

Спасибо всем за ответы.
Все что Вы ранее высказывали, я прекрасно понимаю.
Но тут вещь в следующем.
Компилятор никогда не отображает локальные переменные на одни и теже адреса, если они пересекаются при вызове процедур (РАЗНЫХ ПРОЦЕДУР). Если переменные пересекаются (отображаются на одни и теже адреса) (РЕГИСТРЫ НАПРИМЕР), то компилятор при входе в такую процедуру сохраняет все эти регистры в стек. Если компилятор этого не делает - то это плохой компилятор. Анализирую код Keil'а он (Кейл) именно так и делает. Если я в перерывании (или процедуре) использую вызов процедуры, то в прерывании, перед ее выполнением, сохряняются все регистры и PSW в стек. Если я неиспользую каких либо регистров (ну компилятор в процессе компиляции ничего не задействовал), то на входе он ничего не сохраняет. Для Кейла я это проверял. Правда весь листинг виден только в самом проце, в выходных файлах этого нет.

В данном примере происходит все именно так.
Если у меня есть ряд процедур, которые могут вызваться из прерывания и у них есть локальные переменные, то компилятор не отражает на адреса этих переменных ничего из других процедур.
В данном примере я заметил пересекающиеся области (две ячейки наложились. Две ячейки локальных переменных разных процедур). Причем ячеки входящих переменных (извиняюсь за корявость, но переменных через которые я передаю данные в процедуру).

Есть функция Spi_RD в которую я передаю ряд параметров. Она вызывается в прерывании.
Есть функция Filtrate_long в которую я передаю одно число и в нутри нее есть переменные счетчики.
Так пересеклись две переменные из входящих переменных функции Spi_RD и внутренние переменные-счетчики процедуры Filtrate_long.

На примере других функций я вижу, что используемые регистры для локальных переменных в процедурах, компилятор сохраняет в стек, при вызове внутри одной процедуры другой (с такими же регистрами). Иначе я вообще никогда не смог бы вызывать внутри процедур другие процедуры. Именно так происходит и тут. Просто количество параметров передаваемых в процедуру Spi_RD наверное большое и они вышли за регистры и компилятор о них забыл (мое предположение).

Вот вызовы процедур с которыми я нашел пересечение переменных:
Код
bit Spi_RD(byte * ptr, byte * complete, word LEN, byte type, byte FIRST_SENT, bit cs);

signed long filtrate_long(signed long input_data);


Пересеклись адреса следующих переменных
type, FIRST_SENT с локальными переменными (счетчиками) внутри функции Filtrate_long.

В данный момент локальные переменные функции Filtrate_long я объявил в области XDATA дабы они попросту ушли из карты памяти в другую область (остальные вроде как ен пересекаются). И все работает без глюков.

Без наездов. Просто пытаюсь разобраться, что я делаю не так. И что мне следует сделать, чтобы такого не происходило впредь.

Ведь я не исключаю того что такое же может произойти в области XDATA.


Цитата(tag @ Dec 11 2008, 13:05) *
Как известно памяти у 51 немного, плюс компилятор разработан так что локальные переменные сохраняет не на стеке, а либо в регистрах, либо в памяти (в этом вы можете убедится просмотрев руководство). В случае когда переменные располагаются в памяти линковщик может перекрывать область памяти локальных переменных одной подпрограммы и такую же область другой подпрограммы. Видимо так сделано с целью экономии памяти. Тогда, конечно подразумевается что эти фукции к примеру, не могут вызывать друг друга из своего тела. Так линковщик работает если ему не указать опцию nooverlay. Когда данная опция указана области локальных переменных подпрограмм не пересекаются и следовательно проблема порчи локальных переменных исчезает.


Добрый день.

Огромное вам спасибо.

Мне кажется - это есть лекарство моей болезни. Так как симптомы вы описали точно.

Черт, а ведь эта директива относится ко всему проекту. С ней области data не хватает smile.gif
А для лечения локально, нужно делать глобальбные переменные. А что же делать с входыми данными процедур??? Тоже глобально??

Сообщение отредактировал AndreyS - Dec 11 2008, 12:11


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
barabek
сообщение Dec 11 2008, 14:00
Сообщение #24


Знающий
****

Группа: Свой
Сообщений: 540
Регистрация: 16-08-07
Из: Владивосток
Пользователь №: 29 831



Цитата(AndreyS @ Dec 11 2008, 22:00) *
Компилятор никогда не отображает локальные переменные на одни и теже адреса, если они пересекаются при вызове процедур (РАЗНЫХ ПРОЦЕДУР). Если переменные пересекаются (отображаются на одни и теже адреса) (РЕГИСТРЫ НАПРИМЕР), то компилятор при входе в такую процедуру сохраняет все эти регистры в стек. Если компилятор этого не делает - то это плохой компилятор. Анализирую код Keil'а он (Кейл) именно так и делает.

Здесь позвольте с Вами не согласиться. Это относится не конкретно к Keil, а вообще к языку C. Локальные переменные, кроме того что они видны только внутри функции (блока), они еще имеют время жизни только на протяжении выполнения этой функции (блока). Ни в коем случае не гарантируется, что при очередном вызове функции локальная переменная будет иметь тоже значение, что было раньше.
Цитата
Если я в перерывании (или процедуре) использую вызов процедуры, то в прерывании, перед ее выполнением, сохряняются все регистры и PSW в стек. Если я неиспользую каких либо регистров (ну компилятор в процессе компиляции ничего не задействовал), то на входе он ничего не сохраняет. Для Кейла я это проверял. ,,,
....Если у меня есть ряд процедур, которые могут вызваться из прерывания и у них есть локальные переменные, то компилятор не отражает на адреса этих переменных ничего из других процедур.
В данном примере я заметил пересекающиеся области...

Чтобы не сохранять регистры лучше использовать using. А вот для этого случая - просто функции и функции вызываемой из прерывания, Keil действительно подкачал smile.gif, видимо у него нет такой проверки на перекрытие переменных sad.gif. Я когда-то тоже обжигался на этом. Теперь прерывания только наиболее короткие, без вызовов, с использованием отдельного банка памяти для прерываний одинакового приоритета.
Цитата
Правда весь листинг виден только в самом проце, в выходных файлах этого нет.

А в *.LST разве в конце не расписаны адреса под переменные (при соответствующих галочках в настройках), или это не то?

Код
bit Spi_RD(byte * ptr, byte * complete, word LEN, byte type, byte FIRST_SENT, bit cs);

Меня, честно говоря, пугают такие функции с огромным количеством входных переменных smile.gif. Не воспринимайте мои слова близко к сердцу. Это конечно не ошибка, просто я бы использовал структуру и передавал бы указатель на нее.

Сообщение отредактировал barabek - Dec 11 2008, 14:10
Go to the top of the page
 
+Quote Post
AndreyS
сообщение Dec 11 2008, 14:37
Сообщение #25


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Цитата(barabek @ Dec 11 2008, 17:00) *
Здесь позвольте с Вами не согласиться. Это относится не конкретно к Keil, а вообще к языку C. Локальные переменные, кроме того что они видны только внутри функции (блока), они еще имеют время жизни только на протяжении выполнения этой функции (блока). Ни в коем случае не гарантируется, что при очередном вызове функции локальная переменная будет иметь тоже значение, что было раньше.

Меня, честно говоря, пугают такие функции с огромным количеством входных переменных smile.gif. Не воспринимайте мои слова близко к сердцу. Это конечно не ошибка, просто я бы использовал структуру и передавал бы указатель на нее.


Я согласен что локальные переменные имеют время жизни только на протяжении функции. Но при этом регистры то он в стек сохраняет (т.е. если я из функци вызову другую, то локальные переменные первой функции он сохранит в стек или будет использовать локальные переменные второй функции в другой области), а вот локальные переменные, что вылезли за диапазон регистров он в стек не сохраняет. Т.е. если я передаю в функцию большое количество параметров есть вероятность пересечения. Тут же у меня была проблемма с изменением ячейки. Я не понял почему это происходит (оставил для разбирательств на потом) просто завел внутри процедуры дублирующую локальную переменную (она видно отразилась в регистры) и в нее перебрасывал срузу при входе в процедуру данные из ихменяющейся переменной. Все заработало номарльно. Тогда я не врубился кто виноват (думал сам куда за диапазон выхожу), а теперь понял. Надо шерстить весь проект на предмет большого числа передаваемых параметров. Делать либо как у вас (заполнять структуру и передавать указатель. Просто я хотел унивирсальности, а так прийдется везде еще таскать обписание этой структуры.) либо урезать часть передаваемых параметров в функцию и переносить их например в глобальные.

Обидно все это. Регистры сохраняет (так как знает что их портит), а часть используемых ячеек не сохраняет. sad.gif


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
AndreyS
сообщение Dec 12 2008, 10:38
Сообщение #26


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Добрый день.

Написал для прерывания специальную функцию Spi_RD, но без передачи параметров. Все параметры передал через глобальную структуру.

Пересекающихся адресов теперь нет. Все работает нормально.

Печально это sad.gif
И печально именно то, что оказалось что Keil не сохраняет все локальные переменные при входе в процедуры. Сохраняет он только регистры. sad.gif

А еще более печально то, что прошло столько времени, а подобное в нем досих пор существует.

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

Мда. Что еще может подстерегать???

По сему заинтересовал вопрос перехода с Keil на другой компилятор и линковщик. Или просто другой линковщик (вроде как именно он раскидывает переменые по адресам???).

Как себя ведут другие компиляторы касательно данной проблеммы??

Не ну быть может это просто некрасиво конечно с точки зрения написания кода, передавать в функцию такое количество параметров как у меня. Но я надеялся, что компилятору это позубам. А иначе накой хрен переходить с Asm, когда мне и тут приходится заботиться о том чтобы небыло пересекающихся адресов?? На Си ведь переходят для удобства и быстроты написания программы (частный случай и лично мое мнение), быстрой модификации программы. В общем чтобы не иметь в частности такой головной боли как раскидывание по адресам локальных переменных.


--------------------
Удачи.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 12 2008, 12:03
Сообщение #27


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(AndreyS @ Dec 12 2008, 12:38) *
И печально именно то, что оказалось что Keil не сохраняет все локальные переменные при входе в процедуры. Сохраняет он только регистры. sad.gif
Смею предположить, что Кейл все делает правильно. Для цепочки вызовов он строит компилируемый стек и вы сами заметили, что локальные переменные разных функций в такой цепочке не пересекаются. Axel в сообщении №19 сказал вам, что ваш случай отличается от обычного, что вам надо найти способ указать компилятору, что функция является реентерабельной. Штудируйте документацию компилятора на предмет ключевого слова reentrant или подобного. Если бы компилятор каждую функцию делал реентерабельной - вы бы первый взвыли, что он сохраняет кучу лишнего и генерит неэффективный код. Кейлом и x51 уже очень давно не пользуюсь, поэтому не могу подсказать конкретнее, но на 99% уверен, что в нем предусмотрен какой-то атрибут вроде reentrant для функций.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
c8051
сообщение Dec 12 2008, 16:52
Сообщение #28


Участник
*

Группа: Участник
Сообщений: 21
Регистрация: 10-06-08
Из: Украина, Черкассы-Киев
Пользователь №: 38 188



Цитата(Сергей Борщ @ Dec 12 2008, 15:03) *
Смею предположить, что Кейл все делает правильно.
...вам надо найти способ указать компилятору, что функция является реентерабельной.
Штудируйте документацию компилятора на предмет ключевого слова reentrant или подобного.

c51.pdf
page 129
Reentrant functions can be called recursively and can be called simultaneously
by two or more processes. Reentrant functions are often required in real-time
applications or in situations where interrupt code and non-interrupt code must
share a function.


или в ситуациях, когда код прерывания и код непрерывания должны
совместно использовать функцию.


Сообщение отредактировал c8051 - Dec 12 2008, 16:53
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 12 2008, 18:37
Сообщение #29


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(c8051 @ Dec 12 2008, 18:52) *
c51.pdf
page 129
Фух... Кейла оправдали. Очередной "глюк" оказался просто ленью прочитать документацию.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
AndreyS
сообщение Dec 13 2008, 20:34
Сообщение #30


Местный
***

Группа: Участник
Сообщений: 235
Регистрация: 28-01-05
Из: Санкт-Петербург
Пользователь №: 2 276



Цитата(Сергей Борщ @ Dec 12 2008, 21:37) *
Фух... Кейла оправдали. Очередной "глюк" оказался просто ленью прочитать документацию.


Добрый день.

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

Большое спасибо за наставление.
Пойду читать документацию (просто читать ее всю подряд, от корки до корки, я не хотел. В этом я действительно поленился smile.gif.

Спасибо.

PS. Да а на сообщение Axel я не обратил внимания laughing.gif

Сообщение отредактировал AndreyS - Dec 13 2008, 20:44


--------------------
Удачи.
Go to the top of the page
 
+Quote Post

2 страниц V  < 1 2
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 28th July 2025 - 06:39
Рейтинг@Mail.ru


Страница сгенерированна за 0.01508 секунд с 7
ELECTRONIX ©2004-2016