LCD TFT displej TF028 s paralelním 8 bitovým rozhraním původně určený pro Arduino se mi podařilo rozchodit na Raspberry Pi Pico. Funguje velmi rychle, ale má některé hardwarové chyby. Odporový touchscreen je totiž připojen na stejné piny jako ovládání TFT displeje. Toto je první verze, ke komunikaci používám PIO, zatím bez DMA.
Hardware
Displej musí být napájen z 5V, na 3.3V nefunguje.

Problém je v tom, že piny LCD_CS a LCD_RS jsou spojeny s piny touchscreenu X+ a Y+.


| displej pin | Pico pin |
|---|---|
Power VCC (5V) |
5V (na 3.3V displej nefunguje) |
Power GOND |
GND |
Reset signal (LCD_RST) |
3.3V (bez toho displej nefunguje) |
Chip select (LCD CS) |
GPIO15 |
Command/Data select (LCD_RS) |
GPIO14 |
Write signal (LCD_WR) |
GPIO13 |
Read signal (LCD_RD) |
GPIO12 |
LCD Data bit 0 (LCD_D0) |
GPIO4 |
LCD Data bit 1 (LCD_D1) |
GPIO5 |
LCD Data bit 2 (LCD_D2) |
GPIO6 |
LCD Data bit 3 (LCD_D3) |
GPIO7 |
LCD Data bit 4 (LCD_D4) |
GPIO8 |
LCD Data bit 5 (LCD_D5) |
GPIO9 |
LCD Data bit 6 (LCD_D6) |
GPIO10 |
LCD Data bit 7 (LCD_D7) |
GPIO11 |
Software
Sestavovací soubor pro cmake.
CMakeLists.txt
# cmake version
cmake_minimum_required(VERSION 3.13)
# include the sdk.cmake file
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
# give the project a name (anything you want)
project(ili_pio_test)
# initialize the sdk
pico_sdk_init()
#######
# name anything you want
add_executable(ili_pio_test)
# must match with pio filename and executable name from above
pico_generate_pio_header(ili_pio_test ${CMAKE_CURRENT_LIST_DIR}/ili9341.pio)
# must match with executable name and source file names
target_sources(ili_pio_test PRIVATE
main.c
ili9341.c
logo.c
pico.c
)
# must match with executable name
target_link_libraries(ili_pio_test PRIVATE
pico_stdlib
hardware_pio
hardware_dma
hardware_irq
)
# must match with executable name
pico_add_extra_outputs(ili_pio_test)
pico_enable_stdio_usb(ili_pio_test 1)
pico_enable_stdio_uart(ili_pio_test 0)
#add_compile_options(-Ofast)
Komunikace s displejem
Komunikaci s diplejem zajištuje PIO program.
Časová posloupnost zápisu na displej s odpovídajícím kódem Pio asembleru
Časová posloupnost zápis z datového listu str. 28
|
PIO asembler ili9341.pio
|
.program ili9341_pio_cmd
; CSX, D/CX, WRX, RDX --> gpio 15, 14, 13, 12 (set pins), RESET připojit na 3.3V
.wrap_target
start:
pull
set pins, 0b0011
mov x, osr ; command code, if 0x0, command nop, only data
jmp !x param
set pins, 0b0001
out pins, 8 [1]
set pins, 0b0011 [1]
param:
set pins, 0b0111
pull
mov x, osr ; how many parameters
jmp !x, start ; no parameter return start
jmp x--, param_data
param_data:
pull ; write data
set pins, 0b0101
out pins, 8 [1]
set pins, 0b0111 [1]
jmp x--, param_data
set pins, 0b1111
.wrap
Pojmenování řídících registrů displeje.
registers.h
#define ILI9341_NOP 0x00
#define ILI9341_SOFTRESET 0x01
#define ILI9341_SLEEPIN 0x10
#define ILI9341_SLEEPOUT 0x11
#define ILI9341_NORMALDISP 0x13
#define ILI9341_INVERTOFF 0x20
#define ILI9341_INVERTON 0x21
#define ILI9341_GAMMASET 0x26
#define ILI9341_DISPLAYOFF 0x28
#define ILI9341_DISPLAYON 0x29
#define ILI9341_COLADDRSET 0x2A
#define ILI9341_PAGEADDRSET 0x2B
#define ILI9341_MEMORYWRITE 0x2C
#define ILI9341_MEMORYREAD 0x2E
#define ILI9341_PIXELFORMAT 0x3A
#define ILI9341_MEMORYWRITECONT 0x3C
#define ILI9341_MEMORYREADCONT 0x3E
#define ILI9341_FRAMECONTROL 0xB1
#define ILI9341_DISPLAYFUNC 0xB6
#define ILI9341_ENTRYMODE 0xB7
#define ILI9341_POWERCONTROL1 0xC0
#define ILI9341_POWERCONTROL2 0xC1
#define ILI9341_VCOMCONTROL1 0xC5
#define ILI9341_VCOMCONTROL2 0xC7
#define ILI9341_VSCROLLDEF 0x33
#define ILI9341_VSCROLLADDR 0x37
#define ILI9341_MEMCONTROL 0x36
#define ILI9341_MADCTL 0x36
#define ILI9341_PGAMCOR 0xE0
#define ILI9341_NGAMCOR 0xE1
#define ILI9341_MADCTL_MY 0x80
#define ILI9341_MADCTL_MX 0x40
#define ILI9341_MADCTL_MV 0x20
#define ILI9341_MADCTL_ML 0x10
#define ILI9341_MADCTL_RGB 0x00
#define ILI9341_MADCTL_BGR 0x08
#define ILI9341_MADCTL_MH 0x04
Zápis na displej a grafické funkce.
ili9341.c
#include "stdio.h"
#include "stdlib.h"
#include "ili9341.pio.h"
#include "pico/stdlib.h"
#include "hardware/clocks.h"
#include "string.h"
#include "registers.h"
#include "bitmap_typedefs.h"
//#include "ili9341.h"
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
PIO ili9341_pio = pio1;
uint ili9341_sm = 0;
uint out_base_pin = 4;
uint in_base_pin = 4;
uint set_base_pin = 12;
//TFT width and height default global variables
uint16_t ili_tftwidth = 320;
uint16_t ili_tftheight = 240;
static uint8_t p_count = 0;
static uint8_t d_count = 0;
static uint8_t data_recv = 0;
static uint8_t params[8];
static uint32_t data[8];
// poslání příkazu displeji
void ili9341_cmd(uint8_t cmd, uint32_t count, uint8_t *param)
{
pio_sm_restart(ili9341_pio,ili9341_sm);
pio_sm_put_blocking(ili9341_pio, ili9341_sm, cmd); // command code
pio_sm_put_blocking(ili9341_pio, ili9341_sm, count); // how many parameters
for( int i = 0; i < count; i++) {
pio_sm_put_blocking( ili9341_pio, ili9341_sm, param[i]);
}
}
// inicializace PIO
void ili9431_pio_cmd_init(PIO pio, uint sm, uint out_base, uint set_base, uint32_t freq)
{
uint offset = 0;
pio_sm_config c;
offset = pio_add_program(pio, &ili9341_pio_cmd_program);
c = ili9341_pio_cmd_program_get_default_config(offset);
for (int i = 0; i < 8; i++)
pio_gpio_init(pio, out_base + i);
for (int i = 0; i < 4; i++)
pio_gpio_init(pio, set_base + i);
pio_sm_set_consecutive_pindirs(pio, sm, out_base, 8, true);
pio_sm_set_consecutive_pindirs(pio, sm, set_base, 4, true);
sm_config_set_out_pins(&c, out_base, 8);
sm_config_set_set_pins(&c, set_base, 4);
sm_config_set_out_shift(&c, true, false, 32);
float div = clock_get_hz(clk_sys) / freq;
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
/* ili3941 kreslící funkce */
// ---------------------------------------------------------------------------------------
// převod z formátu 5R6G5B na uint16_t
uint16_t ili9341_color_565RGB(uint8_t R, uint8_t G, uint8_t B)
{
// uint16_t c;
return (((uint16_t)R) >> 3) << 11 | (((uint16_t)G) >> 2) << 5 | ((uint16_t)B) >> 3;
//return c;
}
// nastaveni adresy displje
void ili9341_set_address_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
uint8_t addr[4];
addr[0] = (uint8_t)(x1 >> 8);
addr[1] = (uint8_t)(x1 & 0xff);
addr[2] = (uint8_t)(x2 >> 8);
addr[3] = (uint8_t)(x2 & 0xff);
ili9341_cmd(ILI9341_COLADDRSET, 4, addr);
addr[0] = (uint8_t)(y1 >> 8);
addr[1] = (uint8_t)(y1 & 0xff);
addr[2] = (uint8_t)(y2 >> 8);
addr[3] = (uint8_t)(y2 & 0xff);
ili9341_cmd(ILI9341_PAGEADDRSET, 4, addr);
ili9341_cmd(ILI9341_MEMORYWRITE, 0, NULL);
}
// nakresleni bodu na displeji
void ili9341_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
{
if (x < 0 || x > SCREEN_WIDTH || y < 0 || y > SCREEN_HEIGHT)
return;
ili9341_set_address_window(x, y, x, y);
ili9341_cmd(ILI9341_NOP, 2, (uint8_t[2]){(uint8_t)(color), (uint8_t)(color>>8)});
}
// invertování barev dipleje
void ili9341_invert_display(bool invert)
{
if (invert)
ili9341_cmd(ILI9341_INVERTON, 0, NULL);
else
ili9341_cmd(ILI9341_INVERTOFF, 0, NULL);
}
// nakreslení vyplněného obdélníka
void ili9341_fill_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color)
{
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x + width > SCREEN_WIDTH)
width = SCREEN_WIDTH - x;
if (y + height > SCREEN_HEIGHT)
height = SCREEN_HEIGHT - y;
ili9341_set_address_window(x, y, x + width, y + height);
for (int j = y; j < y + height; j++)
for (int i = x; i < x + width; i++)
ili9341_cmd(ILI9341_NOP, 2, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)(color & 0x00ff)});
}
// nakreslení čáry
void ili9341_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color)
{
/* algorithm from https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm */
int dx = abs(x1 - x0);
int sx = x0 < x1 ? 1 : -1;
int dy = -abs(y1 - y0);
int sy = y0 < y1 ? 1 : -1;
int error = dx + dy;
int e2;
while (1)
{
// plot(x0, y0)
ili9341_draw_pixel(x0, y0, color);
if (x0 == x1 && y0 == y1)
break;
e2 = 2 * error;
if (e2 >= dy)
{
if (x0 == x1)
break;
error = error + dy;
x0 = x0 + sx;
}
if (e2 <= dx)
{
if (y0 == y1)
break;
error = error + dx;
y0 = y0 + sy;
}
}
}
void ili9341_draw_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color) {
int x;
int y;
int error;
int old_error;
x=0;
y=-r;
error=2-2*r;
do{
ili9341_draw_pixel(x0-x, y0+y, color);
ili9341_draw_pixel(x0-y, y0-x, color);
ili9341_draw_pixel(x0+x, y0-y, color);
ili9341_draw_pixel(x0+y, y0+x, color);
if ((old_error=error)<=x) error+=++x*2+1;
if (old_error>y || error>x) error+=++y*2+1;
} while(y<0);
}
// Draw circle of filling
// x0: Central X coordinate
// y0: Central Y coordinate
// r: radius
// color:color
void ili9341_draw_fill_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color) {
int x;
int y;
int error;
int old_error;
int ChangeX;
x=0;
y=-r;
error=2-2*r;
ChangeX=1;
do{
if(ChangeX) {
ili9341_draw_line(x0-x, y0-y,x0-x, y0+y, color);
ili9341_draw_line(x0+x, y0-y, x0+x, y0+y, color);
} // endif
ChangeX=(old_error=error)<=x;
if (ChangeX) error+=++x*2+1;
if (old_error>y || error>x) error+=++y*2+1;
} while(y<=0);
}
// nakreslení bitmapy
void ili9341_draw_bitmap(uint16_t x, uint16_t y, const tImage *bitmap)
{
uint16_t width = 0, height = 0;
width = bitmap->width;
height = bitmap->height;
uint16_t total_pixels = width * height;
ili9341_set_address_window(x, y, x + width - 1, y + height - 1);
for (uint16_t pixels = 0; pixels < total_pixels; pixels++)
{
ili9341_cmd(ILI9341_NOP, 1, (uint8_t[1]){(uint8_t)(bitmap->data[2 * pixels])});
ili9341_cmd(ILI9341_NOP, 1, (uint8_t[1]){(uint8_t)(bitmap->data[2 * pixels + 1])});
}
}
void ili9431_init_config()
{
ili9341_cmd(ILI9341_SOFTRESET, 0, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_DISPLAYOFF, 0, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_PIXELFORMAT, 1, (uint8_t[1]){0x55});
ili9341_cmd(ILI9341_POWERCONTROL1, 1, (uint8_t[1]){0x05}); // 0x05 :3.3V
ili9341_cmd(ILI9341_POWERCONTROL2, 1, (uint8_t[1]){0x10});
ili9341_cmd(ILI9341_VCOMCONTROL1, 2, (uint8_t[2]){0x3E, 0x28});
ili9341_cmd(ILI9341_VCOMCONTROL2, 1, (uint8_t[1]){0x86});
// ili9341_cmd(ILI9341_MADCTL, 1, (uint8_t[1]){0x40}); // MY,MX,MV,ML,BRG,MH,0,0
ili9341_cmd(ILI9341_MADCTL, 1, (uint8_t[1]){0x48}); // MY,MX,MV,ML,RGB,MH,0,0
ili9341_cmd(ILI9341_FRAMECONTROL, 2, (uint8_t[2]){0x00, 0x1B}); // Default 70Hz
ili9341_cmd(ILI9341_DISPLAYFUNC, 4, (uint8_t[4]){0x0A, 0xA2, 0x27, 0x04});
ili9341_cmd(ILI9341_GAMMASET, 1, (uint8_t[1]){0x01});
ili9341_cmd(ILI9341_PGAMCOR, 15, (uint8_t[15]){0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00});
ili9341_cmd(ILI9341_NGAMCOR, 15, (uint8_t[15]){0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f});
ili9341_cmd(ILI9341_SLEEPOUT, 0, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_DISPLAYON, 0, NULL);
sleep_ms(500);
}
void ili9341_init()
{
ili9431_pio_cmd_init(ili9341_pio, ili9341_sm, out_base_pin, set_base_pin, 70 * 1000000); // 70 MHz
ili9431_init_config();
}
// kreslení znaků a řetězců
/*
* Render a character glyph on the display. Called by `_ili_draw_string_main()`
* User need NOT call it
*/
void _ili_render_glyph(uint16_t x, uint16_t y, uint16_t fore_color, uint16_t back_color, const tImage *glyph, uint8_t is_bg)
{
uint16_t width = 0, height = 0;
width = glyph->width;
height = glyph->height;
uint16_t temp_x = x;
uint16_t temp_y = y;
uint8_t mask = 0x80;
uint8_t bit_counter = 0;
const uint8_t *glyph_data_ptr = (const uint8_t *)(glyph->data);
uint8_t glyph_data = 0;
// font bitmaps are stored in column major order (scanned from left-to-right, not the conventional top-to-bottom)
// as font glyphs have heigher height than width, this scanning saves some storage.
// So, we also render in left-to-right manner.
// Along x axis (width)
for (int i = 0; i < width; i++)
{
// Along y axis (height)
for (int j = 0; j < height; j++)
{
// load new data only when previous byte (or word, depends on glyph->dataSize) is completely traversed by the mask
// bit_counter = 0 means glyph_data is completely traversed by the mask
if (bit_counter == 0)
{
glyph_data = *glyph_data_ptr++;
bit_counter = glyph->dataSize;
}
// Decrement bit counter
bit_counter--;
//If pixel is blank
if (glyph_data & mask)
{
//Has background color (not transparent bg)
if (is_bg)
{
ili9341_draw_pixel(temp_y, temp_x, back_color);
}
}
//if pixel is not blank
else
{
ili9341_draw_pixel(temp_y, temp_x, fore_color);
}
glyph_data <<= 1;
temp_y++;
}
//New col starts. So, row is set to initial value and col is increased by one
temp_y = y;
temp_x++;
//Reset the bit counter cause we're moving to next column, so we start with a new byte
bit_counter = 0;
}
}
/**
* Renders a string by drawing each character glyph from the passed string.
* Called by `ili_draw_string()` and `ili_draw_string_withbg()`.
* Text is wrapped automatically if it hits the screen boundary.
* x_padding and y_padding defines horizontal and vertical distance (in px) between two characters
* is_bg=1 : Text will habe background color, is_bg=0 : Text will have transparent background
* User need NOT call it.
*/
void _ili_draw_string_main(uint16_t x, uint16_t y, char *str, uint16_t fore_color, uint16_t back_color, const tFont *font, uint8_t is_bg)
{
uint16_t x_temp = x;
uint16_t y_temp = y;
uint8_t x_padding = 0;
uint8_t y_padding = 0;
const tImage *img = {0};
uint16_t width = 0, height = 0;
while (*str)
{
if (*str == '\n')
{
x_temp = x; //go to first col
y_temp += (font->chars[0].image->height + y_padding); //go to next row (row height = height of space)
}
else if (*str == '\t')
{
x_temp += 4 * (font->chars[0].image->height + y_padding); //Skip 4 spaces (width = width of space)
}
else
{
for (uint8_t i = 0; i < font->length; i++)
{
if (font->chars[i].code == *str)
{
img = font->chars[i].image;
break;
}
}
// No glyph (img) found, so return from this function
if (img == NULL)
{
return;
}
width = img->width;
height = img->height;
if(y_temp + (height + y_padding) > ili_tftheight - 1) //not enough space available at the bottom
return;
if (x_temp + (width + x_padding) > ili_tftwidth - 1) //not enough space available at the right side
{
x_temp = x; //go to first col
y_temp += (height + y_padding); //go to next row
}
if (is_bg)
_ili_render_glyph(x_temp, y_temp, fore_color, back_color, img, 1);
else
_ili_render_glyph(x_temp, y_temp, fore_color, back_color, img, 0);
x_temp += (width + x_padding); //next char position
}
str++;
}
}
/**
* Draws a character at a given position, fore color, back color.
* @param x Start col address
* @param y Start row address
* @param character the ASCII character to be drawn
* @param fore_color foreground color
* @param back_color background color
* @param font Pointer to the font of the character
* @param is_bg Defines if character has background or not (transparent)
*/
void ili_draw_char(uint16_t x, uint16_t y, char character, uint16_t fore_color, uint16_t back_color, const tFont *font, uint8_t is_bg)
{
const tImage *img = NULL;
for (uint8_t i = 0; i < font->length; i++)
{
if (font->chars[i].code == character)
{
img = font->chars[i].image;
break;
}
}
// No glyph (img) found, so return from this function
if (img == NULL)
{
return;
}
if (is_bg)
_ili_render_glyph(x, y, fore_color, back_color, img, 1);
else
_ili_render_glyph(x, y, fore_color, back_color, img, 0);
}
/**
* Draws a string on the display with `font` and `color` at given position.
* Background of this string is transparent
* @param x Start col address
* @param y Start y address
* @param str pointer to the string to be drawn
* @param color 16-bit RGB565 color of the string
* @param font Pointer to the font of the string
*/
void ili9341_draw_string(uint16_t x, uint16_t y, char *str, uint16_t color, const tFont *font)
{
_ili_draw_string_main(x, y, str, color, 0, font, 0);
}
/**
* Draws a string on the display with `font`, `fore_color`, and `back_color` at given position.
* The string has background color
* @param x Start col address
* @param y Start y address
* @param str pointer to the string to be drawn
* @param foe_color 16-bit RGB565 color of the string
* @param back_color 16-bit RGB565 color of the string's background
* @param font Pointer to the font of the string
*/
void ili9341_draw_string_withbg(uint16_t x, uint16_t y, char *str, uint16_t fore_color, uint16_t back_color, const tFont *font)
{
_ili_draw_string_main(x, y, str, fore_color, back_color, font, 1);
}
Hlavičkový soubor ke grafickým funkcím
ili9341.h
#ifndef _ILI9431_H_
#define _ILI9431_H_
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
#include "pico/stdlib.h"
#include "bitmap_typedefs.h"
#include "hardware/pio.h"
void ili9341_init();
void ili9431_init_config();
void ili9341_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
// uint16_t ili9341_read_pixel(uint16_t x, uint16_t y);
// void ili9341_set_address_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
// void ili9341_memory_write_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void ili9341_cmd(uint8_t cmd, uint32_t count, uint8_t *param);
uint16_t ili9341_color_565RGB(uint8_t R, uint8_t G, uint8_t B);
void ili9341_fill_rect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color);
void ili9341_invert_display(bool invert);
void ili9341_draw_line(uint16_t start_x, uint16_t start_y, uint16_t end_x, uint16_t end_y, uint16_t color);
void ili9341_draw_fill_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
void ili9341_draw_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
void ili9341_draw_string(uint16_t x, uint16_t y, uint8_t* str, uint16_t color, const tFont *font);
void ili9341_draw_string_withbg(uint16_t x, uint16_t y, char *str, uint16_t fore_color, uint16_t back_color, const tFont *font);
void ili9341_draw_bitmap(uint16_t x, uint16_t y, const tImage *bitmap);
void ili9431_pio_cmd_init(PIO pio, uint sm, uint out_base, uint set_base, uint32_t freq);
// void ili9341_vertical_scroll_def(uint16_t top, uint16_t bottom);
// void ili9341_vertical_scrolling(uint16_t addr);
// void ili9341_read_area(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t * pixels);
// void ili9341_write_area(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t * pixels);
// void ili9341_draw_char(uint16_t x, uint16_t y, uint8_t character, uint16_t color, const tFont *font);
// void ili9341_popMessage(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t *message, uint8_t seconds, const tFont *font);
#endif
Typedefy pro obrázky a fonty. Písmenka se dávají na displej jako obrázky.
link:bitmap_typedefs.h
/*
MIT License
Copyright (c) 2019 Avra Mitra
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef INC_BITMAP_TYPEDEFS_H_
#define INC_BITMAP_TYPEDEFS_H_
#include <stdint.h>
#include <inttypes.h>
typedef struct {
const uint8_t *data;
uint16_t width;
uint16_t height;
uint8_t dataSize;
} tImage;
typedef struct {
const uint16_t *data;
uint16_t width;
uint16_t height;
uint8_t dataSize;
} tImage16bit;
typedef struct {
long int code;
const tImage *image;
} tChar;
typedef struct {
int length;
const tChar *chars;
} tFont;
#endif /* INC_BITMAP_TYPEDEFS_H_ */
Na výběr barev ve formátu RGB565 je dobrý web https://rgbcolorpicker.com/565
Testování možností displeje.
main.c
/*
* TFT LCD displej ILI9341 s paralelním 8 bitovým rozhraním TF028, programovaní pomoci PIO
* (c) Jirka Chráska 2025; <jirka@lixis.cz>
* BSD 3 clause licence
*/
#include <stdio.h>
#include "stdlib.h"
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "hardware/pio.h"
#include "ili9341.h"
#include "string.h"
#include "logo.h"
#include "pico.h"
#include "font_ubuntu_mono_24.h"
// https://rgbcolorpicker.com/565
// obvyklé barvy
#define BLACK 0x0000 /* 0, 0, 0 */
#define NAVY 0x000F /* 0, 0, 128 */
#define DARKGREEN 0x03E0 /* 0, 128, 0 */
#define DARKCYAN 0x03EF /* 0, 128, 128 */
#define MAROON 0x7800 /* 128, 0, 0 */
#define PURPLE 0x780F /* 128, 0, 128 */
#define OLIVE 0x7BE0 /* 128, 128, 0 */
#define LIGHTGREY 0xC618 /* 192, 192, 192 */
#define DARKGREY 0x7BEF /* 128, 128, 128 */
#define BLUE 0x001F /* 0, 0, 255 */
#define GREEN 0x07E0 /* 0, 255, 0 */
#define CYAN 0x07FF /* 0, 255, 255 */
#define RED 0xF800 /* 255, 0, 0 */
#define MAGENTA 0xF81F /* 255, 0, 255 */
#define YELLOW 0xFFE0 /* 255, 255, 0 */
#define WHITE 0xFFFF /* 255, 255, 255 */
#define ORANGE 0xFD20 /* 255, 165, 0 */
#define GREENYELLOW 0xAFE5 /* 173, 255, 47 */
#define PINK 0xF81F
void demo()
{
ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, NAVY);
// for (int j = 0; j < 320; j+=5) ili9341_draw_line(0, 0, 239, j, ORANGE);
// for (int i = 0; i < 240; i+=5) ili9341_draw_line(0, 0, i, 319, OLIVE);
// for (int i = 10;i < 150; i+=10) {
// ili9341_fill_rect(i, i, 100, 100, ili9341_color_565RGB(0x70+i, 0x80+i*2, 0x40+i));
// }
ili9341_draw_bitmap(0,0, &pico);
ili9341_draw_string_withbg(4, 108, "Programujeme ILI9341 8bit paralelni displej pomoci PIO koprocesoru (bez DMA).", LIGHTGREY, DARKGREY, &font_ubuntu_mono_24);
ili9341_draw_string_withbg(4, 184, "(c) Jirka Chraska 2025; <jirka@lixis.cz>.", LIGHTGREY, DARKGREY, &font_ubuntu_mono_24);
}
void demo2()
{
int r, g, b;
char buf[60];
for(r=0; r<32; r++) {
for(g=0; g<64; g++) {
for(b=0; b<32; b++) {
ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, ili9341_color_565RGB(r*8,g*4,b*8));
sprintf(buf,"R=%03d G=%03d B=%03d",r,g,b);
ili9341_draw_string_withbg(5,200,buf,0x0000, 0xffff, &font_ubuntu_mono_24);
sleep_ms(20);
}
}
}
}
#define ow 32
#define oh 30
void demo3()
{
static uint16_t ctable[8][10] = {
0x80c3, 0x8103, 0x8123, 0x8163, 0x81a3, 0x81c3, 0x8203, 0x8223, 0x8263, 0x82a3,
0x82c3, 0x8303, 0x8323, 0x8363, 0x8383, 0x83c3, 0x8403, 0x8423, 0x7c23, 0x7423,
0x6c23, 0x6423, 0x5c23, 0x5423, 0x4c23, 0x4423, 0x3c23, 0x3423, 0x2c23, 0x2423,
0x1c23, 0x1c24, 0x1c25, 0x1c26, 0x1c27, 0x1c28, 0x1c29, 0x1c2a, 0x1c2b, 0x1c2c,
0x1c2d, 0x1c2e, 0x1c2f, 0x1c30, 0x1bf0, 0x1bd0, 0x1b90, 0x1b50, 0x1b30, 0x1af0,
0x1ab0, 0x1a90, 0x1a70, 0x1a30, 0x19f0, 0x19d0, 0x38d0, 0x40d0, 0x48d0, 0x50d0,
0x58d0, 0x60d0, 0x68d0, 0x70d0, 0x70d8, 0x78d0, 0x80d0, 0x80cf, 0x80ce, 0x80cc,
0x80ca, 0x80cb, 0x80c8, 0x80c7, 0x80c6, 0x80c5, 0x80c4, 0x80c3, 0x0000, 0xffff
};
ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, 0xffff);
for( int i=0; i<10; i++) {
for( int j=0; j<8; j++) {
ili9341_fill_rect(j*30,i*32,30,32,ctable[j][i]);
}
}
}
int main()
{
bool LED_state = false ;
gpio_init(25) ;
gpio_set_dir(25, GPIO_OUT) ;
gpio_put(25, true);
sleep_ms(250);
stdio_init_all();
sleep_ms(500);
gpio_put(25, false);
sleep_ms(250);
printf("Testujeme ili9341 8bit paralelni.\n");
gpio_put(25, true);
sleep_ms(250);
// inicializace displeje
ili9341_init();
// nastavení pozadí
ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, 0xf800);
printf("Červený displej\n");
// kreslení čar
for (int j = 0; j < 320; j+=5) {
ili9341_draw_line(0, 0, 239, j, WHITE);
// printf("Čára 1\n");
}
sleep_ms(3000);
for (int i = 0; i < 240; i+=5) {
ili9341_draw_line(239, 319, 239-i, 0, BLACK);
// printf("Čára 2\n");
}
sleep_ms(3000);
// kreslení vyplněných obdélníků
for (int i = 10;i < 150; i+=10) {
ili9341_fill_rect(i, i, 100, 100, ili9341_color_565RGB(0x60+i, 0x70+i*2, 0x30+i));
printf("Obdélník %d\n", ili9341_color_565RGB(0x60+i, 0x70+i*2, 0x30+i));
}
sleep_ms(3000);
ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, 0x0000);
ili9341_draw_bitmap(62,62, &logo);
sleep_ms(3000);
// invertování barev
ili9341_draw_string_withbg(62, 0, "Inverze displeje.", DARKGREY, BLACK, &font_ubuntu_mono_24);
ili9341_invert_display(true);
printf("Inverujeme.\n");
sleep_ms(3000);
ili9341_draw_string_withbg(62, 0, "Inverze zpet. ", DARKGREY, BLACK, &font_ubuntu_mono_24);
ili9341_invert_display(false);
printf("Inverujeme zpět.\n");
sleep_ms(3000);
// psaní textu
printf("Budeme psát:\n");
ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, BLACK);
ili9341_draw_string_withbg(0, 10, "Pismenka: abcdefghijklmnopgrstuvwxyz", LIGHTGREY, BLACK, &font_ubuntu_mono_24);
ili9341_draw_string_withbg(0, 80, "Font: Ubuntu mono 24px", LIGHTGREY, BLACK, &font_ubuntu_mono_24);
// kreslení kruhů
printf("Kruhy:\n");
for (int i = 0;i < 130; i+=10) {
ili9341_draw_fill_circle(160, 2*i+50, 50, ili9341_color_565RGB(250-i/2,28,100+i/2));
}
sleep_ms(6000);
// demo a měření rychlosti vykreslování
char buf[60];
while (true) {
absolute_time_t t1 = get_absolute_time();
demo();
absolute_time_t t2 = get_absolute_time();
sprintf(buf, "%d ms", absolute_time_diff_us(t1,t2)/1000);
ili9341_draw_string_withbg(232, 212, buf, LIGHTGREY, BLACK, &font_ubuntu_mono_24);
LED_state = LED_state? false : true ;
gpio_put(25, LED_state);
sleep_ms(2500);
t1 = get_absolute_time();
demo3();
t2 = get_absolute_time();
sprintf(buf, "%d ms", absolute_time_diff_us(t1,t2)/1000);
ili9341_draw_string_withbg(232, 212, buf, LIGHTGREY, BLACK, &font_ubuntu_mono_24);
LED_state = LED_state? false : true ;
gpio_put(25, LED_state);
sleep_ms(2500);
}
}
Výsledky
ffmpeg -i VID_20250805_042539.mp4 -vf "transpose=1" -acodec copy ili9341_parallel1.mp4
Chyby
Displej má špatné barevné podání. Je to zpsobeno tím, že je špatně nastevené posílání bitů do displeje. Displej očekává 5 bitů modré, 6 bitů zelené a 5 bitů modré. My posíláme 5 bitů červené, 6 bitů zelené a 5 bitů modré barevné složky. Dá se to spravit počátečním nastavením displeje.

