Теперь поговорим о широтно-импульсной модуляции. В этой лекции мы поговорим о генерации сигналов. Сначала мы представим, что такое широтно-импульсная модуляция. Затем мы познакомимся с тем, как работает ШИМ в микроконтроллере FE310. И снова мы увидим аппаратное обеспечение из руководства и программное обеспечение в библиотеке Freedom Metal. Далее мы узнаем, как избежать дребезга кнопки. Дребезг - неизбежная проблема с кнопками, и мы узнаем, как ее решить. И на этом этапе мы получим возможность создать приложение для управления яркостью светодиодов. Затем мы сможем внести в это приложение несколько корректировок, чтобы превратить его в приложение для управления шаговыми сервоприводами.
Итак, давайте повеселимся!
К концу этой лекции, вы должны:
Широтно-импульсная модуляция, или сокращенно ШИМ, - это модное название очень простой техники передачи энергии. Она заключается в управлении количеством мощности, передаваемой на нагрузку с течением времени. Во многих случаях эта передаваемая мощность является средней по времени.
Начнем с мысленного эксперимента: рассмотрим два соседних дома на пляже, которые не заселены, и в каждом из них пока горит только одна лампочка.
Владелец дома А установил таймер, который включает 100-ваттную лампочку на 12 часов, а затем выключает ее на 12 часов каждый день.
Владелец дома Б просто установил 50-ваттную лампочку и держит ее включенной все время.
К концу месяца счет за электроэнергию будет одинаковым для обоих домов, потому что общее потребление энергии будет одинаковым.
Теперь ускорьте эксперимент в меньшем масштабе: представьте на минуту, что вы можете очень быстро включать и выключать выключатель. Насколько быстро? Ну, скажем, вы можете включить светодиод на 500 микросекунд и выключить еще на 500 микросекунд снова и снова, тысячу раз в секунду. Если бы вы могли это сделать, вы бы включали светодиод половину времени и выключали половину времени.
(рис 4.1) Мысленный эксперимент для многократного включения и выключения светодиода
Оказалось, что светодиод будет светить в половину своей полной яркости. Это потому, что каждый раз, когда светодиод включен, он будет излучать свет с полной яркостью, а каждый раз, когда он выключен, он не будет излучать свет. Таким образом, в среднем он излучает свет с половиной своей полной яркости.
К счастью, люди не могут заметить, что светодиод мигает так быстро, поэтому мы склонны просто воспринимать среднюю яркость. Этот эффект известен как постоянство зрения, или сокращенно POV.
Конечно, человек не может управлять переключателем с такой скоростью, но схема может: цифровая схема, такая как микроконтроллер, способна периодически выдавать 5 В в течение 500 микросекунд и 0 В в течение 500 микросекунд. Подача этого сигнала на вышеуказанную пару резистор-светодиод (вместо батарейки и выключателя) позволит добиться того же, что и в нашем эксперименте с выключателем.
Сигнал, который приведет светодиод к уменьшению яркости в два раза, относится к семейству сигналов, известных как прямоугольные волны, которые имеют несколько параметров. Вот параметры, которые нас интересуют:
Амплитуда
Вертикальный размер сигнала (измеряется в вольтах для сигнала напряжения или в амперах для сигнала тока). Он может быть выражен в виде высокого и низкого значений, но в данном типе прямоугольной волны низкое значение равно 0. В нашем примере сигнал будет иметь высокое значение 5 В и низкое значение 0 В.
Период
Горизонтальный размер повторяющейся части волны (измеряется в секундах, миллисекундах или микросекундах). Для нашего примера сигнал имеет период 1 мс.
Частота
Частота повторения сигнала (измеряется в герцах). Она является обратной (взаимной) величиной периода. В нашем примере сигнал имеет частоту 1 кГц.
Ширина импульса
Это длительность высокой части волны (иногда это длительность низкой части, в этом случае делается различие). Этот параметр также является временем, поэтому он измеряется в секундах. В нашем примере сигнал имеет длительность импульса 500us (500 микросекунд).
Рабочий цикл
Отношение ширины импульса к периоду. Это процент, который выражает, сколько процентов от общего периода прямоугольный сигнал остается в состоянии высокого уровня. В нашем примере сигнал имеет рабочий цикл 500us/1,000us = 50%.
Здесь мы имеем 3 сигнала с одинаковым периодом, но разными рабочими циклами:
(рис 4.2) Три периодических сигнала, показывающие рабочие циклы 50%, 75% и 25%, соответственно (Взято с сайта SparkFun Electronics, предоставлено по лицензии CC BY-SA 4.0)
Как уже говорилось, широтно-импульсная модуляция - это модное название чего-то очень простого.
В технике, модуляция - это процесс изменения параметров сигнала для передачи сообщения. Модифицированный сигнал называется несущим, а сообщение - модулирующим сигналом. Возможно, вы знакомы с амплитудной модуляцией и частотной модуляцией - диапазонами AM и FM в радиоприемнике.
Широтно-импульсная модуляция - это процесс изменения ширины импульса прямоугольной волны для передачи определенного процента мощности на нагрузку. В этом случае несущий сигнал - это прямоугольная волна, изменяющаяся в диапазоне от 0 до 1 (например, от 0 до 5 В или от 0 до 3,3 В), модулирующий сигнал - это процент мощности, которую мы хотим передать, а параметр несущего сигнала, который мы изменяем, - это ширина импульса. Поскольку период постоянен, ширина импульса должна иметь длительность между 0 и периодом.
Давайте снова рассмотрим пример ШИМ-сигналов:
(рис 4.3) Три периодических сигнала с рабочими циклами 50%, 75% и 25% соответственно (Взято с сайта SparkFun Electronics, предоставлено по лицензии CC BY-SA 4.0)
Это три случая одного и того же ШИМ-сигнала. Обратите внимание, что период одинаков: все три сигнала достигают высокого уровня в одно и то же время, то есть их длительность (или период) одинаковы.
Если мы подадим эти сигналы на нагрузку (светодиод, двигатель или нагреватель), нагрузка получит дозу общей возможной мощности, эквивалентную рабочему циклу.
Светодиод будет излучать 50%, 75% или 25% от своей полной яркости.
Двигатель будет работать на 50%, 75% или 25% от своей полной скорости.
Нагреватель будет вырабатывать 50%, 75% или 25% своего полного тепла.
Вот и весь ШИМ.
Практически все микроконтроллеры имеют модуль ШИМ. Чаще всего микроконтроллеры поддерживают ШИМ как функцию в модуле таймера. Модули таймера обычно реализуют операции ввода и вывода. Их схема проста:
Таймеры имеют регистр, который работает как счетчик.
Этот счетчик увеличивает свое значение в каждом цикле входного тактового сигнала.
Этот тактовый сигнал может поступать из нескольких источников, например, от системных часов, и обычно имеется делитель для уменьшения частоты, если он вам нужен.
Этот свободно работающий счетчик с постоянной входной частотой обычно поступает от надежного осциллятора, выступающего в качестве эталона времени.
Таймеры имеют специальный набор регистров, называемых регистрами значений, для выполнения операций ввода и вывода. На каждый входной/выходной вывод приходится один регистр значений.
Здесь представлены 3 наиболее популярные операции ввода/вывода, реализуемые модулями таймеров:
FE310 нетипичен тем, что реализует ШИМ в специальном типе устройства, называемом широтно-импульсным модулятором. У него их 3 (PWM0, PWM1, PWM2), и в нем нет таймеров общего назначения с функциями захвата входа и сравнения выхода. Каждое устройство PWM способно управлять максимум 4мя каналами (0, 1, 2, 3) через назначенные пины GPIO.
Широтно-импульсный модулятор FE310 имеет несколько режимов работы, некоторые из которых накладывают определенные ограничения на генерируемые сигналы. По этой причине в данной лекции мы рассмотрим только самый гибкий режим работы, который также является наиболее распространенным случаем использования. Этот режим позволит нам выводить ШИМ-сигналы через каналы 1, 2 и 3, и активируется он установкой конфигурационного бита pwmzerocmp.
Если вы хотите узнать о других режимах работы (ШИМ с выравниванием по центру, вывод 4-го ШИМ-сигнала через канал 0, периодические прерывания и многое другое), пожалуйста, подождите, пока вы не пройдете эту главу, а затем обратитесь к руководству.
Из-за большого количества регистров и терминов конфигурация и использование ШИМ-модуля могут быть чрезмерно сложными и запутанными. Поэтому мы рассмотрим интересующий нас конкретный случай использования с простым объяснением.
Допустим, наш счетчик настроен на счет от 0 до 6. Счетчик виден в регистре pwms.
Следующая временная диаграмма показывает поведение всех 8 возможных ШИМ-сигналов, генерируемых в канале X (1, 2 или 3). Регистры значений называются регистрами сравнения, поэтому для канала X регистр сравнения будет pwmcmpX.
(рис 4.4) Временная диаграмма всех возможных случаев сигнала с периодом 7 единиц счета (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Обратите внимание на следующие детали временной диаграммы:
pwmcmpX управляет шириной импульса низкого уровня, а не высокого.pwmcmpX управлять широтой импульсов высокого уровня, мы можем инвертировать выход пина GPIO. Помните регистр out_xor?Здесь мы рассмотрим упрощенную блок-схему ШИМ-контроллера FE310. Важно знать эту схему, поскольку она поможет вам понять, почему нам нужно взаимодействовать с каждым из задействованных регистров.
Пожалуйста, обратите внимание: пусть вас не пугает аппаратное обеспечение. Оно не очень сложное, и вы в любом случае будете использовать высокоуровневые программные инструменты для управления им. Просто сядьте поудобнее и терпеливо наблюдайте. В итоге вы получите доступ к высокоуровневой библиотеке для настройки и использования всех модулей.
Для каждого ШИМ-устройства (в FE310 их 3) имеется следующая схема, которая управляет до 4 выходных выводов:
(рис 4.5) Схема для каждого ШИМ-устройства в микроконтроллере FE310 (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Теперь мы рассмотрим некоторые детали этой схемы.
Длинные прямоугольники с треугольником справа - это регистры конфигурации ШИМ-устройства. Все они привязаны к памяти (имеют адрес и битовое смещение), и вы можете записывать в них данные программно. Вот что они делают:
pwmcfg: Регистр конфигурации ШИМ Этот регистр содержит биты конфигурации для управления поведением ШИМ-устройства. Все именованные сигналы, подключенные к входам на схеме (кроме wurst), являются битами из этого регистра.pwmcount: Регистр подсчета ШИМ Это счетчик. Он имеет (cmpwidth + 15) бит, причем cmpwidth - это размер регистров для сравнения (8 бит для PWM0, и 16 бит для PWM1 и PWM2). Остальные старшие биты до 31 всегда читаются как 0.
Для PWM0 pwmcount - это 23-битный счетчик, поскольку регистры сравнения имеют ширину 8 бит. Для PWM1 и PWM2 pwmcount является 31-битным счетчиком, поскольку регистры сравнения имеют ширину 16 бит.pwms: Масштабированный регистр подсчета ШИМ Это умная и элегантная реализация пределителя от SiFive. Вместо деления входящей тактовой частоты, конфигурируемая часть счетчика наблюдается и отправляется в регистры сравнения. Технически, это не обязательно должен быть регистр, просто селектор битов cmpwidth в pwmcount. Смещение битов для этого выбора находится в поле pwmscale в регистре pwmconfig. Посмотрите на схему, она хорошо показывает эту логику.pwmcmp{1,2,3}: Регистры сравнения ШИМ 1, 2, 3 Это регистры значений. Как показано на схеме, когда счет становится больше или равен значению в одном из этих регистров, его компаратор выдает 1. Когда счет меньше значения в одном из этих регистров, его компаратор выдает 0. Каждый из этих компараторов может влиять на один выходной вывод.pwmcmp0: Регистр сравнения ШИМ 0 Это специальный регистр среди 4 регистров сравнения, поскольку он используется для хранения периода ШИМ-сигналов, которые могут генерироваться на выходных каналах 1, 2 и 3. Выходной вывод, связанный с каналом 0, не будет генерировать ШИМ-сигнал. Вместо этого, когда масштабированный счетчик достигает значения в pwmcmp0, счетчик обнуляется. Это означает, что pwmcmp0 эффективно управляет периодом всех трех выходных сигналов.FE310 имеет 3 устройства ШИМ, пользователю доступно до 12 выводов (9 в выбранном нами режиме работы). Как и в случае с GPIO, все регистры конфигурации упакованы в блоки, которые начинаются с соответствующего базового адреса регистра каждого устройства, как показано в таблице 88 руководства пользователя:
(рис 4.6) Экземпляры устройств ШИМ в микроконтроллере FE310 (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
ncmp - количество каналов, поддерживаемых ШИМ-устройством.
cmpwidth - ширина регистров сравнения.
Каждый 32-битный конфигурационный регистр расположен по определенному смещению. Эти смещения указаны в таблице 89 руководства пользователя:
(рис 4.7) Смещение и описание регистров конфигурации ШИМ (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Помните, что мы можем легко вычислить адрес каждого из конфигурационных регистров ШИМ-устройства, добавив базовый адрес к смещению регистра. Например, pwmcmp1 PWM1 находится по адресу 0x10025024, а pwms - по адресу 0x10025010
pwmcfg имеет множество битовых полей конфигурации, которые влияют на работу ШИМ-устройства. Они подробно описаны в таблице 91 руководства пользователя:
(рис 4.8) Битовые поля регистра конфигурации ШИМ (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
И снова мы сосредоточимся только на тех полях, которые влияют на желаемый режим работы:
pwmscale Это 4-битное поле управляет смещением счетчика ШИМ перед подачей его на компараторы ШИМ. Значение в pwmscale - это битовая позиция в регистре pwmcount начала cmpwidth-битового поля pwms. Значение 0 в pwmscale означает отсутствие масштабирования, и pwms будет равно младшим битам cmpwidth регистра pwmcount. Максимальное значение 15 в pwmscale соответствует делению входящей тактовой частоты на $$2^{15} (32 768)$$, поэтому для входной тактовой частоты шины 16 МГц pwms будет увеличиваться с частотой 488,3 Гц. Аналогично, значение 1 в pwmscale означает деление входной частоты на 2, значение 2 - деление на 4, значение 3 - деление на 8, и так далее, и так далее.pwmzerocmp Установка этого бита приводит к обнулению счетчика через один цикл после достижения им значения в pwmcmp0. Таким образом, мы хотим установить этот бит.pwmdeglitch Установка этого бита предотвращает переход ШИМ-сигнала с высокого уровня на низкий в цикле ШИМ. Это позволяет нам свободно изменять регистры сравнения, не беспокоясь о глюках в ширине импульсов. Таким образом, мы хотим установить этот бит.pwmenalways Этот бит включает счетчик безусловно. Другими словами, когда этот бит установлен, у нас есть свободно работающий счетчик. Таким образом, мы хотим установить этот бит.Теперь, если мы хотим передать наши сигналы ШИМ во внешний мир, нам лучше знать, какие пины использовать. Давайте посмотрим на отображение GPIO IOF в таблице 53 руководства FE301:
(рис 4.9) Сопоставление IOF0 и IOF1 для каждого вывода GPIO (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Обратите внимание, что только 5 выводов имеют две альтернативные функции IOF. Кажется, что SiFive могла бы обойтись только одной функцией IOF для этого микроконтроллера. Однако большинство микроконтроллеров принадлежат к семейству, члены которого имеют больше функций ввода/вывода, чем другие, поэтому мы, возможно, рассматриваем младшего брата этого семейства.
Теперь вернитесь к схеме Red-V Thing Plus и обратите внимание, что все 12 выходов ШИМ подключены к контактам в заголовках ввода/вывода:
(рис 4.10) Частичная схема Red-V Thing Plus, производитель SparkFun Electronics (Изображение любезно предоставлено SparkFun Electronics)
Помните, что для режима работы, который мы собираемся использовать, у нас есть только 9 доступных выводов ШИМ: 3 на каждое ШИМ-устройство. Выводы канала 0 недоступны для ШИМ, поскольку регистры pwmcmp0 используются для управления периодом сигналов ШИМ. Однако эти 3 вывода по-прежнему доступны для других функций.
Теперь, когда мы знакомы с библиотекой Freedom Metal Library, давайте рассмотрим несколько функций, которые мы будем использовать.
Допустим, мы хотим управлять яркостью светодиода, подключенного к выходу канала 1 в PWM1 (сокращенно PWM1_1).
Для генерации ШИМ-сигнала частотой 1 кГц с рабочим циклом 70% в PWM1_1 мы можем просто использовать этот код:
struct metal_pwm *pwm_1; // Экземпляр ШИМ pwm_1 = metal_pwm_get_device(1); // Получение экземпляра ШИМ-устройства metal_pwm_enable(pwm_1); // Включить ШИМ1 metal_pwm_set_freq(pwm_1, 1, 1000); // Установить частоту ШИМ1_1 на 1 кГц metal_pwm_set_duty(pwm_1, 1, 70, METAL_PWM_PHASE_CORRECT_DISABLE); // Установить рабочий цикл для ШИМ1_1 metal_pwm_trigger(pwm_1, 1, METAL_PWM_CONTINUOUS); // Запуск в непрерывном режиме
Код может быть уже понятен, но обратите внимание на следующие детали:
GPIO0, вывод 19. Поэтому, возможно, мы захотим изменить значение io_en после использования этой функции. Об этом позаботится функция metal_gpio_disable_pinmux(). Пины GPIO, связанные с PWM1, - это 19, 20, 21 и 22, поэтому мы можем вызвать эту функцию для тех пинов, которые мы хотим использовать в качестве GPIO.metal_pwm_set_freq() Эта функция получает в качестве третьего аргумента частоту, которую вы хотите получить от этого ШИМ-сигнала. Это хорошая функция, которая избавляет вас от необходимости вычислять период в единицах предварительно масштабированного счетчика.metal_pwm_set_duty() Эта функция получает в качестве третьего аргумента нужный вам рабочий цикл, поэтому он должен быть числом от 0 до 100. Более того, это беззнаковое число. В четвертом аргументе мы имеем макрос METAL_PWM_PHASE_CORRECT_DISABLE, что нормально. Альтернативой этому является генерация выровненного по центру ШИМ-сигнала, но это невозможно в выбранном нами режимеmetal_pwm_trigger() Это запускает счетчик в непрерывном режимеВспомните, что код, который мы только что рассмотрели, будет генерировать ШИМ-сигнал с активным низким уровнем. Другими словами, рабочий цикл выходного сигнала будет составлять 30% вместо 70%. Это может быть нормально, если вы управляете нагрузкой с активным низким уровнем, но, если вам нужно инвертировать логику, вы можете просто установить бит XOR для используемого пина GPIO. В случае с PWM1_1 это будет пин 19 GPIO0.
Мы также можем использовать высокий уровень выходного сигнала, чтобы иметь возможность управлять уровнем яркости внешнего светодиода. Как и в случае с включением подтягивающих резисторов, в библиотеке Freedom Metal Library нет функций для включения или отключения высокой выходной мощности или XOR выхода для инвертирования его значения. Поэтому мы можем использовать макрофункции Red_V_enable_XOR() и Red_V_enable_DS(), приведенные ниже.
#define Red_V_enable_DS(x) *((uint32_t *) 0x10012014) |= (1<<(x)) #define Red_V_enable_XOR(x) *((uint32_t *) 0x10012040) |= (1<<(x)) // Мы будем использовать PWM1_1, который использует GPIO0_19. Red_V_enable_DS(19); // Высокий уровень сигнала на выходе Red_V_enable_XOR(19); // Инвертируем выход для активно-высокого ШИМ
Как и в случае с GPIO, макроопределения базового адреса и смещения регистра можно найти в файле./bsp/install/include/metal/machine/platform.h:
/* Из pwm@10015000 */ #define METAL_SIFIVE_PWM0_10015000_BASE_ADDRESS 268521472UL #define METAL_SIFIVE_PWM0_0_BASE_ADDRESS 268521472UL #define METAL_SIFIVE_PWM0_10015000_SIZE 4096UL #define METAL_SIFIVE_PWM0_0_SIZE 4096UL /* Из pwm@10025000 */ #define METAL_SIFIVE_PWM0_10025000_BASE_ADDRESS 268587008UL #define METAL_SIFIVE_PWM0_1_BASE_ADDRESS 268587008UL #define METAL_SIFIVE_PWM0_10025000_SIZE 4096UL #define METAL_SIFIVE_PWM0_1_SIZE 4096UL /* Из pwm@10035000 */ #define METAL_SIFIVE_PWM0_10035000_BASE_ADDRESS 268652544UL #define METAL_SIFIVE_PWM0_2_BASE_ADDRESS 268652544UL #define METAL_SIFIVE_PWM0_10035000_SIZE 4096UL #define METAL_SIFIVE_PWM0_2_SIZE 4096UL #define METAL_SIFIVE_PWM0 #define METAL_SIFIVE_PWM0_PWMCFG 0UL #define METAL_SIFIVE_PWM0_PWMCOUNT 8UL #define METAL_SIFIVE_PWM0_PWMS 16UL #define METAL_SIFIVE_PWM0_PWMCMP0 32UL #define METAL_SIFIVE_PWM0_PWMCMP1 36UL #define METAL_SIFIVE_PWM0_PWMCMP2 40UL #define METAL_SIFIVE_PWM0_PWMCMP3 44UL
Забавный факт: PWM0_0, PWM0_1 и PWM0_2 - это имена, которые библиотека присвоила PWM0, PWM1 и PWM2 соответственно.
Если вы обратитесь к таблицам 88 и 89 в руководстве FE310, то увидите, что это базовые адреса ШИМ и смещения регистров. Вот они, для вашего удобства:
(рис 4.11) Базовые адреса ШИМ и смещения регистров конфигурации (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Пришло время для другого проекта.
В этой демонстрации будут использоваться те же входные переключатели, что и в демонстрации GPIO, для управления яркостью светодиода с помощью ШИМ. Теперь мы не сможем управлять яркостью встроенного светодиода, потому что он подключен к GPIO0_5, а этот вывод не поддерживает ШИМ в качестве IOF. Однако мы будем использовать бортовой светодиод для чего-то другого.
Из-за этого ограничения мы будем использовать ШИМ1_1 для управления внешним светодиодом с активным высоким уровнем. Помните, что этот выход ШИМ является IOF GPIO0_19.
Вот принципиальная схема:
(рис 4.12) Принципиальная схема демонстрационного приложения ШИМ
Кнопка 0 уменьшит яркость на ступень, а кнопка 1 увеличит ее на ступень. Внешний светодиод имеет 11 уровней яркости, увеличивающихся с шагом в 10%, поэтому яркость будет увеличиваться от 0% до 100%.
Это контакты ввода/вывода, которые мы будем использовать для подключения кнопок и внешнего светодиода:
(рис 4.13) Виды сверху и снизу Red-V Thing Plus, показывающие контакты, которые будут использоваться в демонстрационном приложении ШИМ (взято из SparkFun Electronics, предоставлено по лицензии CC BY 2.0)
Здесь представлены две фотографии фактических аппаратных соединений:
(рис 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; // Недостижимый код возврата
}
Всеми любимый электромеханический привод - это шаговый сервопривод, также известный как радиоуправляемый сервопривод или просто RC-серво. Не такой скучный, как двигатель постоянного тока, шаговый сервопривод имеет множество применений благодаря вращательному и (как бы) линейному механическому эффекту, который он может производить в окружающей среде.
Существует множество типов сервомеханизмов. Нас интересуют аналоговые, ограниченного вращения и сервоприводы с угловым управлением. Более того, мы будем использовать наиболее часто применяемые параметры для радиоуправляемых авиамоделей. На следующем рисунке представлен ассортимент таких сервоприводов.
(рис 4.15) Ассортимент шаговых сервоприводов (Взято из SparkFun Electronics, предоставлено по CC BY-SA 4.0)
Шаговые сервоприводы - это не простые приводы, как соленоиды или шаговые двигатели, и не электромеханические машины, как реле. Сервоприводы - это полноценные встраиваемые устройства. Они содержат плату микроконтроллера, драйвер силового двигателя, двигатель постоянного тока, редуктор, выходной вал и потенциометр. Редуктор прикреплен к двигателю, снижая его скорость, чтобы выходной вал вращался в механически ограниченном диапазоне вращения. Этот диапазон может составлять от 90 до 270 градусов. Этот вал обычно прикреплен к потенциометру, который действует как прибор обратной связи, посылающий напряжение на микроконтроллер. Микроконтроллер реализует систему управления с замкнутым контуром, перемещая двигатель, чтобы выходной вал достиг желаемого угла.
Попробуйте определить эти части на следующем рисунке:
(рис 4.16) Разобранный шаговый сервопривод (Взято с сайта SparkFun Electronics, предоставлено под CC BY-SA 4.0)
Желаемый угол задается пользователем через линию ввода. Мы сообщаем сервоприводу желаемый угол с помощью прямоугольного сигнала частотой 50 Гц (период 20 мс). Ширина импульса этого сигнала кодирует желаемый угол. Обычно длительность импульса 1 мс кодирует один конец вращения, а 2 мс - другой конец. Середина обычно кодируется как 1,5 мс.
(рис 4.17) Ширина импульсов для обоих концов и центра вращения шагового сервопривода (Получено из SparkFun Electronics, предоставлено под CC BY-SA 4.0)
Чтобы узнать больше о сервоприводах, вы можете ознакомиться с этим учебником от SparkFun Electronics.
Теперь мы будем управлять шаговым сервоприводом!
В этой демонстрации будет использоваться почти то же оборудование, что и в приложении яркости светодиодов, но вместо светодиода мы будем использовать сервопривод.
Вот принципиальная схема:
(рис 4.18) Принципиальная схема для управления шаговым сервоприводом
Кнопка 0 повернет вал сервопривода против часовой стрелки на шаг, а кнопка 1 - по часовой стрелке на шаг.
Это те же самые контакты ввода/вывода, которые мы использовали для подключения кнопок и внешнего светодиода. Просто подключите линию светодиода к линии входного сигнала сервопривода.
(рис 4.19) Виды сверху и снизу Red-V Thing Plus, показывающие контакты, которые мы будем использовать в демонстрационном приложении для сервопривода (взято из SparkFun Electronics, предоставлено по лицензии CC BY 2.0)
Здесь представлены две фотографии фактических аппаратных соединений. Пожалуйста, изучите документацию на ваш собственный сервопривод и НЕ следуйте схеме подключения сервопривода, которую вы видите на следующих фотографиях, поскольку разные сервоприводы имеют разную схему подключения.
(рис 4.20) Аппаратные соединения для демонстрационного приложения сервопривода
Среди всех устройств, используемых в данном курсе, сервопривод, безусловно, является тем, который потребляет больше всего энергии. Именно поэтому вы должны обратить особое внимание на следующие рекомендации:
Сервопривод имеет 3 провода: Питание, Сигнал и Заземление. Не существует стандарта на порядок или цветовую маркировку этих проводов, поэтому обязательно ознакомьтесь с документацией на сервопривод, чтобы сделать правильные подключения.
Вспомните рекомендации по питанию сервопривода в списке деталей:
Напряжение питания сервопривода должно соответствовать напряжению сервопривода, обычно 5 В. Вы можете подойти к этому творчески:
В нашем приложении есть все необходимое для управления сервоприводом. Оно нуждается лишь в нескольких настройках:
Учитывая, что приложение LED требует нескольких доработок, здесь представлена модифицированная версия с использованием директив для условного включения кода (#ifdef). Макрос SERVO определяет, будет ли приложение управлять светодиодом или шаговым сервоприводом. Если вы следите за развитием событий, создайте новый проект на основе проекта-примера 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.
Alternatively, a hobby servo can be controlled with those buttons.
The servo must receive a 50Hz signal, with pulse widths between 1ms and 2ms.
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>
// Откомментируйте следующее определение для применения сервопривода.
// Оставьте его закомментированным для приложения яркости светодиодов.
#define SERVO
// Наши собственные макросы, определенные для эффективности или потому, что
// 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) {
#ifdef SERVO
unsigned int dc = 7; // Примерно центральное положение сервоаривода
#else
unsigned int dc = 50; // Рабочий цикл по умолчанию 50%
#endif
struct metal_gpio *gpio_0; // Экземпляр GPIO
struct metal_pwm *pwm_1; // Экземпляр ШИМ
// Конфигурация GPIO для встроенного светодиода и кнопок
gpio_0 = metal_gpio_get_device(0); // Получение экземпляра устройства GPIO
// LED
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.
#ifndef SERVO
Red_V_enable_DS(19); // Высокий уровень сигнала на входе
#endif
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кГц или 50Гц
#ifdef SERVO
metal_pwm_set_freq(pwm_1, 1, 50); // Установка частоты ШИМ1_1 на 50Гц для сервопривода
#else
metal_pwm_set_freq(pwm_1, 1, 1000); // Установка частоты ШИМ1_1 на 1 кГц для светодиода
#endif
metal_pwm_set_duty(pwm_1, 1, dc, METAL_PWM_PHASE_CORRECT_DISABLE); // Установить рабочий цикл для PWM1_1
metal_pwm_trigger(pwm_1, 1, METAL_PWM_CONTINUOUS); // Запуск в непрерывном режиме mode
while(1){
if(Red_V_read_pin(0) == 0){ // Считываем входной пин 0
Red_V_set_pin(5); // Включаем светодиод
#ifdef SERVO
dc = (dc>5)? dc - 1 : dc; // Шаг 1% для серво
#else
dc = (dc>0)? dc - 10 : dc; // Шаг 10% для яркости
#endif
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); // Включаем светодиод
#ifdef SERVO
dc = (dc<10)? dc + 1 : dc; // Шаг 1% для серво
#else
dc = (dc<100)? dc + 10 : dc; // Шаг 10% для яркости
#endif
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; // Недостижимый код возврата
}
Поздравляю! Вы наверняка узнали много нового только в этой главе. Давайте вспомним, что мы сделали в этой лекции:
Не стесняйтесь возвращаться к этому материалу, если в будущем вам понадобится подтянуть знания.
Теперь поговорим о широтно-импульсной модуляции. В этой лекции мы поговорим о генерации сигналов. Сначала мы представим, что такое широтно-импульсная модуляция. Затем мы познакомимся с тем, как работает ШИМ в микроконтроллере FE310. И снова мы увидим аппаратное обеспечение из руководства и программное обеспечение в библиотеке Freedom Metal. Далее мы узнаем, как избежать дребезга кнопки. Дребезг - неизбежная проблема с кнопками, и мы узнаем, как ее решить. И на этом этапе мы получим возможность создать приложение для управления яркостью светодиодов. Затем мы сможем внести в это приложение несколько корректировок, чтобы превратить его в приложение для управления шаговыми сервоприводами.
Итак, давайте повеселимся!
К концу этой лекции, вы должны:
Широтно-импульсная модуляция, или сокращенно ШИМ, - это модное название очень простой техники передачи энергии. Она заключается в управлении количеством мощности, передаваемой на нагрузку с течением времени. Во многих случаях эта передаваемая мощность является средней по времени.
Начнем с мысленного эксперимента: рассмотрим два соседних дома на пляже, которые не заселены, и в каждом из них пока горит только одна лампочка.
Владелец дома А установил таймер, который включает 100-ваттную лампочку на 12 часов, а затем выключает ее на 12 часов каждый день.
Владелец дома Б просто установил 50-ваттную лампочку и держит ее включенной все время.
К концу месяца счет за электроэнергию будет одинаковым для обоих домов, потому что общее потребление энергии будет одинаковым.
Теперь ускорьте эксперимент в меньшем масштабе: представьте на минуту, что вы можете очень быстро включать и выключать выключатель. Насколько быстро? Ну, скажем, вы можете включить светодиод на 500 микросекунд и выключить еще на 500 микросекунд снова и снова, тысячу раз в секунду. Если бы вы могли это сделать, вы бы включали светодиод половину времени и выключали половину времени.
(рис 4.1) Мысленный эксперимент для многократного включения и выключения светодиода
Оказалось, что светодиод будет светить в половину своей полной яркости. Это потому, что каждый раз, когда светодиод включен, он будет излучать свет с полной яркостью, а каждый раз, когда он выключен, он не будет излучать свет. Таким образом, в среднем он излучает свет с половиной своей полной яркости.
К счастью, люди не могут заметить, что светодиод мигает так быстро, поэтому мы склонны просто воспринимать среднюю яркость. Этот эффект известен как постоянство зрения, или сокращенно POV.
Конечно, человек не может управлять переключателем с такой скоростью, но схема может: цифровая схема, такая как микроконтроллер, способна периодически выдавать 5 В в течение 500 микросекунд и 0 В в течение 500 микросекунд. Подача этого сигнала на вышеуказанную пару резистор-светодиод (вместо батарейки и выключателя) позволит добиться того же, что и в нашем эксперименте с выключателем.
Сигнал, который приведет светодиод к уменьшению яркости в два раза, относится к семейству сигналов, известных как прямоугольные волны, которые имеют несколько параметров. Вот параметры, которые нас интересуют:
Амплитуда
Вертикальный размер сигнала (измеряется в вольтах для сигнала напряжения или в амперах для сигнала тока). Он может быть выражен в виде высокого и низкого значений, но в данном типе прямоугольной волны низкое значение равно 0. В нашем примере сигнал будет иметь высокое значение 5 В и низкое значение 0 В.
Период
Горизонтальный размер повторяющейся части волны (измеряется в секундах, миллисекундах или микросекундах). Для нашего примера сигнал имеет период 1 мс.
Частота
Частота повторения сигнала (измеряется в герцах). Она является обратной (взаимной) величиной периода. В нашем примере сигнал имеет частоту 1 кГц.
Ширина импульса
Это длительность высокой части волны (иногда это длительность низкой части, в этом случае делается различие). Этот параметр также является временем, поэтому он измеряется в секундах. В нашем примере сигнал имеет длительность импульса 500us (500 микросекунд).
Рабочий цикл
Отношение ширины импульса к периоду. Это процент, который выражает, сколько процентов от общего периода прямоугольный сигнал остается в состоянии высокого уровня. В нашем примере сигнал имеет рабочий цикл 500us/1,000us = 50%.
Здесь мы имеем 3 сигнала с одинаковым периодом, но разными рабочими циклами:
(рис 4.2) Три периодических сигнала, показывающие рабочие циклы 50%, 75% и 25%, соответственно (Взято с сайта SparkFun Electronics, предоставлено по лицензии CC BY-SA 4.0)
Как уже говорилось, широтно-импульсная модуляция - это модное название чего-то очень простого.
В технике, модуляция - это процесс изменения параметров сигнала для передачи сообщения. Модифицированный сигнал называется несущим, а сообщение - модулирующим сигналом. Возможно, вы знакомы с амплитудной модуляцией и частотной модуляцией - диапазонами AM и FM в радиоприемнике.
Широтно-импульсная модуляция - это процесс изменения ширины импульса прямоугольной волны для передачи определенного процента мощности на нагрузку. В этом случае несущий сигнал - это прямоугольная волна, изменяющаяся в диапазоне от 0 до 1 (например, от 0 до 5 В или от 0 до 3,3 В), модулирующий сигнал - это процент мощности, которую мы хотим передать, а параметр несущего сигнала, который мы изменяем, - это ширина импульса. Поскольку период постоянен, ширина импульса должна иметь длительность между 0 и периодом.
Давайте снова рассмотрим пример ШИМ-сигналов:
(рис 4.3) Три периодических сигнала с рабочими циклами 50%, 75% и 25% соответственно (Взято с сайта SparkFun Electronics, предоставлено по лицензии CC BY-SA 4.0)
Это три случая одного и того же ШИМ-сигнала. Обратите внимание, что период одинаков: все три сигнала достигают высокого уровня в одно и то же время, то есть их длительность (или период) одинаковы.
Если мы подадим эти сигналы на нагрузку (светодиод, двигатель или нагреватель), нагрузка получит дозу общей возможной мощности, эквивалентную рабочему циклу.
Светодиод будет излучать 50%, 75% или 25% от своей полной яркости.
Двигатель будет работать на 50%, 75% или 25% от своей полной скорости.
Нагреватель будет вырабатывать 50%, 75% или 25% своего полного тепла.
Вот и весь ШИМ.
Практически все микроконтроллеры имеют модуль ШИМ. Чаще всего микроконтроллеры поддерживают ШИМ как функцию в модуле таймера. Модули таймера обычно реализуют операции ввода и вывода. Их схема проста:
Таймеры имеют регистр, который работает как счетчик.
Этот счетчик увеличивает свое значение в каждом цикле входного тактового сигнала.
Этот тактовый сигнал может поступать из нескольких источников, например, от системных часов, и обычно имеется делитель для уменьшения частоты, если он вам нужен.
Этот свободно работающий счетчик с постоянной входной частотой обычно поступает от надежного осциллятора, выступающего в качестве эталона времени.
Таймеры имеют специальный набор регистров, называемых регистрами значений, для выполнения операций ввода и вывода. На каждый входной/выходной вывод приходится один регистр значений.
Здесь представлены 3 наиболее популярные операции ввода/вывода, реализуемые модулями таймеров:
FE310 нетипичен тем, что реализует ШИМ в специальном типе устройства, называемом широтно-импульсным модулятором. У него их 3 (PWM0, PWM1, PWM2), и в нем нет таймеров общего назначения с функциями захвата входа и сравнения выхода. Каждое устройство PWM способно управлять максимум 4мя каналами (0, 1, 2, 3) через назначенные пины GPIO.
Широтно-импульсный модулятор FE310 имеет несколько режимов работы, некоторые из которых накладывают определенные ограничения на генерируемые сигналы. По этой причине в данной лекции мы рассмотрим только самый гибкий режим работы, который также является наиболее распространенным случаем использования. Этот режим позволит нам выводить ШИМ-сигналы через каналы 1, 2 и 3, и активируется он установкой конфигурационного бита pwmzerocmp.
Если вы хотите узнать о других режимах работы (ШИМ с выравниванием по центру, вывод 4-го ШИМ-сигнала через канал 0, периодические прерывания и многое другое), пожалуйста, подождите, пока вы не пройдете эту главу, а затем обратитесь к руководству.
Из-за большого количества регистров и терминов конфигурация и использование ШИМ-модуля могут быть чрезмерно сложными и запутанными. Поэтому мы рассмотрим интересующий нас конкретный случай использования с простым объяснением.
Допустим, наш счетчик настроен на счет от 0 до 6. Счетчик виден в регистре pwms.
Следующая временная диаграмма показывает поведение всех 8 возможных ШИМ-сигналов, генерируемых в канале X (1, 2 или 3). Регистры значений называются регистрами сравнения, поэтому для канала X регистр сравнения будет pwmcmpX.
(рис 4.4) Временная диаграмма всех возможных случаев сигнала с периодом 7 единиц счета (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Обратите внимание на следующие детали временной диаграммы:
pwmcmpX управляет шириной импульса низкого уровня, а не высокого.pwmcmpX управлять широтой импульсов высокого уровня, мы можем инвертировать выход пина GPIO. Помните регистр out_xor?Здесь мы рассмотрим упрощенную блок-схему ШИМ-контроллера FE310. Важно знать эту схему, поскольку она поможет вам понять, почему нам нужно взаимодействовать с каждым из задействованных регистров.
Пожалуйста, обратите внимание: пусть вас не пугает аппаратное обеспечение. Оно не очень сложное, и вы в любом случае будете использовать высокоуровневые программные инструменты для управления им. Просто сядьте поудобнее и терпеливо наблюдайте. В итоге вы получите доступ к высокоуровневой библиотеке для настройки и использования всех модулей.
Для каждого ШИМ-устройства (в FE310 их 3) имеется следующая схема, которая управляет до 4 выходных выводов:
(рис 4.5) Схема для каждого ШИМ-устройства в микроконтроллере FE310 (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Теперь мы рассмотрим некоторые детали этой схемы.
Длинные прямоугольники с треугольником справа - это регистры конфигурации ШИМ-устройства. Все они привязаны к памяти (имеют адрес и битовое смещение), и вы можете записывать в них данные программно. Вот что они делают:
pwmcfg: Регистр конфигурации ШИМ Этот регистр содержит биты конфигурации для управления поведением ШИМ-устройства. Все именованные сигналы, подключенные к входам на схеме (кроме wurst), являются битами из этого регистра.pwmcount: Регистр подсчета ШИМ Это счетчик. Он имеет (cmpwidth + 15) бит, причем cmpwidth - это размер регистров для сравнения (8 бит для PWM0, и 16 бит для PWM1 и PWM2). Остальные старшие биты до 31 всегда читаются как 0.
Для PWM0 pwmcount - это 23-битный счетчик, поскольку регистры сравнения имеют ширину 8 бит. Для PWM1 и PWM2 pwmcount является 31-битным счетчиком, поскольку регистры сравнения имеют ширину 16 бит.pwms: Масштабированный регистр подсчета ШИМ Это умная и элегантная реализация пределителя от SiFive. Вместо деления входящей тактовой частоты, конфигурируемая часть счетчика наблюдается и отправляется в регистры сравнения. Технически, это не обязательно должен быть регистр, просто селектор битов cmpwidth в pwmcount. Смещение битов для этого выбора находится в поле pwmscale в регистре pwmconfig. Посмотрите на схему, она хорошо показывает эту логику.pwmcmp{1,2,3}: Регистры сравнения ШИМ 1, 2, 3 Это регистры значений. Как показано на схеме, когда счет становится больше или равен значению в одном из этих регистров, его компаратор выдает 1. Когда счет меньше значения в одном из этих регистров, его компаратор выдает 0. Каждый из этих компараторов может влиять на один выходной вывод.pwmcmp0: Регистр сравнения ШИМ 0 Это специальный регистр среди 4 регистров сравнения, поскольку он используется для хранения периода ШИМ-сигналов, которые могут генерироваться на выходных каналах 1, 2 и 3. Выходной вывод, связанный с каналом 0, не будет генерировать ШИМ-сигнал. Вместо этого, когда масштабированный счетчик достигает значения в pwmcmp0, счетчик обнуляется. Это означает, что pwmcmp0 эффективно управляет периодом всех трех выходных сигналов.FE310 имеет 3 устройства ШИМ, пользователю доступно до 12 выводов (9 в выбранном нами режиме работы). Как и в случае с GPIO, все регистры конфигурации упакованы в блоки, которые начинаются с соответствующего базового адреса регистра каждого устройства, как показано в таблице 88 руководства пользователя:
(рис 4.6) Экземпляры устройств ШИМ в микроконтроллере FE310 (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
ncmp - количество каналов, поддерживаемых ШИМ-устройством.
cmpwidth - ширина регистров сравнения.
Каждый 32-битный конфигурационный регистр расположен по определенному смещению. Эти смещения указаны в таблице 89 руководства пользователя:
(рис 4.7) Смещение и описание регистров конфигурации ШИМ (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Помните, что мы можем легко вычислить адрес каждого из конфигурационных регистров ШИМ-устройства, добавив базовый адрес к смещению регистра. Например, pwmcmp1 PWM1 находится по адресу 0x10025024, а pwms - по адресу 0x10025010
pwmcfg имеет множество битовых полей конфигурации, которые влияют на работу ШИМ-устройства. Они подробно описаны в таблице 91 руководства пользователя:
(рис 4.8) Битовые поля регистра конфигурации ШИМ (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
И снова мы сосредоточимся только на тех полях, которые влияют на желаемый режим работы:
pwmscale Это 4-битное поле управляет смещением счетчика ШИМ перед подачей его на компараторы ШИМ. Значение в pwmscale - это битовая позиция в регистре pwmcount начала cmpwidth-битового поля pwms. Значение 0 в pwmscale означает отсутствие масштабирования, и pwms будет равно младшим битам cmpwidth регистра pwmcount. Максимальное значение 15 в pwmscale соответствует делению входящей тактовой частоты на $$2^{15} (32 768)$$, поэтому для входной тактовой частоты шины 16 МГц pwms будет увеличиваться с частотой 488,3 Гц. Аналогично, значение 1 в pwmscale означает деление входной частоты на 2, значение 2 - деление на 4, значение 3 - деление на 8, и так далее, и так далее.pwmzerocmp Установка этого бита приводит к обнулению счетчика через один цикл после достижения им значения в pwmcmp0. Таким образом, мы хотим установить этот бит.pwmdeglitch Установка этого бита предотвращает переход ШИМ-сигнала с высокого уровня на низкий в цикле ШИМ. Это позволяет нам свободно изменять регистры сравнения, не беспокоясь о глюках в ширине импульсов. Таким образом, мы хотим установить этот бит.pwmenalways Этот бит включает счетчик безусловно. Другими словами, когда этот бит установлен, у нас есть свободно работающий счетчик. Таким образом, мы хотим установить этот бит.Теперь, если мы хотим передать наши сигналы ШИМ во внешний мир, нам лучше знать, какие пины использовать. Давайте посмотрим на отображение GPIO IOF в таблице 53 руководства FE301:
(рис 4.9) Сопоставление IOF0 и IOF1 для каждого вывода GPIO (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Обратите внимание, что только 5 выводов имеют две альтернативные функции IOF. Кажется, что SiFive могла бы обойтись только одной функцией IOF для этого микроконтроллера. Однако большинство микроконтроллеров принадлежат к семейству, члены которого имеют больше функций ввода/вывода, чем другие, поэтому мы, возможно, рассматриваем младшего брата этого семейства.
Теперь вернитесь к схеме Red-V Thing Plus и обратите внимание, что все 12 выходов ШИМ подключены к контактам в заголовках ввода/вывода:
(рис 4.10) Частичная схема Red-V Thing Plus, производитель SparkFun Electronics (Изображение любезно предоставлено SparkFun Electronics)
Помните, что для режима работы, который мы собираемся использовать, у нас есть только 9 доступных выводов ШИМ: 3 на каждое ШИМ-устройство. Выводы канала 0 недоступны для ШИМ, поскольку регистры pwmcmp0 используются для управления периодом сигналов ШИМ. Однако эти 3 вывода по-прежнему доступны для других функций.
Теперь, когда мы знакомы с библиотекой Freedom Metal Library, давайте рассмотрим несколько функций, которые мы будем использовать.
Допустим, мы хотим управлять яркостью светодиода, подключенного к выходу канала 1 в PWM1 (сокращенно PWM1_1).
Для генерации ШИМ-сигнала частотой 1 кГц с рабочим циклом 70% в PWM1_1 мы можем просто использовать этот код:
struct metal_pwm *pwm_1; // Экземпляр ШИМ pwm_1 = metal_pwm_get_device(1); // Получение экземпляра ШИМ-устройства metal_pwm_enable(pwm_1); // Включить ШИМ1 metal_pwm_set_freq(pwm_1, 1, 1000); // Установить частоту ШИМ1_1 на 1 кГц metal_pwm_set_duty(pwm_1, 1, 70, METAL_PWM_PHASE_CORRECT_DISABLE); // Установить рабочий цикл для ШИМ1_1 metal_pwm_trigger(pwm_1, 1, METAL_PWM_CONTINUOUS); // Запуск в непрерывном режиме
Код может быть уже понятен, но обратите внимание на следующие детали:
GPIO0, вывод 19. Поэтому, возможно, мы захотим изменить значение io_en после использования этой функции. Об этом позаботится функция metal_gpio_disable_pinmux(). Пины GPIO, связанные с PWM1, - это 19, 20, 21 и 22, поэтому мы можем вызвать эту функцию для тех пинов, которые мы хотим использовать в качестве GPIO.metal_pwm_set_freq() Эта функция получает в качестве третьего аргумента частоту, которую вы хотите получить от этого ШИМ-сигнала. Это хорошая функция, которая избавляет вас от необходимости вычислять период в единицах предварительно масштабированного счетчика.metal_pwm_set_duty() Эта функция получает в качестве третьего аргумента нужный вам рабочий цикл, поэтому он должен быть числом от 0 до 100. Более того, это беззнаковое число. В четвертом аргументе мы имеем макрос METAL_PWM_PHASE_CORRECT_DISABLE, что нормально. Альтернативой этому является генерация выровненного по центру ШИМ-сигнала, но это невозможно в выбранном нами режимеmetal_pwm_trigger() Это запускает счетчик в непрерывном режимеВспомните, что код, который мы только что рассмотрели, будет генерировать ШИМ-сигнал с активным низким уровнем. Другими словами, рабочий цикл выходного сигнала будет составлять 30% вместо 70%. Это может быть нормально, если вы управляете нагрузкой с активным низким уровнем, но, если вам нужно инвертировать логику, вы можете просто установить бит XOR для используемого пина GPIO. В случае с PWM1_1 это будет пин 19 GPIO0.
Мы также можем использовать высокий уровень выходного сигнала, чтобы иметь возможность управлять уровнем яркости внешнего светодиода. Как и в случае с включением подтягивающих резисторов, в библиотеке Freedom Metal Library нет функций для включения или отключения высокой выходной мощности или XOR выхода для инвертирования его значения. Поэтому мы можем использовать макрофункции Red_V_enable_XOR() и Red_V_enable_DS(), приведенные ниже.
#define Red_V_enable_DS(x) *((uint32_t *) 0x10012014) |= (1<<(x)) #define Red_V_enable_XOR(x) *((uint32_t *) 0x10012040) |= (1<<(x)) // Мы будем использовать PWM1_1, который использует GPIO0_19. Red_V_enable_DS(19); // Высокий уровень сигнала на выходе Red_V_enable_XOR(19); // Инвертируем выход для активно-высокого ШИМ
Как и в случае с GPIO, макроопределения базового адреса и смещения регистра можно найти в файле./bsp/install/include/metal/machine/platform.h:
/* Из pwm@10015000 */ #define METAL_SIFIVE_PWM0_10015000_BASE_ADDRESS 268521472UL #define METAL_SIFIVE_PWM0_0_BASE_ADDRESS 268521472UL #define METAL_SIFIVE_PWM0_10015000_SIZE 4096UL #define METAL_SIFIVE_PWM0_0_SIZE 4096UL /* Из pwm@10025000 */ #define METAL_SIFIVE_PWM0_10025000_BASE_ADDRESS 268587008UL #define METAL_SIFIVE_PWM0_1_BASE_ADDRESS 268587008UL #define METAL_SIFIVE_PWM0_10025000_SIZE 4096UL #define METAL_SIFIVE_PWM0_1_SIZE 4096UL /* Из pwm@10035000 */ #define METAL_SIFIVE_PWM0_10035000_BASE_ADDRESS 268652544UL #define METAL_SIFIVE_PWM0_2_BASE_ADDRESS 268652544UL #define METAL_SIFIVE_PWM0_10035000_SIZE 4096UL #define METAL_SIFIVE_PWM0_2_SIZE 4096UL #define METAL_SIFIVE_PWM0 #define METAL_SIFIVE_PWM0_PWMCFG 0UL #define METAL_SIFIVE_PWM0_PWMCOUNT 8UL #define METAL_SIFIVE_PWM0_PWMS 16UL #define METAL_SIFIVE_PWM0_PWMCMP0 32UL #define METAL_SIFIVE_PWM0_PWMCMP1 36UL #define METAL_SIFIVE_PWM0_PWMCMP2 40UL #define METAL_SIFIVE_PWM0_PWMCMP3 44UL
Забавный факт: PWM0_0, PWM0_1 и PWM0_2 - это имена, которые библиотека присвоила PWM0, PWM1 и PWM2 соответственно.
Если вы обратитесь к таблицам 88 и 89 в руководстве FE310, то увидите, что это базовые адреса ШИМ и смещения регистров. Вот они, для вашего удобства:
(рис 4.11) Базовые адреса ШИМ и смещения регистров конфигурации (Изображение из руководства пользователя FE310-G002, воспроизведено с разрешения компании SiFive, Inc.)
Пришло время для другого проекта.
В этой демонстрации будут использоваться те же входные переключатели, что и в демонстрации GPIO, для управления яркостью светодиода с помощью ШИМ. Теперь мы не сможем управлять яркостью встроенного светодиода, потому что он подключен к GPIO0_5, а этот вывод не поддерживает ШИМ в качестве IOF. Однако мы будем использовать бортовой светодиод для чего-то другого.
Из-за этого ограничения мы будем использовать ШИМ1_1 для управления внешним светодиодом с активным высоким уровнем. Помните, что этот выход ШИМ является IOF GPIO0_19.
Вот принципиальная схема:
(рис 4.12) Принципиальная схема демонстрационного приложения ШИМ
Кнопка 0 уменьшит яркость на ступень, а кнопка 1 увеличит ее на ступень. Внешний светодиод имеет 11 уровней яркости, увеличивающихся с шагом в 10%, поэтому яркость будет увеличиваться от 0% до 100%.
Это контакты ввода/вывода, которые мы будем использовать для подключения кнопок и внешнего светодиода:
(рис 4.13) Виды сверху и снизу Red-V Thing Plus, показывающие контакты, которые будут использоваться в демонстрационном приложении ШИМ (взято из SparkFun Electronics, предоставлено по лицензии CC BY 2.0)
Здесь представлены две фотографии фактических аппаратных соединений:
(рис 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; // Недостижимый код возврата
}
Всеми любимый электромеханический привод - это шаговый сервопривод, также известный как радиоуправляемый сервопривод или просто RC-серво. Не такой скучный, как двигатель постоянного тока, шаговый сервопривод имеет множество применений благодаря вращательному и (как бы) линейному механическому эффекту, который он может производить в окружающей среде.
Существует множество типов сервомеханизмов. Нас интересуют аналоговые, ограниченного вращения и сервоприводы с угловым управлением. Более того, мы будем использовать наиболее часто применяемые параметры для радиоуправляемых авиамоделей. На следующем рисунке представлен ассортимент таких сервоприводов.
(рис 4.15) Ассортимент шаговых сервоприводов (Взято из SparkFun Electronics, предоставлено по CC BY-SA 4.0)
Шаговые сервоприводы - это не простые приводы, как соленоиды или шаговые двигатели, и не электромеханические машины, как реле. Сервоприводы - это полноценные встраиваемые устройства. Они содержат плату микроконтроллера, драйвер силового двигателя, двигатель постоянного тока, редуктор, выходной вал и потенциометр. Редуктор прикреплен к двигателю, снижая его скорость, чтобы выходной вал вращался в механически ограниченном диапазоне вращения. Этот диапазон может составлять от 90 до 270 градусов. Этот вал обычно прикреплен к потенциометру, который действует как прибор обратной связи, посылающий напряжение на микроконтроллер. Микроконтроллер реализует систему управления с замкнутым контуром, перемещая двигатель, чтобы выходной вал достиг желаемого угла.
Попробуйте определить эти части на следующем рисунке:
(рис 4.16) Разобранный шаговый сервопривод (Взято с сайта SparkFun Electronics, предоставлено под CC BY-SA 4.0)
Желаемый угол задается пользователем через линию ввода. Мы сообщаем сервоприводу желаемый угол с помощью прямоугольного сигнала частотой 50 Гц (период 20 мс). Ширина импульса этого сигнала кодирует желаемый угол. Обычно длительность импульса 1 мс кодирует один конец вращения, а 2 мс - другой конец. Середина обычно кодируется как 1,5 мс.
(рис 4.17) Ширина импульсов для обоих концов и центра вращения шагового сервопривода (Получено из SparkFun Electronics, предоставлено под CC BY-SA 4.0)
Чтобы узнать больше о сервоприводах, вы можете ознакомиться с этим учебником от SparkFun Electronics.
Теперь мы будем управлять шаговым сервоприводом!
В этой демонстрации будет использоваться почти то же оборудование, что и в приложении яркости светодиодов, но вместо светодиода мы будем использовать сервопривод.
Вот принципиальная схема:
(рис 4.18) Принципиальная схема для управления шаговым сервоприводом
Кнопка 0 повернет вал сервопривода против часовой стрелки на шаг, а кнопка 1 - по часовой стрелке на шаг.
Это те же самые контакты ввода/вывода, которые мы использовали для подключения кнопок и внешнего светодиода. Просто подключите линию светодиода к линии входного сигнала сервопривода.
(рис 4.19) Виды сверху и снизу Red-V Thing Plus, показывающие контакты, которые мы будем использовать в демонстрационном приложении для сервопривода (взято из SparkFun Electronics, предоставлено по лицензии CC BY 2.0)
Здесь представлены две фотографии фактических аппаратных соединений. Пожалуйста, изучите документацию на ваш собственный сервопривод и НЕ следуйте схеме подключения сервопривода, которую вы видите на следующих фотографиях, поскольку разные сервоприводы имеют разную схему подключения.
(рис 4.20) Аппаратные соединения для демонстрационного приложения сервопривода
Среди всех устройств, используемых в данном курсе, сервопривод, безусловно, является тем, который потребляет больше всего энергии. Именно поэтому вы должны обратить особое внимание на следующие рекомендации:
Сервопривод имеет 3 провода: Питание, Сигнал и Заземление. Не существует стандарта на порядок или цветовую маркировку этих проводов, поэтому обязательно ознакомьтесь с документацией на сервопривод, чтобы сделать правильные подключения.
Вспомните рекомендации по питанию сервопривода в списке деталей:
Напряжение питания сервопривода должно соответствовать напряжению сервопривода, обычно 5 В. Вы можете подойти к этому творчески:
В нашем приложении есть все необходимое для управления сервоприводом. Оно нуждается лишь в нескольких настройках:
Учитывая, что приложение LED требует нескольких доработок, здесь представлена модифицированная версия с использованием директив для условного включения кода (#ifdef). Макрос SERVO определяет, будет ли приложение управлять светодиодом или шаговым сервоприводом. Если вы следите за развитием событий, создайте новый проект на основе проекта-примера 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.
Alternatively, a hobby servo can be controlled with those buttons.
The servo must receive a 50Hz signal, with pulse widths between 1ms and 2ms.
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>
// Откомментируйте следующее определение для применения сервопривода.
// Оставьте его закомментированным для приложения яркости светодиодов.
#define SERVO
// Наши собственные макросы, определенные для эффективности или потому, что
// 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) {
#ifdef SERVO
unsigned int dc = 7; // Примерно центральное положение сервоаривода
#else
unsigned int dc = 50; // Рабочий цикл по умолчанию 50%
#endif
struct metal_gpio *gpio_0; // Экземпляр GPIO
struct metal_pwm *pwm_1; // Экземпляр ШИМ
// Конфигурация GPIO для встроенного светодиода и кнопок
gpio_0 = metal_gpio_get_device(0); // Получение экземпляра устройства GPIO
// LED
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.
#ifndef SERVO
Red_V_enable_DS(19); // Высокий уровень сигнала на входе
#endif
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кГц или 50Гц
#ifdef SERVO
metal_pwm_set_freq(pwm_1, 1, 50); // Установка частоты ШИМ1_1 на 50Гц для сервопривода
#else
metal_pwm_set_freq(pwm_1, 1, 1000); // Установка частоты ШИМ1_1 на 1 кГц для светодиода
#endif
metal_pwm_set_duty(pwm_1, 1, dc, METAL_PWM_PHASE_CORRECT_DISABLE); // Установить рабочий цикл для PWM1_1
metal_pwm_trigger(pwm_1, 1, METAL_PWM_CONTINUOUS); // Запуск в непрерывном режиме mode
while(1){
if(Red_V_read_pin(0) == 0){ // Считываем входной пин 0
Red_V_set_pin(5); // Включаем светодиод
#ifdef SERVO
dc = (dc>5)? dc - 1 : dc; // Шаг 1% для серво
#else
dc = (dc>0)? dc - 10 : dc; // Шаг 10% для яркости
#endif
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); // Включаем светодиод
#ifdef SERVO
dc = (dc<10)? dc + 1 : dc; // Шаг 1% для серво
#else
dc = (dc<100)? dc + 10 : dc; // Шаг 10% для яркости
#endif
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; // Недостижимый код возврата
}
Поздравляю! Вы наверняка узнали много нового только в этой главе. Давайте вспомним, что мы сделали в этой лекции:
Не стесняйтесь возвращаться к этому материалу, если в будущем вам понадобится подтянуть знания.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.