Дисплеи на основе контроллера HD44780 хороши собой и очень удобны в использовании, но имеют один недостаток - ограниченный набор символов. Может возникнуть ситуация, когда нужного для нашего проекта символа в стандартном наборе просто не окажется и что же тогда делать. Выход из этой ситуации есть.
Производители контроллеров HD44780 предусмотрели возможность такой ситуации, поэтому в памяти контроллера есть специальные символы по адресам 0х00 - 0х07 - это переопределяеемые символы, графическое изображение которых может назначить сам пользователь. а значит что мы в контроллер можем записать до 8 необходимых нам символов.
Разберемся как же это сделать.
Ответ я нашел в одном из примеров, который шел вместе с CodeVisionAVR (кстати это не единственный полезны и интересный пример, они располагаются Папка с программой\examples\).
И так для записи одного символа в контроллер дисплея нам необходимо сделать следующее:
typedef unsigned char byte; \\ Объявляем новый тип переменной
\\ Определяем непосредственно сам символ (имеет 5 точек в ширину и 7 в высоту)
flash byte char0[8]={
0b10000000,
0b10001111,
0b10000011,
0b10000101,
0b10001001,
0b10010000,
0b10100000,
0b11000000};
Единичками на дисплее будут закрашенные точки, все что оставлено нулями будет пустым.
\\ Определяем функцию записи нашего символа в экран
void define_char(byte flash *pc,byte char_code)
{
byte i,a;
a=(char_code<<3)|0x40;
for (i=0; i<8; i++) lcd_write_byte(a++,*pc++);
}
Это была предварительная подготовка. Теперь в основном теле функции main необходимо произвести запись символа в контроллер дисплея, что делаем командой define_char(char0,0); в скобках указываем название символа, которые мы задали выше и его адрес в контроллере, т.е. символ char0 будет доступен по адресу 0х00.
Запись символа производится строго только после инициализации дисплея, никак не до ( то есть после lcd_init(16);).
Все основное для записи у нас есть, теперь используем на примере.
Пример 1
В прошлой статье я рассматривал способ создания прогрессбара на стандартном символе 0xFF. Прогрессбар состоял из 16 прямоугольников и заполнялся скажем так несколько грубовато, добавив несколько своих символов в экран мы можем сделать его плавным.
Подготовим коды новых символов, так как символ состоит из 5 столбцов то можно нарисовать пять этих столбцов и постепенно закрашивать символ целиком:
flash byte char0[8]={
0b0010000,
0b0010000,
0b0010000,
0b0010000,
0b0010000,
0b0010000,
0b0010000,
0b0000000};
flash byte char1[8]={
0b0011000,
0b0011000,
0b0011000,
0b0011000,
0b0011000,
0b0011000,
0b0011000,
0b0000000};
flash byte char2[8]={
0b0011100,
0b0011100,
0b0011100,
0b0011100,
0b0011100,
0b0011100,
0b0011100,
0b0000000};
flash byte char3[8]={
0b0011110,
0b0011110,
0b0011110,
0b0011110,
0b0011110,
0b0011110,
0b0011110,
0b0000000};
flash byte char4[8]={
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0000000};
Последний символ есть среди стандартных, но я его решил тоже записать для упрощения кода анимации, потому как символы находящиеся друг за другом перебирать проще чем разбросанные.
#include <delay.h> // Подключаем библиотеку реализации задержек
#define pause delay_ms(100) // Объявляем переменную pause, которая будет осуществлять задержку
char i; // Переменная для счетчика для анимации
char j; // Переменная для счетчика для анимации
typedef unsigned char byte; // Новый тип переменной для наших символов
// Побитное изображение нашего символа для записи в контроллер дисплея
flash byte char0[8]={
0b0010000,
0b0010000,
0b0010000,
0b0010000,
0b0010000,
0b0010000,
0b0010000,
0b0000000};
flash byte char1[8]={
0b0011000,
0b0011000,
0b0011000,
0b0011000,
0b0011000,
0b0011000,
0b0011000,
0b0000000};
flash byte char2[8]={
0b0011100,
0b0011100,
0b0011100,
0b0011100,
0b0011100,
0b0011100,
0b0011100,
0b0000000};
flash byte char3[8]={
0b0011110,
0b0011110,
0b0011110,
0b0011110,
0b0011110,
0b0011110,
0b0011110,
0b0000000};
flash byte char4[8]={
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0000000};
// Функция записи символа в контроллер дисплея
void define_char(byte flash *pc,byte char_code)
{
byte i,a;
a=(char_code<<3)|0x40;
for (i=0; i<8; i++) lcd_write_byte(a++,*pc++);
}
// Функция инициализации микроконтроллера
void avr_init(void)
{
PORTA=0x00;
DDRA=0x00;
PORTB=0x00;
DDRB=0x00;
PORTC=0x00;
DDRC=0x00;
PORTD=0x00;
DDRD=0x00;
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
MCUCR=0x00;
MCUCSR=0x00;
TIMSK=0x00;
ACSR=0x80;
SFIOR=0x00;
}
// Основная программа
void main(void)
{
avr_init();
lcd_init(16); // Инициализация экрана
// Запись символов char0 - char4 по адресам 0х00 - 0х04 соответственноdefine_char(char0,0);
define_char(char1,1);
define_char(char2,2);
define_char(char3,3);
define_char(char4,4);
lcd_clear(); // Очищаем экран
while (1)
{i=0; // код вывода анимации из новых символов на дисплей
lcd_clear();
j=0;
while(j<16){
i=0;
while(i<5){
lcd_gotoxy(j,0);
lcd_putchar(i);
i++;
pause;
};
j++;
};
};
}
выполнения будет прогрессбар, плавно заполняющий первую строку дисплея:
Пример 2
Индикатор заряда аккумулятора или батареи.
Каждый из нас регулярно наблюдает такой индикатор на своем мобильном телефоне. Обычно это изображение батарейки, которая по мере разряда аккумулятора пустеет. Допустим ваше устройство использует в своей работе аккумулятор или питается от него и вы хотите себе индикатор заряда как на своем мобильном - легко. сейчас я не буду разбирать как работать с АЦП микроконтроллера и делать измерения, а в продолжение темы просто приведу пример такого индикатора.
#include <delay.h> // Подключаем библиотеку реализации задержек
#define pause delay_ms(100) // Объявляем переменную pause, которая будет осуществлять задержку
char i; // Переменная для счетчика для анимации
typedef unsigned char byte; // Новый тип переменной для наших символов
// Побитное изображение нашего символа для записи в контроллер дисплея
flash byte char0[8]={
0b0001110,
0b0010001,
0b0010001,
0b0010001,
0b0010001,
0b0010001,
0b0011111,
0b0000000};
flash byte char1[8]={
0b0001110,
0b0010001,
0b0010001,
0b0010001,
0b0010001,
0b0011111,
0b0011111,
0b0000000};
flash byte char2[8]={
0b0001110,
0b0010001,
0b0010001,
0b0010001,
0b0011111,
0b0011111,
0b0011111,
0b0000000};
flash byte char3[8]={
0b0001110,
0b0010001,
0b0010001,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0000000};
flash byte char4[8]={
0b0001110,
0b0010001,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0000000};
flash byte char5[8]={
0b0001110,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0011111,
0b0000000};
// Функция записи символа в контроллер дисплея
void define_char(byte flash *pc,byte char_code)
{
byte i,a;
a=(char_code<<3)|0x40;
for (i=0; i<8; i++) lcd_write_byte(a++,*pc++);
}
// Функция инициализации микроконтроллера
void avr_init(void)
{
PORTA=0x00;
DDRA=0x00;
PORTB=0x00;
DDRB=0x00;
PORTC=0x00;
DDRC=0x00;
PORTD=0x00;
DDRD=0x00;
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
MCUCR=0x00;
MCUCSR=0x00;
TIMSK=0x00;
ACSR=0x80;
SFIOR=0x00;
}
// Основная программа
void main(void)
{
avr_init();
lcd_init(16); // Инициализация экрана
// Запись символов char0 - char4 по адресам 0х00 - 0х04 соответственноdefine_char(char0,0);
define_char(char1,1);
define_char(char2,2);
define_char(char3,3);
define_char(char4,4);
define_char(char5,5);
lcd_clear(); // Очищаем экран
while (1)
{ // код вывода анимации из новых символов на дисплей
i=5;
while(i>0){
lcd_gotoxy(0,1);
lcd_putchar(i);
pause;
i--;
};
};
}
Результат выполнения:
Все изложенное выше это не прямая инструкция к применению, а просто идеи и попытки изучения стандартных возможностей экрана. Это лишь несколько примеров, но рассмотренны все основные принципы, а вариантов использованиия может быть очень много.