Операция записи для 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
}
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 - курсор движется вправо, сдвига нет
}
Для 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.