Ранее на нашем сайте мы рассматривали основы работы с микроконтроллерами PIC, настройку в них битов конфигурации (фьюзов), мигание светодиодом и работу с таймерами. В этой же статье мы рассмотрим подключение ЖК дисплея 16×2 к микроконтроллеру PIC с использованием программ MPLABX и XC8.
Ранее на нашем сайте мы рассматривали подключение ЖК дисплея 16×2 к следующим микроконтроллерам (платам):
- к микроконтроллеру AVR;
- к плате Arduino;
- к плате Raspberry Pi;
- к плате Raspberry Pi Pico;
- к модулю ESP32;
- к плате STM32 Blue Pill.
Необходимые компоненты
- Микроконтроллер PIC16F877A (купить на AliExpress).
- Программатор PicKit 3 (купить на AliExpress).
- ЖК дисплей 16×2 (купить на AliExpress).
- Держатель микросхем на 40 контактов (купить на AliExpress).
- Кварцевый генератор на 20 МГц (купить на AliExpress).
- Конденсаторы 22 пФ – 2 шт. (купить на AliExpress).
- Светодиод любого цвета (купить на AliExpress).
- Набор для пайки.
- Перфорированная плата (Perf board).
- Соединительные провода.
Реклама: ООО «АЛИБАБА.КОМ (РУ)» ИНН: 7703380158
Функции для подключения ЖК дисплея 16×2 к микроконтроллеру PIC
Для облегчения подключения ЖК дисплея 16×2 к микроконтроллеру PIC мы создадим собственную библиотеку. Для ее подключения мы будем использовать заголовочный файл «MyLCD.h» – в нем будут содержаться все функции, необходимые для подключения и взаимодействия микроконтроллера PIC с ЖК дисплеем 16×2. Далее мы объясним содержание этих функций.
Существует два способа добавить эти функции в ваш проект. Вы можете либо полностью скопировать их код и вставить его в свою программу перед функцией void main(), либо вы можете скачать весь заголовочный файл по приведенной выше ссылке и подключать его в свою программу с помощью команды #include «MyLCD.h».
В этом проекте мы скопировали и вставили код заголовочного файла в нашу основную программу на языке C (в этом случае вам не нужно отдельно скачивать и подключать заголовочный файл). Необходимо отметить, что представленные коды функций поддерживаются только микроконтроллерами серии PIC16F.
Далее кратко рассмотрим каждую из этих функций.
void Lcd_Start(): эту функцию необходимо вызывать первой при работе с ЖК дисплеем. Ее необходимо вызывать только один раз чтобы потом избежать задержек в программе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
void Lcd_Start() { Lcd_SetBit(0x00); for(int i=1065244; i<=0; i--) NOP(); Lcd_Cmd(0x03); __delay_ms(5); Lcd_Cmd(0x03); __delay_ms(11); Lcd_Cmd(0x03); Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD Lcd_Cmd(0x08); //Select Row 1 (выбираем 1-ю строку) Lcd_Cmd(0x00); //Clear Row 1 Display (очищаем 1-ю строку) Lcd_Cmd(0x0C); //Select Row 2 (выбираем 2-ю строку) Lcd_Cmd(0x00); //Clear Row 2 Display (очищаем 2-ю строку) Lcd_Cmd(0x06); } |
Lcd_Clear(): эта функция используется для очистки экрана ЖК дисплея.
1 2 3 4 5 |
Lcd_Clear() { Lcd_Cmd(0); //Clear the LCD Lcd_Cmd(1); //Move the cursor to first position } |
void Lcd_Set_Cursor(x pos, y pos): эта функция позволяет установить курсор ЖК дисплея в позицию, определяемую координатами x и y. К примеру, если нам нужно будет установить курсор на 5-й символ 1-й строки, то достаточно будет вызвать функцию Lcd_Set_Cursor(1, 5).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void Lcd_Set_Cursor(char a, char b) { char temp,z,y; if(a== 1) { temp = 0x80 + b - 1; //80H is used to move the cursor z = temp>>4; //Lower 8-bits y = temp & 0x0F; //Upper 8-bits Lcd_Cmd(z); //Set Row Lcd_Cmd(y); //Set Column } else if(a== 2) { temp = 0xC0 + b - 1; z = temp>>4; //Lower 8-bits y = temp & 0x0F; //Upper 8-bits Lcd_Cmd(z); //Set Row Lcd_Cmd(y); //Set Column } } |
void Lcd_Print_Char(char data): когда курсор установлен в нужную позицию, можно вывести символ на данной позиции с помощью этой функции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void Lcd_Print_Char(char data) //Send 8-bits through 4-bit mode { char Lower_Nibble,Upper_Nibble; Lower_Nibble = data&0x0F; Upper_Nibble = data&0xF0; RS = 1; // => RS = 1 Lcd_SetBit(Upper_Nibble>>4); //Send upper half by shifting by 4 EN = 1; for(int i=2130483; i<=0; i--) NOP(); EN = 0; Lcd_SetBit(Lower_Nibble); //Send Lower half EN = 1; for(int i=2130483; i<=0; i--) NOP(); EN = 0; } |
void Lcd_Print_String(char *a): данная функция позволяет вывести на экран ЖК дисплея строку символов.
1 2 3 4 5 6 |
void Lcd_Print_String(char *a) { int i; for(i=0;a[i]!='\0';i++) Lcd_Print_Char(a[i]); //Split the string using pointers and call the Char function } |
Каждый раз при вызове функции Lcd_Print_Char(char data) соответствующий символ передается на ЖК дисплей. Эти символы передаются на контроллер дисплея HD44780U в форме битов. Данный контроллер, принимая эти биты, отображает соответствующий символ на экране ЖК дисплея в соответствии со следующей таблицей.
Эту таблицу можно найти в даташите на контроллер ЖК дисплея HD44780U.
Схема проекта
Схема подключения ЖК дисплея 16×2 к микроконтроллеру PIC представлена на следующем рисунке.
Схема соединений между ЖК дисплеем 16×2 и микроконтроллером PIC приведена в следующей таблице.
№ контакта ЖК дисплея | Обозначение контакта ЖК дисплея | Обозначение контакта микроконтроллера PIC | № контакта микроконтроллера PIC |
1 | Ground | Ground | 12 |
2 | VCC | +5V | 11 |
3 | VEE | Ground | 12 |
4 | Register Select | RD2 | 21 |
5 | Read/Write | Ground | 12 |
6 | Enable | RD3 | 22 |
7 | Data Bit 0 | NC | — |
8 | Data Bit 1 | NC | — |
9 | Data Bit 2 | NC | — |
10 | Data Bit 3 | NC | — |
11 | Data Bit 4 | RD4 | 27 |
12 | Data Bit 5 | RD5 | 28 |
13 | Data Bit 6 | RD6 | 29 |
14 | Data Bit 7 | RD7 | 30 |
15 | LED Positive | +5V | 11 |
16 | LED Negative | Ground | 12 |
Внешний вид собранной конструкции проекта показан на следующем рисунке.
Также следует помнить о том, что в коде программы необходимо указать контакты микроконтроллера PIC, к которым подключен ЖК дисплей. В нашем проекте это будут следующие контакты:
1 2 3 4 5 6 |
#define RS RD2 #define EN RD3 #define D4 RD4 #define D5 RD5 #define D6 RD6 #define D7 RD7 |
Вы можете изменить их по своему усмотрению – только помните о том, что изменения необходимо будет внести и в схему, и в программу проекта.
После сборки схемы проекта и загрузки программы в микроконтроллер PIC вы можете приступить к тестированию работы проекта.
Исходный код программы
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
#define _XTAL_FREQ 20000000 #define RS RD2 #define EN RD3 #define D4 RD4 #define D5 RD5 #define D6 RD6 #define D7 RD7 #include <xc.h> #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled) #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) //LCD Functions Developed by Circuit Digest. void Lcd_SetBit(char data_bit) //Based on the Hex value Set the Bits of the Data Lines { if(data_bit& 1) D4 = 1; else D4 = 0; if(data_bit& 2) D5 = 1; else D5 = 0; if(data_bit& 4) D6 = 1; else D6 = 0; if(data_bit& 8) D7 = 1; else D7 = 0; } void Lcd_Cmd(char a) { RS = 0; Lcd_SetBit(a); //Incoming Hex value EN = 1; __delay_ms(4); EN = 0; } Lcd_Clear() { Lcd_Cmd(0); //очищаем экран ЖК дисплея Lcd_Cmd(1); //перемещаем курсор на 1-ю позицию } void Lcd_Set_Cursor(char a, char b) { char temp,z,y; if(a== 1) { temp = 0x80 + b - 1; //80H is used to move the curser z = temp>>4; //Lower 8-bits y = temp & 0x0F; //Upper 8-bits Lcd_Cmd(z); //Set Row Lcd_Cmd(y); //Set Column } else if(a== 2) { temp = 0xC0 + b - 1; z = temp>>4; //младшие 8 бит y = temp & 0x0F; // старшие 8 бит Lcd_Cmd(z); //Set Row Lcd_Cmd(y); //Set Column } } void Lcd_Start() { Lcd_SetBit(0x00); for(int i=1065244; i<=0; i--) NOP(); Lcd_Cmd(0x03); __delay_ms(5); Lcd_Cmd(0x03); __delay_ms(11); Lcd_Cmd(0x03); Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD Lcd_Cmd(0x08); //Select Row 1 Lcd_Cmd(0x00); //Clear Row 1 Display Lcd_Cmd(0x0C); //Select Row 2 Lcd_Cmd(0x00); //Clear Row 2 Display Lcd_Cmd(0x06); } void Lcd_Print_Char(char data) //Send 8-bits through 4-bit mode { char Lower_Nibble,Upper_Nibble; Lower_Nibble = data&0x0F; Upper_Nibble = data&0xF0; RS = 1; // => RS = 1 Lcd_SetBit(Upper_Nibble>>4); //Send upper half by shifting by 4 EN = 1; for(int i=2130483; i<=0; i--) NOP(); EN = 0; Lcd_SetBit(Lower_Nibble); //Send Lower half EN = 1; for(int i=2130483; i<=0; i--) NOP(); EN = 0; } void Lcd_Print_String(char *a) { int i; for(i=0;a[i]!='\0';i++) Lcd_Print_Char(a[i]); //Split the string using pointers and call the Char function } int main() { unsigned int a; TRISD = 0x00; Lcd_Start(); while(1) { Lcd_Clear(); Lcd_Set_Cursor(1,1); Lcd_Print_String("Circuit Digest"); Lcd_Set_Cursor(2,1); Lcd_Print_String("WORKING!!"); __delay_ms(2000); } return 0; } |