Запретить прерывание внутри функции обработки прерывания?
Offline
Зарегистрирован: 12.03.2012
Есть некоторая система, построена на mega
Есть два обработчика прерывания — один работает по таймеру, второй — «слушает» пин.
Оба прерывания нужны, оба прерывания важны.
Первое прерывание используется для «графики», второе — для приема данных по радиоканалу.
Сейчас работает, но, когда приходит радиосигнал — есть нарушения в графике (помаргивания и т.п.) — очевидно, что во время отработки прерывания по таймеру возникло аппаратное прерывание и это нарушило «тайминги» и динамическая индикация отрабатывает с косяками.
Попробовал сделать так:
ISR(TIMER1_COMPA_vect) { noInterrupts(); // .............. // что-то тут делаем // .............. interrupts(); }
Не сработало — вижу те же «помаргивания». Как обойти?
Я уже касался темы прерываний в статье Многозадачность и прерывания на Arduino. Сегодня я постараюсь раскрыть тему прерываний более подробно.
Большинство микроконтроллеров имеют прерывания. Прерывания позволяют реагировать на «внешние» события пока выполняется еще что-то. Например, если вы готовите ужин, то вы можете положить картофель вариться на 20 минут. Вместо того, чтобы смотреть на часы непрерывно в течение 20 минут, вы можете установить таймер, а затем пойти посмотреть телевизор. Когда таймер подаст звуковой сигнал, вы «прерываете» просмотр вашего телевизора, для того, чтобы сделать что-то с картофелем.
Что такое прерывание, ISR и вектор прерывания?
- Прерывание (англ. interrupt) — сигнал, сообщающий процессору о наступлении какого-либо события. При этом выполнение текущей последовательности команд приостанавливается, и управление передаётся обработчику прерывания.
- Обработчик прерывания (функция обработки прерывания, процедура обработки прерывания) по английски называется Interrupt Service Routine, или сокращенно ISR, реагирует на событие и обслуживает его, после чего возвращает управление в прерванный код.
- Вектор прерывания — это указатель на адрес расположения инструкций, которые должны быть выполнены при вызове данного прерывания. То есть это адрес программы обработки данного прерывания. Векторы прерываний объединяются в таблицу векторов прерываний, содержащую адреса обработчиков прерываний. Местоположение таблицы зависит от типа и режима работы процессора.
Что не является прерыванием?
Прерывания — это не просто когда вы передумываете и делаете что-то другое. Например:
- Я перестал смотреть телевизор и принял ванну
- Я читал книгу, когда пришли гости и оторвали меня от книги
- Пользователь нажал красную кнопку, чтобы остановить робота
В представленных выше ситуациях делается что-то вместо того, что делалось до этого. Такие действия не совершаются в подпрограмме обработки прерывания. Самое большое, что могла бы сделать процедура обработки прерывания, так это запомнить, что была нажата красная кнопка.
Пример прерывания
Показать/скрыть код
const byte LED = 13; const byte BUTTON = 2; // Функция обработки прерывания (ISR) void pinChange () { if (digitalRead (BUTTON) == HIGH) digitalWrite (LED, HIGH); else digitalWrite (LED, LOW); } void setup () { pinMode (LED, OUTPUT); // устанавливаем пин в режим вывода digitalWrite (BUTTON, HIGH); // встроенный подтягивающий резистор attachInterrupt (0, pinChange, CHANGE); // подключаем обработчик прерывания } void loop () { // не делаем здесь ничего }
Этот пример показывает, что, несмотря на то, что в основном цикле loop не выполняется никаких действий, вы можете включать и выключать светодиод на 13 пине, нажимая кнопку, подключенную к D2.
Чтобы проверить это, просто нажмите кнопку, подключенную между 2 выводом и землей. Внутренний подтягивающий резистор (подключается в функции setup) в нормальном состоянии (когда нет внешнего сигнала) поддерживает этот вывод в состоянии HIGH. Когда вы замыкаете его на замлю, он переходит в состояние LOW. Изменение состояния пина определяется при помощи прерывания CHANGE, которое является причиной вызова функции обработки прерывания.
В более сложном примере, основной цикл loop может делать что-то делать полезное, например, снимать показания температуры и позволять обработчику прерывания обнаруживать нажатие кнопки.
Доступные прерывания
Ниже приведен список прерываний для микроконтроллера ATmega328 в порядке их приоритетности
Показать/скрыть таблицу
Пр. | Прерывание | Обработчик |
1 | Сброс | RESET_vect |
2 | Внешнее прерывание 0 (вывод D2) | INT0_vect |
3 | Внешнее прерывание 1 (вывод D3) | INT1_vect |
4 | Изменение состояния вывода 1 (выводы с D8 по D13) | PCINT0_vect |
5 | Изменение состояния вывода 2 (выводы с A0 по A5) | PCINT1_vect |
6 | Изменение состояния вывода 3 (выводы с D0 по D7) | PCINT2_vect |
7 | Переполнение сторожевого таймера | WDT_vect |
8 | Прерывание по сравнению, канал A таймера/счетчика 2 | TIMER2_COMPA_vect |
9 | Прерывание по сравнению, канал B таймера/счетчика 2 | TIMER2_COMPB_vect |
10 | Переполнение таймера/счетчика 2 | TIMER2_OVF_vect |
11 | Прерывание таймера/счетчика 1 по захвату события | TIMER1_CAPT_vect |
12 | Прерывание по сравнению, канал A таймера/счетчика 1 | TIMER1_COMPA_vect |
13 | Прерывание по сравнению, канал A таймера/счетчика 1 | TIMER1_COMPB_vect |
14 | Переполнение таймера/счетчика 2 | TIMER1_OVF_vect |
15 | Прерывание по сравнению, канал A таймера/счетчика 0 | TIMER0_COMPA_vect |
16 | Прерывание по сравнению, канал B таймера/счетчика 0 | TIMER0_COMPB_vect |
17 | Переполнение таймера/счетчика 0 | TIMER0_OVF_vect |
18 | Завершение передачи по SPI | SPI_STC_vect |
19 | Завершение приема по каналу USART | USART_RX_vect |
20 | Регистр данных USART пуст | USART_UDRE_vect |
21 | Завершение передачи по каналу USART | USART_TX_vect |
22 | Преобразование АЦП завершено | ADC_vect |
23 | EEPROM готова | EE_READY_vect |
24 | Аналоговый компаратор переключился | ANALOG_COMP_vect |
25 | Событие двухпроводного интерфейса (I2C) | TWI_vect |
26 | Готовность флеш-памяти | SPM_READY_vect |
Встроенные названия обработчика, которые вы можете использовать для обратных вызовов ISR указаны в третьем столбце под названием Обработчик.
Если вы ошибетесь в названии векторов прерываний, даже просто перепутав заглавные буквы с прописными (очень легко ошибиться!) обработчик прерывания не будет вызван, и вы не получите ошибку компилятора.
Основные причины по которым вы можете использовать прерывания:
- Определить изменения состояния вывода (например, колесные энкодеры или нажатия кнопки)
- Сторожевой таймер (если ничего не произойдет через 8 секунд, сбрось меня)
- Прерывания по таймеру — используются для сравнения/переполнения таймеров
- Передача данных по SPI
- Передача данных по I2C
- Передача данных по USART
- Анало-цифровые преобразования
- Готовность к использованию EEPROM
- Готовность флеш-памяти (памяти программ)
Различные варианты для передачи данных используются, чтобы позволить программу делать что-то еще пока данные будут отправляться или получаться через Serial- порт, SPI или I2C.
Вывод процессора из спящего режима
Внешние прерывания, прерывания по изменению состояния пинов и прерывание сторожевого таймера также могут быть использованы для пробуждения процессора. Это может быть очень удобно, так как в спящем режиме процессор может быть настроен на меньшее энергопотребление (около 10 мкА). Прерывания по фронту, спаду или низкому уровню сигнала могут использоваться, чтобы разбудить гаджет. Например, если вы нажмете кнопку на нем, или же можно будить его периодически, используя прерывание сторожевого таймера, чтобы, к примеру, проверить время или температуру.
Прерывания по изменению состояния пинов могут использоваться для пробуждения процессора при нажатии кнопки на клавиатуре или чем-то похожим.
Процессор также может быть выведен из спящего режима прерыванием таймера при достижении определенного значения или переполнения и некоторыми другими событиями, такими как входящее сообщение I2C.
Разрешение и запрещение прерываний
Прерывание RESET не может быть запрещено, но другие прерывания могут временно запрещаться сбросом флага прерывания.
Вы можете разрешить прерывания, испольуя функции interrupts или sei:
interrupts(); // или... sei(); // устанавливаем флаг прерывания
Если вам нужно запретить прерывания, можно воспользоваться функцией noInterrupts или сбросить флаг прерывания:
noInterrupts(); // или ... cli(); // сбрасываем флаг прерывания
И тот и другой метод дают один и тот же результат. Использование функций interrupts и noInterrupts немного проще запомнить.
По умолчанию, прерывания в Arduino разрешены. Не запрещайте их надолго, иначе такие вещи как таймеры, например, не будут работать корректно.
Зачем запрещать прерывания?
Могут быть критичные ко времени участки кода, выполнение которых вам не хотелось бы прерывать, например, прерыванием таймера.
Кроме того, если многобайтовые поля обновляются с помощью обработчика прерывания, то вам, возможно, потребуется отключить прерывания, так чтобы вы могли получить данные «неделимыми». В противном случае один байт может быть обновлен обработчиком, пока вы читаете другой.
Например:
noInterrupts(); long myCounter = isrCounter; // получаем значение, выданное ISR interrupts();
Временное отключение прерываний гарантирует, что isrCounter (счетчик внутри обработчика прерывания) не изменится, пока мы получаем его значение.
Если вы не уверены в том, что прерывания уже включены или нет, вам нужно сохранить текущее состояние и восстановить его впоследствии.
Например, это то, что делает код внутри функции millis:
unsigned long millis() { unsigned long m; uint8_t oldSREG = SREG; // запрещаем прерывания, пока мы читаем timer0_millis или же мы можем получить // несостоятельное значение (т.е. в середине записи в timer0_millis) cli(); m = timer0_millis; SREG = oldSREG; return m; }
Обратите внимание на строки 4 и 10, где сохраняется текущий статусный регистр SREG, который включает флаг прерывания. После того, как мы получаем значение таймера (длиной 4 байта) мы возвращаем статусный регистр таким же каким он и был.
Что такое приоритет прерывания?
Поскольку существует 25 прерываний (за исключением RESET), возможно что произойдет одновременно больше, чем одно прерывание или же прерывание произойдет раньше, сем будет обработано другое. Также событие прерывания может произойти в момент, когда прерывания запрещены.
Приоритетный порядок является последовательностью, в которой процессор следит за событиями прерываний. Более высокая позиция в списке означает более высокий приоритет. Так, например, внешнее прерывание 0 (пин D2) будет обработано перед внешним прерыванием 1 (пин D3).
Может прерывание произойти пока прерывания запрещены?
События прерываний (именно события) могут произойти в любое время и большинство из них запоминается, установкой флага interrupt event внутри процессора. Если прерывания запрещены, то это прерывание может быть обработано, когда вновь станет разрешено в порядке приоритетности.
Что такое переменные volatile?
Переменные, к которым имеют доступ функции обработки прерываний и обычные функции должны быть объявлены как volatile. Такое объявление сообщит компилятору, что эти переменные могут измениться в любой момент и поэтому компилятор должен перезагрузить переменную каждый раз, когда вы ссылаетесь на нее, а не полагаться на копию, которая может храниться в регистре процессора.
Показать/скрыть пример кода
volatile boolean flag; // Процедура обработки прерывания (ISR) void isr () { flag = true; } void setup () { attachInterrupt (0, isr, CHANGE); // подключаем обработчик прерывания } void loop () { if (flag) { // произошло прерывание } }
Как использовать прерывания
- Написать обработчик прерывания (ISR). Он будет вызван при возникновении прерывания.
- Сообщить процессору, когда вы хотите запустить прерывание
Написание функции обработки прерывания
Процедуры обработки прерываний являются функциями без аргументов. В некоторых библиотеках Arduino используется вызов своих собственных функций, так что вы просто подставляете обычную функцию (как в приведенных выше примерах).
// Функция обработки прерывания (ISR) void pinChange () { flag = true; }
Однако, если в библиотеке нет своего «крючка» к функции обработки прерывания, то вы можете сделать свой собственный:
volatile char buf [100]; volatile byte pos; // функция обработки прерывания SPI ISR (SPI_STC_vect) { byte c = SPDR; // захватываем байт из регистра данных SPI // добавляем в буфер, если возможно if (pos < sizeof buf) { buf [pos++] = c; } }
В этом случае, вы используете предопределенную функцию ISR и подставляете имя соответствующего вектора прерывания. В этом примере, процедура обработки прерывания обрабатывает завершение передачи по SPI (ранее, вместо ISR использовалась функция SIGNAL, но теперь она упразднена).
Соединение обработчика с прерыванием
Для прерываний, уже обрабатываемых библиотеками, вы просто используете документированный интерфейс. Например:
void receiveEvent (int howMany) { while (Wire.available () > 0) { char c = Wire.receive (); // делаем что-нибудь с поступившим байтом } } void setup () { Wire.onReceive(receiveEvent); }
В этом случае, библиотека I2C разработана таким образом, чтобы обрабатывать входящие байты по I2C внутри себя, и затем вызывать предназначенную для пользователя функцию в конце входящего потока данных. В этом случае, receiveEvent не является чистой процедурой обработки прерывания (она имеет аргумент), но она называется встроенной ISR.
Другой пример представляет внешнее прерывание на пине:
// Процедура обработки прерывания (ISR) void pinChange () { // здесь обработка изменения состояния пина } void setup () { attachInterrupt (0, pinChange, CHANGE); // подключаем обработчик прерывания }
В этом случае, функция attachInterrupt добавляет функцию pinChange во внутреннюю таблицу и, кроме того, конфигурирует соответствующие флаги прерываний в процессоре.
Конфигурирование процессора для обработки прерывания
Следующим шагом, после того, как у нас появилась функция обработки прерывания, будет сообщение процессору, что вы хотите конкретное условие для вызова прерывания.
В качестве примера, для внешнего прерывания 0 (пин D2), можно сделать что-то типа такого:
EICRA &= ~3; // сбрасываем существующие флаги EICRA |= 2; // устанавливаем желаемые флаги (прерывание по спаду) EIMSK |= 1; // разрешаем его
В более читабельном варианте используются предопределенные имена:
EICRA &= ~(bit(ISC00) | bit (ISC01)); // сбрасываем существующие флаги EICRA |= bit (ISC01); // устанавливаем желаемые флаги (прерывание по спаду) EIMSK |= bit (INT0); // разрешаем его
EICRA (External Interrupt Control Register A) должен быть установлен в соответствии с таблицей из технического руководства ATmega328 (см. на стр.71). Это значение определяет точный тип желаемого прерывания:
- 0: Низкий уровень INT0 сгенерирует запрос прерывания (LOW прерывание)
- 1: Любое изменение состояния на INT0 сгенерирует запрос прерывания (CHANGE прерывание)
- 2: Спад INT0 сгенерирует запрос прерывания (FALLING прерывание)
- 3: Фронт INT0 сгенерирует запрос прерывания (RISING прерывание)
EIMSK (External Interrupt Mask Register), на самом деле, разрешает прерывание.
К счастью, нет необходимости запоминать эти цифры, потому что attachInterrupt сделает это за вас. Однако это то, что происходит на самом деле, и для других прерываний вам, возможно, придется «вручную» установить флаги.
Может ли обработчик прерывания быть прерван?
Если коротко — то нет, если вы хотите, чтобы прерывания были.
Когда производится вход в обработчик прерывания, то прерывания запрещаются. Естественно, что они должны были быть включены в первую очередь, в противном случае в обработчик прерывания не попасть. Однако, чтобы избежать то, что сама процеда обработуи прерывания прерывается, процессор запрещает прерывания.
При выходе из обработчика, прерывания вновь разрешаются. Компилятор также генерирует код внутри обработчика прерываний для сохранения регистроф и статусных флагов для того, что бы все что вы делали до возникновения прерывания не было затронуто.
Тем не менее, вы можете включить прерывания внутри обработчика прерываний, если посчитаете, что вам это жизненно необходимо. Например:
// Процедура обработки прерывания (ISR) void pinChange () { // здесь обработка изменения состояния пина interrupts (); // позволить еще прерывания }
Как правило, у вас должен быть серьезный повод, чтобы сделать это, так как другое прерывание может вызвать рекурсивный вызов pinChange и, вполне возможно, приведет к нежеланному результату.
Как долго может выполняться обработчик прерывания?
В соответствии с техническим руководством, минимально требуемое время для обработки прерывания составляет 4 такта (для помещения текущего программного счетчика в стек), затем выполняется код, размещенный по вектору прерывания. Как правило, там содержится переход на процедуру обработки прерывания, что занимает еще 3 такта.
Затем функция ISR делает что-то вроде этого:
// процедура обработки SPI ISR (SPI_STC_vect) 118: 1f 92 push r1 (2) // сохраняем R1 - нулевой регистр 11a: 0f 92 push r0 (2) // сохраняем регистр R0 11c: 0f b6 in r0, 0x3f (1) // берем SREG (статусный регистр) 11e: 0f 92 push r0 (2) // сохраняем SREG 120: 11 24 eor r1, r1 (1) // гарантируем, что R1 - ноль 122: 8f 93 push r24 (2) 124: 9f 93 push r25 (2) 126: ef 93 push r30 (2) 128: ff 93 push r31 (2) {
Это еще 16 тактов (указаны в круглых скобках). Итак, с момента, как произошло прерывание до того, как будет выполнена первая строка кода пройдет 16+7=23 такта, по 62 нс на такт, итого 1.4375 мкс. Это для частоты 16 МГц.
При выходе из обработчика прерывания, мы имеем следующий код:
} // конец процедуры обработки прерывания SPI_STC_vect 152: ff 91 pop r31 (2) 154: ef 91 pop r30 (2) 156: 9f 91 pop r25 (2) 158: 8f 91 pop r24 (2) 15a: 0f 90 pop r0 (2) // получаем старый SREG 15c: 0f be out 0x3f, r0 (1) // возвращаем SREG 15e: 0f 90 pop r0 (2) // теперь помещаем старый регистр R0 обратно 160: 1f 90 pop r1 (2) // возвращаем старое значение R1 162: 18 95 reti (4) // возвращаемся из прерывания, включаем прерывания
Это еще 19 тактов (1.1875 мкс). Таким образом, процедура обработки прерывания, используя предопределенную функцию ISR займет 2.626 мкс плюс код, который будет внутри.
Однако, внешние прерывания (где используется attachInterrupts) выполняются немного дольше:
Показать/скрыть код
SIGNAL(INT0_vect) { 182: 1f 92 push r1 (2) 184: 0f 92 push r0 (2) 186: 0f b6 in r0, 0x3f (1) 188: 0f 92 push r0 (2) 18a: 11 24 eor r1, r1 (1) 18c: 2f 93 push r18 (2) 18e: 3f 93 push r19 (2) 190: 4f 93 push r20 (2) 192: 5f 93 push r21 (2) 194: 6f 93 push r22 (2) 196: 7f 93 push r23 (2) 198: 8f 93 push r24 (2) 19a: 9f 93 push r25 (2) 19c: af 93 push r26 (2) 19e: bf 93 push r27 (2) 1a0: ef 93 push r30 (2) 1a2: ff 93 push r31 (2) if(intFunc[EXTERNAL_INT_0]) 1a4: 80 91 00 01 lds r24, 0x0100 (2) 1a8: 90 91 01 01 lds r25, 0x0101 (2) 1ac: 89 2b or r24, r25 (2) 1ae: 29 f0 breq .+10 (2) intFunc[EXTERNAL_INT_0](); 1b0: e0 91 00 01 lds r30, 0x0100 (2) 1b4: f0 91 01 01 lds r31, 0x0101 (2) 1b8: 09 95 icall (3) } 1ba: ff 91 pop r31 (2) 1bc: ef 91 pop r30 (2) 1be: bf 91 pop r27 (2) 1c0: af 91 pop r26 (2) 1c2: 9f 91 pop r25 (2) 1c4: 8f 91 pop r24 (2) 1c6: 7f 91 pop r23 (2) 1c8: 6f 91 pop r22 (2) 1ca: 5f 91 pop r21 (2) 1cc: 4f 91 pop r20 (2) 1ce: 3f 91 pop r19 (2) 1d0: 2f 91 pop r18 (2) 1d2: 0f 90 pop r0 (2) 1d4: 0f be out 0x3f, r0 (1) 1d6: 0f 90 pop r0 (2) 1d8: 1f 90 pop r1 (2) 1da: 18 95 reti (4)
Я насчитал здесь 82 такта (итого 5.125 мкс на 16 Мгц) и плюс еще время, которое требуется указанной функции обработчика, то есть 2.9375 мкс перед входом в обработчик и 2.1875 при возвращении.
Сколько времени проходит до того, как процессор начинает входить в обработчик?
Тут могут быть различные варианты. Приведенные выше цифры являются идеальными для случая, когда прерывание обрабатывалось немедленно. Некоторые факторы могут привнести задержку:
- Если процессор в режиме сна, то существует время пробуждения, которое может занять до нескольких миллисекунд пока тактирование опять наберет скорость. Это время будет зависеть от установок фьюзов (конфигурационных регистров) и того, насколько глубоко процессор ушел в сон.
- Если процедура обработки прерывания уже выполняется, то следующие прерывания не могут быть введены либо до ее окончания, либо пока они не будут разрешены в самой этой процедуре. Вот почему нужно делать процедуру обработки прерывания как можно короче, каждая микросекунда, которая будет здесь потрачена, затянет выполнение другой обработки.
- Иногда код выключает прерывания. Например, вызов millis ненадолго выключает прерывания. Поэтому, время обработки прерывания увеличится на то время, пока прерывания были запрещены.
- Прерывания могут обрабатываться только в конце инструкции процессора. Так, если конкретная команда занимает три такта, и она только началась, то прерывание будет отложено, по крайней мере, на пару тактов.
- Событие, которое возвращает прерывание обратно (например, возвращение из процедуры обработки прерывания) гарантированно выполнит, по крайней мере, еще одну инструкцию. Таким образом, даже если процедура обработки прерывания уже завершилась и ожидает следующее прерывание, то ему придется ожидать не менее одной инструкции, прежде чем оно будет обработано.
- Так как прерывания имеют приоритет, то прерывания с более высоким приоритетом могут быть обработаны до нужного вам прерывания.
Производительность
Прерывания могут повысить производительность во многих ситуациях потому, что позволяют реализовать основную программу без постоянного отслеживания, например, нажатия кнопки. Однако, cлужебные процедуры, обслуживающие прерывания, как уже сказано выше, будут выполняться дольше, чем циклический опрос входного порта. Если вам совершенно необходимо, отреагировать на события, скажем, за микросекунды, то прерывание для этого будет слишком медленным. В этом случае вы можете отключить прерывания (например, таймеры) и просто следить в цикле за изменением состояния пина.
Какова очередность прерываний?
Существует два типа прерываний:
- Прерывания, которые устанавливают флаг прерывания и они обрабатываются в приоритетном порядке, даже если вызывающее событие завершилось. Например, прерывание по фронту, спаду или изменению уровня на выводе D2.
- Другой тип прерываний проверяется только если они происходят «прямо сейчас». Например, прерывание, связанное с низким уровнем на выводе D2.
Те, которые устанавливают флаг, могут рассматриваться как помещаемые в очередь. Флаг прерывания остается установленным до тех пор, пора не будет осуществлен вход в процедуру обработки прерывания, после чего флаг сбрасывается процессором. Так как существует только один флаг, то если то же самое условие для возникновения прерывания создастся вновь до того, как первое будет обработано, обработка не будет произведена дважды.
Что-то в программе должно знать, что эти флаги должны быть установлены до того, как вы подключите обработчик прерывания. Например, для прерывания по фронту или спаду на выводе D2 можно установить соответствующий флаг и, как только, вы вызовете функцию attachInterrupt прерывание возникает немедленно, даже если событие произошло час назад. Чтобы избежать этого, вы можете вручную сбросить флаг. Например:
EIFR = 1; // сбрасываем флаг для прерывания 0 EIFR = 2; // сбрасываем флаг для прерывания 1
или в более читабельной форме:
EIFR = bit (INTF0); // сбрасываем флаг прерывания 0 EIFR = bit (INTF1); // сбрасываем флаг прерывания 1
Однако, низкоуровневые прерывания проверяются непрерывно, так что если не быть осторожным, то они будут возникать, даже после того как прерывание было вызвано. То есть, будет произведен выход из процедуры обработчика прерывания, а затем немедленно прерывание возникнет снова. Чтобы избежать этого, необходимо сделать detachInterrupt сразу же после того, как вы узнали, что возникло прерывание. В качестве примера рассмотрим переход в спящий режим:
Показать/скрыть код
#include <avr/sleep.h> // процедура обработуи прерывания в спящем режиме void wake () { sleep_disable (); // первая вещь после выхода из сна } void sleepNow () { set_sleep_mode (SLEEP_MODE_PWR_DOWN); sleep_enable (); // разрешаем бит sleep в регистре mcucr attachInterrupt (0, wake, LOW); // пробуждение по уровню low sleep_mode (); // здесь устройство действительно переходит в сон!! detachInterrupt (0); // останавливаем прерывание по LOW }
Обратите внимание, что прерывания запрещаются сразу после пробуждения. Однако, нужно быть осторожным, помещая detachInterrupt внутрь функции обработки прерывания wake, потому что прерывание может возникнуть между подключением прерывания и переходом в сон, что может привести к тому, что мы никогда не сможем выйти из сна.
Улучшенная версия кода, представленная ниже решает эту проблему путем отключения прерываний перед вызовом attachInterrupt. Таким образом, одна инструкция будет выполнена после повторного разрешения прерываний и мы уверены, что вызов sleep_cpu будет сделан до возникновения прерывания.
Показать/скрыть код
#include <avr/sleep.h> // процедура обработки прерывания в режиме сна void wake () { sleep_disable (); // первое, что мы делаем после пробуждения detachInterrupt (0); // останавливаем прерывания по LOW } void sleepNow () { set_sleep_mode (SLEEP_MODE_PWR_DOWN); noInterrupts (); // мы не получим прерывание до того, как спим sleep_enable (); // рарешаем бит sleep в регистре mcucr attachInterrupt (0, wake, LOW); // пробуждаемся по уровню low interrupts (); // теперь прерывания разрешены, следующая инструкция БУДЕТ выполнена sleep_cpu (); // здесь устройство помещается в сон } // end of sleepNow
Эти примеры иллюстрируют каким образом можно снизить энергопотребление, используя прерывание LOW. Хотя можно использовать и любой другой тип прерываний (RISING, FALLING, CHANGE) — все они выведут процессор из состояния сна.
Советы по написанию функции обработки прерывания
Делайте обработчик прерывания как можно короче! Пока выполняется обработчик прерывания, вы не можете обрабатывать другие прерывания. Таким образом, можно легко пропустить нажатие кнопки или входящее сообщение по Serial-порту если сделать его слишком большим. В частности, в обработчик не нужно помещать отладочные операторы print. Это, скорее, создаст проблем больше, чем поможет решить.
Разумным будет установить однобайтный флаг, а затем проверять его в основном цикле loop. Или сохранить входящий байт из последовательного порта в буфер. Встроенные прерывания таймера отслеживают время, прошедшее с момента возникновения внутреннего переполнения таймера и, таким образом, вы можете работать с прошедшим временем, зная, сколько раз таймер переполняется.
Напомню, что внутри обработчика все прерывания отключены. Таким образом, надеясь, что время, возвращаемое при вызове функции millis изменится, вы разочаруетесь. Можно получить время этим способом, но нужно помнить, что таймер не увеличивается. Слишком долго находясь в обработчике, таймер может пропустить событие переполнения, что приведет к тому, что время, возвращаемое millis станет неверным.
Тест показал, что для микроконтроллера ATmega328, работающего на частоте 16 МГц, вызов функции micros занимает 3.5625 мкс, вызов millis занимает 1.9375 мкс. Запись (сохранение) текущего значения таймера разумно сделать в обработчике прерывания. Определение числа прошедших миллисекунд быстрее, чем прошедших микросекунд (количество миллисекунд просто извлекается из переменной). Однако количество микросекунд определяется путем добавления текущего значения таймера 0 (хранит увеличение) к сохраненному «Timer 0 overflow count».
Так как прерывания запрещены внутри обработчика и из-за того, что Arduino IDE использует прерывания для чтения и записи данных через Serial порт, а также для увеличения счетчика, использующего millis и delay — не пытайтесь использовать эти функции внутри обработчика прерываний.
Иначе говоря:
- Не пытайтесь использовать функцию delay
- Вы можете получить время, вызвав millis, однако оно не будет увеличиватся
- Не используйте вывод в Serial-порт (например: Serial.println(«ISR entered»);)
- Не пытайтесь читать данные с Serial-порта.
Прерывания по изменению состояния вывода
Существует два способа определить внешние события на пине. Первый — это специальное «внешнее прерывание» выводов D2 и D3. Это внешние дискретные события прерывания, по-одному на пин. Вы можете получить их, используя attachInterrupt для каждого пина. Вы можете определить условия по фронту, спаду, изменению или же низкому уровню для прерывания.
Однако, также существуют прерывания по «изменению пина» для всех выводов (верно для ATmega328). Они действуют на группы выводов: D0-D7, D8-D13, A0-A5. Имеют более низкий приоритет, чем события для внешних прерываний. Можно реализовать обработчик прерываний для обработки изменений на пинах D8-D13 следующим образом:
ISR (PCINT0_vect) { // состояние одного из выводов D8-D13 изменилось }
Очевидно, что необходим дополнительный код для определения того, состояние какого вывода/выводов изменились (например, сравнением с предыдущим значением).
Каждое прерывание по изменению состояния пина имеет связанный байт «маски» в процессоре, так что возможно сконфигурировать их реагировать только, например, на D8, D10 и D12, а не на изменения любого из D8-D13. Однако, по-прежнему нужны дополнительные операции, чтобы выяснить, состояние каких именно выводов изменилось.
Пример прерывания сторожевого таймера
Показать/скрыть код
#include <avr/sleep.h> #include <avr/wdt.h> #define LED 13 // процедура обработки прерывания по нажатию кнопки void wake () { wdt_disable(); // отключаем сторожевой таймер } // прерывание сторожевого таймера ISR (WDT_vect) { wake (); } void myWatchdogEnable (const byte interval) { noInterrupts (); MCUSR = 0; // сбрасываем различные флаги WDTCSR |= 0b00011000; // устанавливаем WDCE, WDE WDTCSR = 0b01000000 | interval; // устанавливаем WDIE, и соответсвующую задержку wdt_reset(); byte adcsra_save = ADCSRA; ADCSRA = 0; // запрещаем работу АЦП power_all_disable (); // выключаем все модули set_sleep_mode (SLEEP_MODE_PWR_DOWN); // устанавливаем режим сна sleep_enable(); attachInterrupt (0, wake, LOW); // позволяем заземлить pin 2 для выхода из сна interrupts (); sleep_cpu (); // переходим в сон и ожидаем прерывание detachInterrupt (0); // останавливаем прерывание LOW ADCSRA = adcsra_save; // останавливаем понижение питания power_all_enable (); // включаем все модули } void setup () { digitalWrite (2, HIGH); // кнопка с подтягивающим резистором } void loop() { pinMode (LED, OUTPUT); digitalWrite (LED, HIGH); delay (5000); digitalWrite (LED, LOW); delay (5000); // шаблон битов sleep: // 1 секунда: 0b000110 // 2 секунды: 0b000111 // 4 секунды: 0b100000 // 8 секунд: 0b100001 // засыпаем на 8 секунд myWatchdogEnable (0b100001); // 8 секунд }
«Голая» плата, то есть без регулятора напряжения, USB-интерфейса и прочего, с запущенным на ней приведенным выше кодом, потребляет:
- Со светящимся светодиодом — 19.5 мА
- При включенном светодиоде — 16.5 мА
- В режиме сна — 6 мкА
Использование сторожевого таймера в комбинации с режимом сна может сберечь значительное количество энергии во время периодов, когда процессор может быть не нужен.
Это также иллюстрирует, что можно выйти из режима сна двумя различными способами. Один из них — нажатие клавиши (то есть заземление пина D2), другой — это периодическое просыпание (каждые 8 секунд), хотя вы можете сделать это каждые 1, 2, 4 или 8 секунд (возможно, даже короче, но для этого нужно подробнее изучить техническое руководство на микроконтроллер).
После того, как процессор проснулся, возможно, потребуется некоторое время, чтобы стабилизировать свой таймер. Например, вы можете увидеть «мусор» на последовательном порту, пока он синхронизируется. Если это проблема есть, вы можете установить короткую задержку после выхода из режима сна.
Замечание по-поводу определения пониженного напряжения: полученные выше цифры получены с отключенным определением пониженного напряжения (brownout detection). Для создания опорного напряжения для определения пониженного напряжения требуется небольшой ток. Если его включить, то в режиме сна будет потребляться около 70 мкА (вместо 6 мкА). Одним из способов выключить определение пониженного напряжения является использование компилятора avrdude из командной строки:
avrdude -c usbtiny -p m328p -U efuse:w:0x07:m
При помощи параметров устанавливается efuse (дополнительные фьюзы) равным 7, что отключает определение пониженного напряжения. Здесь предполагается, что используется программатор USBtinyISP.
Неявное использование прерываний
Окружение Arduino уже использует прерывания, даже если вы персонально и не пытались делать это. При вызове функций millis и micros используется свойство «переполнение таймера». Один из внутренних таймеров (timer 0) настроен на прерывание примерно 1000 раз в секунду, и инкрементирует внутренний счетчик, который эффективно становится счетчиком millis. Небольшим улучшением может стать настройка точного значения тактовой частоты.
Также, аппаратная библиотека для передачи данных через Serial-соединение использует прерывания для обработки входящих и исходящих данных. Это очень полезно, так как ваша программа может делать что-то еще, пока не произойдет прерывание и заполнит внутренний буфер. Затем, когда производится проверка Serial.available, можно выяснить, есть ли что-нибудь в этом буфере.
Выполнение следующей инструкции после разрешения прерываний
Существует три основных способа разрешить прерывания, которые не были до этого включены:
sei (); // установить флаг разрешения прерываний SREG |= 0x80; // установить старший бит в статусном регистре reti ; // использовать ассемблерную инструкцию "return from interrupt"
Во всех случаях, процессор гарантирует, что следующая инструкция после того, как прерывания разрешены (если они были ранее запрещены) всегда выполнится, даже если событие прерывания находится в ожидании. Под «следующей» инструкцией здесь понимается следующая в программной последовательности, а не обязательно следующая в коде.
Это позволяет написать следующий код:
sei (); sleep_cpu ();
Если бы не эта гарантия, прерывание могло бы произойти до того, как процессор уснул, а затем, возможно, он никогда бы не проснулся.
Пустые прерывания
Если вы хотите, чтобы прерывание просто вывело процессор из режима сна, но не делало что-либо конкретно, можно воспользоваться предопределенной функцией EMPTY_INTERRUPT:
EMPTY_INTERRUPT (PCINT1_vect);
Она просто сгенерирует инструкцию reti (вернуться из прерывания). Так как она не пытается сохранить или восстановить регистры, то это будет самым быстрым способом вызвать прерывание, чтобы разбудить процессор.
Вы можете пропустить чтение записи и оставить комментарий. Размещение ссылок запрещено.
-
KingT
Member
- Публикаций:
-
0
- Регистрация:
- 12 янв 2006
- Сообщения:
- 203
Что будет если дать команду cli из под ring0?
Приведет ли это к запрету всех прерываний?
Я тут подумал что так можно повысить производительность программы(процессор после cli не
реагирует на прерывание от таймера и не переключается на другие задачи и даже потоки).
Кроме того помогает точно замерить время выполнения кода или инструкции. -
приведет. на то это и ринг0
-
Ага. Только остаются ещё немаскируемые прерывания
-
nitrotoluol
New Member
- Публикаций:
-
0
- Регистрация:
- 5 сен 2006
- Сообщения:
- 848
TempINT dd 0
;============================
; Запрещаем прерывания
;============================
cli
mov eax,cr0
and eax,0xFFFEFFFF
mov cr0,eax
mov dword ptr [TempINT], eax;============================
; Возвращаем все на место
;============================
mov eax, dword ptr [TempINT]
mov cr0, eax
sti -
Vilco
-
;Запрет прерываний (в том числе NMI)
можт лучче так:
да и при чем wp-bit к запрету прерываний?
-
-
rei3er
maxim
- Публикаций:
-
0
- Регистрация:
- 15 янв 2007
- Сообщения:
- 917
- Адрес:
- minsk
FreeManCPM
а есть еще SMI#, INIT# -
wasm_test
wasm test user
- Публикаций:
-
0
- Регистрация:
- 24 ноя 2006
- Сообщения:
- 5.582
попробуй KfRaiseIrql( HIGH_LEVEL )
-
SII
Воин против дзена
Вообще-то NMI и прочие «необычные» прерывания особого резона запрещать нет: они возникают только в очень специфических условиях, а не при нормальном функционировании компутера.
-
KingT
Member
- Публикаций:
-
0
- Регистрация:
- 12 янв 2006
- Сообщения:
- 203
Йа вот тут такую фишку придумал:пишем драйвер который предоставляет возможность запрещать и разрешать прерывания.
Затем в риг3 посылаем IOCTL драйверу,он запрещает прерывания наш код выполняется с повышенной производительностью и затем через дпрайвер разрешаем прерывания. -
SII
Воин против дзена
KingT
Только эта «фишка» позволит программе пользовательского режима нарушить работу всей системы в целом. Например, она запретит прерывания и благополучно зависнет. Что делать-то будете? Только ресет или отключение питания и остаётся
-
KingT
Member
- Публикаций:
-
0
- Регистрация:
- 12 янв 2006
- Сообщения:
- 203
А почему,если там нечему будет зависать.
-
SII
Воин против дзена
KingT
Если программа серьёзная, то доказать её правильность, а значит, и невозможность нештатного поведения, невозможно. А поднимать производительность вычислителя квадратных уравнений нет смысла.
Кстати, если полностью запретить прерывания, станет невозможно параллельно слушать музыку, записывать СД/ДВД и т.д. — в общем, делать всё то, что требует быстрой реакции системы на прерывания.
-
wasm_test
wasm test user
- Публикаций:
-
0
- Регистрация:
- 24 ноя 2006
- Сообщения:
- 5.582
И будет это страшно глючным..
Если запретить прерывания через cli, то контекст все равно будет принудительно переключен при вызове некоторых винапи, преимущественно ожидающих (WaitForSingleObject, …), т.к. поток при этом отправляется в State==Waiting и система найдет следующий. При этом прерывания, насколько я помню, разрешатся и фсио)
а если ты irql поднимешь выше apc level, вообще скорее всего всё закончится BSoD’ом =)Вообще, ринг3 приложения не должны иметь возможность прибить работу всей системы. Это неправильно
-
Deyton
Member
- Публикаций:
-
0
- Регистрация:
- 7 мар 2007
- Сообщения:
- 94
Прерывания существуют не для того, чтобы их запрещать. Запрет прерываний — это КРАЙНОСТИ, которые нужны только в строго определенных случаях. Запрещать прерывания длительное время НЕЛЬЗЯ. В системе есть куча железок, запросы которых должны быть вовремя обработанны процессором, иначе это приведет к деградации и крушению системы.
Например, про SpinLock-и в MSDN написанно:
Запрета прерываний это еще больше касается.
-
wasm_test
wasm test user
- Публикаций:
-
0
- Регистрация:
- 24 ноя 2006
- Сообщения:
- 5.582
все на самом деле не так ужасно, но соглашусь, что не стоит надолго их запрещать
Форум РадиоКот :: Просмотр темы — Когда надо запрещать прерывания?
Автор: | FeCat [ Вт дек 02, 2014 13:36:38 ] |
Заголовок сообщения: | Когда надо запрещать прерывания? |
Программирую 8 битные AVR на C/C++, ассемблера не знаю, объясните пожалуйста, когда в программе надо запрещать прерывания? Надо ли запрещать при сложной для микроконтроллера математике пусть и в рамках одно байтовых чисел? К примеру, умножение, возведение в степень и т.д. А если она приводит к потери части результата из-за того, что он не помещается в 8 бит? |
Автор: | ARV [ Вт дек 02, 2014 14:13:52 ] |
Заголовок сообщения: | Re: Когда надо запрещать прерывания? |
FeCat писал(а): прерывания должны быть запрещены при: FeCat писал(а): Обращении к двухбайтовым регистрам 8 бит. нет, если работа с такими регистрами делается правильно. компилятор, как правило, это делает правильно. FeCat писал(а): Любой работе с переменными больше 1 байта нет, не любой, а только той, которая может быть изменена обработчиком прерывания FeCat писал(а): В обработчике прерывания, не стоит без особой необходимости снимать запрет на обработку прерывания да. только надо четко понимать особую необходимость, потому что криминала в разрешении прерываний большого нет. |
Автор: | FeCat [ Вт дек 02, 2014 15:22:41 ] |
Заголовок сообщения: | Re: Когда надо запрещать прерывания? |
Если, обрабатываются данные, не связаные с данными обрабатываемыми в прерывании, то прерывание их “нашинковать” не может (если в прерывании сохраняется, а потом восстанавливается SREG)? |
Автор: | pyzhman [ Вт дек 02, 2014 16:08:17 ] |
Заголовок сообщения: | Re: Когда надо запрещать прерывания? |
Си-компилятор сам всё сделает. Единственный момент — запрещать прерывания, когда идет обработка времязависимого протокола. Иначе врезавшийся обработчик прерывания съест драгоценные доли секунд, и приемник/передатчик будут тупо ожидать — «ну когда же передатчик/приёмник ответит?!». |
Автор: | ARV [ Вт дек 02, 2014 18:47:34 ] |
Заголовок сообщения: | Re: Когда надо запрещать прерывания? |
прерывания необходимо запрещать в следующих случаях: вроде бы все варианты перечислил… только вариант 1 действительно критически важен, остальные могут быть обойдены грамотным проектированием алгоритма, т.е. возможно обойтись и без запрета, если думать тщательно и долго |
Страница 1 из 1 | Часовой пояс: UTC + 3 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |
Запрещение и разрешение прерываний
Запрещение и разрешение прерываний
Для локального запрещения прерываний на текущем процессоре (и только на текущем процессоре) и последующего разрешения можно использовать следующий код.
local_irq_disable();
/* прерывания запрещены … */
local_irq_enable();
Эти функции обычно реализуются в виде одной инструкции на языке ассемблера (что, конечно, зависит от аппаратной платформы). Для платформы x86 функция local_irq_disable() — это просто машинная инструкция cli, а функция local_irq_enable() — просто инструкция sti. Для хакеров, не знакомых с платформой x86, sti и cli — это ассемблерные вызовы, которые соответственно позволяют установить (set) или очистить (clear) флаг разрешения прерываний (allow interrupt flag). Другими словами, они разрешают или запрещают доставку прерываний на вызвавшем их процессоре.
Функция local_irq_disable() является опасной в случае, когда перед ее вызовом прерывания уже были запрещены. При этом соответствующий ей вызов функции local_irq_enable() разрешит прерывания независимо от того, были они запрещены первоначально (до вызова local_irq_disable()) или нет. Для того чтобы избежать такой ситуации, необходим механизм, который позволяет восстанавливать состояние системы обработки прерывании в первоначальное значение. Это требование имеет общий характер, потому что некоторый участок кода ядра в одном случае может выполняться при разрешенных прерываниях, а в другом случае— при запрещенных, в зависимости от последовательности вызовов функций. Например, пусть показанный фрагмент кода является частью функции. Эта функция вызывается двумя другими функциями, и в первом случае перед вызовом прерывания запрещаются, а во втором — нет. Так как при увеличении объема кода ядра становится сложно отслеживать все возможные варианты вызова функции, значительно безопаснее сохранять состояние системы прерываний перед тем, как запрещать прерывания. Вместо разрешения прерываний просто восстанавливается первоначальное состояние системы обработки прерываний следующим образом.
unsigned long flags;
local_irq_save(flags);
/* прерывания запрещены . . */
local_irq_restore(flags);
/* состояние системы прерываний восстановлено
в первоначальное значение … */
Нужно заметить, что эти функции являются макросами, поэтому передача параметра flags выглядит как передача по значению. Этот параметр содержит зависящие от аппаратной платформы данные, которые в свою очередь содержат состояние системы прерываний. Так как, по крайней мере для одной аппаратной платформы (SPARC), в этой переменной хранится информация о стеке, то параметр flags нельзя передавать в другие функции (другими словами, он должен оставаться в одном и том же стековом фрейме). По этой причине вызовы функций сохранения и восстановления должны выполняться в теле одной функции.
Все описанные функции могут вызываться как из обработчика прерываний, так и из контекста процесса.
Больше нет глобального вызова cli()
Ранее ядро предоставляло функцию, с помощью которой можно было запретить прерывания на всех процессорах системы. Более того, если какой-либо процессор вызывал эту функцию, то он должен был ждать, пока прерывания не будут разрешены. Эта функция называлась cli(), а соответствующая ей разрешающая функция — sti(); очень «x86-центрично» (хотя и было доступно для всех аппаратных платформ). Эти интерфейсы были изъяты во время разработки ядер серии 2.5, и, следовательно, все операции по синхронизации обработки прерываний должны использовать комбинацию функций по управлению локальными прерываниями и функций работы со спин-блокировками (обсуждаются в главе 9, «Средства синхронизации в ядре»). Это означает, что код, который ранее должен был всего лишь глобально запретить прерывания для того, чтобы получить монопольный доступ к совместно используемым данным, теперь должен выполнить несколько больше работы.
Ранее разработчики драйверов могли считать, что если в их обработчике прерывания и в любом другом коде, который имеет доступ к совместно используемым данным, вызывается функция cli(), то это позволяет получить монопольный доступ. Функция cli() позволяла гарантировать, что ни один из обработчиков прерываний (в том числе и другие экземпляры текущего обработчика) не выполняется. Более того, если любой другой процессор входит в участок кода, защищенный с помощью функции cli(), то он не продолжит работу, пока первый процессор не выйдет из участка кода, защищенного с помощью функции cli(), т.е. не вызовет функцию sti().
Изъятие глобальной функции cli() имеет несколько преимуществ. Во-первых, это подталкивает разработчиков драйверов к использованию настоящих блокировок. Специальные блокировки на уровне мелких структурных единиц работают быстрее, чем глобальные блокировки, к которым относится и функция cli(). Во-вторых, это упрощает значительную часть кода и позволяет удалить большой объем кода. В результате система обработки прерываний стала проще и понятнее.
Читайте также
Обработчики прерываний
Обработчики прерываний
Функция, которую выполняет ядро в ответ на определенное прерывание, называется обработчиком прерывания (interrupt handler) или подпрограммой обслуживания прерывания (interrupt service routine). Каждому устройству, которое генерирует прерывания, соответствует свой
Запрещение определенной линии прерывания
Запрещение определенной линии прерывания
В предыдущем разделе были рассмотрены функции, которые позволяют запретить доставку всех прерываний на определенном процессоре. В некоторых случаях полезным может оказаться запрещение определенной линии прерывания во всей
Запрещение обработки нижних половин
Запрещение обработки нижних половин
Обычно только одного запрещения обработки нижних половин недостаточно. Наиболее часто, чтобы полностью защитить совместно используемые данные, необходимо захватить блокировку и запретить обработку нижних половин. Методы, которые
Обработчик прерываний таймера
Обработчик прерываний таймера
Теперь, когда мы разобрались, что такое jiffies и HZ, а также какова роль системного таймера, рассмотрим реализацию обработчика прерываний системного таймера. Обработчик прерываний таймера разбит на две части: часть, зависимую от аппаратной
Написание обработчиков прерываний
Написание обработчиков прерываний
Давайте посмотрим, как настроить обработчики прерываний — вызовы, характеристики и кое-какие стратегии
Подключение обработчиков прерываний
Подключение обработчиков прерываний
Для подключения к источнику прерывания воспользуйтесь функцией InterruptAttach() или InterruptAttachEvent().#include <sys/neutrino.h>int InterruptAttachEvent(int intr, const struct sigevent *event, unsigned flags);int InterruptAttach(int intr, const struct sigevent* (*handler)(void *area, int id), const void *area, int size, unsigned flags);Параметр
Обработчики прерываний
Обработчики прерываний
Обработчики прерываний в QNX4 могли либо возвратить идентификатор прокси (указывая этим, что надо переключить прокси и таким образом уведомить ее владельца о прерывании), либо возвратить нуль (что означало бы, что в дальнейшем ничего делать не
Практическое запрещение кэширования
Практическое запрещение кэширования
Запретить кэширование можно и прямо из конфигурации Apache (подробная конфигурация для оптимальной производительности приводится в восьмой главе). Для этого нам нужны следующие строки:# Проверяем, что подключен mod_headers# Тогда выставляем
Обработка прерываний таймера
Обработка прерываний таймера
Каждый компьютер имеет аппаратный таймер или системные часы, которые генерируют аппаратное прерывание через фиксированные интервалы времени. Временной интервал между соседними прерываниями называется тиком процессора или просто тиком (CPU
10.1.3 Программы обработки прерываний
10.1.3 Программы обработки прерываний
Как уже говорилось выше (раздел 6.4.1), возникновение прерывания побуждает ядро запускать программу обработки прерываний, в основе алгоритма которой лежит соотношение между устройством, вызвавшим прерывание, и смещением в таблице
Запрещение использования сеансов MS-DOS и однозадачного режима MS-DOS (Windows 98)
Запрещение использования сеансов MS-DOS и однозадачного режима MS-DOS (Windows 98)
Windows позволяет открывать окно ms-dos и выполнять старые приложения ms-dos. Эта команда доступна в подменю Программы меню Пуск. Для запрета использования сеансов ms-dos откройте (создайте) раздел
Запрещение запуска программ
Запрещение запуска программ
Windows позволяет ограничить доступ к программам, кроме разрешенных в специальном списке.Для ограничения запускаемых программ надо открыть раздел HKEY_CURRENT_USERSOFTWAREMicrosoftWindowsCurrentVersonPoliciesExplorer и создать там ключ RestrictRun типа DWORD со значением 0х00000001. Затем
Запрещение запуска редактора реестра
Запрещение запуска редактора реестра
Вы можете запретить запуск редактор реестраДля этого в разделе HKEY_CURRENT_USERSOFTWAREMicrosoftWindowsCurrentVersonPoliciesSystem нужно добавить ключ DisableRegistryTools со значением 0х00000001 типа DWORD. Запуск редактора реестра будет запрещен, однако останется возможность
Запрещение/Разрешения на запуск программ
Запрещение/Разрешения на запуск программ
Запрещение запуска программWindows позволяет ограничить доступ к программам, кроме разрешенных в специальном списке.Для ограничения запускаемых программ надо открыть раздел HKCUSOFTWAREMicrosoftWindowsCurrentVersonPoliciesExplorerи создать там ключ
Обработчики прерываний
Обработчики прерываний
Везде, кроме последней главы, все, что мы пока делали в ядре, сводилось к запросам и ответам разным процессам или работали со специальными файлом, посылали ioctl или выдавали системный вызов. Но работа ядра не должна сводится только к обработке
Перенаправление прерываний
Перенаправление прерываний
Начнем с попытки устранить самое неприятное из возможных прерываний: к вам обращаются с проблемой, которую должен решить кто-то другой. Может быть, поступим так:— Том, проблема с веб-сервером.— Отлично! Сообщи мне о результатах, когда
0 / 0 / 0 Регистрация: 19.08.2014 Сообщений: 430 |
|
1 |
|
Запрет всех прерываний кроме одного.21.01.2016, 10:09. Показов 6401. Ответов 8
Привет.
0 |
1 / 1 / 0 Регистрация: 05.10.2017 Сообщений: 2,048 |
|
21.01.2016, 10:40 |
2 |
Почему бы этому прерыванию просто не назначить более высокий приоритет чем у остальных прерываний? Почитайте про NVIC.
0 |
0 / 0 / 0 Регистрация: 11.10.2013 Сообщений: 326 |
|
21.01.2016, 10:50 |
3 |
Пример назначения первого приоритета от прерывания по таймеру. Код NVIC_EnableIRQ(TIM3_IRQn); // Прерывание от TIM3 - разрешаем NVIC_SetPriority(TIM3_IRQn,0x01); // Приоритет 01
0 |
0 / 0 / 0 Регистрация: 19.08.2014 Сообщений: 430 |
|
21.01.2016, 11:05 |
4 |
Я поставил высокий уровень прерывания прямо в кубе у всех нулики а ему поставил 1. Но все равно болтает не нормированное время перехода на вектор.
0 |
0 / 0 / 0 Регистрация: 24.02.2010 Сообщений: 804 |
|
21.01.2016, 11:07 |
5 |
Я поставил высокий уровень прерывания прямо в кубе у всех нулики а ему поставил 1. Но все равно болтает не нормированное время перехода на вектор. Насколько я помню, приоритеты наоборот считаются, 0 — высокий, 1 — ниже, и т.д.
0 |
0 / 0 / 0 Регистрация: 19.08.2014 Сообщений: 430 |
|
21.01.2016, 11:14 |
6 |
Разве ? Вроде в верх всегда были.
0 |
1 / 1 / 0 Регистрация: 18.01.2012 Сообщений: 1,418 |
|
21.01.2016, 13:20 |
7 |
Разве ? Вроде в верх всегда были. Ага. У стм чем ниже число, тем выше приоритет.
0 |
0 / 0 / 0 Регистрация: 24.02.2010 Сообщений: 804 |
|
21.01.2016, 17:40 |
8 |
Ну и потом, как у вас определена маска, по которой определяется Preemption- и Sub-Priority? А то мож у вас и нет вовсе Sub-Priority?
0 |
0 / 0 / 0 Регистрация: 19.08.2014 Сообщений: 430 |
|
03.02.2016, 17:29 |
9 |
Поставил максимальный приоритет в кубе. Но все равно время выхода на вектор прерывания разное. На фото показан принимаемый сигнал с компаратора. На вход компаратора подается гармонический сигнал частотой примерно 5 МГц. Мне нужно отловить растущий фронт первого импульса все остальные для меня не важны, важен только первый. И в обработчики прерываний записать число до которого досчитал счетчик. А тут разное время выхода на вектор прерывания то 250 нс то 1 мкс и вот получается сам контролер создает ошибку почета времени прохождения сигнала. На фото Ком. компаратор, В.П-вектор прерывания. Подскажите в чем может быть дело? Вот ссылка на видио https://yody.sk/i/VW01RysxoPxzx <Изображение удалено>
0 |
IT_Exp Эксперт 87844 / 49110 / 22898 Регистрация: 17.06.2006 Сообщений: 92,604 |
03.02.2016, 17:29 |
9 |