[Введение]
Микроконтроллерная технология является незаменимой основной технологией в современной промышленной автоматизации, электронике, электротехнике и Интернете вещей (IoT). Поскольку наша жизнь становится все более умной, технология микроконтроллеров проникла практически во все аспекты нашей повседневной жизни, например, в умные рисоварки, умные колонки и многое другое.
Учитывая это, серия статей «Повторное изучение микроконтроллера 51» призвана помочь новичкам начать работу с технологией микроконтроллеров. Мы начнем с самой простой задачи — -включения одного светодиода- и постепенно перейдем к реализации таких модулей, как кнопки управления, дисплей LCD1602, датчики температуры DS18B20 и DS1302, а также средства связи между двумя микроконтроллерами. Мы также рассмотрим аппаратные протоколы связи, такие как UART, I²C и SPI. Объединив эти концепции с методами программирования на C, мы будем использовать реальные-инженерные проекты для иллюстрации подходов к программированию, что позволит вам гибко применять указатели и структуры C для реализации модульного программирования.
Теперь вернемся к основной теме: использованию микроконтроллера 51 для управления светодиодом и создания эффекта дышащего света.
[Как работает дыхательное освещение]
Давайте сначала посмотрим, как работает эффект дышащего света.
Дыхательный свет постепенно становится ярче, а затем постепенно тускнеет, повторяя этот цикл, напоминающий дыхание. Однако, поскольку контакты микроконтроллера могут выводить только 1 (включено) или 0 (выключено), как можно достичь эффекта постепенного перехода?
Это связано с сохранением зрения в наших глазах. Когда мы смотрим на что-то, изображение, сформированное нашими глазами, сохраняется в течение 0,04 секунды (эта цифра была найдена в Интернете).
Если мы посчитаем на основе 0,04 секунды, это будет равно 40 мс. Поэтому, когда светодиод включается и выключается по 20 мс, человеческому глазу кажется, что он горит постоянно.

Является ли эффект от мигания светодиода в течение 20 мс и выключения в течение 20 мс таким же, как если бы он оставался включенным постоянно?
Хаха, это определенно другое. Когда свет попеременно включается и выключается каждые 20 миллисекунд, эффект, который мы видим, более тусклый, чем когда он горит постоянно. Если предположить, что яркость постоянно горящего источника света равна 100 %, то яркость источника света, который включается и выключается каждые 20 миллисекунд, равна 50 %. Исходя из этого, мы можем регулировать яркость светодиода.

На этом этапе мы можем регулировать яркость светодиода (устанавливая продолжительность высокого уровня в пределах цикла 40 мс). Это принцип, лежащий в основе хорошо известного-метода ШИМ (широтно-импульсной модуляции) управления яркостью, а установка продолжительности высокого уровня эквивалентна регулировке рабочего цикла (т. е. длительности высокого уровня, разделенной на общий цикл: 20/40=50%).
Здесь наиболее важным фактором является этот рабочий цикл. Например, если период составляет 20 мс, а светодиод попеременно светится от 10 мс до 10 мс, воспринимаемая яркость по-прежнему составляет 50 % (т. е. рабочий цикл составляет 10/20=50%).
Далее посмотрим, как это реализовано в программе.
[Реализация программы]
Включение светодиода
Сначала начнем с включения светодиода, а затем постепенно реализуем эффект дышащего света. Аппаратное обеспечение, которое мы будем использовать, следующее:
| Совет по развитию | Плата для разработки микроконтроллеров ZeroOne |
|---|---|
| Модель микроконтроллера | STC89C52 |
| Светодиодный интерфейс | Контакт P4^4 |

Из схемы видно, что светодиод подключен к выводу P4^4 микроконтроллера. Когда микроконтроллер выдает 1, светодиод включается; когда он выводит 0, светодиод гаснет. Поэтому программа включения светодиода довольно проста, как показано ниже:

Программа для включения светодиода довольно проста; Я уверен, что все знают, как это сделать.
Регулировка яркости светодиода
Далее мы реализуем функцию, которая позволит нам регулировать яркость (т. е. регулировать рабочий цикл) следующим образом:

