В предыдущей статье на нашем сайте мы рассмотрели мигание одним светодиодом с помощью микроконтроллера PIC. Схему проекта мы собрали на перфорированной плате и использовали PICkit 3, ICSP и MPLAB IPE для загрузки программы в микроконтроллер. В этой же статье мы задействуем большее число контактов микроконтроллера PIC16F877A и рассмотрим управление с его помощью последовательностью из 7 светодиодов.
Также в статье мы рассмотрим основы управления несколькими входами и выходами микроконтроллеров PIC, использование функций и цикла типа ‘for’ в данных микроконтроллерах.
Перфорированную плату с микроконтроллером PIC16F877A можно использовать из нашего предыдущего проекта, а светодиоды с токоограничивающими резисторами разместим на другой перфорированной плате как показано на следующем рисунке. Также в схему добавим кнопку для запуска процесса мигания последовательности светодиодов.
Необходимые компоненты
- Микроконтроллер PIC16F877A (купить на AliExpress).
- Программатор PicKit 3 (купить на AliExpress).
- Держатель микросхем на 40 контактов (купить на AliExpress).
- Кварцевый генератор на 20 МГц (купить на AliExpress).
- Конденсаторы 33 пФ – 2 шт. (купить на AliExpress).
- Резисторы 680 Ом, 10 кОм и 560 Ом (купить на AliExpress).
- Светодиоды (купить на AliExpress).
- Микросхема регулятора напряжения 7805 (купить на AliExpress).
- Набор для пайки.
- Перфорированная плата (Perf board).
- Соединительные провода.
- Адаптер питания на 12V.
Схема проекта
Схема проекта мигания последовательностью светодиодов с помощью микроконтроллера PIC представлена на следующем рисунке.
Объяснение программы для микроконтроллера PIC
Полный код программы для мигания последовательностью светодиодов приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Мигание последовательности светодиодов будет начинаться после нажатия кнопки в схеме нашего проекта. Мы рекомендуем вам сначала посмотреть видео с работой проекта, приведенное в конце статьи, чтобы лучше понять принцип работы нашей программы.
В первых строках программы мы установим значения битов конфигурации, назначение которых мы рассматривали в данной статье.
1 2 3 |
TRISB0=1; //Instruct the MCU that the PORTB pin 0 is used as input for button. TRISD = 0x00; //Instruct the MCU that all pins are output PORTD=0x00; //Initialize all pins to 0 |
Регистры TRIS в микроконтроллерах PIC используются для задания режимов работы его контактов (на ввод или вывод данных), а слово PORT используется для установки на контактах уровней High/Low. В нашем случае команда TRISB0=1 означает что 0-й контакт PORT B будет работать на ввод данных (в качестве цифрового входа) – к нему подключена кнопка в схеме нашего проекта. Команда TRISD = 0x00 устанавливает режим работы всех контактов порта D на вывод данных, а команда PORTD=0x00 устанавливает на всех контактах этого порта уровень LOW.
В схеме нашего проекта кнопка подключена к контакту B0 микроконтроллера PIC. При нажатии кнопки на контакт B0 будет подавать уровень с общего провода схемы (ground). Чтобы сделать работу данного фрагмента схемы устойчивой к случайным перепадам напряжения кнопку можно подключить к микроконтроллеру с помощью подтягивающего резистора как показано на следующем рисунке.
Но микроконтроллеры PIC имеют внутренние подтягивающие резисторы на своих контактах, которые можно подключить программным способом. Данные подтягивающие резисторы особенно полезны в проектах когда к микроконтроллеру необходимо подключать несколько кнопок.
Подтягивающие резисторы бывают двух типов: "слабые" и "сильные". "Слабые" подтягивающие резисторы (Weak Pull Up) имеют большой номинал сопротивления и поэтому могут пропускать через себя лишь небольшие токи, а "сильные" подтягивающие резисторы (Strong Pull Up) имеют небольшой номинал сопротивления и поэтому могут пропускать через себя значительные токи. Большинство современных микроконтроллеров имеют на своих контактах встроенные "слабые" подтягивающие резисторы.
Для того чтобы задействовать в нашем микроконтроллере PIC внутренние подтягивающие резисторы, необходимо посмотреть на его даташит в раздел OPTION_REG (option register) как показано на следующем скриншоте.
Как показано на представленном рисунке, бит 7 управляет работой "слабых" подтягивающих резисторов. Чтобы их активировать необходимо в данный бит записать значение 0. Это можно сделать с помощью команды OPTION_REG<7>=0 – эта команда позволяет изменить только значение бита 7 не затрагивая значения остальных битов. Когда значение этого бита установлено, далее мы в цикле loop будем проверять нажата ли кнопка (if RB0==0). Если условие выполнено, то мы будем вызывать функцию мигания светодиодами с параметрами 1, 3, 7 и 15.
1 2 3 4 |
sblink(1); //FUNCTION CALL 1 with parameter 1 sblink(3); //FUNCTION CALL 3 with parameter 3 sblink(7); //FUNCTION CALL 7 with parameter 7 sblink(15); //FUNCTION CALL 4 with parameter 15 |
Почему в программе мы используем функции
Функции используются для уменьшения общего количества строк в программе. Почему мы должны стремиться к уменьшению количества строк в программе? Ответ на это вопрос прост – память микроконтроллера ограничена и если наша программа будет очень большой по объему, то она просто не сможет записаться полностью в ограниченный объем памяти микроконтроллера. Поэтому в большинстве случаев, если есть возможность, необходимо стремиться к уменьшению общего количества строк/объема программы.
Любую функцию необходимо запрограммировать отдельным блоком в программе под необходимым названием (в нашем случае это sblink(int get)) и затем вызывать ее в основной программе с заданным значением параметра/параметров (например, sblink(1)).
Тело нашей функции sblink(int get) выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 |
for (int i=0; i<=7 && RB0==0; i++) { PORTD = get << i; //LED move Left Sequence __delay_ms(50); } for (int i=7; i>=0 && RB0==0; i--) { PORTD = get << i; //LED move Left Sequence __delay_ms(50); } |
В коде этой функции вам показаться странной команда PORTD = get << i. Попробуем объяснить что она значит.
"<<" – это оператор сдвига влево, который сдвигает все биты влево. К примеру, если мы будем вызывать в основной программе нашу функцию sblink(int get) с параметром ‘1’, то есть sblink(1), что в двоичном виде можно представить как 0b00000001. То есть наша команда будет выглядеть следующим образом: PORTD = 0b00000001<< i.
Значение переменной "i" изменяется в цикле (int i=0; i<=7 && RB0==0; i++) в диапазоне от 0 до 7. Изменение значения переменной "i" в диапазоне от 0 до 7 приведет к следующим результатам нашей операции:
Как вы можете видеть, в результате мы включаем только один наш светодиод из 7, остальные 6 будут находиться в выключенном состоянии. В результате выполнения второго цикла нашей функции (int i=7; i>=0 && RB0==0; i--) мы будем аналогичным образом в один момент времени включать один светодиод, но в данном случае светодиоды будут включаться в обратной последовательности. Мы используем задержку 200ms чтобы человеческий глаз смог комфортно различать моменты включения/выключения светодиодов.
Если же мы нашу функцию sblink(int get) будем вызывать с параметром 3, то есть sblink(3), что в двоичном коде означает 0b00000011, то в результате наших операций сдвига мы получим следующую картину:
То есть в данном случае одновременно будут зажигаться по 2 светодиода. Аналогичным образом, в случае вызова sblink(7) и sblink(15), будут одновременно зажигаться 3 и 4 светодиода соответственно. Затем, если кнопка еще нажата, мы будем зажигать все светодиоды с помощью команды PORTD=0xFF. Более подробно работу этого проекта вы можете посмотреть на видео, приведенном в конце статьи
Исходный код программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
// 'C' source line config statements // CONFIG #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled) #pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming) #pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. #include <xc.h> #define _XTAL_FREQ 20000000 //Specify the XTAL crystall FREQ (задаем частоту кварцевого генератора) /*******Sequence blink*******/ sblink(int get) //Function definition with "get" as parameter { for (int i=0; i<=7 && RB0==0; i++) { PORTD = get << i; //LED move Left Sequence (сдвигаем последовательность влево) __delay_ms(50); } for (int i=7; i>=0 && RB0==0; i--) { PORTD = get << i; //LED move Left Sequence __delay_ms(50); } } /*******MAIN Function*******/ void main() //The main function { TRISB0=1; //Instruct the MCU that the PORTB pin 0 is used as input for button. (режим работы контакта, к которому подключена кнопка, на ввод данных) TRISD = 0x00; //Instruct the MCU that all pins are output (режим работы контактов, к которым подключены светодиоды, на вывод данных) PORTD=0x00; //Initialize all pins to 0 (на все контакты порта D подаем 0) OPTION_REG= 0b00000000;//Enable pull up R on port B (подключаем внутренние подтягивающие резисторы на порту B) while(1) //Get into the Infinite While loop { if (RB0==0) { sblink(1); //FUNCTION CALL 1 with parameter 1 (вызываем функцию с параметром 1) sblink(3); //FUNCTION CALL 3 with parameter 3 (вызываем функцию с параметром 3) sblink(7); //FUNCTION CALL 7 with parameter 7 (вызываем функцию с параметром 7) sblink(15); //FUNCTION CALL 4 with parameter 15 (вызываем функцию с параметром 15) while(RB0==0) //If button is still pressed (если кнопка все еще нажата) { PORTD=0xFF; //Turn ON all LEDs (включаем все светодиоды) } } } } |