Опубликован: 24.11.2024 | Доступ: свободный | Студентов: 2 / 0 | Длительность: 05:47:00
Лекция 6:

Прерываемые приложения

Хронология исполнения кода приложения приборной панели при использовании подхода опроса

Рис. 5.3. Хронология исполнения кода приложения приборной панели при использовании подхода опроса

Теперь давайте рассмотрим альтернативу, управляемую прерываниями.

void touch_screen_ISR();  // 100ms
void GPS_ISR();           // 300ms
void fuel_level_ISR();    // 200ms
void velocity_ISR();      // 200ms
void temperature_ISR();   // 100ms
void door_light_ISR();    // 100ms
void main(){
  while(1)
    refresh_display();         // 200ms
}

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

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

Здесь мы имеем возможную временную шкалу примера с прерыванием:

Хронология исполнения кода приложения приборной панели при использовании прерываний

Рис. 5.4. Хронология исполнения кода приложения приборной панели при использовании прерываний

Поскольку запросы ввода-вывода происходят нечасто, основной цикл может работать без сбоев.

Объединение двух приложений

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

Простым примером для иллюстрации этого является объединение двух приложений, которые мы уже видели ранее: Проект Blinky и Демонстрация управления яркостью светодиодов. Исходный код этих приложений будет объединен в один файл C. Это объединенное приложение будет использовать те же входные переключатели и светодиоды, что и демонстрация ШИМ. Вот принципиальная схема:

Принципиальная схема демонстрационного приложения, использующего метод опроса

Рис. 5.5. Принципиальная схема демонстрационного приложения, использующего метод опроса

Кнопка 0 уменьшит яркость внешнего светодиода на ступень, а кнопка 1 увеличит ее на ступень.

На этот раз внешний светодиод будет иметь 5 уровней яркости, увеличивающихся с шагом в 25%, поэтому он также будет переходить от 0% к 100%. Тем временем, встроенный светодиод будет мигать с частотой 1 Гц независимо.

Это контакты ввода/вывода, которые мы будем использовать для подключения кнопок и внешнего светодиода:

Виды сверху и снизу Red-V Thing Plus, показывающие контакты, которые будут использоваться в демонстрационном приложении для опроса (Изображение взято с сайта SparkFun Electronics, по лицензии CC BY 2.0)

Рис. 5.6. Виды сверху и снизу Red-V Thing Plus, показывающие контакты, которые будут использоваться в демонстрационном приложении для опроса (Изображение взято с сайта SparkFun Electronics, по лицензии CC BY 2.0)

Здесь представлены две фотографии фактических аппаратных соединений:

Аппаратные соединения для приложения управления светодиодом, основанном на методе опроса

Рис. 5.7. Аппаратные соединения для приложения управления светодиодом, основанном на методе опроса

Комбинированный код приложения GPIO

Наша первая попытка использует простую адаптацию кода на языке C для обоих приложений, используя подход опроса.

Здесь приведен код приложения для опроса светодиодов. Если вы следите за развитием событий, создайте новый проект на основе проекта примера hello, как обычно, и скопируйте следующий код, чтобы заменить содержимое файла hello.c.

/******************************************************************************
     Red-V Thing Plus LED_Polling demo,
     by Eduardo Corpe?o
     Using the built-in LED and two external buttons to control its brightness,
     while the on-board LED blinks at 1Hz independently.
     Date: June 20, 2022
     Developed using Freedom Studio v4.18.0.2021-04-1 on Windows 10
     LICENSE: This code is released under the MIT License
     (http://opensource.org/licenses/MIT)
******************************************************************************/
#include <metal/gpio.h>
#include <metal/pwm.h>
#include <stdint.h>
#include <time.h>

#define Red_V_enable_output(x) * ((uint32_t * ) 0x10012008) |= (1 << (x))
#define Red_V_set_pin(x) * ((uint32_t * ) 0x1001200C) |= (1 << (x))
#define Red_V_clear_pin(x) * ((uint32_t * ) 0x1001200C) &= ~(1 << (x))
#define Red_V_read_pin(x)( * ((uint32_t * ) 0x10012000) & (1 << (x))))
#define Red_V_enable_pullup(x) * ((uint32_t * ) 0x10012010) |= (1 << (x))
#define Red_V_enable_DS(x) * ((uint32_t * ) 0x10012014) |= (1 << (x))
#define Red_V_enable_XOR(x) * ((uint32_t * ) 0x10012040) |= (1 << (x))

