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

Широкоимпульсная модуляция

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

Аппаратные соединения для демонстрационного приложения LED PWM

Рис. 4.14. Аппаратные соединения для демонстрационного приложения LED PWM
Дребезг кнопки

Механические переключатели имеют неизбежный недостаток, известный как дребезг.

Название выдает его: когда вы нажимаете на кнопку, вы заставляете две металлические части соприкасаться, и это действие кажется мгновенным, но это не так. После первоначального удара кусочки металла много раз подпрыгивают, могут тереться друг о друга, а через некоторое время их контакт стабилизируется. То же самое происходит, когда вы отпускаете кнопку.

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

Как это может произойти, если отскок происходит так быстро? Ну, для нас, людей, это происходит быстро. Паразитные импульсы непостоянного переключателя происходят в течение порядка миллисекунд, но цифровая система может воспринимать изменения в течение порядка наносекунд. Это на 6 порядков быстрее! Другими словами, цифровая схема может реагировать на один миллион импульсов, когда переключатель испытывает дребезг. Мгновение для нас - это вечность для компьютера.

Наше ШИМ-приложение страдает от дребезга дважды, потому что у него 2 кнопки, и поскольку оно имеет то же входное оборудование, что и наше приложение GPIO, у этого приложения тоже есть дребезг. Однако обнаружить дребезг в нашем приложении GPIO невозможно, потому что оно использует одну кнопку для включения светодиода, а другую - для его выключения. Вы не можете включить или выключить светодиод "сильнее". В нашем приложении PWM нажатие кнопки "Яркость вниз", когда яркость светодиода составляет 50%, может привести к полному выключению светодиода. Почему? Из-за неповоротливой кнопки.

Избавление от дребезга кнопок

Существует множество подходов к развязке выключателя. Здесь представлены два наиболее популярных:

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

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

Мы реализуем программное избавление от дребезга в нашем ШИМ-приложении. Встроенный светодиод будет мигать в течение периода дребезга при нажатии кнопки, как показано ниже:

while(1){
  if(Red_V_read_pin(0) == 0){      // Считываем входной пин 0
    Red_V_set_pin(5);              // Включаем светодиод
    /* Полезный код здесь */      // Реагируем на нажатие кнопки
    delay(300);                    // Избегаем дребезг
    Red_V_clear_pin(5);            // Выключаем светодиод
    while(Red_V_read_pin(0) == 0); // Ждем, пока кнопка будет отпущена
    delay(300);                    // Избегаем дребезг
  }
  else if(Red_V_read_pin(1) == 0){ // Читаем входной пин 1
    Red_V_set_pin(5);              // Включаем светодиод
    /* Полезный код здесь */    // Реагируем на нажатие кнопки
    delay(300);                    // Избегаем дребезг
    Red_V_clear_pin(5);            // Выключаем светодиод
    while(Red_V_read_pin(1) == 0); // Ждем, пока кнопка будет отпущена
    delay(300);                    // Избегаем дребезг
  }
}

Итак, функция задержки - это быстрый и грязный цикл потери времени, который работает с произвольными единицами времени. Задержка в 300 единиц, использованная выше, была выбрана экспериментально для достижения задержки около 50 миллисекунд

void delay(int t){
      volatile uint32_t x, y;
      x = t;
      while(x--){
            y = x;
            while(y--);
      }
}

Если вы чувствуете, что с этой функцией задержки что-то не так, то вы угадали. Потеря времени не будет оптимизирована из-за квалификатора volatile, поэтому она действительно будет терять время пропорционально аргументу. Однако эта техника сильно зависит от настроек компилятора, поэтому она может привести к разным задержкам для конфигураций debug и release, не говоря уже об использовании других цепочек компиляторов.

Существует множество более подходящих способов сделать функцию задержки с помощью микроконтроллера. Например, мы можем использовать таймер, такой как модуль ШИМ, или часы реального времени. На самом деле, мы видели довольно хорошую функцию задержки в проекте Blinky. Если вы хотите провести эксперимент, замените эту быструю и грязную функцию задержки на функцию из проекта Blinky, используя задержку в 50 миллисекунд (это аргумент в 50 000), и не забудьте #include <time.h>.

Чтобы узнать больше о развязывающих переключателях, вы можете прочитать эту статью от Digi-Key Electronics.

Демонстрационный код приложения для управления яркостью светодиодов

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

Бесконечный цикл просто проверяет значение входных контактов (деблокированных) и соответственно увеличивает или уменьшает яркость светодиода.

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

/******************************************************************************
    Red-V Thing Plus PWM_Demo,
    by Eduardo Corpe?o
    Controlling the brightness of the built-in LED with two external buttons using PWM.
    Date: June 20, 2022
    Developed using Freedom Studio v4.18.0.2021-04-1 on Windows 10
    LICENSE: This code is released under the CC BY-SA 4.0 License
    (https://creativecommons.org/licenses/by-sa/4.0/) ******************************************************************************/
#include <metal/pwm.h>
#include <metal/gpio.h>
#include <stdint.h> 
// Наши собственные макросы, определенные для эффективности или потому, что 
// Freedom Metal Library не обеспечила их функциональность.
#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 t){
     volatile uint32_t x, y;
     x=t;
     while(x--){
           y=x;
           while(y--);
     }
}
int main (void) {
  unsigned int dc = 50;        // Рабочий цикл по умолчанию 50%
  struct metal_gpio *gpio_0;   // Экземпляр GPIO
  struct metal_pwm *pwm_1;     // Экземпляр ШИМ
  // Конфигурация GPIO для встроенного светодиода и кнопок

  gpio_0 = metal_gpio_get_device(0);     // Получение экземпляра устройства GPIO
  // Светодиод
  metal_gpio_disable_input(gpio_0, 5);   // Отключить ввод 5
  Red_V_enable_output(5);                // Включить вывод 5
  metal_gpio_disable_pinmux(gpio_0, 5);  // Отключить альтернативные функции для пина 5
  // Нажатие кнопок
  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
  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);                 // Запуск в непрерывном режиме
  while(1){
          if(Red_V_read_pin(0) == 0){    // Считываем входной пин 0
            Red_V_set_pin(5);            // Включаем светодиод
            dc = (dc>0)? dc - 10 : dc;   // Шаг 10% для яркости
            metal_pwm_set_duty(pwm_1, 1, dc, METAL_PWM_PHASE_CORRECT_DISABLE);
            delay(300);                   // Избегаем дребезг
            Red_V_clear_pin(5);           // Выключаем светодиод
            while(Red_V_read_pin(0) == 0);// Ждем, пока кнопка будет отпущена
            delay(300);                   // Избегаем дребезг
          }
          else if(Red_V_read_pin(1) == 0){ // Считываем входной пин 1
            Red_V_set_pin(5);              // Включаем светодиод
            dc = (dc<100)? dc + 10 : dc;  // Шаг 10% для яркости
            metal_pwm_set_duty(pwm_1, 1, dc, METAL_PWM_PHASE_CORRECT_DISABLE);
            delay(300);                    // Избегаем дребезг
            Red_V_clear_pin(5);            // Выключаем светодиод
            while(Red_V_read_pin(1) == 0); // Ждем, пока кнопка будет отпущена
            delay(300);                    // Избегаем дребезг
          }
  }
  return 0; // Недостижимый код возврата
}