void ili9431_init_config()
{
ili9341_cmd(ILI9341_SOFTRESET, 0, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_DISPLAYOFF, 0, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_PIXELFORMAT, 1, (uint8_t[1]){0x55});
ili9341_cmd(ILI9341_POWERCONTROL1, 1, (uint8_t[1]){0x05}); // 0x05 :3.3V
ili9341_cmd(ILI9341_POWERCONTROL2, 1, (uint8_t[1]){0x10});
ili9341_cmd(ILI9341_VCOMCONTROL1, 2, (uint8_t[2]){0x3E, 0x28});
ili9341_cmd(ILI9341_VCOMCONTROL2, 1, (uint8_t[1]){0x86});
// ili9341_cmd(ILI9341_MADCTL, 1, (uint8_t[1]){0x40}); // MY,MX,MV,ML,BGR,MH,0,0 (1)
ili9341_cmd(ILI9341_MADCTL, 1, (uint8_t[1]){0x48}); // MY,MX,MV,ML,RGB,MH,0,0 (2)
ili9341_cmd(ILI9341_FRAMECONTROL, 2, (uint8_t[2]){0x00, 0x1B}); // Default 70Hz
ili9341_cmd(ILI9341_DISPLAYFUNC, 4, (uint8_t[4]){0x0A, 0xA2, 0x27, 0x04});
ili9341_cmd(ILI9341_GAMMASET, 1, (uint8_t[1]){0x01});
ili9341_cmd(ILI9341_PGAMCOR, 15, (uint8_t[15]){0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00});
ili9341_cmd(ILI9341_NGAMCOR, 15, (uint8_t[15]){0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f});
ili9341_cmd(ILI9341_SLEEPOUT, 0, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_DISPLAYON, 0, NULL);
sleep_ms(500);
}
| 1 | Nastavení displeje na BGR (špatně). |
| 2 | Nastavení displeje na RGB (správně). |
Datasheet říká, že 3. bit D3 má být 0, praxe ukazuje, že pro RGB tam má být 1. Po změně MADCTL 3. bitu na 1 to funguje správně. Buďto je v datasheetu chyba a nebo si musím sehnat stádo ovcí, přestat programovat a raději pást ovečky.
Celý projekt ke stažení je zde: TFT_LCD2.8_ILI9341_parallel_v1.tar.gz


