From 84d65b6f27b88e79866a08635b996077fc3e17da Mon Sep 17 00:00:00 2001 From: Jonathan Gevaryahu Date: Sun, 26 Nov 2023 15:18:52 -0500 Subject: HD44780: Implement timing (busy/status and blink interval) based on the input clock. Add notes about device variants. [Lord Nightmare] (#11776) * HD44780: Implement timing (busy/status and blink interval) based on the input clock. Add notes about device variants. [Lord Nightmare] --- src/devices/video/hd44780.cpp | 145 ++++++++++++++++++++++++++++++++++++------ src/devices/video/hd44780.h | 4 +- 2 files changed, 128 insertions(+), 21 deletions(-) diff --git a/src/devices/video/hd44780.cpp b/src/devices/video/hd44780.cpp index a56273b66e4..ce60a911c76 100644 --- a/src/devices/video/hd44780.cpp +++ b/src/devices/video/hd44780.cpp @@ -5,9 +5,116 @@ Hitachi HD44780 LCD controller TODO: - - dump internal CGROM - - emulate osc pin, determine video timings and busy flag duration from it, - and if possible, remove m_busy_factor + - dump internal CGROM via decap + - finish emulating osc pin: determine video timings from it, + and if possible, remove m_busy_factor + The internal oscillator is known to be based on the value of the resistor + Rf, presumably (based on the HD44780U datasheet diagrams) with an + equation close to this: + For 5V power, Frequency in Hz = 1 / (2 * PI * Rf * 6.5pF) + For 3V power, Frequency in Hz = 1 / (2 * PI * Rf * 7.86pF) + The vast majority of devices use the internal oscillator with an Rf + resistor of 91kOhms, for ~270kHz clock speed. + However, a few Hitachi-made LCD modules use an Rf resistor of + 200kOhms instead, for a ~122.4kHz clock speed, and it isn't clear + why they did this, as it is lower than the minimum intended clock + speed on the datasheet. + DONE + - busy flag durations in clock cycles + - blink timing in clock cycles + + BUSY TIMINGS: + Manufacturer: Hitachi Epson Samsung + Device: HD44780 SED1278 KS0066+S6A0069 + Clear display: 410 cycles 410 cycles 410 cycles + Return Home: 410 cycles 410 cycles 410 cycles + Entry Mode: 10 cycles 10 cycles 10 cycles + Display on/off: 10 cycles 10 cycles 10 cycles + Cursor/Display Shift: 10 cycles 10 cycles 10 cycles + Function Set: 10 cycles 10 cycles 10 cycles + Set CGRAM: 10 cycles 10 cycles 10 cycles + Set DDRAM: 10 cycles 10 cycles 10 cycles + Data Write: 10 cycles[1] 10 cycles 11 cycles + Data Read: 10 cycles[1] 10 cycles 11 cycles[2,3] + Blink Invert Rate: 102400 cycles 102400 cycles ? cycles[4] + [1] HD44780 bug: BUSY is only active for 10 cycles, but the ac inc/dec on + data read or write happens on the 11th cycle. + This bug is documented in the later HD44780U datasheet, as well as in + the SED1278 datasheet to note the bug is fixed there. + We do not emulate this bug, but emulate as SED1278 where it is fixed. + [2] KS0066 bug: a Data read from CGRAM after a CGRAM write + + increment/decrement has completed will read the previous byte, not + the new one the address counter now points to! + This bug is documented in the datasheet. + [3] S6A0069 note: the first data read from CGRAM must have a set address + command or a display shift command sent before the read command, or + it will instead return the potentially garbage data that was most + recently put into the read buffer register as the serially-accessed + ram hasn't had a chance to fully complete a serial cycle and update + said buffer. Also, if the AC direction has not been explicitly set + beforehand by a display shift command, the byte address + auto-increment vs auto-decrement might be doing either one randomly + after power-up. + In short, this documentation change plus some minor behavior change + "fixes" the bug of the KS0066, with the above caveat. + [4] In all likelyhood, this is almost certainly 102400 for compatibility + with the original HD44780A00 + + Known HD44780-compatible clones: + Epson SED1278 + Samsung KS0066[U] + Samsung S6A0069 (equivalent to KS0066U?) + Sitronix ST7066U + Sunplus SPLC780C (timings equivalent to SED1278?) + + Possibly compatible clones: + Toshiba T1719A (may be custom for Brother) + + Similar, but not quite compatible variants: + Hitachi HD66780 LCD-IIA (more or less the same as 44780 but has a + different LCD driver waveform, and like 44780 has + 16 Common/40 Segment drivers) + Samsung KS0073 (uses different DPRAM memory mapping than HD44780, and + has 34 Common/64 Segment drivers; it also has a low power mode and + 6-pixel character width modes) + Samsung S6A0073 (equivalent to KS0073) + Novatek NT7603 (clocked at twice the speed, has different LCD driver + waveforms, and has 16 Common/80 Segment drivers) + Novatek NT7605 (clocked at twice the speed, has different LCD driver + waveforms, and has 16 Common/100 Segment drivers) + Solomon Systech SSD1803 (2.7-3.45V only, 34 Common/100 Segment drivers, + has a 6-pixel character width mode, and when in 4-line mode, has a + memory map like KS0073; has two unique CGROMs) + + + Character set equivalency: + Hitachi Samsung Epson Sitronix Sunplus [Supported] + HD44780A00 KS0066F00 SED1278F0A ST7066-0A SPLC780C-01 Y + HD44780UA00 Y + HD44780UA01 + SPLC780C-17 + HD44780UA02 ST7066-0R Y + SED1278F0H ST7066-0T SPLC780C-02 + SED1278F0B ST7066-0B SPLC780C-03 Y + KS0066F03 SPLC780C-08 + KS0066F04 SPLC780C-13 + KS0066F05 ST7066-0E SPLC780C-11 Y + KS0066F06 SPLC780C-12 + ST7066-1G SPLC780C-14 + SPLC780C-15 + SPLC780C-19 + SED1278F0C + KS0066F59 SED1278F0E + SED1278F0G SPLC780C-18 + + + DPRAM 80-byte wrap behavior is based on increment/decrement: + In 1-line mode: + 4E 4F 00 01 02 ... 4D 4E 4F 00 01 02 ... + + In 2-line mode: + L1: 26 27 00 01 02 ... 24 25 26 27 00 01 02 ... + L2: 66 67 40 41 42 ... 64 65 66 67 40 41 42 ... ***************************************************************************/ @@ -86,6 +193,8 @@ ROM_START( ks0066 ) ROM_END +// TODO: Sitronix and Sunplus devices/variants + //************************************************************************** // live device //************************************************************************** @@ -165,7 +274,7 @@ void hd44780_device::device_start() m_busy_timer = timer_alloc(FUNC(hd44780_device::clear_busy_flag), this); m_blink_timer = timer_alloc(FUNC(hd44780_device::blink_tick), this); - m_blink_timer->adjust(attotime::from_msec(409), 0, attotime::from_msec(409)); + m_blink_timer->adjust(attotime::from_ticks(102400, clock()), 0, attotime::from_ticks(102400, clock())); // blink happens every 102400 cycles // state saving save_item(NAME(m_busy_factor)); @@ -223,7 +332,7 @@ void hd44780_device::device_reset() m_rs_state = 0; m_rw_state = 0; - set_busy_flag(1520); + set_busy_flag(410); } //------------------------------------------------- @@ -254,12 +363,10 @@ TIMER_CALLBACK_MEMBER(hd44780_device::blink_tick) // HELPERS //************************************************************************** -void hd44780_device::set_busy_flag(uint16_t usec) +void hd44780_device::set_busy_flag(uint16_t cycles) { m_busy_flag = true; - - usec = float(usec) * m_busy_factor + 0.5; - m_busy_timer->adjust(attotime::from_usec(usec)); + m_busy_timer->adjust(attotime::from_ticks(cycles, clock() / m_busy_factor)); } void hd44780_device::correct_ac() @@ -519,7 +626,7 @@ void hd44780_device::control_write(u8 data) m_active_ram = DDRAM; m_ac = m_ir & 0x7f; correct_ac(); - set_busy_flag(37); + set_busy_flag(10); LOG("HD44780: set DDRAM address %x\n", m_ac); return; @@ -529,7 +636,7 @@ void hd44780_device::control_write(u8 data) // set CGRAM address m_active_ram = CGRAM; m_ac = m_ir & 0x3f; - set_busy_flag(37); + set_busy_flag(10); LOG("HD44780: set CGRAM address %x\n", m_ac); return; @@ -548,7 +655,7 @@ void hd44780_device::control_write(u8 data) m_data_len = BIT(m_ir, 4) ? 8 : 4; m_num_line = BIT(m_ir, 3) + 1; correct_ac(); - set_busy_flag(37); + set_busy_flag(10); LOG("HD44780: char size 5x%d, data len %d, lines %d\n", m_char_size, m_data_len, m_num_line); return; @@ -565,7 +672,7 @@ void hd44780_device::control_write(u8 data) else update_ac(direction); - set_busy_flag(37); + set_busy_flag(10); } else if (BIT(m_ir, 3)) { @@ -573,7 +680,7 @@ void hd44780_device::control_write(u8 data) m_display_on = BIT(m_ir, 2); m_cursor_on = BIT(m_ir, 1); m_blink_on = BIT(m_ir, 0); - set_busy_flag(37); + set_busy_flag(10); LOG("HD44780: display %d, cursor %d, blink %d\n", m_display_on, m_cursor_on, m_blink_on); } @@ -582,7 +689,7 @@ void hd44780_device::control_write(u8 data) // entry mode set m_direction = (BIT(m_ir, 1)) ? +1 : -1; m_shift_on = BIT(m_ir, 0); - set_busy_flag(37); + set_busy_flag(10); LOG("HD44780: entry mode set: direction %d, shift %d\n", m_direction, m_shift_on); } @@ -595,7 +702,7 @@ void hd44780_device::control_write(u8 data) m_active_ram = DDRAM; m_direction = 1; m_disp_shift = 0; - set_busy_flag(1520); + set_busy_flag(410); } else if (BIT(m_ir, 0)) { @@ -607,7 +714,7 @@ void hd44780_device::control_write(u8 data) m_direction = 1; m_disp_shift = 0; memset(m_ddram, 0x20, sizeof(m_ddram)); - set_busy_flag(1520); + set_busy_flag(410); // Some machines do a "clear display" first, even though the datasheet insists "function set" must come before all else return; @@ -663,10 +770,10 @@ void hd44780_device::data_write(u8 data) else m_cgram[m_ac] = m_dr; + set_busy_flag(10); update_ac(m_direction); if (m_shift_on) shift_display(m_direction); - set_busy_flag(41); } u8 hd44780_device::data_read() @@ -685,8 +792,8 @@ u8 hd44780_device::data_read() if (!machine().side_effects_disabled()) { + set_busy_flag(10); update_ac(m_direction); - set_busy_flag(41); } return data; diff --git a/src/devices/video/hd44780.h b/src/devices/video/hd44780.h index b31f3e6af60..a998d1979f4 100644 --- a/src/devices/video/hd44780.h +++ b/src/devices/video/hd44780.h @@ -78,7 +78,7 @@ private: enum { DDRAM, CGRAM }; // internal helper - void set_busy_flag(uint16_t usec); + void set_busy_flag(uint16_t cycles); void correct_ac(); void update_ac(int direction); void update_nibble(int rs, int rw); @@ -86,8 +86,8 @@ private: void pixel_update(bitmap_ind16 &bitmap, u8 line, u8 pos, u8 y, u8 x, int state); // internal state - emu_timer * m_blink_timer; emu_timer * m_busy_timer; + emu_timer * m_blink_timer; u8 m_lines; // number of lines u8 m_chars; // chars for line -- cgit v1.2.3