mirror of
https://github.com/olikraus/u8g2.git
synced 2026-09-11 09:12:33 +00:00
mt12864k: add driver for MELT MT-12864K (128x64, I2C, RW1065 x2, INF8574A)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user