diff --git a/cppsrc/U8g2lib.h b/cppsrc/U8g2lib.h index f5a929b8..2bd076b0 100644 --- a/cppsrc/U8g2lib.h +++ b/cppsrc/U8g2lib.h @@ -26187,6 +26187,60 @@ class U8G2_KS0108_ERM19264_F : public U8G2 { u8x8_SetPin_KS0108(getU8x8(), d0, d1, d2, d3, d4, d5, d6, d7, enable, dc, cs0, cs1, cs2, reset); } }; +class U8G2_MT12864K_128X64_1_SW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_1_SW_I2C(const u8g2_cb_t *rotation, uint8_t clock, uint8_t data, uint8_t reset = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_1(&u8g2, rotation, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_SW_I2C(getU8x8(), clock, data, reset); + } +}; +class U8G2_MT12864K_128X64_1_HW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_1_HW_I2C(const u8g2_cb_t *rotation, uint8_t reset = U8X8_PIN_NONE, uint8_t clock = U8X8_PIN_NONE, uint8_t data = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_1(&u8g2, rotation, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_HW_I2C(getU8x8(), reset, clock, data); + } +}; +class U8G2_MT12864K_128X64_1_2ND_HW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_1_2ND_HW_I2C(const u8g2_cb_t *rotation, uint8_t reset = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_1(&u8g2, rotation, u8x8_byte_arduino_2nd_hw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_HW_I2C(getU8x8(), reset); + } +}; +class U8G2_MT12864K_128X64_2_SW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_2_SW_I2C(const u8g2_cb_t *rotation, uint8_t clock, uint8_t data, uint8_t reset = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_2(&u8g2, rotation, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_SW_I2C(getU8x8(), clock, data, reset); + } +}; +class U8G2_MT12864K_128X64_2_HW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_2_HW_I2C(const u8g2_cb_t *rotation, uint8_t reset = U8X8_PIN_NONE, uint8_t clock = U8X8_PIN_NONE, uint8_t data = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_2(&u8g2, rotation, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_HW_I2C(getU8x8(), reset, clock, data); + } +}; +class U8G2_MT12864K_128X64_2_2ND_HW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_2_2ND_HW_I2C(const u8g2_cb_t *rotation, uint8_t reset = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_2(&u8g2, rotation, u8x8_byte_arduino_2nd_hw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_HW_I2C(getU8x8(), reset); + } +}; +class U8G2_MT12864K_128X64_F_SW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_F_SW_I2C(const u8g2_cb_t *rotation, uint8_t clock, uint8_t data, uint8_t reset = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_f(&u8g2, rotation, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_SW_I2C(getU8x8(), clock, data, reset); + } +}; +class U8G2_MT12864K_128X64_F_HW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_F_HW_I2C(const u8g2_cb_t *rotation, uint8_t reset = U8X8_PIN_NONE, uint8_t clock = U8X8_PIN_NONE, uint8_t data = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_f(&u8g2, rotation, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_HW_I2C(getU8x8(), reset, clock, data); + } +}; +class U8G2_MT12864K_128X64_F_2ND_HW_I2C : public U8G2 { + public: U8G2_MT12864K_128X64_F_2ND_HW_I2C(const u8g2_cb_t *rotation, uint8_t reset = U8X8_PIN_NONE) : U8G2() { + u8g2_Setup_mt12864k_i2c_128x64_f(&u8g2, rotation, u8x8_byte_arduino_2nd_hw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_HW_I2C(getU8x8(), reset); + } +}; class U8G2_T7932_150X32_1 : public U8G2 { public: U8G2_T7932_150X32_1(const u8g2_cb_t *rotation, uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7, uint8_t enable, uint8_t dc, uint8_t cs0, uint8_t cs1, uint8_t cs2, uint8_t reset = U8X8_PIN_NONE) : U8G2() { u8g2_Setup_t7932_150x32_1(&u8g2, rotation, u8x8_byte_arduino_ks0108, u8x8_gpio_and_delay_arduino); diff --git a/cppsrc/U8x8lib.h b/cppsrc/U8x8lib.h index 0a799165..bc444a98 100644 --- a/cppsrc/U8x8lib.h +++ b/cppsrc/U8x8lib.h @@ -8997,6 +8997,24 @@ class U8X8_KS0108_ERM19264 : public U8X8 { u8x8_SetPin_KS0108(getU8x8(), d0, d1, d2, d3, d4, d5, d6, d7, enable, dc, cs0, cs1, cs2, reset); } }; +class U8X8_MT12864K_128X64_SW_I2C : public U8X8 { + public: U8X8_MT12864K_128X64_SW_I2C(uint8_t clock, uint8_t data, uint8_t reset = U8X8_PIN_NONE) : U8X8() { + u8x8_Setup(getU8x8(), u8x8_d_mt12864k_128x64, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_sw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_SW_I2C(getU8x8(), clock, data, reset); + } +}; +class U8X8_MT12864K_128X64_HW_I2C : public U8X8 { + public: U8X8_MT12864K_128X64_HW_I2C(uint8_t reset = U8X8_PIN_NONE, uint8_t clock = U8X8_PIN_NONE, uint8_t data = U8X8_PIN_NONE) : U8X8() { + u8x8_Setup(getU8x8(), u8x8_d_mt12864k_128x64, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_HW_I2C(getU8x8(), reset, clock, data); + } +}; +class U8X8_MT12864K_128X64_2ND_HW_I2C : public U8X8 { + public: U8X8_MT12864K_128X64_2ND_HW_I2C(uint8_t reset = U8X8_PIN_NONE) : U8X8() { + u8x8_Setup(getU8x8(), u8x8_d_mt12864k_128x64, u8x8_cad_ssd13xx_i2c, u8x8_byte_arduino_2nd_hw_i2c, u8x8_gpio_and_delay_arduino); + u8x8_SetPin_HW_I2C(getU8x8(), reset); + } +}; class U8X8_T7932_150X32 : public U8X8 { public: U8X8_T7932_150X32(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7, uint8_t enable, uint8_t dc, uint8_t cs0, uint8_t cs1, uint8_t cs2, uint8_t reset = U8X8_PIN_NONE) : U8X8() { u8x8_Setup(getU8x8(), u8x8_d_t7932_150x32, u8x8_cad_001, u8x8_byte_arduino_ks0108, u8x8_gpio_and_delay_arduino); diff --git a/csrc/u8g2.h b/csrc/u8g2.h index e387dd79..75f983f9 100644 --- a/csrc/u8g2.h +++ b/csrc/u8g2.h @@ -1601,6 +1601,9 @@ void u8g2_Setup_ks0108_128x64_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_ms void u8g2_Setup_ks0108_erm19264_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); void u8g2_Setup_ks0108_erm19264_2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); void u8g2_Setup_ks0108_erm19264_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); +void u8g2_Setup_mt12864k_i2c_128x64_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); +void u8g2_Setup_mt12864k_i2c_128x64_2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); +void u8g2_Setup_mt12864k_i2c_128x64_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); void u8g2_Setup_t7932_150x32_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); void u8g2_Setup_t7932_150x32_2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); void u8g2_Setup_t7932_150x32_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); diff --git a/csrc/u8g2_d_setup.c b/csrc/u8g2_d_setup.c index bb06646a..b2434601 100644 --- a/csrc/u8g2_d_setup.c +++ b/csrc/u8g2_d_setup.c @@ -8155,6 +8155,34 @@ void u8g2_Setup_ks0108_erm19264_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_ buf = u8g2_m_24_8_f(&tile_buf_height); u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation); } +/* mt12864k */ +/* mt12864k 1 */ +void u8g2_Setup_mt12864k_i2c_128x64_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) +{ + uint8_t tile_buf_height; + uint8_t *buf; + u8g2_SetupDisplay(u8g2, u8x8_d_mt12864k_128x64, u8x8_cad_ssd13xx_i2c, byte_cb, gpio_and_delay_cb); + buf = u8g2_m_16_8_1(&tile_buf_height); + u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation); +} +/* mt12864k 2 */ +void u8g2_Setup_mt12864k_i2c_128x64_2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) +{ + uint8_t tile_buf_height; + uint8_t *buf; + u8g2_SetupDisplay(u8g2, u8x8_d_mt12864k_128x64, u8x8_cad_ssd13xx_i2c, byte_cb, gpio_and_delay_cb); + buf = u8g2_m_16_8_2(&tile_buf_height); + u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation); +} +/* mt12864k f */ +void u8g2_Setup_mt12864k_i2c_128x64_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) +{ + uint8_t tile_buf_height; + uint8_t *buf; + u8g2_SetupDisplay(u8g2, u8x8_d_mt12864k_128x64, u8x8_cad_ssd13xx_i2c, byte_cb, gpio_and_delay_cb); + buf = u8g2_m_16_8_f(&tile_buf_height); + u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation); +} /* t7932 */ /* t7932 1 */ void u8g2_Setup_t7932_150x32_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) diff --git a/csrc/u8x8.h b/csrc/u8x8.h index 8a3a5889..ae7a892c 100644 --- a/csrc/u8x8.h +++ b/csrc/u8x8.h @@ -1035,6 +1035,7 @@ uint8_t u8x8_d_uc1698_160x160(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void * uint8_t u8x8_d_uc1698_240x64(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); uint8_t u8x8_d_ks0108_128x64(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); uint8_t u8x8_d_ks0108_erm19264(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); +uint8_t u8x8_d_mt12864k_128x64(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); uint8_t u8x8_d_t7932_150x32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); /* t7932 and hd44102 are compatible */ uint8_t u8x8_d_hd44102_100x64(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); /* t7932 and hd44102 are compatible */ uint8_t u8x8_d_sbn1661_122x32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); diff --git a/csrc/u8x8_d_mt12864k.c b/csrc/u8x8_d_mt12864k.c new file mode 100644 index 00000000..4678edef --- /dev/null +++ b/csrc/u8x8_d_mt12864k.c @@ -0,0 +1,285 @@ +/* + + u8x8_d_mt12864k.c + + MELT (МЭЛТ) MT-12864K: 128x64 monochrome graphic LCD module, I2C. + + The module contains two RW1065 LCD controller/driver chips (KS0108/KS108 + compatible command set) and one INF8574A (PCF8574A compatible) control + register. All three devices are I2C slaves, write only. + + I2C addresses (7-bit, default): + left controller (chip 1): 0x3C (8-bit write address: 0x078) + right controller (chip 2): 0x3D (8-bit write address: 0x07A) + control register : 0x3B (8-bit write address: 0x076) + + Control register bits (INF8574A, after POR all bits are "1"): + bit 0: ^RST hardware reset of both controllers, active low + bit 1: BL_ON1 backlight on (full brightness), active high + bit 2: BL_ON2 backlight on (half brightness), active high + bit 3: P_ON built-in power converter on, active high + bits 4-6: CT_* contrast, active low + bit 7: not used + + The controllers use the two byte control protocol with a significant C0 bit + (bit 7, see datasheet table 5): + C0=0: this is the last control byte, all following bytes are payload + (commands if D/~C=0, data if D/~C=1) until STOP or RE-START. + C0=1: "control byte + payload byte" pairs, another control byte follows. + Control byte values: 0x80 = cmd (C0=1), 0x00 = cmd (C0=0), 0x40 = data (C0=0), + 0xC0 = data (C0=1). u8x8_cad_ssd13xx_i2c is compatible: each command is its + own I2C transaction "0x00 cmd" and each data block its own transaction + "0x40 data...", i.e. every transaction carries a single C0=0 control byte + followed by a homogeneous payload stream, terminated by STOP. + + Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/) + + Copyright (c) 2026, Nick Gagin + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ +#include "u8x8.h" + +/* control register INF8574A at I2C address 0x3B */ +#define U8X8_MT12864K_REG_ADR 0x076 /* 8-bit address, 0x3B << 1 */ + +/* default control register value after POR: all bits "1" */ +/* bit 0 (^RST) = 1 -> controllers not in reset */ +#define U8X8_MT12864K_REG_DEFAULT 0x0ff +/* same value with ^RST = 0 (reset asserted) */ +#define U8X8_MT12864K_REG_RESET 0x0fe + +/* 8-bit I2C addresses of the two RW1065 controllers */ +#define U8X8_MT12864K_CHIP1_ADR 0x078 /* 0x3C << 1, left half */ +#define U8X8_MT12864K_CHIP2_ADR 0x07a /* 0x3D << 1, right half */ + +static const uint8_t u8x8_d_mt12864k_init_seq[] = { + U8X8_C(0x0d), /* Mode Set: EXT=1, extended command mode */ + U8X8_C(0x02), /* Set Power Save Mode: PS=0, normal power mode */ + U8X8_C(0x0c), /* Mode Set: EXT=0, back to KS0108-compatible mode */ + U8X8_C(0x0c0), /* start at the top */ + U8X8_END() /* end of sequence */ +}; + +static const uint8_t u8x8_d_mt12864k_powersave0_seq[] = { + U8X8_C(0x03f), /* display on */ + U8X8_END() /* end of sequence */ +}; + +static const uint8_t u8x8_d_mt12864k_powersave1_seq[] = { + U8X8_C(0x03e), /* display off */ + U8X8_END() /* end of sequence */ +}; + + +/* select the i2c address for the next controller transfer */ +static void u8x8_mt12864k_select(u8x8_t *u8x8, uint8_t adr) +{ + u8x8->i2c_address = adr; +} + +/* write a single byte to the INF8574A control register (0x3B, 8-bit 0x076). + The register is an 8 bit latch: every write sets all bits, so the value + must always contain the state of all other control bits (^RST, backlight, + P_ON) as well. */ +static void u8x8_mt12864k_write_register(u8x8_t *u8x8, uint8_t value) +{ + u8x8_mt12864k_select(u8x8, U8X8_MT12864K_REG_ADR); + u8x8_byte_StartTransfer(u8x8); + u8x8_byte_SendByte(u8x8, value); + u8x8_byte_EndTransfer(u8x8); +} + +/* reset both LCD controllers through the INF8574A control register */ +static void u8x8_mt12864k_reset(u8x8_t *u8x8) +{ + /* assert ^RST (bit 0 = 0), keep the other bits at their default value */ + u8x8_mt12864k_write_register(u8x8, U8X8_MT12864K_REG_RESET); + + u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_MILLI, 1); + + /* release ^RST (bit 0 = 1) */ + u8x8_mt12864k_write_register(u8x8, U8X8_MT12864K_REG_DEFAULT); + + /* datasheet: 10 ms delay after ^RST goes inactive */ + u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_MILLI, 10); +} + +/* send the sequence to both controllers */ +static void u8x8_mt12864k_send_sequence(u8x8_t *u8x8, const uint8_t *seq) +{ + u8x8_mt12864k_select(u8x8, U8X8_MT12864K_CHIP1_ADR); + u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 1, NULL); + u8x8_cad_SendSequence(u8x8, seq); + u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 0, NULL); + + u8x8_mt12864k_select(u8x8, U8X8_MT12864K_CHIP2_ADR); + u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 2, NULL); + u8x8_cad_SendSequence(u8x8, seq); + u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 0, NULL); +} + + +struct u8x8_mt12864k_vars +{ + uint8_t *ptr; + uint8_t x; + uint8_t c; + uint8_t arg_int; +}; + +static void u8x8_mt12864k_out(u8x8_t *u8x8, struct u8x8_mt12864k_vars *v, void *arg_ptr) +{ + uint8_t cnt; + u8x8_cad_SendCmd(u8x8, 0x040 | ((v->x << 3) & 63) ); + u8x8_cad_SendCmd(u8x8, 0x0b8 | (((u8x8_tile_t *)arg_ptr)->y_pos)); + + while( v->arg_int > 0 ) + { + /* calculate tiles to next boundary (end or chip limit) */ + cnt = v->x; + cnt += 8; + cnt &= 0x0f8; + cnt -= v->x; + + if ( cnt > v->c ) + cnt = v->c; + + v->x +=cnt; + v->c-=cnt; + cnt<<=3; + u8x8_cad_SendData(u8x8, cnt, v->ptr); /* note: SendData can not handle more than 255 bytes */ + v->ptr += cnt; + + if ( v->c == 0 ) + { + v->ptr = ((u8x8_tile_t *)arg_ptr)->tile_ptr; + v->c = ((u8x8_tile_t *)arg_ptr)->cnt; + v->arg_int--; + } + if ( ((v->x) & 7) == 0 ) + break; + } +} + + +static const u8x8_display_info_t u8x8_mt12864k_128x64_display_info = +{ + /* chip_enable_level = */ 0, /* not used */ + /* chip_disable_level = */ 1, /* not used */ + + /* post_chip_enable_wait_ns = */ 100, + /* pre_chip_disable_wait_ns = */ 20, + /* reset_pulse_width_ms = */ 5, /* not used, reset is done via I2C control register */ + /* post_reset_wait_ms = */ 6, /* not used, reset is done via I2C control register */ + /* sda_setup_time_ns = */ 10, + /* sck_pulse_width_ns = */ 140, /* 400 kHz I2C */ + /* sck_clock_hz = */ 4000000UL, /* not used */ + /* spi_mode = */ 0, /* not used */ + /* i2c_bus_clock_100kHz = */ 4, /* 400 kHz */ + /* data_setup_time_ns = */ 10, + /* write_pulse_width_ns = */ 10, /* not used */ + /* tile_width = */ 16, /* width of 16*8=128 pixel */ + /* tile_height = */ 8, + /* default_x_offset = */ 0, + /* flipmode_x_offset = */ 0, + /* pixel_width = */ 128, + /* pixel_height = */ 64 +}; + +uint8_t u8x8_d_mt12864k_128x64(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) +{ + struct u8x8_mt12864k_vars v; + switch(msg) + { + case U8X8_MSG_DISPLAY_SETUP_MEMORY: + u8x8_d_helper_display_setup_memory(u8x8, &u8x8_mt12864k_128x64_display_info); + break; + case U8X8_MSG_DISPLAY_INIT: + u8x8_gpio_Init(u8x8); + u8x8_cad_Init(u8x8); + + /* reset both controllers through the control register */ + u8x8_mt12864k_reset(u8x8); + + u8x8_mt12864k_send_sequence(u8x8, u8x8_d_mt12864k_init_seq); + break; + case U8X8_MSG_DISPLAY_SET_POWER_SAVE: + if ( arg_int == 0 ) + u8x8_mt12864k_send_sequence(u8x8, u8x8_d_mt12864k_powersave0_seq); + else + u8x8_mt12864k_send_sequence(u8x8, u8x8_d_mt12864k_powersave1_seq); + break; +/* The MT-12864K controllers can not mirror the cols and rows, use U8g2 for rotation */ +// case U8X8_MSG_DISPLAY_SET_FLIP_MODE: +// break; +/* The MT-12864K has no internal contrast command. Contrast is controlled by + the INF8574A register bits 4-6. The datasheet (table 4) defines only four + discrete levels, the CT_* bits are mutually exclusive: when one of them is + "0" the other two must be "1". Bits 0-3 (^RST, backlight, P_ON) and bit 7 + stay at their default value "1". */ + case U8X8_MSG_DISPLAY_SET_CONTRAST: + { + uint8_t ct; + if ( arg_int < 64 ) + ct = 0x060; /* CT_LOW: contrast reduced */ + else if ( arg_int < 128 ) + ct = 0x070; /* normal contrast */ + else if ( arg_int < 192 ) + ct = 0x050; /* CT_HIGH1: contrast slightly increased */ + else + ct = 0x030; /* CT_HIGH2: contrast strongly increased */ + u8x8_mt12864k_write_register(u8x8, 0x08f | ct); + } + break; + case U8X8_MSG_DISPLAY_DRAW_TILE: + + v.ptr = ((u8x8_tile_t *)arg_ptr)->tile_ptr; + v.x = ((u8x8_tile_t *)arg_ptr)->x_pos; + v.c = ((u8x8_tile_t *)arg_ptr)->cnt; + v.arg_int = arg_int; + + if ( v.x < 8 ) + { + u8x8_mt12864k_select(u8x8, U8X8_MT12864K_CHIP1_ADR); + u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 1, NULL); + u8x8_mt12864k_out(u8x8, &v, arg_ptr); + u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 0, NULL); + } + if ( v.x < 16 ) + { + u8x8_mt12864k_select(u8x8, U8X8_MT12864K_CHIP2_ADR); + u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 2, NULL); + u8x8_mt12864k_out(u8x8, &v, arg_ptr); + u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 0, NULL); + } + break; + default: + return 0; + } + return 1; +} diff --git a/sys/arduino/u8g2_full_buffer/HelloWorld/HelloWorld.ino b/sys/arduino/u8g2_full_buffer/HelloWorld/HelloWorld.ino index d76a203d..60b9787c 100644 --- a/sys/arduino/u8g2_full_buffer/HelloWorld/HelloWorld.ino +++ b/sys/arduino/u8g2_full_buffer/HelloWorld/HelloWorld.ino @@ -203,6 +203,8 @@ //U8G2_HX1230_96X68_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); //U8G2_KS0108_128X64_F u8g2(U8G2_R0, 8, 9, 10, 11, 4, 5, 6, 7, /*enable=*/ 18, /*dc=*/ 17, /*cs0=*/ 14, /*cs1=*/ 15, /*cs2=*/ U8X8_PIN_NONE, /* reset=*/ U8X8_PIN_NONE); // Set R/W to low! //U8G2_KS0108_ERM19264_F u8g2(U8G2_R0, 8, 9, 10, 11, 4, 5, 6, 7, /*enable=*/ 18, /*dc=*/ 17, /*cs0=*/ 14, /*cs1=*/ 15, /*cs2=*/ 16, /* reset=*/ U8X8_PIN_NONE); // Set R/W to low! +//U8G2_MT12864K_128X64_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // MELT MT-12864K, 128x64 graphic LCD over I2C (two RW1065 controllers, INF8574A control register) +//U8G2_MT12864K_128X64_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // MELT MT-12864K, software I2C //U8G2_HD44102_100X64_F u8g2(U8G2_R0, 4, 5, 6, 7, 8, 9, 10, 11, /*enable=*/ 2, /*dc=*/ 3, /*cs0=*/ A0, /*cs1=*/ A1, /*cs2=*/ A2, /* reset=*/ U8X8_PIN_NONE); // Set R/W to low! //U8G2_T7932_150X32_F u8g2(U8G2_R0, 4, 5, 6, 7, 8, 9, 10, 11, /*enable=*/ 2, /*dc=*/ 3, /*cs0=*/ A0, /*cs1=*/ A1, /*cs2=*/ A2, /* reset=*/ U8X8_PIN_NONE); // Set R/W to low! //U8G2_ST7920_256X32_F_8080 u8g2(U8G2_R0, 8, 9, 10, 11, 4, 5, 6, 7, /*enable=*/ 18, /*cs=*/ U8X8_PIN_NONE, /*dc=*/ 17, /*reset=*/ U8X8_PIN_NONE); diff --git a/sys/sdl/mt12864k_i2c/Makefile b/sys/sdl/mt12864k_i2c/Makefile new file mode 100644 index 00000000..e2387733 --- /dev/null +++ b/sys/sdl/mt12864k_i2c/Makefile @@ -0,0 +1,11 @@ +CFLAGS = -g -Wall -I../../../csrc/. + +SRC = $(shell ls ../../../csrc/*.c) main.c + +OBJ = $(SRC:.c=.o) + +main: $(OBJ) + $(CC) $(CFLAGS) $(OBJ) -o main + +clean: + -rm -f main $(OBJ) diff --git a/sys/sdl/mt12864k_i2c/main.c b/sys/sdl/mt12864k_i2c/main.c new file mode 100644 index 00000000..f2f9db65 --- /dev/null +++ b/sys/sdl/mt12864k_i2c/main.c @@ -0,0 +1,312 @@ +/* + MT-12864K driver test. + + This test runs the MT-12864K display driver without any hardware: + a fake I2C byte callback records every I2C transfer (address + bytes), + and the recorded trace is checked against the expected protocol. + + Build: make + Run: ./main (prints the trace and the test result) +*/ + +#include "u8g2.h" +#include +#include + +#define MAX_TRANSFERS 512 +#define MAX_TDATA 256 + +struct trace_entry +{ + uint8_t adr; + uint16_t cnt; + uint8_t data[MAX_TDATA]; +}; + +static struct trace_entry trace[MAX_TRANSFERS]; +static int trace_cnt = 0; + +static int in_transfer = 0; +static uint8_t cur_adr = 0; +static int cur_len = 0; + +static void trace_close(void) +{ + if ( in_transfer && trace_cnt < MAX_TRANSFERS ) + { + trace[trace_cnt].adr = cur_adr; + trace[trace_cnt].cnt = (uint16_t)cur_len; + trace_cnt++; + } + in_transfer = 0; + cur_len = 0; +} + +static uint8_t u8x8_byte_mt12864k_trace(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) +{ + uint8_t *data; + int i; + switch(msg) + { + case U8X8_MSG_BYTE_START_TRANSFER: + trace_close(); + cur_adr = u8x8_GetI2CAddress(u8x8); + in_transfer = 1; + break; + case U8X8_MSG_BYTE_END_TRANSFER: + trace_close(); + break; + case U8X8_MSG_BYTE_SEND: + data = (uint8_t *)arg_ptr; + for( i = 0; i < arg_int && cur_len < MAX_TDATA; i++ ) + trace[trace_cnt].data[cur_len++] = data[i]; + break; + case U8X8_MSG_BYTE_SET_DC: + case U8X8_MSG_BYTE_INIT: + break; + default: + return 0; + } + return 1; +} + +static unsigned long delay_ms_total = 0; + +static uint8_t u8x8_gpio_mt12864k_test(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) +{ + switch(msg) + { + case U8X8_MSG_GPIO_AND_DELAY_INIT: + break; + case U8X8_MSG_DELAY_MILLI: + delay_ms_total += arg_int; + break; + default: + break; + } + return 1; +} + +static int check_transfer(int idx, uint8_t adr, const uint8_t *data, int cnt) +{ + int i; + if ( idx >= trace_cnt ) + { + printf(" FAIL: missing transfer %d (only %d recorded)\n", idx, trace_cnt); + return 0; + } + if ( trace[idx].adr != adr ) + { + printf(" FAIL: transfer %d address 0x%02x, expected 0x%02x\n", idx, trace[idx].adr, adr); + return 0; + } + if ( trace[idx].cnt != cnt ) + { + printf(" FAIL: transfer %d length %d, expected %d\n", idx, trace[idx].cnt, cnt); + return 0; + } + for( i = 0; i < cnt; i++ ) + if ( trace[idx].data[i] != data[i] ) + { + printf(" FAIL: transfer %d byte %d: 0x%02x, expected 0x%02x\n", idx, i, trace[idx].data[i], data[i]); + return 0; + } + return 1; +} + +static void print_trace(void) +{ + int i, j; + printf("recorded transfers: %d\n", trace_cnt); + for( i = 0; i < trace_cnt; i++ ) + { + printf(" %3d: adr 0x%02x len %3d:", i, trace[i].adr, trace[i].cnt); + for( j = 0; j < trace[i].cnt && j < 24; j++ ) + printf(" %02x", trace[i].data[j]); + if ( trace[i].cnt > 24 ) + printf(" ..."); + printf("\n"); + } +} + +u8g2_t u8g2; + +int main(void) +{ + uint8_t *buf; + uint8_t d; + uint8_t d2[2]; + int ok = 1; + int i, page, t; + int cnt_cmd, cnt_data_l, cnt_data_r, data_bytes_l, data_bytes_r; + + printf("MT-12864K driver test (fake I2C trace)\n"); + printf("======================================\n\n"); + + u8g2_Setup_mt12864k_i2c_128x64_f(&u8g2, &u8g2_cb_r0, u8x8_byte_mt12864k_trace, u8x8_gpio_mt12864k_test); + + u8x8_InitDisplay(u8g2_GetU8x8(&u8g2)); + u8x8_SetPowerSave(u8g2_GetU8x8(&u8g2), 0); + + printf("after init + power on:\n"); + + /* 1. reset pulse through the control register (0x3B -> 8-bit 0x076) */ + d = 0x0fe; /* ^RST asserted (bit 0 = 0), all other bits = 1 */ + if ( !check_transfer(0, 0x076, &d, 1) ) ok = 0; + d = 0x0ff; /* ^RST released (bit 0 = 1) */ + if ( !check_transfer(1, 0x076, &d, 1) ) ok = 0; + if ( delay_ms_total < 11 ) + { + printf(" FAIL: reset timing too short (delay_ms_total=%lu)\n", delay_ms_total); + ok = 0; + } + + /* 2. init sequence sent to both controllers: + extended commands 0x0d (Mode Set EXT=1), 0x02 (power save PS=0), + 0x0c (Mode Set EXT=0) followed by 0xc0 (start line) */ + d2[0] = 0x00; d2[1] = 0x0d; + if ( !check_transfer(2, 0x078, d2, 2) ) ok = 0; /* left controller 0x3C */ + d2[1] = 0x02; + if ( !check_transfer(3, 0x078, d2, 2) ) ok = 0; + d2[1] = 0x0c; + if ( !check_transfer(4, 0x078, d2, 2) ) ok = 0; + d2[1] = 0x0c0; + if ( !check_transfer(5, 0x078, d2, 2) ) ok = 0; + d2[1] = 0x0d; + if ( !check_transfer(6, 0x07a, d2, 2) ) ok = 0; /* right controller 0x3D */ + d2[1] = 0x02; + if ( !check_transfer(7, 0x07a, d2, 2) ) ok = 0; + d2[1] = 0x0c; + if ( !check_transfer(8, 0x07a, d2, 2) ) ok = 0; + d2[1] = 0x0c0; + if ( !check_transfer(9, 0x07a, d2, 2) ) ok = 0; + + /* 3. display on (0x3f) on both controllers */ + d2[0] = 0x00; d2[1] = 0x03f; + if ( !check_transfer(10, 0x078, d2, 2) ) ok = 0; + if ( !check_transfer(11, 0x07a, d2, 2) ) ok = 0; + + /* 4. contrast: single byte writes to the control register (0x076). + Only four discrete levels are available (datasheet table 4): + value 0..63 -> CT_LOW (reduced) register 0x0ef + value 64..127 -> normal register 0x0ff + value 128..191 -> CT_HIGH1 (slight incr.) register 0x0df + value 192..255 -> CT_HIGH2 (strong incr.) register 0x0bf + All boundary values are checked. */ + { + const uint8_t contrast_values[] = { 0, 63, 64, 127, 128, 191, 192, 255 }; + const uint8_t contrast_reg[] = { 0x0ef, 0x0ef, 0x0ff, 0x0ff, + 0x0df, 0x0df, 0x0bf, 0x0bf }; + int i; + for( i = 0; i < 8; i++ ) + { + u8x8_SetContrast(u8g2_GetU8x8(&u8g2), contrast_values[i]); + if ( !check_transfer(12 + i, 0x076, &contrast_reg[i], 1) ) ok = 0; + } + } + + /* 5. full buffer: left half 0x55, right half 0xaa, then send */ + buf = u8g2_GetBufferPtr(&u8g2); + for( page = 0; page < 8; page++ ) + { + memset(buf + page*128, 0x55, 64); + memset(buf + page*128 + 64, 0xaa, 64); + } + + printf("\nsending buffer...\n"); + u8g2_SendBuffer(&u8g2); + + /* verify the draw phase: + for each of the 8 page rows, the driver must emit for each chip: + set column command (0x00,0x40) + set page command (0x00,0xb8|page) + and the 64 data bytes (0x40 control byte + 64 bytes) split into + 24+24+16 byte I2C transfers + left chip data must be 0x55, right chip data must be 0xaa + */ + cnt_cmd = 0; + cnt_data_l = 0; + cnt_data_r = 0; + data_bytes_l = 0; + data_bytes_r = 0; + t = 20; /* transfers 0..19 were init, power on and contrast */ + for( page = 0; page < 8; page++ ) + { + int chip; + for( chip = 0; chip < 2; chip++ ) + { + uint8_t adr = (chip == 0) ? 0x078 : 0x07a; + uint8_t col_cmd[2] = { 0x00, 0x040 }; + uint8_t page_cmd[2] = { 0x00, 0x0b8 | page }; + int bytes_sent = 0; + + if ( !check_transfer(t, adr, col_cmd, 2) ) ok = 0; + if ( !check_transfer(t+1, adr, page_cmd, 2) ) ok = 0; + t += 2; + + while( bytes_sent < 64 ) + { + int n = 64 - bytes_sent; + if ( n > 24 ) n = 24; + if ( trace[t].adr != adr ) + { + printf(" FAIL: data block %d address 0x%02x, expected 0x%02x\n", t, trace[t].adr, adr); + ok = 0; + } + if ( trace[t].cnt != n+1 || trace[t].data[0] != 0x040 ) + { + printf(" FAIL: data block %d header (len=%d, first=0x%02x)\n", t, trace[t].cnt, trace[t].data[0]); + ok = 0; + } + for( i = 1; i <= n; i++ ) + if ( trace[t].data[i] != (chip == 0 ? 0x55 : 0xaa) ) + { + printf(" FAIL: data block %d byte %d value 0x%02x\n", t, i, trace[t].data[i]); + ok = 0; + break; + } + if ( chip == 0 ) + { + cnt_data_l++; + data_bytes_l += n; + } + else + { + cnt_data_r++; + data_bytes_r += n; + } + bytes_sent += n; + t++; + } + cnt_cmd += 2; + } + } + + if ( t != trace_cnt ) + { + printf(" FAIL: unexpected transfer count (%d, expected %d)\n", trace_cnt, t); + ok = 0; + } + if ( cnt_cmd != 32 || cnt_data_l != 24 || cnt_data_r != 24 ) + { + printf(" FAIL: transfer mix cmd=%d data_l=%d data_r=%d\n", cnt_cmd, cnt_data_l, cnt_data_r); + ok = 0; + } + if ( data_bytes_l != 512 || data_bytes_r != 512 ) + { + printf(" FAIL: data bytes left=%d right=%d\n", data_bytes_l, data_bytes_r); + ok = 0; + } + + printf("\n"); + print_trace(); + + printf("\n"); + if ( ok ) + { + printf("ALL TESTS PASSED\n"); + return 0; + } + printf("TEST FAILED\n"); + return 1; +} diff --git a/tools/codebuild/codebuild.c b/tools/codebuild/codebuild.c index 52e4a994..eb9b8914 100644 --- a/tools/codebuild/codebuild.c +++ b/tools/codebuild/codebuild.c @@ -2174,6 +2174,15 @@ struct controller controller_list[] = }, }, + { + "mt12864k", 16, 8, "u8g2_ll_hvline_vertical_top_lsb", "u8x8_cad_ssd13xx_i2c", "i2c", COM_I2C, + "", /* is_generate_u8g2_class= */ 1, + { + { "128x64" }, + { NULL } + }, + }, + { "t7932", 19, 4, "u8g2_ll_hvline_vertical_top_lsb", "u8x8_cad_001", "", COM_KS0108, "", /* is_generate_u8g2_class= */ 1,