mt12864k: add driver for MELT MT-12864K (128x64, I2C, RW1065 x2, INF8574A)

This commit is contained in:
Nick Gagin
2026-08-07 23:14:22 +03:00
parent ab9e48b222
commit 1dad5260cd
10 changed files with 682 additions and 0 deletions
+54
View File
@@ -26183,6 +26183,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);
+18
View File
@@ -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);
+3
View File
@@ -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);
+28
View File
@@ -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)
+1
View File
@@ -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);
+263
View File
@@ -0,0 +1,263 @@
/*
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;
}
/* reset both LCD controllers through the INF8574A control register */
static void u8x8_mt12864k_reset(u8x8_t *u8x8)
{
u8x8_mt12864k_select(u8x8, U8X8_MT12864K_REG_ADR);
/* assert ^RST (bit 0 = 0), keep the other bits at their default value */
u8x8_byte_StartTransfer(u8x8);
u8x8_byte_SendByte(u8x8, U8X8_MT12864K_REG_RESET);
u8x8_byte_EndTransfer(u8x8);
u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_MILLI, 1);
/* release ^RST (bit 0 = 1) */
u8x8_byte_StartTransfer(u8x8);
u8x8_byte_SendByte(u8x8, U8X8_MT12864K_REG_DEFAULT);
u8x8_byte_EndTransfer(u8x8);
/* 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 */
// case U8X8_MSG_DISPLAY_SET_CONTRAST:
// 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;
}
@@ -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);
+11
View File
@@ -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)
+293
View File
@@ -0,0 +1,293 @@
/*
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 <stdio.h>
#include <string.h>
#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. 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 = 12; /* transfers 0..11 were init + power on */
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;
}
+9
View File
@@ -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,