Перейти к содержанию
    

Передача аргумента в прерывание (язык C)

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

1. В обработчик прерываний нельзя передать параметры. Если несколько задумаетесь, поймете сами почему.

 

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

 

3. Переходите на С++ и инкапсулируйте в классах обращение к таким переменным через соответствующие функции Set... и Get... По крайней мере вся работа с такими переменными будет локализована в одном программном модуле (cpp или h, в зависимости от реализации), а при использовании inline и потерь по скорости практически не будет.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В обработчик прерывания параметр не передашь. И вообще, как вы себе это представляете?

Идёт идёт себе Ваша основная программа, а тут бац, срабатывает прерывание, ну, скажем, буфер UART опустел и начинает исполняться обработчик. В какой именно момент времени вы собрались в таком случае передать в прерывание аргумент?

 

Возможно вам поможет сужение области видимости глобальных переменных одним модулем(файлом). Это достигается путем объявления переменной со словом static. А ещё не забывайте, что переменные, доступ к которым производится из обработчика прерывания - должны быть объявлены как volatile. В случае с UART, к примеру, делаете отдельный модуль(uart.c) в этом модуле обявляете передающий буфер(как static), обработчик прерывания в этот же модуль определите и инициализацию в отдельную функцию. Вот и решатся все ваши проблемы. С внешним миром uart.c связывается по средством функций(типа putch(), getch() и т.д.).

А ещё используйте структуры. Всё, что касается состояния uart - в структуру. Можно для этого дела даже typedef сделать, а все функции в модуле сделать таким образом, чтобы принимали параметром указатель на такую структуру. Тогда ваш uart.c будет универсальным и одинаково хорошо будет справляться с десятком портов uart... нужно только удачно всё спроектировать и будет красиво)

 

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

И вообще, ИМХО, проблема с прерываниями - мелочь, по сравнению с удачностью общего дизайна.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

sergeeff, sigmaN, большое спасибо за ответы. По поводу передачи аргументов в подпрограмму обработки прерываний: это, конечно, был риторический вопрос, т.с. провокационный ;) Мне понравилась идея упаковывать глобальные переменные в структуры и применять static, для ограничения области видимости. Но вот по поводу volatileне совсем понял. Почему это очень важно именно для прерываний (если можно поподробней расскажите)?

To sergeeff: спасибо за совет, я уже давно подумываю начать использовать С++.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Насчет volatile все очень просто. Это запрет компилятору оптимизировать операции с переменными этого типа, потому как если внутри некоторой функции выполняются какие либо операции с глобальной переменной, декларированной в другой единице компиляции, оптимизатор, скорее всего, такую операцию просто выкинет, посчитав ее ненужной в рамках данной функции. Исходя из этого, вообще лучше все глобальные переменные объявлять как volatile, чтобы не нарваться на трудноуловимые ошибки, связанные в работой оптимизатора.

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Понятно, volatile это "защита" от сильно умного (оптимизирующего) компилятора. А если оптимизацию не использовать? Или это зависит от конкретного компилятора?

 

Редактировано: sergeeff Вы меня опередили с ответом на мой вопрос :-) .

Изменено пользователем Nikitoc

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

От "умности" компилятора сильно зависит. Не использовать опции компилятора по оптимизации просто глупо. По сему использование volatile можно рассматривать, как некое соглашение между вами, программистом, и компилятором, по типу "все оптимизируем, кроме...".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А если оптимизацию не использовать?
Тогда лучше писать на ассемблере. Хотя бы не будут возникать возмущения "что за г.. этот компилятор сотворил из такой простой конструкции, я бы сделал в 10 раз лучше".

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Постараюсь внести ясность.

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

Но что, если адрес в памяти, по которому лежит переменная, меняется и компилятор не может это проследить? А тут, к примеру, цикл wile( flag ) ; Компилятор может в самом начале функции запихать переменную в регистр и больше не обращаться к памяти. А скорее всего и вовсе этот цикл уберет, т.к. посчитает, что он никогда не исполнится. Или же воспримет цикл как бесконечный и уберет всё, что дальше...зависит от кода.

К чему я клоню.. а к тому, что для компилятора обработчик прерывания - это функция, которая не вызывается никогда. Компилятор просто не может предугадать, когда именно сработает обработчик и изменит flag. И пусть даже он сработает и изменит flag прямо в памяти - ожидающий цикл не будет перечитывать flag из памяти при каждой итерации.

