- Принципиальная электрическая схема:
- Код последовательности мигания светодиода PIC микроконтроллера PIC16F877A и объяснение работы:
В нашем предыдущем руководстве мы узнали о мигании светодиода с помощью микроконтроллера PIC и построили ту же схему на плате Perf. Затем мы использовали PICkit 3, ICSP и MPLAB IPE для записи программы на нашу плату Perf. Теперь в этом руководстве мы продвинемся к использованию большего количества контактов на микроконтроллере PIC. Мы будем использовать 7 выходов (светодиодов) и один вход. В этом уроке мы будем использовать старую плату Perf (показано ниже) и добавим палочки Берга, чтобы вытащить необходимые контакты на вторую плату светодиодов. В конце этого руководства мы сгенерируем последовательность мигающих светодиодов с помощью микроконтроллера PIC PIC16F877A и узнаем, как использовать несколько входов и выходов, а также узнаем некоторые основы цикла for и вызова функций.
Плата светодиодов - это не что иное, как еще одна перфорированная плата, на которую мы припаяем светодиоды с токоограничивающим резистором (показано ниже). Мы также добавим кнопку для включения мигания светодиода последовательности.
Принципиальная электрическая схема:
Код последовательности мигания светодиода PIC микроконтроллера PIC16F877A и объяснение работы:
Полный код приведен ниже (проверьте в конце), здесь мы рассмотрим его построчно. Этот код начнет последовательно загораться светодиодами при нажатии кнопки. Чтобы понять последовательность действий, посмотрите видео в конце урока. Я бы рекомендовал вам сравнить вывод, показанный на видео, с приведенным ниже кодом и попытаться понять программу.
Давайте посмотрим на код построчно. Первые несколько строк предназначены для настройки битов конфигурации, которые были объяснены в предыдущем руководстве, поэтому я пока их пропускаю. Лучший способ понять любую программу - начать с функции main ( void main () ), так что давайте сделаем это
TRISB0 = 1; // Сообщаем MCU, что вывод PORTB 0 используется в качестве входа для кнопки. TRISD = 0x00; // Сообщаем MCU, что все контакты выведены PORTD = 0x00; // Инициализируем все контакты на 0
Слово TRIS используется для определения того, используется ли вывод как ввод / вывод, а слово PORT используется для определения высокого / низкого уровня вывода. Линия TRISB0 = 1 сделает 0-й вывод ПОРТА B входом. Это будет наша кнопка. Строки TRISD = 0x00; PORTD = 0x00; сделает все выводы порта D как Output и присвоит этим выводам начальное значение LOW.
Поскольку мы сказали, что B0 используется как вход, мы подключим один конец кнопки к контакту B0, а другой конец - к земле. К тому времени всякий раз, когда мы нажимаем кнопку, контакт будет заземлен, как показано на схеме подключения выше. Но чтобы это произошло, мы должны использовать подтягивающий резистор, чтобы вывод удерживался высоко, когда кнопка не нажата. Подтягивающий резистор примерно такой.
Но наш PIC MCU имеет внутренний слабый подтягивающий резистор, который может быть активирован программно, что избавляет от лишних хлопот (когда нужно подключить больше кнопок).
Что такое слабый подтягивающий резистор?
Существует два типа подтягивающих резисторов : слабое подтягивающее и сильное подтягивающее. Слабые подтягивающие резисторы имеют высокую стоимость и, таким образом, пропускают слабый ток, а сильные подтягивающие резисторы имеют низкую стоимость, что позволяет протекать сильному току. Все MCU в основном используют слабые подтягивающие резисторы. Чтобы активировать это в нашем микроконтроллере PIC, мы должны заглянуть в нашу таблицу данных для OPTION_REG (регистр опций), как показано на снимке ниже.
Как показано, бит 7 касается слабого подтягивающего резистора. Для его активации необходимо обнулить. Это делается OPTION_REG <7> = 0 . В частности, это касается бита 7, оставляя для остальных бит значения по умолчанию. При этом мы попадаем в наш цикл while, где он проверяет, нажата ли кнопка, используя if (RB0 == 0). Если условие выполнено, мы вызываем нашу функцию с параметрами 1, 3, 7 и 15.
sblink (1); // ВЫЗОВ ФУНКЦИИ 1 с параметром 1 sblink (3); // ВЫЗОВ ФУНКЦИИ 3 с параметром 3 sblink (7); // ВЫЗОВ ФУНКЦИИ 7 с параметром 7 sblink (15); // ВЫЗОВ ФУНКЦИИ 4 с параметром 15
Почему мы используем функции?
Функции используются для уменьшения количества строк в нашем коде. Это то, что знало бы большинство из нас. Но зачем нам уменьшать количество строк, особенно если речь идет о программировании MCU. Причиной является ограниченным пространством в нашей памяти программы. Если мы не оптимизируем код должным образом, нам может не хватить места в памяти. Это пригодится, когда мы будем писать длинные страницы кода.
Любая функция будет иметь определение функции (в нашем случае sblink (int get) ) и вызов функции ( в нашем случае sblink (1) ). Объявление функции необязательно, чтобы избежать этого, я поместил определение функции перед вызовом функции в моей основной функции.
Параметры функции - это значение, которое будет передано из вызова функции в определение функции. В нашем случае целочисленные значения (1, 3, 7, 15) - это параметры, которые передаются из вызова функции, а переменная get получает значение параметров в определение функции для их обработки. Функция может иметь более одного параметра.
После вызова функции будут выполнены следующие строки в определении функции.
for (int i = 0; i <= 7 && RB0 == 0; i ++) {PORTD = get << i; // Перемещение светодиода влево __delay_ms (50); } for (int i = 7; i> = 0 && RB0 == 0; i--) {PORTD = get << i; // Перемещение светодиода влево __delay_ms (50); }
Теперь эта строка кажется странной: PORTD = get << i . Я объясню, что здесь происходит на самом деле.
«<<» - это оператор сдвига влево, который сдвигает все биты в левую позицию. Теперь, когда мы вызываем функцию sblink (int get) с параметром '1' как sblink (1), она делает значение 'get' равным 1, что в двоичном формате равно 0b00000001. Следовательно, эта строка будет иметь вид PORTD = 0b00000001 << i .
Значение «i» будет варьироваться от 0 до 7, поскольку мы использовали цикл for для (int i = 0; i <= 7 && RB0 == 0; i ++). Значение i от 0 до 7 изменит результат следующим образом:
Как видите, мы включили по одному светодиоду (слева направо), оставив остальные выключенными. Следующий цикл for для (int i = 7; i> = 0 && RB0 == 0; i--) также будет делать то же самое, но на этот раз светодиод будет включаться справа налево в последовательности, поскольку мы начали с 7 и опустились до 0. Мы использовали задержку в 200 мс, чтобы мы могли визуализировать, как светодиод включается и выключается.
Теперь, когда мы передаем значение 3 в функцию sblink (int get) , будет выполняться функция sblink (3) , которая делает значение 'get' равным 0b00000011, следовательно, результат в PORTD будет:
Итак, теперь на этот раз при использовании sblink (3) будут гореть два светодиода в любой момент времени . Аналогично для sblink (7) и sblink (15) три и четыре светодиода будут гореть последовательно. Как только это будет завершено, мы включим все светодиоды, используя строку PORTD = 0xFF . Посмотрите видео ниже для полной демонстрации.
Надеюсь, вы поняли код и, таким образом, узнали, как использовать функции, цикл for и while для получения желаемых результатов. Теперь вы можете настроить код, чтобы получить другую последовательность мигания светодиода. Скомпилируйте свой код, сбросьте его на свой MCU и наслаждайтесь результатом. Вы можете использовать раздел комментариев, если где-то застряли. Я также приложил сюда файлы моделирования и программы.
Вот и все, в нашем следующем руководстве мы узнаем, как использовать таймеры PIC16F877A вместо использования функций задержки. Вы можете просмотреть все руководства по микроконтроллерам PIC здесь.