Вывод символов и строк на LCD дисплей 1602A, с управляющим контроллером KS0066U.
В прошлой статье мы рассмотрели как произвести инициализацию LCD дисплея по 4-битной шине, но вместо проверки флага занятости, мы использовали задержку. Давайте с его реализации и начнём.
Для проверки флага занятости надо выполнить следующие действия:
Давайте оформим это в виде кода.
Флаг занятости надо опрашивать перед началом любой операции, на этом по флагу занятости всё.
Последовательность действий для отправки команд/данных, была описана в прошлой статье и отличается лишь битом RS, для отправки команд RS=0, для отправки данных RS=1, поэтому вынесем общую часть в отдельную функцию.
Тогда отправка команд будет выглядеть следующим образом
А отправка данных
В документации на контроллер есть таблица команд, которая позволяет с ним работать
Пользуясь этой таблицей, давайте разберёмся как вывести символ на экран.
Для вывода символа на LCD дисплей надо:
Указать позицию на экране можно с помощью команды, изображённой ниже.
Битами AC0 – AC6 кодируется адрес позиции на экране.
А записать данные в эту позицию можно с помощью следующей инструкции.
Битами D0 – D7 кодируется адрес символа из таблицы
Кодировка английских букв в таблице знакогенератора соответствует ascii кодам, что очень удобно, можно передавать в функцию строковые литералы.
Выводить один символ на дисплей требуется крайне редко, поэтому давайте напишем функцию для вывода строки на дисплей.
Как известно строка в си оканчивается экранированным нулём('\0'), экранировка означает, что в память запишется непосредственно сам символ, а не его кодировка.
Ниже видно как в памяти хранятся следующие строки.
Функция посимвольно выводит строку на дисплей, пока не встретит его.
А теперь выведем на дисплей название сайта.
А на этом всё, в следующей статье мы попробуем нарисовать свой символ на дисплее.
Для проверки флага занятости надо выполнить следующие действия:
- порт данных на вход с подтяжкой
- RS = 0
- R/W = 1
- поднимаем строб E=1
- задержка
- читаем старшую тетраду и фиксируем чему равен 7-й бит
- опускаем строб E=0
- задержка
- поднимаем строб E=1
- задержка, младшую тетраду читать не будем
- опускаем строб E=0
- если 7-й бит равен единице(контроллер занят), то повторяем операцию снова, если равен нулю — то выставляем порт данных на выход и выходим
Давайте оформим это в виде кода.
uint8_t Busy_Flag = 0;
void Check_Busy(void)
{
//шина данных - вход с подтяжкой
LCD_DDR &= ~DATA_BUS;
LCD_PORT |= DATA_BUS;
//будем читать из lcd
LCD_PORT |= (1<<RW);
LCD_PORT &= ~(1<<RS);
do
{
//поднимаем строб
LCD_PORT |= (1<<E);
_delay_us(2);
//фиксируем чему равен 7-й бит
Busy_Flag = (LCD_PIN & 0X80);
//опускаем строб
LCD_PORT &= ~(1<<E);
_delay_us(1);
LCD_PORT |= (1<<E);
_delay_us(2);
LCD_PORT &= ~(1<<E);
}
while (Busy_Flag);
//шина данных - выход
LCD_DDR |= DATA_BUS;
//переводим LCD в режим записи
LCD_PORT &= ~(1<<RW);
}
Флаг занятости надо опрашивать перед началом любой операции, на этом по флагу занятости всё.
Последовательность действий для отправки команд/данных, была описана в прошлой статье и отличается лишь битом RS, для отправки команд RS=0, для отправки данных RS=1, поэтому вынесем общую часть в отдельную функцию.
void Common_Write_Func(uint8_t data)
{
//переменная для хранения старшей тетрады
uint8_t temp = (data & 0XF0);
//будем писать
LCD_PORT &= ~(1<<RW);
//шина данных на выход
LCD_DDR |= DATA_BUS;
//затираем прошлую отправку нулями
LCD_PORT &= ~DATA_BUS;
//выставляем старшую тетраду в шину
LCD_PORT |= temp ;
Strob();
//затираем прошлую отправку нулями
LCD_PORT &= ~DATA_BUS;
//выставляем старшую тетраду в шину
LCD_PORT |= (data << 4);
Strob();
}
Тогда отправка команд будет выглядеть следующим образом
void Write_Command(uint8_t data)
{
Check_Busy();
LCD_PORT &= ~(1<<RS);
Common_Write_Func(data);
}
А отправка данных
void Write_Data(uint8_t data)
{
Check_Busy();
LCD_PORT |= (1<<RS);
Common_Write_Func(data);
}
В документации на контроллер есть таблица команд, которая позволяет с ним работать
Пользуясь этой таблицей, давайте разберёмся как вывести символ на экран.
Для вывода символа на LCD дисплей надо:
- указать его позицию на экране
- записать в эту позицию код символа
Указать позицию на экране можно с помощью команды, изображённой ниже.
Битами AC0 – AC6 кодируется адрес позиции на экране.
А записать данные в эту позицию можно с помощью следующей инструкции.
Битами D0 – D7 кодируется адрес символа из таблицы
Кодировка английских букв в таблице знакогенератора соответствует ascii кодам, что очень удобно, можно передавать в функцию строковые литералы.
Выводить один символ на дисплей требуется крайне редко, поэтому давайте напишем функцию для вывода строки на дисплей.
void Draw_String(const char *str)
{
uint8_t data = 0;
while (*str)
{
data = *str++;
Write_Data(data);
}
}
Как известно строка в си оканчивается экранированным нулём('\0'), экранировка означает, что в память запишется непосредственно сам символ, а не его кодировка.
Ниже видно как в памяти хранятся следующие строки.
Draw_String("hubstub");
Draw_String(".ru");
Функция посимвольно выводит строку на дисплей, пока не встретит его.
А теперь выведем на дисплей название сайта.
#define F_CPU 8000000UL
#define LCD_PORT PORTA
#define LCD_DDR DDRA
#define LCD_PIN PINA
#define DATA_BUS 0XF0
#define RS 0
#define RW 1
#define E 2
#include <avr/io.h>
#include <avr/delay.h>
uint8_t Busy_Flag = 0;
void Check_Busy(void)
{
//шина данных - вход с подтяжкой
LCD_DDR &= ~DATA_BUS;
LCD_PORT |= DATA_BUS;
//будем читать из lcd
LCD_PORT |= (1<<RW);
//команды
LCD_PORT &= ~(1<<RS);
do
{
LCD_PORT |= (1<<E);
_delay_us(2);
Busy_Flag = (LCD_PIN & 0X80);
LCD_PORT &= ~(1<<E);
_delay_us(1);
LCD_PORT |= (1<<E);
_delay_us(2);
LCD_PORT &= ~(1<<E);
}
while (Busy_Flag);
//шина данных - выход
LCD_DDR |= DATA_BUS;
//переводим LCD в режим записи
LCD_PORT &= ~(1<<RW);
}
void Strob(void)
{
LCD_PORT |= (1<<E);
_delay_us(2);
LCD_PORT &= ~(1<<E);
}
void Common_Write_Func(uint8_t data)
{
uint8_t temp = (data & 0XF0);
LCD_PORT &= ~(1<<RW);
LCD_DDR |= DATA_BUS;
//затираем прошлую отправку нулями
LCD_PORT &= ~DATA_BUS;
LCD_PORT |= temp ;
Strob();
//затираем прошлую отправку нулями
LCD_PORT &= ~DATA_BUS;
LCD_PORT |= (data << 4);
Strob();
}
void Write_Command(uint8_t data)
{
Check_Busy();
LCD_PORT &= ~(1<<RS);
Common_Write_Func(data);
}
void Write_Data(uint8_t data)
{
Check_Busy();
LCD_PORT |= (1<<RS);
Common_Write_Func(data);
}
void Write_Init_Command(uint8_t data)
{
//ножки по которым передаются команды/данные на выход
LCD_DDR |= DATA_BUS;
//будем слать команду
LCD_PORT &= ~(1<<RS);
LCD_PORT &= ~(1<<RW);
//затираем прошлую отправку нулями
LCD_PORT &= ~DATA_BUS;
//выводим команду в шину
LCD_PORT |= data;
Strob();
_delay_us(100);
}
void LCD_Init(void)
{
_delay_ms(50);
Write_Init_Command(0x20);
Write_Init_Command(0x20);
//включаем дисплей, в режиме 2-х линий
Write_Init_Command(0xC0);
_delay_us(50);
Write_Init_Command(0x00);
//включаем отображение курсора
Write_Init_Command(0xE0);
_delay_us(50);
Write_Init_Command(0x00);
//очищаем дисплей
Write_Init_Command(0x10);
_delay_ms(2);
Write_Init_Command(0x00);
//значение DDRAM увеличивается, без сдвига экрана
Write_Init_Command(0x60);
}
__inline void LCD_Clear(void)
{
Write_Command(0X01);
}
void Draw_String(const char *str)
{
uint8_t data = 0;
while (*str)
{
data = *str++;
Write_Data(data);
}
}
__inline void Set_Ddram_Address(uint8_t address)
{
Write_Command(address | 0x80);
}
int main(void)
{
LCD_DDR = 0XFF;
LCD_Init();
Set_Ddram_Address(0X43);
Draw_String("hubstub");
Draw_String(".ru");
while(1)
{
}
}
А на этом всё, в следующей статье мы попробуем нарисовать свой символ на дисплее.
Похожие статьи