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

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

Код приложения "Мигание и яркость", управляемого прерыванием

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

Запустите приложение, чтобы убедиться, что оно работает правильно.

/******************************************************************************
          Red-V Thing Plus LED_Interrupts demo,
     автор Эдуардо Корпеньо
     Использование встроенного светодиода и двух внешних кнопок для управления его яркостью путем опроса, в то время как встроенный светодиод мигает с частотой 1 Гц независимо с помощью прерываний.
     Дата: 20 июня 2022 г.
     Разработано с использованием Freedom Studio v4.18.0.2021-04-1 на Windows 10
     ЛИЦЕНЗИЯ: Этот код выпущен под лицензией MIT License
     (http://opensource.org/licenses/MIT)
******************************************************************************/
#include <metal/gpio.h>
#include <metal/pwm.h>
#include <metal.csr.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))
#define Red_V_PWM2_1_set_value(x) *((uint32_t *) 0x10035024) = (x)
#define Red_V_PWM2_sticky() *((uint32_t *) 0x10035000) |= (1< <8)
#define Red_V_PWM2_clear_flag(x) *((uint32_t *) 0x10035000) &= ~(1< < ((x)+28))
#define Red_V_PLIC_PWM2_set_priority(ch,p) *((uint32_t *) (0x0C0000C0+4*(ch))) = (p)
#define Red_V_PLIC_clear_ie() *((uint32_t *) 0x0C002000) = 0;\
                                           *((uint32_t *) 0x0C002004) = 0
#define Red_V_PLIC_set_ie1(x) *((uint32_t *) 0x0C002000) |= (1< < (x))
#define Red_V_PLIC_set_ie2(x) *((uint32_t *) 0x0C002004) |= (1< < (x))
#define Red_V_PLIC_claim *((uint32_t *) 0x0C200004)
void pwm2_isr(void) __attribute__((interrupt, aligned(64))));
void pwm2_isr(){
      static uint32_t count = 0;
      uint32_t plic_id;
      count++;
    // Мигающий код
    if(count == 500)
        Red_V_clear_pin(5); // Выключите бортовой светодиод
    if(count == 1000){
      Red_V_set_pin(5); // Включите бортовой светодиод
      count = 0;
    }
    Red_V_PWM2_clear_flag(0); // Очистить флаг PWM2_0
    plic_id = Red_V_PLIC_claim; // Уровень 2: заявить о прерывании GPIO
    Red_V_PLIC_claim = plic_id;
}
// Пользовательская функция задержки
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_2; // Экземпляр PWM
  struct metal_pwm *pwm_1; // Экземпляр ШИМ
  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); // Устанавливаем рабочий цикл для PWM1_1
  metal_pwm_trigger(pwm_1, 1, METAL_PWM_CONTINUOUS); // Запуск в непрерывном режиме
  // Конфигурация ШИМ. Мы будем использовать PWM2 в качестве периодического прерывания.
  pwm_2 = metal_pwm_get_device(2); // Получение экземпляра ШИМ-устройства
  while (pwm_2 == NULL); // Остановка. Что-то пошло не так
  metal_pwm_enable(pwm_2); // Включить PWM0
  metal_gpio_disable_pinmux(gpio_0, 10); // Отключить альтернативные функции для пина 20
  metal_gpio_disable_pinmux(gpio_0, 11); // Отключить альтернативные функции для вывода 21
  metal_gpio_disable_pinmux(gpio_0, 12); // Отключение альтернативных функций для вывода 22
  metal_gpio_disable_pinmux(gpio_0, 13); // Отключение альтернативных функций для вывода 22
  // Установите частоту ШИМ2 на 1 кГц
  metal_pwm_set_freq(pwm_2, 1, 1000); // Установите частоту ШИМ2_1 на 1 кГц для светодиода
  metal_pwm_trigger(pwm_2, 1, METAL_PWM_CONTINUOUS); // Запуск в непрерывном режиме
  Red_V_PWM2_sticky();
  Red_V_PWM2_1_set_value(8000);
  // Конфигурация прерываний
  // Уровень 3: нет регистра ie для каналов ШИМ.
  Red_V_PWM2_clear_flag(0); // Очистить флаг PWM2_0
  // Уровень 2: Настройка PLIC для PWM2
  Red_V_PLIC_PWM2_set_priority(0,7); // Канал 0, приоритет 7
  // Уровень 2: PLIC (IE2, бит 16) для PWM2_0
  Red_V_PLIC_clear_ie(); // Отключите все остальные прерывания
  Red_V_PLIC_set_ie2(16); // Включить ШИМ2_0
  // Уровень 1: включение прерываний с помощью MIE в mstatus[3]
  volatile uintptr_t saved_config;
  METAL_CPU_GET_CSR(mstatus,saved_config);
  saved_config |= (0x1U< <3);
  METAL_CPU_SET_CSR(mstatus,saved_config);
  // Уровень 1: Установка базового вектора mtvec
  METAL_CPU_SET_CSR(mtvec,&pwm2_isr);
  // Уровень 1: включение прерываний с помощью MEIE в mie[11]
  METAL_CPU_GET_CSR(mie,saved_config);
  saved_config |= (0x1U< <11);
  METAL_CPU_SET_CSR(mie,saved_config);
  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); // Программная задержка
    }
  }
  return 0; // Недоступный код
}

Подведение итогов лекции

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

Давайте вспомним, что мы рассмотрели в этой лекции:

  • Мы узнали, как работают опрос и обработка прерываний.
  • Мы рассмотрели некоторые аргументы в пользу прерываний по сравнению с опросом при обработке спорадических запросов ввода-вывода.
  • Мы испытали, как задачи опроса могут мешать друг другу до такой степени, что приложение перестает реагировать.
  • Мы рассмотрели типичную организацию аппаратных элементов в контроллере прерываний.
  • Мы видели конкретные аппаратные средства обработки прерываний в микроконтроллере FE310.
  • Мы создали простое приложение GPIO, управляемое прерываниями.
  • Мы исправили наше неисправное приложение "мигание и яркость", превратив его в приложение, управляемое прерываниями.

Не стесняйтесь возвращаться к этому материалу, если в будущем вам понадобится подтянуть знания.