Есть ли хороший способ реализации связи между ISR и остальной частью программы для встроенной системы, которая избегает глобальных переменных?
Кажется, что общая схема состоит в том, чтобы иметь глобальную переменную, которая совместно используется ISR и остальной частью программы и используется в качестве флага, но это использование глобальных переменных идет вразрез со мной. Я включил простой пример с использованием ISR в стиле avr-libc:
volatile uint8_t flag;
int main() {
...
if (flag == 1) {
...
}
...
}
ISR(...) {
...
flag = 1;
...
}
Я не могу видеть вокруг того, что по существу является проблемой определения объема; Любые переменные, доступные как ISR, так и остальной части программы, по своей природе должны быть глобальными? Несмотря на это, я часто видел, как люди говорят что-то вроде «глобальные переменные - это один из способов реализации связи между ISR и остальной частью программы» (выделено мной), что, по-видимому, подразумевает, что существуют другие методы; если есть другие методы, каковы они?
Ответы:
Существует де-факто стандартный способ сделать это (при условии программирования на C):
extern
ключевым словом или просто по ошибке.static
. Такая переменная не является глобальной, но ограничена файлом, в котором она объявлена.volatile
. Примечание: это не дает атомарного доступа и не разрешает повторный вход!источник
inline
становится устаревшим, так как компиляторы становятся умнее и умнее при оптимизации кода. Я бы сказал, что беспокойство об издержках - это «преждевременная оптимизация» - в большинстве случаев эти затраты не имеют значения, если они вообще присутствуют в машинном коде.Это настоящая проблема. Преодолей это.
Теперь, перед тем, как колени будут сразу же разглагольствовать о том, что это нечисто, позвольте мне немного уточнить это. Существует определенная опасность в использовании глобальных переменных для чрезмерного. Но они также могут повысить эффективность, что иногда имеет значение в небольших системах с ограниченными ресурсами.
Главное - подумать о том, когда вы можете разумно использовать их и вряд ли попадете в беду, а не просто в ждущую ошибку. Всегда есть компромиссы. Хотя в целом избегание глобальных переменных для связи между кодом прерывания и передним планом является приемлемой рекомендацией, принятие его, как и большинства других рекомендаций, к экстремальным религиям является контрпродуктивным.
Вот некоторые примеры, где я иногда использую глобальные переменные для передачи информации между прерыванием и кодом переднего плана:
Я уверен, что тики 1 мс, 10 мс и 100 мс имеют размер слова, который можно прочитать за одну атомарную операцию. При использовании языка высокого уровня обязательно сообщите компилятору, что эти переменные могут изменяться асинхронно. Например, в Си вы объявляете их внешне изменчивыми . Конечно, это то, что входит в стандартный файл включения, поэтому вам не нужно помнить это для каждого проекта.
Иногда я делаю счетчик тиков на 1 с общим счетчиком истекшего времени, поэтому его ширина составляет 32 бита. Это не может быть прочитано в одной атомарной операции на многих маленьких микро, которые я использую, так что это не стало глобальным. Вместо этого предоставляется процедура, которая читает значение из нескольких слов, обрабатывает возможные обновления между операциями чтения и возвращает результат.
Конечно, могли быть процедуры для получения меньших 1 мс, 10 мс и т. Д., А также счетчики тиков. Однако это действительно очень мало для вас, добавляет много инструкций вместо чтения одного слова и использует другое расположение стека вызовов.
Какой минус? Я предполагаю, что кто-то может сделать опечатку, которая случайно записывает данные на один из счетчиков, что затем может испортить другие временные характеристики в системе. Записывать счетчику преднамеренно не имеет смысла, поэтому такого рода ошибка должна быть чем-то непреднамеренным, например опечатка. Кажется очень маловероятным. Я не помню, чтобы это когда-либо происходило в более чем 100 небольших проектах микроконтроллеров.
Например, A / D может считывать выходной сигнал от 0 до 3 В делителя напряжения для измерения напряжения 24 В. Многие показания проходят через некоторую фильтрацию, затем масштабируются так, чтобы окончательное значение было в милливольтах. Если напряжение питания составляет 24,015 В, то окончательное значение составляет 24015.
Остальная часть системы просто видит текущее обновленное значение, указывающее напряжение питания. Он не знает и не должен заботиться, когда именно это обновляется, тем более что оно обновляется гораздо чаще, чем время установления фильтра нижних частот.
Опять же, интерфейсная процедура может использоваться, но вы получаете от этого очень мало пользы. Просто использовать глобальную переменную, когда вам нужно напряжение питания, намного проще. Помните, что простота не только для машины, но и более простая означает меньшую вероятность человеческой ошибки.
источник
extern int ticks10ms
на неinline int getTicks10ms()
будет иметь абсолютно никакого значения в скомпилированной сборке, в то время как с другой стороны будет трудно случайно изменить ее значение в других частях программы, а также позволит вам способ «привязки» к этому вызову (например, для проверки времени во время модульного тестирования, для регистрации доступа к этой переменной или чего-либо еще). Даже если вы утверждаете, что шанс программиста san изменить эту переменную на ноль, встроенный метод получения не будет стоить дорого.Любое конкретное прерывание будет глобальным ресурсом. Однако иногда бывает полезно, чтобы несколько прерываний использовали один и тот же код. Например, система может иметь несколько UART, каждый из которых должен использовать аналогичную логику отправки / получения.
Хороший подход к обработке, который заключается в том, чтобы поместить объекты, используемые обработчиком прерываний или указатели на них, в объект структуры, а затем получить фактические аппаратные обработчики прерываний, которые будут выглядеть примерно так:
Объекты
uart1_info
,uart2_info
и т.д. бы глобальные переменные, но они были бы только глобальные переменные , используемые обработчики прерываний. Все остальное, к чему будут обращаться обработчики, будет обрабатываться внутри них.Обратите внимание, что все, к чему обращается как обработчик прерываний, так и код основной линии, должно быть квалифицировано
volatile
. Возможно, проще всего просто объявитьvolatile
все, что будет использоваться обработчиком прерываний, но, если важна производительность, можно написать код, который копирует информацию во временные значения, оперирует ими, а затем записывает их обратно. Например, вместо того, чтобы писать:записывать:
Первый подход может быть легче читать и понимать, но он будет менее эффективным, чем второй. Является ли это проблемой, зависит от приложения.
источник
Вот три идеи:
Объявите переменную флага как статическую, чтобы ограничить область действия одним файлом.
Сделайте переменную флага частной и используйте функции getter и setter для доступа к значению флага.
Используйте сигнальный объект, такой как семафор, вместо переменной-флага. ISR установит / опубликует семафор.
источник
Прерывание (т. Е. Вектор, указывающий на ваш обработчик) является глобальным ресурсом. Так что даже если вы используете какую-то переменную в стеке или в куче:
или объектно-ориентированный код с «виртуальной» функцией:
… Первый шаг должен включать фактическую глобальную (или, по крайней мере, статическую) переменную для достижения этих других данных.
Все эти механизмы добавляют косвенность, поэтому обычно этого не делается, если вы хотите выжать последний цикл из обработчика прерываний.
источник
Сейчас я пишу для Cortex M0 / M4, и подход, который мы используем в C ++ (тега C ++ нет, поэтому этот ответ может быть не по теме), заключается в следующем:
Мы используем класс,
CInterruptVectorTable
который содержит все подпрограммы обработки прерываний, которые хранятся в фактическом векторе прерываний контроллера:Класс
CInterruptVectorTable
реализует абстракцию векторов прерываний, поэтому вы можете связывать различные функции с векторами прерываний во время выполнения.Интерфейс этого класса выглядит следующим образом:
Вам необходимо создать функции, которые хранятся в таблице векторов,
static
потому что контроллер не может предоставитьthis
-pointer, поскольку таблица векторов не является объектом. Таким образом, чтобы обойти эту проблему, у нас есть статическийpThis
указатель внутриCInterruptVectorTable
. После входа в одну из статических функций прерывания он может получить доступ кpThis
-pointer, чтобы получить доступ к членам одного объектаCInterruptVectorTable
.Теперь в программе вы можете использовать
SetIsrCallbackfunction
указатель функции дляstatic
функции, которая должна вызываться при возникновении прерывания. Указатели хранятся вInterruptVectorTable_t virtualVectorTable
.И реализация функции прерывания выглядит так:
Так что это вызовет
static
метод другого класса (который может бытьprivate
), который затем может содержать другойstatic
this
-pointer, чтобы получить доступ к переменным-членам этого объекта (только один).Я думаю, вы могли бы построить и интерфейс, как
IInterruptHandler
и хранить указатели на объекты, поэтому вам не нуженstatic
this
-pointer во всех этих классах. (возможно, мы попробуем это на следующей итерации нашей архитектуры)Другой подход хорошо работает для нас, так как единственные объекты, которым разрешено реализовывать обработчик прерываний, это те, которые находятся на уровне аппаратной абстракции, и у нас обычно есть только один объект для каждого аппаратного блока, так что это нормально работает с
static
this
-pointers. И уровень аппаратной абстракции предоставляет еще одну абстракцию для прерываний,ICallback
которая называется, которая затем реализуется на уровне устройств над аппаратными средствами.У вас есть доступ к глобальным данным? Конечно, но вы можете сделать большую часть необходимых глобальных данных частными, как, например,
this
-pointers и функции прерывания.Это не пуленепробиваемый, и это добавляет накладные расходы. Вы будете бороться за реализацию стека IO-Link, используя этот подход. Но если вы не очень сильно привязаны к временным интервалам, это работает очень хорошо, чтобы получить гибкую абстракцию прерываний и связи в модулях без использования глобальных переменных, которые доступны везде.
источник