本文章延續前一篇[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