Учебный курс. Подключение lcd к микроконтроллеру. Добавляем поддержку 4-ех разрядной шины
Запись команд/данных
Операция записи для 4-ех разрядной шины выглядит так:
1. установить RS (1- данные , 0 - команды)
2. вывести старшую тетраду байта данных на линии шины DB4 – DB7
3. установить E = 1
4. программная задержка 1
5. установить E = 0
6. вывести младшую тетраду байта данных на линии шины DB4 – DB7
7. установить E = 1
8. программная задержка 1
9. установить E = 0
10. программная задержка 2
Ничего сложного и можно “в лоб” написать, функцию записи команд.
void LCD_WriteCom(unsigned char data)
{
ClearBit(PORT_SIG, RS); //RS = 0
PORT_DATA = data; //выводим старшую тетраду
SetBit(PORT_SIG, EN);
_delay_us(2);
ClearBit(PORT_SIG, EN);
data = __swap_nibbles(data); //меняем тетрады местами
PORT_DATA = data; //выводим младшую тетраду
SetBit(PORT_SIG, EN);
_delay_us(2);
ClearBit(PORT_SIG, EN);
_delay_us(40);
}
void LCD_WriteCom(unsigned char data)
{
ClearBit(PORT_SIG, RS); //RS = 0
unsigned char tmp;
tmp = PORT_DATA & 0x0f;
tmp |= (data & 0xf0);
PORT_DATA = tmp; //выводим старшую тетраду
SetBit(PORT_SIG, EN);
_delay_us(2);
ClearBit(PORT_SIG, EN);
data = __swap_nibbles(data); //меняем тетрады местами
tmp = PORT_DATA & 0x0f;
tmp |= (data & 0xf0);
PORT_DATA = data; //выводим младшую тетраду
SetBit(PORT_SIG, EN);
_delay_us(2);
ClearBit(PORT_SIG, EN);
_delay_us(40);
}
В хедер добавляем
#define BUS_4BIT
В сишный файл
#pragma inline = forced
void LCD_CommonFunc(unsigned char data)
{
#ifdef BUS_4BIT
//код для 4-ех разрядной шины
unsigned char tmp;
tmp = PORT_DATA & 0x0f;
tmp |= (data & 0xf0);
PORT_DATA = tmp;
SetBit(PORT_SIG, EN);
_delay_us(2);
ClearBit(PORT_SIG, EN);
data = __swap_nibbles(data);
tmp = PORT_DATA & 0x0f;
tmp |= (data & 0xf0);
PORT_DATA = data;
SetBit(PORT_SIG, EN);
_delay_us(2);
ClearBit(PORT_SIG, EN);
#else
//код для 8-ми разрядной шины
PORT_DATA = data;
SetBit(PORT_SIG, EN);
_delay_us(2);
ClearBit(PORT_SIG, EN);
#endif
}
Инициализация lcd
DDRX_DATA |= 0xf0;
PORT_DATA |= 0xf0;
DDRX_SIG |= (1<<RW)|(1<<RS)|(1<<EN);
PORT_SIG |= (1<<RW)|(1<<RS)|(1<<EN);
ClearBit(PORT_SIG, RW);
_delay_ms(40);
LCD_WriteCom(0x28); //4-ех разрядная шина, 2 строки
LCD_WriteCom(0xf); //0b00001111 - дисплей, курсор, мерцание включены
LCD_WriteCom(0x1); //0b00000001 - очистка дисплея
_delay_ms(2);
LCD_WriteCom(0x6); //0b00000110 - курсор движется вправо, сдвига нет
А так будет выглядеть исправленная функции инициализации нашей библиотеки.
void LCD_Init(void)
{
#ifdef BUS_4BIT
DDRX_DATA |= 0xf0;
PORT_DATA |= 0xf0;
DDRX_SIG |= (1<<RW)|(1<<RS)|(1<<EN);
PORT_SIG |= (1<<RW)|(1<<RS)|(1<<EN);
ClearBit(PORT_SIG, RW);
_delay_ms(40);
LCD_WriteCom(0x28); //4-ех разрядная шина, 2 строки
#else
DDRX_DATA |= 0xff;
PORT_DATA |= 0xff;
DDRX_SIG |= (1<<RW)|(1<<RS)|(1<<EN);
PORT_SIG |= (1<<RW)|(1<<RS)|(1<<EN);
ClearBit(PORT_SIG, RW);
_delay_ms(40);
LCD_WriteCom(0x38); //0b00111000 - 8 разрядная шина, 2 строки
#endif
LCD_WriteCom(0xf); //0b00001111 - дисплей, курсор, мерцание включены
LCD_WriteCom(0x1); //0b00000001 - очистка дисплея
_delay_ms(2);
LCD_WriteCom(0x6); //0b00000110 - курсор движется вправо, сдвига нет
}
Чтение флага занятости lcd
Для 4-ех разрядной шины цикл чтения выглядит следующим образом
1. Сделать порт входом
2. Установить RW = 1
3. Установить RS = 0
4. Установить EN = 1
5. Програмная задержка 1
6. Считать старшую тетраду байта данных
7. Установить EN = 0
8. Установить EN = 1
9. Програмная задержка 1
10. Считать младшую тетраду байта данных
11. Установить EN = 0
Код на Си будет такой
unsigned char data;
DDRX_DATA &= 0x0f; //конфигурируем порт на вход
PORT_DATA |= 0xf0; //включаем pull-up резисторы
SetBit(PORT_SIG, RW); //RW в 1 чтение из lcd
ClearBit(PORT_SIG, RS); //RS в 0 команды
SetBit(PORT_SIG, EN);
_delay_us(2);
data = PIN_DATA & 0xf0; //чтение данных с порта
ClearBit(PORT_SIG, EN);
data = __swap_nibbles(data);
SetBit(PORT_SIG, EN);
_delay_us(2);
data |= PIN_DATA & 0xf0; //чтение данных с порта
ClearBit(PORT_SIG, EN);
data = __swap_nibbles(data);
7 бит считанного байта – будет флагом занятости BF.
Исправим функцию ожидания флага BF
Без "начинки" она будет выглядеть так:
void LCD_Wait(void)
{
#ifdef CHECK_FLAG_BF
#ifdef BUS_4BIT
//код для 4-ех разрядной шины
#else
//код для 8-ми разрядной шины
#endif
#else
_delay_us(40);
#endif
}
Окончательный вариант функции LCD_Wait() будет таким:
#pragma inline = forced
void LCD_Wait(void)
{
#ifdef CHECK_FLAG_BF
#ifdef BUS_4BIT
unsigned char data;
DDRX_DATA &= 0x0f; //конфигурируем порт на вход
PORT_DATA |= 0xf0; //включаем pull-up резисторы
SetBit(PORT_SIG, RW); //RW в 1 чтение из lcd
ClearBit(PORT_SIG, RS); //RS в 0 команды
do{
SetBit(PORT_SIG, EN);
_delay_us(2);
data = PIN_DATA & 0xf0; //чтение данных с порта
ClearBit(PORT_SIG, EN);
data = __swap_nibbles(data);
SetBit(PORT_SIG, EN);
_delay_us(2);
data |= PIN_DATA & 0xf0; //чтение данных с порта
ClearBit(PORT_SIG, EN);
data = __swap_nibbles(data);
}while((data & (1<<FLAG_BF))!= 0 );
ClearBit(PORT_SIG, RW);
DDRX_DATA |= 0xf0;
#else
unsigned char data;
DDRX_DATA = 0; //конфигурируем порт на вход
PORT_DATA = 0xff; //включаем pull-up резисторы
SetBit(PORT_SIG, RW); //RW в 1 чтение из lcd
ClearBit(PORT_SIG, RS); //RS в 0 команды
do{
SetBit(PORT_SIG, EN);
_delay_us(2);
data = PIN_DATA; //чтение данных с порта
ClearBit(PORT_SIG, EN);
}while((data & (1<<FLAG_BF))!= 0 );
ClearBit(PORT_SIG, RW);
DDRX_DATA = 0xff;
#endif
#else
_delay_us(40);
#endif
}
Все. Библиотека исправлена, вот что получилось - драйвер символьного lcd.
Файлы
Подключение LCD по 4-ех разрядной шине. Проект для IARa.Подключение LCD по 4-ех разрядной шине. Проект для WINAVR.
Comments
скажите куда отправить .
Хочется на нее посмотреть.
А вот протеус в это верить отказывается напрочь, поэтому пришлось всё же прописать отправку по частям. На практике пока не проверял.
по 4-х разрядной шине не удается вывести 2-ю и 4-ю строку. Инициализируетс я он как 2-х строчный, "условно первая" строка (32 символа)в нем выводится как 1-я и 3-я, а "условно вторую" не удается вывести как 2-ю и 4-ю. Как подправить драйвер для работы с 4-х строчным индикатором? Инфа по работе с таким индикатором практически полностью отсутствует в интернете!
#define __swap_nibbles( Data) Data=((Data&0x0 f)4)
растолкуйте, что это все значит?
Y - может принимать значения 0 и 1. Поэтому для безопасности добавлен код y&1. Какой бы Y вы туда не подставили, его значение не превысит единицу.
x - может принимать значения от 0 до 7. Поэтому добавлен код, ограничивающий его возможные значения - x&7.
Нижняя строка дисплея начинается с адреса 0x40, верхняя 0x00. Умножая значение Y на 0x40 мы получаем соответствующий адрес строки, а прибавляя значение x&7, получаем адрес определенного знакоместа.
|128 - установка 7-ого разряда в единицу. Он сообщает контроллеру дисплея, о том что передаваемая команда является DDRAM адресом. Ну по сути адресом в который должен встать курсор дисплея.
...x ведь может принимать значение до 40 (реальная длина строки дисплея)
Второй пример - предположим вторая строка, пятое знакоместо. (х,у 1 и 4)
Что-то у меня по этой логике не срабатывает
#define SwapNibbles(Data) (((Data)&0x0f)<<4)+(((Data)&0xf0)>>4))
Нельзя для объединения битовых полей использовать сложение, только битовое "или", т.е. |
Хоть во многих случаях это прокатывает, как и в представленном случае, но однажды можно нарваться на переустановку уже установленного бита и часть поля после этого бита (включая его) поедет влево.
Code:
PORT_DATA = tmp;
Аналогично:
Code:
PORT_DATA = 0b0000XXXX;
а не отдельным его пинам(PD4-PD7) при 4х разрядном подключении. Ведь PD0-PD3 могут использоваться для какихто других целей, например включать какую-нибудь нагрузку, что приведет к её отключении.
Code:
unsigned char tmp;
tmp = PORT_DATA & 0x0f;
tmp |= (data & 0xf0);
RSS feed for comments to this post