Вот весь механизм и суть тут именно в том, что компилятор не в состоянии отследить изменение переменной. Она меняется за пределами Си кода либо компилятор не в состоянии проследить вызов. То-же самое справедливо и для функций. Компилятор удаляет функции, так или иначе не вызываемые из main(за исключением обработчиков прерываний, они - особый случай).

И если для переменных есть volatile, то для функций как правило предоставляется какая-нибудь #pragma или аттрибут. Суть состоит в том, чтобы показать компилятору, что эта функция вызывается не из Си кода(к примеру из asm модуля) и функцию удалять не следует.

 

Далее, что касается volatile: такие переменные при каждом обращении считываются из памяти и не кэшируются в регистрах.

Вот так, надеюсь, будет понятно уже)

 

Исходя из этого, вообще лучше все глобальные переменные объявлять как volatile, чтобы не нарваться на трудноуловимые ошибки, связанные в работой оптимизатора.
Не согласен с Вами. Это излишества, к тому-же бьющие оптимизатору ниже пояса, снижая эффективность генерируемого кода.

И дело всё в том, что если работа с переменной ведётся только из Си кода - то без всяких volatile будет всё прекрасно и без глюков работать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну а я вам такую штуку про оптимизации расскажу. Для меня было большим удивлением. Работаю под MS VS2008 C/C++. Имею библиотеку функций, подключаемую к проекту. Появилась идея написать некий простенький модуль, который выводил бы мне на консоль версию и дату последних изменений файлов библиотеки в svn, и аналогичные модули поместить во все библиотеки, используемые в проекте.

 

Делаем файл revision.h

 

#ifndef __REVISION_H__
#define __REVISION_H__

#define REV_STRING   888
#define DATE_STRING  "11:11:11 22-11-2010" 
#define LIB_NAME     "aaa.lib" 

#endif //__REVISION_H__

 

файл crevision.h

 

#ifndef _CREVISION_H_
#define _CREVISION_H_

#include <stdio.h>
#include "revision.h"

class CRevision
{
   public:
   CRevision() 
   { 
      printf("LIB :: %s, Rev.%d [%s]\n", LIB_NAME, REV_STRING, DATE_STRING);
   }
};          


#endif   // _CREVISION_H_

 

и, наконец, crevision.cpp

 

#include "CRevision.h"

const CRevision caaa;

 

Во всех книгах по C++ написано, что для переменной гарантировано будет вызван конструктор. Так вот: фига с два. Основная программа никогда не обращается к caaa, ее благополучно "соптимизировали" компилятор/линкер.

 

Если в основной программе объявить

 

const CRevision caaa;

 

все работает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3. Переходите на С++ и инкапсулируйте в классах обращение к таким переменным через соответствующие функции Set... и Get... По крайней мере вся работа с такими переменными будет локализована в одном программном модуле (cpp или h, в зависимости от реализации), а при использовании inline и потерь по скорости практически не будет.

А в си какие проблемы то же самое сделать?

Объявили статические переменные в модуле и функции сеттера и геттера (или аксессора и мутатора :) )

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Понятно, volatile это "защита" от сильно умного (оптимизирующего) компилятора. А если оптимизацию не использовать? Или это зависит от конкретного компилятора?

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

 

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

 

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

 

Во всех книгах по C++ написано, что для переменной гарантировано будет вызван конструктор. Так вот: фига с два.

Конструктор будет вызван для объекта, который реально создается. А тут просто сам объект был выкинут из программы, т.к. на него нигде не присутствует ссылок. Раз нет объекта, значит и конструировать нечего и конструктор вызывать не зачем.

 

Основная программа никогда не обращается к caaa, ее благополучно "соптимизировали" компилятор/линкер.

Да, диагноз верный. Только вот, как я понял, и при объявлении в основной программе к этому объекту тоже никто не обращается. И если в этом случае объект все-таки создается, то это уже особенности реализации. Т.е. формально компилятор имеет полное право и тут выкинуть оный объект. Чтобы гарантировано не выкидывал, надо чтобы была хотя бы одна ссылка.

 

Помнится у IAR было даже специально расширение для подобных случаев: __root. Объекты, объявленные с таким квалификатором не выбрасывались из программы ни при каких обстоятельствах.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Помнится у IAR было даже специально расширение для подобных случаев: __root. Объекты, объявленные с таким квалификатором не выбрасывались из программы ни при каких обстоятельствах.

А в GCC бывает__attribute__((used))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...