Хотел описать свою попытку написать универсальный драйвер для символьного дисплея с контроллером HD44780 или его аналогом.
Что-бы он был универсальным, надо что-бы функции задержки и дрыганья ножек на всех микроконтроллерах были одинаковы, естественно этого добиться нельзя.
Поэтому я написал свой код так, что-бы его было очень просто перенести на другие микроконтроллеры — для этого надо реализовать функцию задержки, в которую передаются параметры на сколько микросекунд задержка, и функцию дрыганья одной ножки для каждой ножки подключенной к тому или иному pin дисплея (см. код — timer6wait, setRS, clearRS,setRW, clearRW, setE, clearE, setDB0, clearDB0, setDB1,clearDB1, setDB2, clearDB2, setDB3, clearDB3, setDB4,clearDB4, setDB5, clearDB5, setDB6, clearDB6, setDB7,clearDB7). т.е. в эти функции поместить код, который будет через регистры вашего микроконтроллера, управлять вашим микроконтроллером для того, что-бы он делал то что нужно — задержку на N мкс, сброс напряжения на нужной ножке в 0, установка напряжения в логическую 1 на нужной ножке.
Весь остальной код универсален и его не нужно менять при переходе с одного микроконтроллера на другой(PIC, STM32,STM8, ATMEGA и т.д.). У меня код написан для STM32L1 и для 4 строчного дисплея(по 20 символов в каждой строке).
Разберем код.
В файле hd44780.c реализованы основные функции управления дисплеем в 8-битном режиме.
Функция инициализации дисплея.
void hd44780_init(void)
{
send_DB_0_7_lines(0);
clear_RW();
clear_RS();
clear_E();
timer_6_wait(40000);
send_DB_0_7_lines(0x30);
timer_6_wait(5000);
blink_E();
timer_6_wait(160);
blink_E();
timer_6_wait(160);
blink_E();
timer_6_wait(160);
send_DB_0_7_lines(0x38);
blink_E();
timer_6_wait(160);
hd44780_command(0x10);
hd44780_command(0x0C);
hd44780_command(0x06);
}
Основные задержки и команды взяты из документации на дисплеи МЭЛТ (см. картинку ниже) и в принципе стандартные на HD44780.
Функция выдачи команды на дисплей.
void hd44780_command(uint8_t lcdcmnd)
{
timer_6_wait(40);
send_DB_0_7_lines(lcdcmnd);
clear_RS();
clear_RW();
blink_E();
if(lcdcmnd < 2)
{
timer_6_wait(1700);
}
}
Функция установки нужных значений на ножках дисплея для выдачи команд — реализована для универсальности кода.
void send_DB_0_7_lines(uint8_t command)
{
if((command & 1) == 1)
{
set_DB0();
}
else
{
clear_DB0();
}
if(((command >> 1) & 1) == 1)
{
set_DB1();
}
else
{
clear_DB1();
}
if(((command >> 2) & 1) == 1)
{
set_DB2();
}
else
{
clear_DB2();
}
if(((command >> 3) & 1) == 1)
{
set_DB3();
}
else
{
clear_DB3();
}
if(((command >> 4) & 1) == 1)
{
set_DB4();
}
else
{
clear_DB4();
}
if(((command >> 5) & 1) == 1)
{
set_DB5();
}
else
{
clear_DB5();
}
if(((command >> 6) & 1) == 1)
{
set_DB6();
}
else
{
clear_DB6();
}
if(((command >> 7) & 1) == 1)
{
set_DB7();
}
else
{
clear_DB7();
}
}
Функция моргания сигналом E — строб импульс, необходима для установки команды.
void blink_E(void)
{
set_E();
timer_6_wait(1);
clear_E();
}
Перенос курсора в нужную позицию — для более удобного вывода на дисплей. Перенос в нужную позицию курсора осуществляется командой 0b10000000 (см. таблицу выше). Эта команда выбирает область в DDRAM для последующих операций. Область памяти для каждого символа указана на рисунке ниже.
Так для первой строки смещение 0x00 и команда 0b10000000 — 0x80, для второй смещение 0x40 и команда 0b10000000 — 0xC0, для третьей смещение 0x14 и команда 0b10000000 — 0x94, для четвертой смещение 0x54 и команда 0b10000000 — 0xD4. Для перехода на на нужный столбец (по x) — необходимо прибавить нужное смещение — что и реализовано в коде ниже.
void hd44780_goto(uint8_t x, uint8_t y)
{
uint8_t addr;
switch (y)
{
case 0:
addr = 0x80;
break;
case 1:
addr = 0xC0;
break;
case 2:
addr = 0x94;
break;
default:
addr = 0xD4;
break;
}
addr = addr + x;
hd44780_command(addr);
}
Вывод символа на дисплей.
void hd44780_char(uint8_t ldata)
{
set_RS();
clear_RW();
timer_6_wait(40);
send_DB_0_7_lines(ldata);
blink_E();
}
Вывод строки на дисплей.
void hd44780_write(char ldata[])
{
char k=0;
set_RS();
clear_RW();
while(ldata[k] != 0)
{
timer_6_wait(40);
send_DB_0_7_lines(ldata[k]);
blink_E();
k++;
}
}
В файле hd44780_4bit.c реализованы все те же функции только для 4-битного режима.
В файле hd44780_driver.c реализованы зависимые, от типа микроконтроллера и от схемы подключения дисплея, функции.
Так например для STM32L1
void set_E(void)
{
//PB10
GPIO_SetBits(GPIOB, GPIO_Pin_10);
}
void clear_E(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_10);
}
void set_DB0(void)
{
//PC6
GPIO_SetBits(GPIOC, GPIO_Pin_6);
}
void clear_DB0(void)
{
GPIO_ResetBits(GPIOC, GPIO_Pin_6);
}
************************
и т.д.
Немного неправильно сделано — то что функция для задержки на N мкс лежит в timeworks.c и называется привязано к моему микроконтроллеру. Правильно конечно было ее вынести в hd44780driver.c и переименовать просто в wait_hd44780() — для того, чтобы красиво сформировать библиотечные файлы.
Задержка для STM32L1:
void timer_6_1us_int(void)
{
TIM_TimeBaseInitTypeDef TIMER_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_TimeBaseStructInit(&TIMER_InitStructure);
TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIMER_InitStructure.TIM_Period = 16;
TIMER_InitStructure.TIM_Prescaler = 1;
TIM_TimeBaseInit(TIM6, &TIMER_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_SelectOnePulseMode(TIM6, TIM_OPMode_Single);
TIM_ITConfig(TIM6, TIM_IT_Update,ENABLE);
}
void timer_6_wait(uint32_t n_usec)
{
extern uint8_t f_timer_6_end;
if(n_usec <= 4000)
{
TIM6->PSC = 0;
TIM6->ARR = (uint16_t)(16 * n_usec);
TIM_Cmd(TIM6, ENABLE);
}
else
{
TIM6->PSC = 1000 - 1;
TIM6->ARR = (uint16_t)(16 * (n_usec / 1000));
TIM_Cmd(TIM6, ENABLE);
}
while(f_timer_6_end == 0){}
f_timer_6_end = 0;
}
Код для работы с дисплеем в 8 битном режиме:
send_DB_0_7_lines(0);
hd44780_init_8_bit();
hd44780_command_8_bit(0x01); // clear screen
timer_6_wait(1700);
hd44780_command_8_bit(0x01); // clear screen
hd44780_write_8_bit("It's Work");
Код для работы с дисплеем в 4 битном режиме:
send_DB_4_7_lines(0);
hd44780_init_4_bit();
hd44780_command_4_bit(0x01); // clear screen
timer_6_wait(1700);
hd44780_command_4_bit(0x01); // clear screen
hd44780_write_4_bit("It's Work");
Проект реализованный в keil mdk arm v5 приложен далее заархивированый 7zip.