Определите статическую переменную «duty_cycle» для хранения рабочего цикла. Когда «flag» равен 1, рабочий цикл постепенно увеличивается до 255, затем установите «flag» на 0, а «duty_cycle» постепенно уменьшается с 255 до 0. Повторите этот цикл.
Хаха, в этот момент вы можете подумать, что дыхательный свет уже работает, но это не так. Если вы мне не верите, попробуйте код выше самостоятельно.
Так в чем именно проблема?
Проблема заключается в нашем прямом вызове функции настройки-яркости `set_led_luminance()`. Этой функции требуется 40 мс для завершения одного цикла, что означает, что рабочий цикл не может быть изменен в течение этих 40 мс; в противном случае регулировка яркости не будет работать. Давайте еще раз взглянем на функцию breath_led. После каждого вызова set_led_luminance() для установки рабочего цикла он немедленно меняет значение duty_cycle, не дожидаясь 40 мс.
На этом этапе нам нужно добавить программный таймер для обновления значения «duty_cycle» по истечении 40 мс. Модифицированная программа выглядит следующим образом:

Примечание. Длительность таймера должна быть больше 40 мс (это означает, что значение `s_breathCounter` должно быть больше 255), но лучше всего установить ее кратной циклу. Например, если наш цикл равен 255 (т. е. 256 значений от 0 до 255), мы можем установить для него удвоенное значение: 256 * 2 - 1=511 (т. е. 512 значений от 0 до 511).
И вот-наш дыхательный светильник готов! Разве это не просто? (* ̄︶ ̄)
Далее идет сегодняшний бонусный раздел.
Хотя нам удалось добиться эффекта дышащего света, код недостаточно лаконичный и элегантный-он использует множество операторов if и else. Давайте посмотрим, сможем ли мы упростить это еще больше.
Во-первых, давайте упростим этот раздел функции set_led_luminance(), как показано на рисунке ниже.

Прежде чем мы упростим это, давайте кратко рассмотрим C: побитовую операцию И.

Отсюда мы знаем, что независимо от того, равно ли это 1 или 0, выполнение побитовой операции И с 0 приводит к 0.
Независимо от того, равно ли это 1 или 0, выполнение побитовой операции И с 1 приводит к исходному значению.
Для удобства мы используем шестнадцатеричную систему счисления (с префиксом «0x»); например, 0xff соответствует 255 в десятичном формате. Поэтому,
Когда число, меньшее или равное 0xff, объединяется с помощью AND с 0xff, результатом является само число, как показано ниже.

Что произойдет, если вы выполните побитовую операцию И между числами больше 0xff и 0xff?

Результатом является остаток от деления этого числа на (0xff + 1) (т. е. результат по-прежнему находится в диапазоне от 0 до 0xff).
С помощью этой побитовой операции И приведенный выше код можно упростить до

Таким образом, значение s_Counter всегда будет находиться в диапазоне от 0x00 до 0xff.
Аналогично, программный таймер в приведенной выше функции Breath_led также можно упростить следующим образом:

В строке 3 0x1ff — это 511 в десятичном формате. Условие истинно, когда значение s_breathCounter равно (0x1ff+1) или 512, потому что 512 & 0x1ff=0. Восклицательный знак перед ним указывает на побитовую операцию НЕ (точнее, условие выполняется, когда значение s_breathCounter кратно 512; это устраняет необходимость сброса s_breathCounter). надеюсь, это объяснение понятно-пожалуйста, подумайте над этим). Если условие выполняется, рабочий цикл начинает увеличиваться или уменьшаться.
Но разве рабочий цикл не находится в диапазоне от 0 до 255? Почему строка 5 также становится 0x1ff (511)? Не волнуйтесь,-посмотрите на строку 8. Мы снова вычитаем 0xff, поэтому диапазон рабочего цикла остается от 0 до 255.
Строки 7–10 означают: Когда `duty_cycle > (0xff)`, т. е. 256–511, вычитание 0xff эквивалентно увеличению от 1 до 255, поэтому яркость постепенно увеличивается.
Когда рабочий_цикл<= 0xff, the duty_cycle increases from 0 to 255, while the set brightness is 255 - duty_cycle. This effectively decreases the brightness from 255 to 0, causing the light to gradually dim. This achieves the breathing light effect.
Ха-ха, вы думали, что наше упрощение закончилось?
Нет, нет, нет
На самом деле строки с 7 по 10 можно было бы упростить еще больше. Вот тут-то и пригодится функция абсолютного значения.
Что? Зачем использовать функцию абсолютного значения?
Посмотрите на строку 10: 0xff - Duty_cycle эквивалентен Duty_cycle - 0xff, а затем принимает абсолютное значение. Хорошо, вот упрощенный код:
Макрофункция для получения абсолютного значения выглядит следующим образом:
Наконец, я включил весь упрощенный код ниже.

Что вы думаете? Разве это не просто? (* ̄︶ ̄)




