2
* ----------------------------------------------------------------------------
3
* "THE BEER-WARE LICENSE" (Revision 42):
4
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
5
* can do whatever you want with this stuff. If we meet some day, and you think
6
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
7
* ----------------------------------------------------------------------------
9
* HD44780 LCD display driver
11
* The LCD controller is used in 4-bit mode with a full bi-directional
12
* interface (i.e. R/~W is connected) so the busy flag can be read.
14
* $Id: hd44780.c,v 1.1.2.3 2006/10/08 21:51:14 joerg_wunsch Exp $
23
#include <util/delay.h>
27
#define GLUE(a, b) a##b
28
#define PORT(x) GLUE(PORT, x)
29
#define PIN(x) GLUE(PIN, x)
30
#define DDR(x) GLUE(DDR, x)
32
#define HD44780_PORTOUT PORT(HD44780_PORT)
33
#define HD44780_PORTIN PIN(HD44780_PORT)
34
#define HD44780_DDR DDR(HD44780_PORT)
36
#define HD44780_DATABITS \
37
(_BV(HD44780_D4)|_BV(HD44780_D5)|_BV(HD44780_D6)|_BV(HD44780_D7))
39
#define HD44780_BUSYFLAG 0x80
42
* Send one pulse to the E signal (enable). Mind the timing
43
* constraints. If readback is set to true, read the HD44780 data
44
* pins right before the falling edge of E, and return that value.
47
hd44780_pulse_e(bool readback) __attribute__((always_inline));
50
hd44780_pulse_e(bool readback)
54
HD44780_PORTOUT |= _BV(HD44780_E);
56
* Guarantee at least 500 ns of pulse width. For high CPU
57
* frequencies, a delay loop is used. For lower frequencies, NOPs
58
* are used, and at or below 1 MHz, the native pulse width will
59
* already be 1 us or more so no additional delays are needed.
65
* When reading back, we need one additional NOP, as the value read
66
* back from the input pin is sampled close to the beginning of a
67
* CPU clock cycle, while the previous edge on the output pin is
68
* generated towards the end of a CPU clock cycle.
71
__asm__ volatile("nop");
72
# if F_CPU > 1000000UL
73
__asm__ volatile("nop");
74
# if F_CPU > 2000000UL
75
__asm__ volatile("nop");
76
__asm__ volatile("nop");
77
# endif /* F_CPU > 2000000UL */
78
# endif /* F_CPU > 1000000UL */
81
x = HD44780_PORTIN & HD44780_DATABITS;
84
HD44780_PORTOUT &= ~_BV(HD44780_E);
90
* Send one nibble out to the LCD controller.
93
hd44780_outnibble(uint8_t n, uint8_t rs)
97
HD44780_PORTOUT &= ~_BV(HD44780_RW);
99
HD44780_PORTOUT |= _BV(HD44780_RS);
101
HD44780_PORTOUT &= ~_BV(HD44780_RS);
102
x = (HD44780_PORTOUT & ~HD44780_DATABITS) | ((n << HD44780_D4) & HD44780_DATABITS);
104
(void)hd44780_pulse_e(false);
108
* Send one byte to the LCD controller. As we are in 4-bit mode, we
109
* have to send two nibbles.
112
hd44780_outbyte(uint8_t b, uint8_t rs)
114
hd44780_outnibble(b >> 4, rs);
115
hd44780_outnibble(b & 0xf, rs);
119
* Read one nibble from the LCD controller.
122
hd44780_innibble(uint8_t rs)
126
HD44780_PORTOUT |= _BV(HD44780_RW);
127
HD44780_DDR &= ~HD44780_DATABITS;
129
HD44780_PORTOUT |= _BV(HD44780_RS);
131
HD44780_PORTOUT &= ~_BV(HD44780_RS);
132
x = hd44780_pulse_e(true);
133
HD44780_DDR |= HD44780_DATABITS;
134
HD44780_PORTOUT &= ~_BV(HD44780_RW);
136
return (x & HD44780_DATABITS) >> HD44780_D4;
140
* Read one byte (i.e. two nibbles) from the LCD controller.
143
hd44780_inbyte(uint8_t rs)
147
x = hd44780_innibble(rs) << 4;
148
x |= hd44780_innibble(rs);
154
* Wait until the busy flag is cleared.
157
hd44780_wait_ready(void)
159
while (hd44780_incmd() & HD44780_BUSYFLAG) ;
163
* Initialize the LCD controller.
165
* The initialization sequence has a mandatory timing so the
166
* controller can safely recognize the type of interface desired.
167
* This is the only area where timed waits are really needed as
168
* the busy flag cannot be probed initially.
174
HD44780_DDR = _BV(HD44780_RS) | _BV(HD44780_RW) | _BV(HD44780_E)
177
_delay_ms(15); /* 40 ms needed for Vcc = 2.7 V */
178
hd44780_outnibble(HD44780_FNSET(1, 0, 0) >> 4, 0);
180
hd44780_outnibble(HD44780_FNSET(1, 0, 0) >> 4, 0);
182
hd44780_outnibble(HD44780_FNSET(1, 0, 0) >> 4, 0);
184
hd44780_outnibble(HD44780_FNSET(0, 1, 0) >> 4, 0);
185
hd44780_wait_ready();
186
hd44780_outcmd(HD44780_FNSET(0, 1, 0));
187
hd44780_wait_ready();
188
hd44780_outcmd(HD44780_DISPCTL(0, 0, 0));
189
hd44780_wait_ready();