Обычно передачу данных на символьные ЖК-дисплеи осуществляют по 8 линиям. Но в целях экономии количества выводов микроконтроллера такую передачу можно осуществить всего лишь по четырем линиям.

Нижеприведенный код на языке C позволяет осуществить четырехпроводное подключение микроконтроллера ATMEGA328P к символьному дисплею MC21605A6W (16x2). При этом следует учесть, что выводы DB0:3 и RW должны быть заземлены.
#include <stdint.h>
#include <avr/io.h>
#define LCD_POS_DB 0x02
#define LCD_PORT_DB PORTD
#define LCD_DIR_DB DDRD
#define LCD_MASK_DB (0x0f << LCD_POS_DB)
#define LCD_POS_EN 0x06
#define LCD_PORT_EN PORTD
#define LCD_DIR_EN DDRD
#define LCD_MASK_EN (0x01 << LCD_POS_EN)
#define LCD_POS_RS 0x07
#define LCD_PORT_RS PORTD
#define LCD_DIR_RS DDRD
#define LCD_MASK_RS (0x01 << LCD_POS_RS)
static inline void wait_50_ns(void)
{
__asm__ __volatile__ ("nop\n\t");
}
static inline void wait_500_ns(void)
{
/* 8 циклов при 16 МГц */
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
}
static inline void wait_50_us(void)
{
/* 800 циклов при 16 МГц */
uint8_t x;
for (x = 0; x < 100; ++x) wait_500_ns();
}
static inline void wait_2_ms(void)
{
wait_50_us();
wait_50_us();
wait_50_us();
wait_50_us();
}
static inline void wait_50_ms(void)
{
uint8_t x;
for (x = 0; x < 25; ++x) wait_2_ms();
}
static inline void lcd_pulse_en(void)
{
/* вывод EN в низкий лог. уровень */
LCD_PORT_EN |= LCD_MASK_EN;
wait_50_us();
LCD_PORT_EN &= ~LCD_MASK_EN;
wait_2_ms();
}
static void lcd_write_db4(uint8_t x)
{
/* настраиваем на 4-битный режим */
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x >> 4) << LCD_POS_DB;
lcd_pulse_en();
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x & 0xf) << LCD_POS_DB;
lcd_pulse_en();
}
static void lcd_write_db8(uint8_t x)
{
/* настраиваем на 4-битный режим */
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x >> 4) << LCD_POS_DB;
lcd_pulse_en();
}
void lcd_setup(void)
{
LCD_DIR_DB |= LCD_MASK_DB;
LCD_DIR_RS |= LCD_MASK_RS;
LCD_DIR_EN |= LCD_MASK_EN;
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_RS &= ~LCD_MASK_RS;
LCD_PORT_EN &= ~LCD_MASK_EN;
/* небольшая задержка для загрузки дисплея */
wait_50_ms();
/* последовательность инициализации в соответствии с документацией */
#define LCD_MODE_BLINK (1 << 0)
#define LCD_MODE_CURSOR (1 << 1)
#define LCD_MODE_DISPLAY (1 << 2)
lcd_write_db8(0x30);
wait_2_ms();
wait_2_ms();
wait_500_ns();
lcd_write_db8(0x30);
wait_2_ms();
lcd_write_db4(0x32);
wait_2_ms();
lcd_write_db4(0x28);
wait_2_ms();
lcd_write_db4((1 << 3) | LCD_MODE_DISPLAY);
wait_2_ms();
lcd_write_db4(0x01);
wait_2_ms();
lcd_write_db4(0x0f);
wait_2_ms();
}
void lcd_clear(void)
{
/* очистка дисплея */
lcd_write_db4(0x01);
wait_2_ms();
}
void lcd_home(void)
{
/* устанавливаем курсор по умолчанию */
lcd_write_db4(0x02);
wait_2_ms();
}
void lcd_set_ddram(uint8_t addr)
{
lcd_write_db4((1 << 7) | addr);
wait_2_ms();
}
void lcd_goto_xy(uint8_t x, uint8_t y)
{
/* 0 <= x < 8 */
/* 0 <= y < 2 */
/* Из документации: */
/* первая строка от 0x00 до 0x27 */
/* вторая строка от 0x40 до 0x67 */
static const uint8_t row[] = { 0x00, 0x40 };
lcd_set_ddram(row[y] | x);
}
void lcd_write(const uint8_t* s, unsigned int n)
{
wait_50_ns();
LCD_PORT_RS |= LCD_MASK_RS;
for (; n; --n, ++s)
{
lcd_write_db4(*s);
wait_2_ms();
}
LCD_PORT_RS &= ~LCD_MASK_RS;
}
Документация на MC21605A6W.
Перевод © digitrode.ru