本文章延續前一篇[Raspberry Pi Pico (c-sdk)] Display: Ep 2 : PIO TFT LCD ILI9341 8-bit parallel C-code Driver只有由MCU輸出到TFT,加入從TFT Graphic RAM讀取pixel資料,以便達成簡易動畫功能,例如popup message與物件軌跡運動等。
在ili9341_cmd加入新的參數dir以決定out pins或in pins的方向。
在Raspberry Pi Pico PIO程式加入 out pindirs, 8指令改變DB0~DB7 pin OUT/IN。
Memory Read的Command code 為2Eh,要讀取4個bytes,第一個byte為dummy byte,第二至第四byte分別R,G,B如下公式轉成uint16_t RGB565格式:
(((uint16_t)buff[1])>>3) << 11 | (((uint16_t)buff[2])>>2) << 5 | (((uint16_t)buff[3])>>3)
(((uint16_t)buff[1])>>3) << 11 | (((uint16_t)buff[2])>>2) << 5 | (((uint16_t)buff[3])>>3)
成果影片展示:
程式碼:
- ili9341.pio
.program ili9341_pio_cmd
; CSX, D/CX, WRX, RDX --> gpio 15, 14, 13, 12 (set pins)
.wrap_target
start:
set y,0 ; set pins out
mov osr, !y
out pindirs 8
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
; input or output
pull ; dir
mov y, osr
out pindirs, 8 ; if 0x00 set to input
jmp x--, param_data
param_data:
set pins, 0b1111
jmp !y read
; write command parameters
write:
pull ; write data
set pins, 0b0101
out pins, 8 ;[1]
set pins, 0b0111 ;[1]
jmp x--, write
jmp finish
; ===
read:
set pins, 0b0110 [3]
in pins, 8
set pins, 0b0111
push
jmp x--, read
finish:
set pins, 0b1111
.wrap
- 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 "ili9341.h"
PIO ili9341_pio = pio1;
uint ili9341_sm=0;
uint in_out_base_pin=4;
uint set_base_pin=12;
void ili9341_cmd(uint8_t cmd, uint32_t count, uint8_t dir, uint8_t *param) {/*dir:write:0xFF, read:0x00*/
if (dir != 0xFF && dir != 0x00) return;
//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
if (count != 0) {
pio_sm_put_blocking(ili9341_pio, ili9341_sm, dir); // write out or read in
if (dir == 0xFF) {
for (int i = 0; i < count; i++) {
pio_sm_put_blocking(ili9341_pio, ili9341_sm, param[i]); // write out parameters
}
} else {
for (int i = 0; i < count; i++) {
param[i] = (uint8_t)(pio_sm_get_blocking(ili9341_pio, ili9341_sm)); // read in data
}
}
}
}
/* ================ */
void pio_irq_read_data() {
if (pio_interrupt_get(ili9341_pio, ili9341_sm)) {
pio_interrupt_clear(ili9341_pio, ili9341_sm);
}
}
void ili9431_pio_cmd_init(PIO pio, uint sm, uint in_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, in_out_base+i);
for (int i=0; i < 4; i++) pio_gpio_init(pio, set_base+i);
pio_sm_set_consecutive_pindirs(pio, sm, in_out_base, 8, true);
pio_sm_set_consecutive_pindirs(pio, sm, set_base, 4, true);
sm_config_set_in_pins(&c, in_out_base);
sm_config_set_out_pins(&c, in_out_base, 8);
sm_config_set_set_pins(&c, set_base, 4);
sm_config_set_out_shift(&c, true, false, 8);
sm_config_set_in_shift(&c, false, false, 8);
float div = clock_get_hz(clk_sys)/freq;
sm_config_set_clkdiv(&c, div);
//uint pio_irq = pio_get_index(pio)? PIO1_IRQ_0:PIO0_IRQ_0;
//pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
//irq_add_shared_handler(pio_irq, pio_irq_read_data, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
//irq_set_enabled(pio_irq, true);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
/* ili3941 draw functions*/
uint16_t ili9341_color_565RGB(uint8_t R, uint8_t G, uint8_t B) {
uint16_t c;
c = (((uint16_t)R)>>3)<<11 | (((uint16_t)G)>>2) << 5 | ((uint16_t)B)>>3;
return c;
}
void ili9341_memory_write_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, 0xFF, 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, 0xFF, addr );
ili9341_cmd(ILI9341_MEMORYWRITE, 0, 0xFF, NULL);
}
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, 0xFF, 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, 0xFF, addr );
}
/* put color at point*/
void ili9341_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
{
if ( x < 0 || x > TFT_WIDTH-1 || y < 0 || y > TFT_HEIGHT-1) return;
ili9341_set_address_window(x,y,x,y);
ili9341_cmd(ILI9341_MEMORYWRITE, 2, 0xFF, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)color});
}
/* read a RGB565 pixel from tft device
read 4 bytes:
0: dummy
1: Red byte
2: Green byte
3: Blue byte
*/
uint16_t ili9341_read_pixel(uint16_t x, uint16_t y)
{
if ( x < 0 || x > TFT_WIDTH-1 || y < 0 || y > TFT_HEIGHT-1) return 0;
uint8_t buff[4];
ili9341_set_address_window(x, y, x,y);
ili9341_cmd(ILI9341_MEMORYREAD, 4, 0x00, buff);
return ((((uint16_t)buff[1])>>3) << 11 | (((uint16_t)buff[2])>>2) << 5 | (((uint16_t)buff[3])>>3));
}
void ili9341_invert_display(bool invert) {
if (invert)
ili9341_cmd(ILI9341_INVERTON, 0, 0xFF, NULL);
else
ili9341_cmd(ILI9341_INVERTOFF, 0, 0xFF, NULL);
}
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 > TFT_WIDTH) width = TFT_WIDTH-x;
if (y+height > TFT_HEIGHT) height = TFT_HEIGHT-y;
ili9341_memory_write_window(x,y, x+width-1, y+height-1);
for (int j = y; j < y+height; j++)
for (int i = x; i< x+width;i++)
ili9341_cmd(ILI9341_NOP, 2, 0xFF, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)(color&0xff)});
}
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);
}
void ili9341_draw_string(uint16_t x, uint16_t y, uint8_t* str, uint16_t color, const tFont *font) {
ili_draw_string(x,y, str, color, 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) {
ili_draw_string_withbg(x, y, str, fore_color, back_color, font);
}
void ili9341_draw_char(uint16_t x, uint16_t y, uint8_t character, uint16_t color, const tFont *font) {
ili_draw_char(x, y, character, color, 0, font, 0);
}
void ili9341_popMessage(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t *message, uint8_t seconds, const tFont *font) {
if (x < 0 || y < 0 || x+width >= TFT_WIDTH || y+height >= TFT_HEIGHT) return;
uint8_t tmpbuf[width*height*2];
uint16_t font_width = font->chars->image->width;
uint16_t font_height = font->chars->image->height;
ili9341_read_area(x, y, width, height, tmpbuf);
ili9341_fill_rect(x, y,width,height, 0xffff);
ili9341_draw_line(x, y, x+width-1, y, 0x001f);
ili9341_draw_line(x+width-1, y, x+width-1, y+height-1, 0x001f);
ili9341_draw_line(x+width-1, y+height-1, x, y+height-1, 0x001f);
ili9341_draw_line(x, y+height-1, x, y, 0x001f);
ili9341_draw_line(x+1, y+1, x+width-2, y+1, 0x001f);
ili9341_draw_line(x+width-2, y+1, x+width-2, y+height-2, 0x001f);
ili9341_draw_line(x+width-2, y+height-2, x+1, y+height-2, 0x001f);
ili9341_draw_line(x+1, y+height-2, x+1, y+1, 0x001f);
uint16_t sx, sy, index;
sx = x+5;
sy = y+5;
index =0;
while (index < strlen(message)) {
ili9341_draw_char(sx, sy,message[index++], 0xf800,font);
sx+=font_width;
if (sx > x+width-1-font_width) {sy+=(font_height+2); sx=25;}
if (sy > y+height-1 - font_height) break;
}
sleep_ms(seconds*1000);
ili9341_write_area(x, y, width, height, tmpbuf);
}
void ili9341_vertical_scroll_def(uint16_t top, uint16_t bottom) {
uint16_t scroll=TFT_HEIGHT - top - bottom;
ili9341_cmd(ILI9341_VSCROLLDEF, 6, 0xFF, (uint8_t[6]){(uint8_t)(top>>8),(uint8_t)(top&0xff),
(uint8_t)(scroll>>8),(uint8_t)(scroll&0xff),(uint8_t)(bottom>>8),(uint8_t)(bottom&0xff)});
}
void ili9341_vertical_scrolling(uint16_t addr) {
ili9341_cmd(ILI9341_VSCROLLADDR, 2, 0xFF, (uint8_t[2]){(uint8_t)(addr>>8),(uint8_t)(addr&0xff),});
}
void ili9341_read_area(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t * pixels) {
if (x < 0 || y < 0 || x >= TFT_WIDTH || y >= TFT_HEIGHT) return;
if (x+width > TFT_WIDTH) width = TFT_WIDTH - x;
if (y+height > TFT_HEIGHT) height = TFT_HEIGHT - y;
uint32_t total_pixels = width*height;
uint8_t tmpbuf[4];
ili9341_set_address_window(x,y,x+width-1, y+height-1);
ili9341_cmd(ILI9341_MEMORYREAD, 1, 0x00, tmpbuf);
for (int i=0; i < total_pixels; i++) {
ili9341_cmd(ILI9341_NOP, 3, 0x00, tmpbuf);
pixels[i*2] = (tmpbuf[0]& 0xf8) | (tmpbuf[1] >> 5);
pixels[i*2+1] = ((tmpbuf[1] << 3) & 0xe0) | (tmpbuf[2] >> 3);
}
}
void ili9341_write_area(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t * pixels) {
if (x < 0 || y < 0 || x >= TFT_WIDTH || y >= TFT_HEIGHT) return;
if (x+width > TFT_WIDTH) width = TFT_WIDTH - x;
if (y+height > TFT_HEIGHT) height = TFT_HEIGHT - y;
uint32_t total_bytes = width*height*2;
ili9341_set_address_window(x,y,x+width-1, y+height-1);
ili9341_cmd(ILI9341_MEMORYWRITE, total_bytes, 0xFF, pixels);
}
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);
ili9341_cmd(ILI9341_MEMORYWRITE, total_pixels*2, 0xFF, (uint8_t*)(bitmap->data));
}
void ili9431_init_config() {
ili9341_cmd(ILI9341_SOFTRESET, 0, 0xFF, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_DISPLAYOFF, 0, 0xFF, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_PIXELFORMAT, 1, 0xFF, (uint8_t[1]){0x55}); //0x55
ili9341_cmd(ILI9341_POWERCONTROL1, 1, 0xFF, (uint8_t[1]){0x05}); // 0x05 :3.3V
ili9341_cmd(ILI9341_POWERCONTROL2, 1, 0xFF, (uint8_t[1]){0x10});
ili9341_cmd(ILI9341_VCOMCONTROL1, 2, 0xFF, (uint8_t[2]){0x3E, 0x28});
ili9341_cmd(ILI9341_VCOMCONTROL2, 1, 0xFF, (uint8_t[1]){0x86});
ili9341_cmd(ILI9341_MADCTL, 1, 0xFF, (uint8_t[1]){0x00}); //MY,MX,MV,ML,BRG,MH,0,0(40)
ili9341_cmd(ILI9341_FRAMECONTROL, 2, 0xFF, (uint8_t[2]){0x00, 0x1B}); // Default 70Hz
ili9341_cmd(ILI9341_DISPLAYFUNC, 4, 0xFF, (uint8_t[4]){0x0A, 0x82, 0x27, 0x04}); //0a,a2,27,04
ili9341_cmd(ILI9341_GAMMASET, 1, 0xFF, (uint8_t[1]){0x01});
//ili9341_cmd(ILI9341_PGAMCOR, 15, 0xFF, (uint8_t[15]){ 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00 });
//ili9341_cmd(ILI9341_NGAMCOR, 15,0xFF, (uint8_t[15]){ 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f });
ili9341_cmd(ILI9341_SLEEPOUT, 0, 0xFF, NULL);
sleep_ms(150);
ili9341_cmd(ILI9341_DISPLAYON, 0, 0xFF, NULL);
sleep_ms(500);
}
void ili9341_init() {
ili9431_pio_cmd_init(ili9341_pio, ili9341_sm, in_out_base_pin, set_base_pin, 35000000);
ili9431_init_config();
}
- ili9341.h
#ifndef _ILI9431_H_
#define _ILI9431_H_
#define TFT_WIDTH 240
#define TFT_HEIGHT 320
#include "pico/stdlib.h"
#include "tft_string_lib/ili_string.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 dir, 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
where could i find all library you are using? thanks in advance
回覆刪除Hi, some files and defines are missing.
回覆刪除in ili9341.h
#include "tft_string_lib/ili_string.h"
const tFont
const tImage
in main.c
#include "pico_img.h"
can you provide them.? Thank you for this excellent tutorial ! Best regards from France .Olivier
ili_string.h
刪除See "https://github.com/abhra0897/stm32f1_ili9341_parallel"
font folder.
pico_img.h
Use the lcd-image-converter application to convert image to C code. Search for "lcd-image-converter"
Hi,
刪除Thank you for the link, it helps me to resolve the problems;
all works (with or without DMA) ,but i have trouble with the draw bitmap function;
I've converted an image with lcd-image-converter, but the displaying result is not what is expected.
I just have a small rectangle in the top right of the display
with colored lines who seems to be the color of my image (it's the flag of a country)
Is there any configuration or setting in lcd-image-converter to apply (scan dir, data size (I set 16bits) etc..)?
Best regards
lcd-image-converter option:
刪除Main Scan Direction:
Top to Buttom
Line Scan Direction
Forward
Block Size:
8 bit
Preset: Color R5G6B5