// Пользовательская функция задержки 
void delay(int number_of_microseconds) {
  clock_t start_time = clock(); // Хранение времени запуска 
  while (clock() < start_time + number_of_microseconds);
}

int main(void) {
  unsigned int dc = 50; // По умолчанию рабочий цикл равен 50% 
  struct metal_gpio * gpio_0; // Экземпляр GPIO 
  struct metal_pwm * pwm_1; // Экземпляр PWM
  gpio_0 = metal_gpio_get_device(0); 
  // Конфигурация встроенного светодиода
  metal_gpio_disable_input(gpio_0, 5);
  Red_V_enable_output(5);
  metal_gpio_disable_pinmux(gpio_0, 5);
  metal_gpio_set_pin(gpio_0, 5, 1);
  // Конфигурация кнопок 
  metal_gpio_enable_input(gpio_0, 0); // включить ввод 0 
  metal_gpio_enable_input(gpio_0, 1); // включить ввод 1
  metal_gpio_disable_output(gpio_0, 0); // отключить вывод 0 
  metal_gpio_disable_output(gpio_0, 1); // отключить вывод 1
  metal_gpio_disable_pinmux(gpio_0, 0); // отключить альтернативные функции для пина 0 
  metal_gpio_disable_pinmux(gpio_0, 1); // // отключить альтернативные функции для пина 1
  Red_V_enable_pullup(0); // включить подтягивающий резистор для пина 0 
  Red_V_enable_pullup(1); // включить подтягивающий резистор для пина 1
  // Конфигурация ШИМ. Мы будем использовать PWM1_1, который использует GPIO0_19.   
  Red_V_enable_DS(19); // Сила высокого выхода 
  Red_V_enable_XOR(19); // Инвертируем выход для активно-высокого ШИМа
  pwm_1 = metal_pwm_get_device(1); // Получение экземпляра ШИМ-устройства 
  while (pwm_1 == NULL); // Остановка.Что-то пошло не так 
  metal_pwm_enable(pwm_1); // Включить ШИМ1 
  metal_gpio_disable_pinmux(gpio_0, 20); // Отключить альтернативные функции для пина 20 
  metal_gpio_disable_pinmux(gpio_0, 21); // Отключить альтернативные функции для пина 21 
  metal_gpio_disable_pinmux(gpio_0, 22); // Отключить альтернативные функции для пина 22
  // Установите частоту ШИМ1 на 1 кГц 
  metal_pwm_set_freq(pwm_1, 1, 1000); // Установите частоту ШИМ1_1 на 1 кГц для светодиода
  metal_pwm_set_duty(pwm_1, 1, dc, METAL_PWM_PHASE_CORRECT_DISABLE); // Устанавливаем рабочий цикл для ШИМ1_1 
  metal_pwm_trigger(pwm_1, 1, METAL_PWM_CONTINUOUS);
  // Запуск в непрерывном режиме
  while (1) {
    // Кнопки и код ШИМ внешнего светодиода 
    if (Red_V_read_pin(0) == 0) { // Чтение входного пина 0 
      dc = (dc > 0) ? dc - 25 : dc; // Шаги по 25% для светодиода 
      metal_pwm_set_duty(pwm_1, 1, dc,
        METAL_PWM_PHASE_CORRECT_DISABLE);
      delay(50000); // Программное ожидание
      while (Red_V_read_pin(0) == 0); // Ждем, пока кнопка не будет отпущена 
      delay(50000); // Программное ожидание
    } else if (Red_V_read_pin(1) == 0) { // Считывание входного пина 1 
      dc = (dc < 100) ? dc + 25 : dc; // Шаги по 25% для светодиода 
      metal_pwm_set_duty(pwm_1, 1, dc, METAL_PWM_PHASE_CORRECT_DISABLE);
      delay(50000); // Программное ожидание
      while (Red_V_read_pin(1) == 0); // Ждем, пока кнопка будет отпущена 
      delay(50000); // Программное ожидание
    }
    // Код мигающего светодиода
    Red_V_clear_pin(5); // Выключение встроенного светодиода 
    delay(500000); // 500 мс в микросекундах 
    Red_V_set_pin(5); // Включение встроенного светодиода 
    delay(500000); // 500 мс в микросекундах 
  }
  return 0; // Недостижимый код 
}