Add drawing to ILI9225 LCD

Signed-off-by: Mahyar Koshkouei <mk@deltabeard.com>
This commit is contained in:
Mahyar Koshkouei
2022-03-27 14:03:39 +01:00
parent 3cc3b524a5
commit 84485d98ab
5 changed files with 883 additions and 48 deletions
+2 -2
View File
@@ -8,12 +8,12 @@ project(RP2040_GB C CXX)
# Initialize the Raspberry Pi Pico SDK
pico_sdk_init()
add_executable(RP2040_GB src/main.c src/hello-world.c)
add_executable(RP2040_GB src/main.c src/rom.c src/mk_ili9225.c)
target_include_directories(RP2040_GB PRIVATE inc)
target_link_libraries(RP2040_GB
pico_stdlib pico_stdio pico_bootrom pico_multicore pico_stdio
hardware_clocks hardware_pio hardware_vreg hardware_pio
hardware_sync hardware_pll hardware_spi hardware_irq
hardware_sync hardware_pll hardware_spi hardware_irq hardware_dma
pico_binary_info)
target_compile_definitions(RP2040_GB PRIVATE
PARAM_ASSERTIONS_DISABLE_ALL=1
+135
View File
@@ -0,0 +1,135 @@
/**
* Copyright (C) 2019-2022 by Mahyar Koshkouei <mk@deltabeard.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
//#define NDEBUG
#ifndef MK_ILI9225_READ_AVAILABLE
# define MK_ILI9225_READ_AVAILABLE 0
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#define SCREEN_SIZE_X 176u
#define SCREEN_SIZE_Y 220u
typedef enum {
ILI9225_COLOR_MODE_FULL = 0,
ILI9225_COLOR_MODE_8COLOR = 1
} ili9225_color_mode_e;
/**
* Controls the reset pin of the ILI9225.
* \param state Set to 0 on low output, else high.
*/
extern void mk_ili9225_set_rst(bool state);
/**
* Controls state of RS pin.
* \param state Set to 0 on low output, else high.
*/
extern void mk_ili9225_set_rs(bool state);
/**
* Controls state of CS pin.
* \param state Set to 0 on low output, else high.
*/
extern void mk_ili9225_set_cs(bool state);
/**
* Sends data to the ILI9225 using SPI. Return only after sending data.
* \param halfword Data to send.
*/
void mk_ili9225_spi_write16(const uint16_t *halfwords, size_t len);
#if MK_ILI9225_READ_AVAILABLE
/**
* Reads 16-bit data from the ILI9225 using SPI.
* \return 16-bit data.
*/
extern uint16_t mk_ili9225_spi_read16(void);
#endif
/**
* Delays execution in milliseconds.
* \param ms Duration to sleep for.
*/
extern void mk_ili9225_delay_ms(unsigned ms);
/**
* Initialise ILI9225 LCD with default settings.
* \return 0 on success, else error due to invalid LCD identification.
*/
unsigned mk_ili9225_init(void);
#if MK_ILI9225_READ_AVAILABLE
/**
* Read the current line being driven by the LCD. Can help with tearing
* mitigation.
* \return Line driven by LCD.
*/
unsigned mk_ili9225_read_driving_line(void);
#endif
/**
* Set the window that pixel will be written to. Address will loop within the
* window.
*
* \param hor_start
* \param hor_end
* \param vert_start
* \param vert_end
*/
void mk_ili9225_set_window(uint16_t hor_start, uint16_t hor_end,
uint16_t vert_start, uint16_t vert_end);
/**
* Set address pointer in GRAM. Must be within window.
* \param x
* \param y
*/
void mk_ili9225_set_address(uint8_t x, uint8_t y);
/**
* Write pixels to ILI9225 GRAM. These pixels will be displayed on screen at
* next vsync.
* \param pixels Pixel data in RGB565 format to write to LCD.
* \param nmemb Number of pixels.
*/
void mk_ili9225_write_pixels(const uint16_t *pixels, uint_fast16_t nmemb);
/**
* Inverts the display.
* @param invert
*/
void mk_ili9225_invert_display(bool invert);
void mk_ili9225_write_pixels_start(void);
void mk_ili9225_write_pixels_end(void);
void mk_ili9225_set_gate_scan(uint16_t hor_start, uint16_t hor_end);
void mk_ili9225_display_control(bool invert, ili9225_color_mode_e colour_mode);
void mk_ili9225_power_control(uint8_t drive_power, bool sleep);
void mk_ili9225_set_drive_freq(uint16_t f);
/**
* Exit and stop using LCD. Currently does nothing.
*/
void mk_ili9225_exit(void);
-32
View File
@@ -1,32 +0,0 @@
const unsigned char hello_world_gb[] = {
0x1a, 0xe0, 0x01, 0x13, 0x05, 0x3e, 0x81, 0xe0, 0x02, 0x76, 0x00, 0x20,
0xf3, 0xc9, 0x3e, 0x09, 0xe0, 0xff, 0x76, 0x00, 0x3e, 0x00, 0xe0, 0x40,
0x11, 0x61, 0x00, 0x06, 0x3d, 0xc7, 0x3e, 0x80, 0xe0, 0x40, 0xc3, 0x25,
0x00, 0x3e, 0x00, 0xe0, 0xff, 0x76, 0x00, 0x18, 0xfc, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x25, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xd9, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x20, 0x69, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x77,
0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x47, 0x61, 0x6d, 0x65,
0x20, 0x42, 0x6f, 0x79, 0x20, 0x45, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x6f,
0x72, 0x21, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfb, 0xc3, 0x50, 0x01, 0xce, 0xed, 0x66, 0x66,
0xcc, 0x0d, 0x00, 0x0b, 0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d,
0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e, 0xdc, 0xcc, 0x6e, 0xe6,
0xdd, 0xdd, 0xd9, 0x99, 0xbb, 0xbb, 0x67, 0x63, 0x6e, 0x0e, 0xec, 0xcc,
0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e, 0x68, 0x65, 0x6c, 0x6c,
0x6f, 0x2d, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x7d, 0xd8, 0x3e,
0xaf, 0xe0, 0x26, 0xc3, 0x0e, 0x00
};
unsigned int hello_world_gb_len = 342;
+194 -14
View File
@@ -1,5 +1,6 @@
#define ENABLE_LCD 0
#define ENABLE_LCD 1
#define ENABLE_SOUND 0
#define USE_DMA 0
/**
* Reducing VSYNC calculation to lower multiple.
@@ -20,12 +21,27 @@
#include <hardware/timer.h>
#include <hardware/vreg.h>
#include <pico/stdlib.h>
#include <pico/bootrom.h>
#include <hardware/spi.h>
#include <hardware/clocks.h>
#include <hardware/dma.h>
/* Project headers */
#include "hedley.h"
#include "peanut_gb.h"
#include "mk_ili9225.h"
extern const unsigned char hello_world_gb[];
/* LCD Connections. */
#define GPIO_CS 1
#define GPIO_CLK 2
#define GPIO_SDA 3
#define GPIO_RS 4
#define GPIO_RST 5
static uint dma_lcd;
extern const unsigned char rom[];
static uint8_t ram[32768];
static int shift = 0;
struct priv_t
{
@@ -35,13 +51,38 @@ struct priv_t
uint8_t *cart_ram;
};
void mk_ili9225_set_rst(bool state)
{
gpio_put(GPIO_RST, state);
}
void mk_ili9225_set_rs(bool state)
{
gpio_put(GPIO_RS, state);
}
void mk_ili9225_set_cs(bool state)
{
gpio_put(GPIO_CS, state);
}
void mk_ili9225_spi_write16(const uint16_t *halfwords, size_t len)
{
spi_write16_blocking(spi0, halfwords, len);
}
void mk_ili9225_delay_ms(unsigned ms)
{
sleep_ms(ms);
}
/**
* Returns a byte from the ROM file at the given address.
*/
uint8_t gb_rom_read(struct gb_s *gb, const uint_fast32_t addr)
{
//const struct priv_t * const p = gb->direct.priv;
return hello_world_gb[addr];
return rom[addr];
}
/**
@@ -49,7 +90,7 @@ uint8_t gb_rom_read(struct gb_s *gb, const uint_fast32_t addr)
*/
uint8_t gb_cart_ram_read(struct gb_s *gb, const uint_fast32_t addr)
{
return 0xFF;
return ram[addr];
//const struct priv_t * const p = gb->direct.priv;
//return p->cart_ram[addr];
}
@@ -60,7 +101,7 @@ uint8_t gb_cart_ram_read(struct gb_s *gb, const uint_fast32_t addr)
void gb_cart_ram_write(struct gb_s *gb, const uint_fast32_t addr,
const uint8_t val)
{
return;
ram[addr] = val;
//const struct priv_t * const p = gb->direct.priv;
//p->cart_ram[addr] = val;
}
@@ -89,8 +130,7 @@ void gb_error(struct gb_s *gb, const enum gb_error_e gb_err, const uint16_t val)
void gb_serial_tx(struct gb_s *gb, const uint8_t tx)
{
(void) gb;
putchar_raw(tx);
//printf("0x%02X ", tx);
//putchar_raw(tx);
}
enum gb_serial_rx_ret_e gb_serial_rx(struct gb_s *gb, uint8_t *rx)
@@ -100,10 +140,33 @@ enum gb_serial_rx_ret_e gb_serial_rx(struct gb_s *gb, uint8_t *rx)
return GB_SERIAL_RX_NO_CONNECTION;
}
void lcd_draw_line(struct gb_s *gb, const uint8_t pixels[160],
const uint_fast8_t line)
{
struct priv_t *priv = gb->direct.priv;
const uint16_t palette[3][4] = {
{ 0x7FFF, 0x5294, 0x294A, 0x0000 },
{ 0x7FFF, 0x5294, 0x294A, 0x0000 },
{ 0x7FFF, 0x5294, 0x294A, 0x0000 }
};
static uint16_t fb[LCD_WIDTH] = { 0 };
//dma_channel_wait_for_finish_blocking(dma_lcd);
for(unsigned int x = 0; x < LCD_WIDTH; x++)
{
fb[x] = palette[(pixels[x] & LCD_PALETTE_ALL) >> 4][pixels[x] & 3];
}
mk_ili9225_set_address(0, 15 + line);
mk_ili9225_write_pixels(fb, LCD_WIDTH + shift);
//dma_channel_transfer_from_buffer_now(dma_lcd, &fb[0], LCD_WIDTH);
}
int main(void)
{
static struct gb_s gb;
enum gb_init_error_e ret;
static const uint16_t clear = 0x0000;
{
/* The value for VCO set here is meant for least power
@@ -121,6 +184,49 @@ int main(void)
(void)getchar();
puts("Starting");
gpio_set_function(GPIO_CS, GPIO_FUNC_SIO);
gpio_set_function(GPIO_CLK, GPIO_FUNC_SPI);
gpio_set_function(GPIO_SDA, GPIO_FUNC_SPI);
gpio_set_function(GPIO_RS, GPIO_FUNC_SIO);
gpio_set_function(GPIO_RST, GPIO_FUNC_SIO);
gpio_set_dir(GPIO_CS, true);
gpio_set_dir(GPIO_RS, true);
gpio_set_dir(GPIO_RST, true);
gpio_set_slew_rate(GPIO_CLK, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(GPIO_SDA, GPIO_SLEW_RATE_FAST);
puts("Initialising SPI");
clock_configure(clk_peri, 0,
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
125 * 1000 * 1000, 125 * 1000 * 1000);
spi_init(spi0, 32*1000*1000);
printf("SPI baudrate = %u\n", spi_get_baudrate(spi0));
spi_set_format(spi0, 16,
SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
puts("Initialising LCD");
mk_ili9225_init();
dma_lcd = dma_claim_unused_channel(true);
dma_channel_config c2 = dma_channel_get_default_config(dma_lcd);
channel_config_set_transfer_data_size(&c2, DMA_SIZE_16);
channel_config_set_dreq(&c2,DREQ_SPI0_TX); // select SPI TX as Dreq for pacing data transfer
channel_config_set_read_increment(&c2, false);
channel_config_set_write_increment(&c2, false);
channel_config_set_ring(&c2, false, 0);
/* Clear LCD screen. */
mk_ili9225_write_pixels_start();
dma_channel_configure(dma_lcd,
&c2,
&spi_get_hw(spi0)->dr,
&clear,
SCREEN_SIZE_X*(SCREEN_SIZE_Y+1),
true);
dma_channel_wait_for_finish_blocking(dma_lcd);
mk_ili9225_write_pixels_end();
/* Initialise context. */
ret = gb_init(&gb, &gb_rom_read, &gb_cart_ram_read,
&gb_cart_ram_write, &gb_error, NULL);
@@ -131,27 +237,101 @@ int main(void)
goto sleep;
}
dma_channel_set_trans_count(dma_lcd, LCD_WIDTH, false);
channel_config_set_read_increment(&c2, true);
mk_ili9225_set_window(15, 144+15, 29, 160+29);
#if ENABLE_LCD
//gb_init_lcd(&gb, &lcd_draw_line);
// gb.direct.interlace = 1;
gb_init_lcd(&gb, &lcd_draw_line);
//gb.direct.interlace = 1;
#endif
gb_init_serial(&gb, gb_serial_tx, gb_serial_rx);
//gb_init_serial(&gb, gb_serial_tx, gb_serial_rx);
uint_fast32_t instrs = 0;
#if USE_DMA
mk_ili9225_write_pixels_start();
#endif
uint_fast32_t frames = 0;
uint64_t start_time = time_us_64();
uint64_t end_time;
while(!gb.direct.ended)
while(1)
{
int cmd;
__gb_step_cpu(&gb);
instrs++;
__gb_step_cpu(&gb);
__gb_step_cpu(&gb);
__gb_step_cpu(&gb);
__gb_step_cpu(&gb);
__gb_step_cpu(&gb);
__gb_step_cpu(&gb);
__gb_step_cpu(&gb);
if(gb.gb_frame == 0)
continue;
frames++;
gb.gb_frame = 0;
cmd = getchar_timeout_us(0);
if(cmd == PICO_ERROR_TIMEOUT)
continue;
switch(cmd)
{
#if USE_DMA == 0
static bool invert = false;
static bool sleep = false;
static uint8_t freq = 1;
static ili9225_color_mode_e colour = ILI9225_COLOR_MODE_FULL;
case 'i':
invert = !invert;
mk_ili9225_display_control(invert, colour);
break;
case 'c':
colour = !colour;
mk_ili9225_display_control(invert, colour);
break;
case 'f':
freq++;
freq &= 0x0F;
mk_ili9225_set_drive_freq(freq);
printf("Freq %u\n", freq);
break;
case 'u':
shift++;
break;
case 'd':
shift--;
break;
#endif
case 'q':
goto out;
default:
break;
}
//mk_ili9225_write_pixels_start();
}
out:
end_time = time_us_64();
puts("\nEmulation Ended");
printf("Instructions: %u\n"
"Time: %llu us\n", instrs, end_time-start_time);
"Time: %llu us\n", frames, end_time-start_time);
mk_ili9225_write_pixels_end();
mk_ili9225_set_rst(true);
reset_usb_boot(0, 0);
/* Sleep forever. */
sleep:
+552
View File
@@ -0,0 +1,552 @@
/**
* Copyright (C) 2019-2022 by Mahyar Koshkouei <mk@deltabeard.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include "mk_ili9225.h"
/* Register Descriptions. */
/**
* The index register (IR) stored the register address.
* The register selection signal (RS) selects register access.
* The read/write signals (nRD/nWR) select read or write command, respectively.
* The data bus D0-D17 are used to read or write data. Register access is
* 16-bit, so 18-bit bus is reduced to 16-bit only (see page 45).
* On writing data GRAM, the internal address is automatically incremented.
*/
/* ILI9225 Registers. */
/**
* [Register Name] ([RO,WO, or WR])
* R ([Read mask]): [Description of what can be read at mask]
* W ([Write mask]): [Description of what can be written at mask]
*/
/**
* Driver Code Read (RW)
* R (0xFFFF): Reads the device code integer 0x9225.
* W: (0xXXX1): Starts (0x1) or stops (0x0) internal oscillator.
* At least 10ms must pass after starting the internal oscillator to allow it
* to stabilise.
*/
#define MK_ILI9225_REG_DRIVER_CODE_READ 0x00
#define MK_ILI9225_REG_START_OSCILLATION 0x00
/**
* Driver Output Control (WO)
* W (0x8XXX): VSPL: Inverts the polarity of VSYNC pin. "1" is high active.
* W (0x4XXX): HSPL: Inverts the polarity of HSYNC pin. "1" is high active.
* W (0x2XXX): DPL: Same as above for DOTCLK.
* W (0x1XXX): EPL: Same as above for ENABLE.
* W (0xX4XX): SM: Scan order by gate driver.
* W (0xX2XX): GS: Shift direction of gate driver.
* W (0xX1XX): SS: Shift direction from source driver.
* W (0xXX1F): NL: Set active gate driver line. Default: 0b11011.
*/
#define MK_ILI9225_REG_DRIVER_OUTPUT_CTRL 0x01
/**
* LCD Driving Waveform Control (WO)
* Set LCD inversion method.
* W (0xX3XX): INV: Sets inversion method.
* W (0xXXX1): FLD: 3-field interlaced scanning function.
*/
#define MK_ILI9225_REG_LCD_AC_DRIVING_CTRL 0x02
/**
* Entry Mode (WO)
* W (0x1XXX): BGR: Swap Red with Blue.
* W (0xX3XX): MDT: Data transmission format for 80-mode.
* W (0xXX2X): ID1: Control horizontal address counter increment (1) or
* decrement (0).
* W (0xXX1X): ID0: Control vertical address counter increment (1) or
* decrement (0).
* W (0xXXX8): AM: GRAM update direction. "0" is horizontal.
*/
#define MK_ILI9225_REG_ENTRY_MODE 0x03
/**
* Display Control 1 (WO)
* W (0x1XXX): TEMON: Sets the frame flag output signal FLM.
* W (0xXX1X): GON: Set output level of gate driver. VGL = 0, Normal = 1.
* W (0xXXX8): CL: Selects 8-colour display mode when 1. Else 2^18 colours.
* W (0xXXX4): REV: Reverses greyscale levels if set.
* W (0xXXX3): D: Control display output. 0b11 to turn on display, else display
* is off.
*/
#define MK_ILI9225_REG_DISPLAY_CTRL 0x07
/**
* Display Control 2 (WO)
* Used for I80, M68, and RGB modes only.
* W (0xXFXX): FP: Specify number of front porch lines.
* W (0xXXXF): BP: Specify number of back porch lines.
*/
#define MK_ILI9225_REG_BLANK_PERIOD_CTRL 0x08
/**
* Frame Cycle Control (WO)
* Used for RGB mode only.
* W (0xFXXX): NO: Set amount of non-overlay for gate output.
* W (0xXFXX): SDT: Set delay from gate edge to source output.
* W (0xXXXF): RTN: Set number of clock cycles for one display line.
*/
#define MK_ILI9225_REG_FRAME_CYCLE_CTRL 0x0B
/**
* RGB Input Interface Control (W)
* Used for RGB mode only.
*/
#define MK_ILI9225_REG_INTERFACE_CTRL 0x0C
/**
* Oscillator Control (W)
* W (0xXFXX): FOSC: Oscillation frequency.
* W (0xXXX1): OSC_EN: Starts oscillation from halt state. 10ms wait required.
*/
#define MK_ILI9225_REG_OSC_CTRL 0x0F
/**
* Power Control 1 (W)
* W (0xXFXX): SAP: Set current level of driver.
* W (0xXXX2): DSTB: Enable deep standby mode. Writing to GRAM is prohibited.
* W (0xXXX1): STB: Enable sleep mode. Stops display and internal oscillator.
*/
#define MK_ILI9225_REG_PWR_CTRL1 0x10
/**
* Power Control 2 (W)
* W (0x1XXX): APON: Use automatic boosting operation.
* W (0xXFXX): PON: Set boosting operation for specific circuits.
* W (0xXX2X): AON: Set operation of amplifier.
* W (0xXX1X): VCL1EN: Set operation of generation amplifier.
* W (0xXXXF): VC: Set VCl1 voltage output.
*/
#define MK_ILI9225_REG_PWR_CTRL2 0x11
/**
* Power Control 3 (W)
* W (0x7XXX): BT: Set output factor of boost converter.
* W (0xX7XX): DC1: Operation frequency of circuit 1 boost converter.
* W (0xXX7X): DC2: Operation frequency of circuit 2 boost converter.
* W (0XXXX7): DC3: Operation frequency of circuit 3 boost converter.
*/
#define MK_ILI9225_REG_PWR_CTRL3 0x12
/**
* Power Control 4 (W)
* W (0xXX7F): GVD: Set gamma voltage (GVDD) from 2.66V to 5.5V.
*/
#define MK_ILI9225_REG_PWR_CTRL4 0x13
/**
* Power Control 5 (W)
* W (0x8XXX): VCOMG: Sets amplitude of VCOM signal.
* W (0x7FXX): VCM: Sets VCOMH voltage, from 0.4015 to 1.1000 * GVDD.
* W (0xXX7F): VML: Sets alternate amplitudes of VCOM from 0.534 to 1.200*GVDD.
*/
#define MK_ILI9225_REG_PWR_CTRL5 0x14
/**
* VCI Recycling (W)
* W (0xXX7X): VCIR: Number of clock cycles in VCI recycling period.
*/
#define MK_ILI9225_REG_VCI_RECYCLING 0x15
/**
* RAM Address Set 1 (W)
* W (0xXXFF): AD[7:0]: Set the initial value of the address counter.
*/
#define MK_ILI9225_REG_RAM_ADDR_SET1 0x20
/**
* RAM Address Set 2 (W)
* W (0xXXFF): AD[15:8]: Set the initial value of the address counter.
*/
#define MK_ILI9225_REG_RAM_ADDR_SET2 0x21
/**
* Read/Write GRAM Data (RW)
* W (0x3FFFF): Write data to GRAM.
* R (0x3FFFF): Read data from GRAM.
*/
#define MK_ILI9225_REG_GRAM_RW 0x22
/**
* Software Reset (W)
* W (0xFFFF): Perform software reset when 0x00CE is written.
*/
#define MK_ILI9225_REG_SOFT_RESET 0x28
/**
* Gate Scan Control (W)
* W (0xXX1F): SCN: Line to start gate scan from.
*/
#define MK_ILI9225_REG_GATE_SCAN_CTRL 0x30
/**
* Vertical Scroll Control 1 (W)
* W (0xXXFF): SEA: Scroll end address.
*/
#define MK_ILI9225_REG_VERT_SCROLL_CTRL1 0x31
/**
* Vertical Scroll Control 2 (W)
* W (0xXXFF): SSA: Scroll start address.
*/
#define MK_ILI9225_REG_VERT_SCROLL_CTRL2 0x32
/**
* Vertical Scroll Control 3 (W)
* W (0xXXFF): SST: Scroll step.
*/
#define MK_ILI9225_REG_VERT_SCROLL_CTRL3 0x33
/**
* Partial Screen Driving Position (W)
* W (0xXXFF): SE: High byte of screen end position.
*/
#define MK_ILI9225_REG_PART_DRIVING_POS1 0x34
/**
* Partial Screen Driving Position (W)
* W (0xXXFF): SS: High byte of screen start position.
*/
#define MK_ILI9225_REG_PART_DRIVING_POS2 0x35
/**
* Horizontal RAM Address Position (W)
* W (0xXXFF): HEA: End horizontal position.
*/
#define MK_ILI9225_REG_HORI_WIN_ADDR1 0x36
/**
* Horizontal RAM Address Position (W)
* W (0xXXFF): HSA: Start horizontal position.
*/
#define MK_ILI9225_REG_HORI_WIN_ADDR2 0x37
/**
* Vertical RAM Address Position (W)
* W (0xXXFF): VEA: End vertical position.
*/
#define MK_ILI9225_REG_VERT_WIN_ADDR1 0x38
/**
* Vertical RAM Address Position (W)
* W (0xXXFF): VSA: Start vertical position.
*/
#define MK_ILI9225_REG_VERT_WIN_ADDR2 0x39
/**
* Gamma Control (W)
*/
#define MK_ILI9225_REG_GAMMA_CTRL1 0x50
#define MK_ILI9225_REG_GAMMA_CTRL2 0x51
#define MK_ILI9225_REG_GAMMA_CTRL3 0x52
#define MK_ILI9225_REG_GAMMA_CTRL4 0x53
#define MK_ILI9225_REG_GAMMA_CTRL5 0x54
#define MK_ILI9225_REG_GAMMA_CTRL6 0x55
#define MK_ILI9225_REG_GAMMA_CTRL7 0x56
#define MK_ILI9225_REG_GAMMA_CTRL8 0x57
#define MK_ILI9225_REG_GAMMA_CTRL9 0x58
#define MK_ILI9225_REG_GAMMA_CTRL10 0x59
#define MK_ILI9225_NV_MEM_DATA_PROG 0x60
#define MK_ILI9225_NV_MEM_CTRL 0x61
#define MK_ILI9225_NV_MEM_STAT 0x62
#define MK_ILI9225_NV_MEM_PROTECTION_KEY 0x63
#define MK_ILI9225_NV_MEM_ID_CODE 0x65
#define MK_ILI9225_REG_MTP_TEST_KEY 0x80
#define MK_ILI9225_REG_MTP_CTRL_REG 0x81
#define MK_ILI9225_REG_MTP_DATA_READ 0x82
/* Useful macros. */
#define ARRAYSIZE(array) (sizeof(array)/sizeof(array[0]))
/* Structure definitions. */
struct reg_dat_pair {
uint16_t reg;
uint16_t dat;
};
/* Local functions. */
static void write_register(uint16_t cmd);
static void write_data(uint16_t dat);
#if MK_ILI9225_READ_AVAILABLE
static uint16_t read_data(void);
static uint16_t get_register(uint16_t reg);
#endif
static void write_register(uint16_t cmd)
{
mk_ili9225_set_rs(0);
mk_ili9225_set_cs(0);
mk_ili9225_spi_write16(&cmd, 1);
mk_ili9225_set_cs(1);
}
static void write_data(uint16_t dat)
{
mk_ili9225_set_rs(1);
mk_ili9225_set_cs(0);
mk_ili9225_spi_write16(&dat, 1);
mk_ili9225_set_cs(1);
}
#if MK_ILI9225_READ_AVAILABLE
static uint16_t read_data(void)
{
uint16_t ret;
mk_ili9225_set_rs(1);
mk_ili9225_set_cs(0);
ret = mk_ili9225_spi_read16();
mk_ili9225_set_cs(1);
return ret;
}
static uint16_t get_register(uint16_t reg)
{
write_register(reg);
return read_data();
}
#endif
static void set_register(uint16_t reg, uint16_t dat)
{
write_register(reg);
write_data(dat);
}
#if MK_ILI9225_READ_AVAILABLE
unsigned mk_ili9225_read_driving_line(void)
{
unsigned line;
mk_ili9225_set_rs(0);
mk_ili9225_set_cs(0);
line = read_data();
mk_ili9225_set_cs(1);
return (line >> 8);
}
#endif
/* Public functions. */
unsigned mk_ili9225_init(void)
{
unsigned ret = 0x9225;
/* Initialise reset pin as not reset. RST must be high before reset. */
mk_ili9225_set_rst(1);
/* Initialise other pins too. */
mk_ili9225_set_cs(1);
mk_ili9225_set_rs(0);
/* Make sure pin is initialised by setting small delay. */
mk_ili9225_delay_ms(1);
/* Put ILI9225 into reset. */
mk_ili9225_set_rst(0);
/* Reset low-level width (Tres) is at least 1ms. */
mk_ili9225_delay_ms(10);
/* Bring ILI9225 out of reset. */
mk_ili9225_set_rst(1);
mk_ili9225_delay_ms(50);
/* Initialise power registers. */
/* FIXME: This may not be required. All are initialised to 0x00,
* FIXME: but VCOMG is set to 1 on init apparently. */
{
const uint8_t regs[] = {
MK_ILI9225_REG_PWR_CTRL1, MK_ILI9225_REG_PWR_CTRL2,
MK_ILI9225_REG_PWR_CTRL3, MK_ILI9225_REG_PWR_CTRL4,
MK_ILI9225_REG_PWR_CTRL5
};
for(uint8_t i = 0; i < sizeof(regs); i++)
set_register(regs[i], 0x0000);
}
mk_ili9225_delay_ms(40);
/* Switch on power control. */
{
/* VCI set to 2.58V. */
/* FIXME: CTRL3 VGH is set to 2.58*6=15.48, but it could be
* FIXME: lower ( 9 < VGH < 16.5).
* FIXME: CTRL3 VGL is set to 2.58*-4=-10.32, but it could be
* FIXME: higher ( -4 < VGL < -16.5).
* FIXME: BT could be set to 0b000 for VGH=10, VGL=-7.5. */
/* CTRL4: GVDD is set to 4.68V. */
/* CTRL5: Set VCM to 0.8030V. Set VML to 1.104V. */
/* CTRL1: Set driving capability to "Medium Fast 1". */
const uint8_t regs[] = {
MK_ILI9225_REG_PWR_CTRL2, MK_ILI9225_REG_PWR_CTRL3,
MK_ILI9225_REG_PWR_CTRL4, MK_ILI9225_REG_PWR_CTRL5,
MK_ILI9225_REG_PWR_CTRL1
};
const uint16_t dat[] = {
0x0018, 0x6121, 0x006F, 0x495F, 0x0800
};
for(uint8_t i = 0; i < sizeof(regs); i++)
set_register(regs[i], dat[i]);
}
mk_ili9225_delay_ms(10);
/* Enable automatic booster operation, and amplifiers.
* Set VCI1 to 2.76V.
* FIXME: why is VCI1 changed from 2.58 to 2.76V? */
set_register(MK_ILI9225_REG_PWR_CTRL2, 0x103B);
mk_ili9225_delay_ms(50);
{
const struct reg_dat_pair cmds[] = {
/* Set shift direction SS from S528 to S1.
* Set active lines NL to 528 * 220 dots. */
{ MK_ILI9225_REG_DRIVER_OUTPUT_CTRL, 0x011C },
/* Set LCD inversion to disabled. */
{ MK_ILI9225_REG_LCD_AC_DRIVING_CTRL, 0x0100 },
/* Use BGR mode (swap blue with red).
* Increment vertical and horizontal address.
* Use vertical image.
* FIXME: Why is BGR enabled? */
/* TODO: Change to horizontal mode. */
{ MK_ILI9225_REG_ENTRY_MODE, 0x1000 },
/* Turn off all display outputs. */
{ MK_ILI9225_REG_DISPLAY_CTRL, 0x0000 },
/* Set porches to 8 lines. */
{ MK_ILI9225_REG_BLANK_PERIOD_CTRL, 0x0808 },
/* Use 1-clock delay to gate output and edge. */
{ MK_ILI9225_REG_FRAME_CYCLE_CTRL, 0x1100 },
/* Ignore RGB interface settings. */
{ MK_ILI9225_REG_INTERFACE_CTRL, 0x0000 },
/* Set oscillation frequency to 266.6 kHz. */
{ MK_ILI9225_REG_OSC_CTRL, 0x0701 },
/* Set VCI recycling to 2 clocks. */
{ MK_ILI9225_REG_VCI_RECYCLING, 0x0020 },
/* Initialise RAM Address to 0x0 px. */
{ MK_ILI9225_REG_RAM_ADDR_SET1, 0x0000 },
{ MK_ILI9225_REG_RAM_ADDR_SET2, 0x0000 },
/* Set scanning position to full screen. */
{ MK_ILI9225_REG_GATE_SCAN_CTRL, 0x0000 },
/* Set end scan position to 219 + 1 px (0xDB). */
{ MK_ILI9225_REG_VERT_SCROLL_CTRL1, 0x00DB },
/* Set start scan position to 0 px. */
{ MK_ILI9225_REG_VERT_SCROLL_CTRL2, 0x0000 },
/* Set scroll step to 0 px (no scrolling). */
{ MK_ILI9225_REG_VERT_SCROLL_CTRL3, 0x0000 },
/* Set partial screen driving end to 219 + 1 px
* (0xDB). */
{ MK_ILI9225_REG_PART_DRIVING_POS1, 0x00DB },
/* Set partial screen driving start to 0 px. */
{ MK_ILI9225_REG_PART_DRIVING_POS2, 0x0000 },
/* Set window to 176 x 220 px (full screen). */
{ MK_ILI9225_REG_HORI_WIN_ADDR1, 0x00AF },
{ MK_ILI9225_REG_HORI_WIN_ADDR2, 0x0000 },
{ MK_ILI9225_REG_VERT_WIN_ADDR1, 0x00DB },
{ MK_ILI9225_REG_VERT_WIN_ADDR2, 0x0000 },
/* Gamma curve data. */
{ MK_ILI9225_REG_GAMMA_CTRL1, 0x0000 },
{ MK_ILI9225_REG_GAMMA_CTRL2, 0x0808 },
{ MK_ILI9225_REG_GAMMA_CTRL3, 0x080A },
{ MK_ILI9225_REG_GAMMA_CTRL4, 0x000A },
{ MK_ILI9225_REG_GAMMA_CTRL5, 0x0A08 },
{ MK_ILI9225_REG_GAMMA_CTRL6, 0x0808 },
{ MK_ILI9225_REG_GAMMA_CTRL7, 0x0000 },
{ MK_ILI9225_REG_GAMMA_CTRL8, 0x0A00 },
{ MK_ILI9225_REG_GAMMA_CTRL9, 0x0710 },
{ MK_ILI9225_REG_GAMMA_CTRL10, 0x0710 },
/* Enable full colour display. */
{ MK_ILI9225_REG_DISPLAY_CTRL, 0x0012 }
};
for(uint_fast8_t i = 0; i < (uint_fast8_t)ARRAYSIZE(cmds); i++)
set_register(cmds[i].reg, cmds[i].dat);
}
mk_ili9225_delay_ms(50);
/**
* FIXME: TEMON is enabled but FLM isn't exposed?
* GON: Enable display.
* CL: Use full colour.
* REV: reverse greyscale levels.
* D: Switch on display.
*/
set_register(MK_ILI9225_REG_DISPLAY_CTRL, 0x1017);
mk_ili9225_delay_ms(50);
#if MK_ILI9225_READ_AVAILABLE
/* ret should be 0 if the returned ID was 0x9225. */
ret -= get_register(MK_ILI9225_REG_DRIVER_CODE_READ);
#else
ret = 0;
#endif
return ret;
}
void mk_ili9225_display_control(bool invert, ili9225_color_mode_e colour_mode)
{
uint16_t dat = 0x0013;
dat |= ((uint16_t)invert << 2);
dat |= ((uint16_t)colour_mode << 3);
set_register(MK_ILI9225_REG_DISPLAY_CTRL, dat);
}
void mk_ili9225_set_window(uint16_t hor_start, uint16_t hor_end,
uint16_t vert_start, uint16_t vert_end)
{
assert(hor_start < hor_end);
assert(hor_end < SCREEN_SIZE_X);
assert(vert_start < vert_end);
assert(vert_end < SCREEN_SIZE_Y);
set_register(MK_ILI9225_REG_RAM_ADDR_SET1, hor_start);
set_register(MK_ILI9225_REG_RAM_ADDR_SET2, vert_start);
set_register(MK_ILI9225_REG_HORI_WIN_ADDR1, hor_end);
set_register(MK_ILI9225_REG_HORI_WIN_ADDR2, hor_start);
set_register(MK_ILI9225_REG_VERT_WIN_ADDR1, vert_end);
set_register(MK_ILI9225_REG_VERT_WIN_ADDR2, vert_start);
}
void mk_ili9225_set_address(uint8_t x, uint8_t y)
{
set_register(MK_ILI9225_REG_RAM_ADDR_SET1, x);
set_register(MK_ILI9225_REG_RAM_ADDR_SET2, y);
}
void mk_ili9225_write_pixels(const uint16_t *pixels, uint_fast16_t nmemb)
{
assert(pixels != NULL);
assert(nmemb > 0);
write_register(MK_ILI9225_REG_GRAM_RW);
mk_ili9225_set_rs(1);
mk_ili9225_set_cs(0);
mk_ili9225_spi_write16(pixels, nmemb);
mk_ili9225_set_cs(1);
}
void mk_ili9225_write_pixels_start(void)
{
write_register(MK_ILI9225_REG_GRAM_RW);
mk_ili9225_set_rs(1);
mk_ili9225_set_cs(0);
}
void mk_ili9225_write_pixels_end(void)
{
mk_ili9225_set_cs(1);
}
void mk_ili9225_power_control(uint8_t drive_power, bool sleep)
{
uint16_t data;
data = (uint16_t)drive_power << 8;
data |= sleep;
set_register(MK_ILI9225_REG_PWR_CTRL1, data);
}
void mk_ili9225_set_gate_scan(uint16_t hor_start, uint16_t hor_end)
{
uint16_t dat = 0x0100;
uint16_t lcd_line_start = hor_end / 8;
set_register(MK_ILI9225_REG_DRIVER_OUTPUT_CTRL, dat | lcd_line_start);
set_register(MK_ILI9225_REG_GATE_SCAN_CTRL, hor_start / 8);
}
void mk_ili9225_set_drive_freq(uint16_t f)
{
f &= 0x000F;
f <<= 8;
f |= 1;
set_register(MK_ILI9225_REG_OSC_CTRL, f);
}
void mk_ili9225_exit(void)
{
}