本文章介紹LVGL繪圖函式庫如何結合本網站先前介紹的Raspberry Pi Pico 利用PIO製作的驅動程式。
- [Raspberry Pi Pico (c-sdk)] Display: Ep 5 :TFT LCD 4-lines Serial(SPI) Driver
- [Raspberry Pi Pico (c-sdk)] Display: Ep 4 : ILI9341 TFT LCD 8-bit parallel
本文章以LVGL 8.4.0為例:
一、LVGL Graphic library所需要的檔案
- src資料夾、
- lvgl.h
- lv_conf.h(由lv_conf_template.h複製)
修改lv_conf.h的內容:- /* clang-format off */
#if 1 /*Set it to "1" to enable content*/ - #define LV_COLOR_DEPTH 16
#define LV_COLOR_16_SWAP 1
CMakeLists.txt內容:二、週期性呼叫 lv_tick_inc(x)
bool lv_inc_timer_cb(repeating_timer_t *rt) { lv_tick_inc(*((uint8_t*)rt->user_data)); return true;}
void pico_lvgl_tick_inc_timer_init(uint8_t tick_inc) { static repeating_timer_t rt; static uint8_t _tick_inc; _tick_inc = tick_inc; add_repeating_timer_ms(tick_inc, lv_inc_timer_cb, (void*) (&_tick_inc), &rt);}
三、呼叫 lv_init()
四、製作draw buffer flush callback function, 將一個block資料寫到TFT Display memory中:void tft_lvgl_draw_bitmap(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t *bitmap){ uint32_t total_pixels = (x2-x1+1) * (y2-y1+1)*2;
tft_set_address_window (x1, y1, x2, y2); tft_cmd_dma(TFT_MEMORYWRITE, total_pixels, bitmap); }
void tft_lvgl_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p){ tft_lvgl_draw_bitmap( (uint16_t)(area->x1), (uint16_t)(area->y1), (uint16_t)(area->x2), (uint16_t)(area->y2), (uint8_t*)color_p ); lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/}其中tft_lvgl_draw_bitmap(...)為前篇文章介紹PIO TFT Display Driver的function。
五、以製作一組音樂播放器為展示範例:
六、程式碼:CmakeList.txt
option(LV_LVGL_H_INCLUDE_SIMPLE
"Use #include \"lvgl.h\" instead of #include \"../../lvgl.h\"" ON)
# Option to define LV_CONF_INCLUDE_SIMPLE, default: ON
option(LV_CONF_INCLUDE_SIMPLE
"Use #include \"lv_conf.h\" instead of #include \"../../lv_conf.h\"" ON)
add_library(lvgl INTERFACE)
file(GLOB_RECURSE SOURCES src/*.c src/*.S)
target_sources(lvgl INTERFACE
${SOURCES}
)
target_include_directories(lvgl INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/..
)
- pico_tft
CMakeLists.txt
add_library(pico_tft INTERFACE)
pico_generate_pio_header(pico_tft ${CMAKE_CURRENT_LIST_DIR}/pico_tft.pio)
target_sources(pico_tft INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_tft.c
${CMAKE_CURRENT_LIST_DIR}/pico_tft_lvgl.c
)
target_include_directories(pico_tft INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/..
)
target_link_libraries(pico_tft INTERFACE
hardware_pio
hardware_dma
)
pico_tft.h
#ifndef _TFT_H_
#define _TFT_H_
#define TFT_WIDTH 320
#define TFT_HEIGHT 480
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "pico_tft_lvgl.h"
void tft_init(PIO pio, uint sm, uint din_base, uint csx_dcx_sck_side_base_pin);
void tft_init_config();
void tft_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
void tft_set_address_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void tft_cmd(uint32_t cmd, uint32_t count, uint8_t *param);
void tft_cmd_dma(uint32_t cmd, uint32_t count, uint8_t *param);
uint16_t tft_color_565RGB(uint8_t R, uint8_t G, uint8_t B);
void tft_lv_draw_bitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t *bitmap);
void tft_pio_cmd_init(PIO pio, uint sm, uint out_base, uint set_base, uint32_t freq);
#endif
pico_tft.c
#include "stdio.h"
#include "stdlib.h"
#include "pico/stdlib.h"
#include "hardware/clocks.h"
#include "string.h"
#include "registers.h"
#include "pico_tft.pio.h"
#include "pico_tft.h"
#include "hardware/dma.h"
#define MAX_BYTE_TRANS (TFT_WIDTH*TFT_HEIGHT*2)
#define PICO_TFT_SERIAL
#define PICO_TFT_DMA
PIO tft_pio = pio1;
uint tft_sm=0;
uint in_out_base_pin=4;
uint set_base_pin=12;
uint sideset_base_pin=16;
uint s_in_out_base_pin=19;
int tft_dma_channel;
void tft_cmd(uint32_t cmd, uint32_t count, uint8_t *param)
{
pio_sm_restart(tft_pio, tft_sm);
#ifdef PICO_TFT_SERIAL
pio_sm_put_blocking(tft_pio, tft_sm, cmd << 24);
#endif
#ifdef PICO_TFT_PARALLEL
pio_sm_put_blocking(tft_pio, tft_sm, cmd);
#endif
pio_sm_put_blocking(tft_pio, tft_sm, count);
for (int i = 0; i < count; i++)
{
#ifdef PICO_TFT_SERIAL
pio_sm_put_blocking(tft_pio, tft_sm, param[i]<<24);
#endif
#ifdef PICO_TFT_PARALLEL
pio_sm_put_blocking(tft_pio, tft_sm, param[i]);
#endif
}
}
//#ifdef PICO_TFT_DMA
void tft_cmd_dma(uint32_t cmd, uint32_t count, uint8_t *param)
{
#ifdef PICO_TFT_SERIAL
tft_cmd(cmd, count, param);
return;
#endif
pio_sm_restart(tft_pio, tft_sm);
pio_sm_put_blocking(tft_pio, tft_sm, cmd);
pio_sm_put_blocking(tft_pio, tft_sm, count);
dma_channel_set_trans_count(tft_dma_channel, count >> DMA_SIZE_8, false);
dma_channel_set_read_addr(tft_dma_channel, param, false);
dma_channel_start(tft_dma_channel);
dma_channel_wait_for_finish_blocking(tft_dma_channel);
}
//#endif
void tft_pio_cmd_init(PIO pio, uint sm, uint in_out_base, uint set_sideset, uint32_t freq) {
uint offset=0;
pio_sm_config c;
#ifdef PICO_TFT_PARALLEL
offset = pio_add_program(pio, &tft_pio_parallel_program);
c = tft_pio_parallel_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_sideset+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_sideset, 4);
sm_config_set_out_shift(&c, true, false, 8);
sm_config_set_in_shift(&c, false, false, 8);
#endif
#ifdef PICO_TFT_SERIAL
offset = pio_add_program(pio, &tft_pio_serial_program);
c = tft_pio_serial_program_get_default_config(offset);
pio_gpio_init(pio, in_out_base);
for (int i=0; i < 3; i++) pio_gpio_init(pio, set_sideset+i);
pio_sm_set_consecutive_pindirs(pio, sm, in_out_base, 1, true);
pio_sm_set_consecutive_pindirs(pio, sm, set_sideset, 3, true);
sm_config_set_in_pins(&c, in_out_base);
sm_config_set_out_pins(&c, in_out_base, 1);
sm_config_set_sideset_pins(&c, set_sideset);
sm_config_set_out_shift(&c, false, false, 8);
sm_config_set_in_shift(&c, true, false, 8);
#endif
//sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
float div = clock_get_hz(clk_sys)/freq;
sm_config_set_clkdiv(&c, div);
//sm_config_set_clkdiv(&c, 1.25);
#ifdef PICO_TFT_DMA
/* DMA */
tft_dma_channel = dma_claim_unused_channel(true);
dma_channel_config dc = dma_channel_get_default_config(tft_dma_channel);
channel_config_set_write_increment(&dc, false);
channel_config_set_read_increment(&dc, true);
channel_config_set_dreq(&dc, pio_get_dreq(pio, sm, true));
channel_config_set_transfer_data_size(&dc, DMA_SIZE_8); //DMA_SIZE_8,16,32
dma_channel_configure(tft_dma_channel, &dc, (void*) (PIO1_BASE+PIO_TXF0_OFFSET),
NULL, MAX_BYTE_TRANS>> DMA_SIZE_8, false); //DMA_SIZE_8 or 16 or 32
/* DMA */
#endif
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
/* tft draw functions*/
uint16_t tft_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 tft_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);
tft_cmd(TFT_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);
tft_cmd(TFT_PAGEADDRSET, 4, addr );
tft_cmd(TFT_MEMORYWRITE, 0, NULL);
}
void tft_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);
tft_cmd(TFT_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);
tft_cmd(TFT_PAGEADDRSET, 4, addr );
}
/* put color at point*/
void tft_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
{
if ( x < 0 || x > TFT_WIDTH-1 || y < 0 || y > TFT_HEIGHT-1) {
printf("over range,x,y\n");
return;
}
tft_set_address_window(x,y,x,y);
tft_cmd(TFT_MEMORYWRITE, 2, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)color});
}
void tft_init_config() {
tft_cmd(TFT_SOFTRESET, 0, NULL);
sleep_ms(120);
tft_cmd(TFT_SLEEPOUT, 0, NULL);
sleep_ms(120);
tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0xC3}); //enable part 1
tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x96}); //enable part 2
tft_cmd(TFT_MADCTL, 1, (uint8_t[1]){0x88}); //MY,MX,MV,ML,BRG,MH,0,0(24), 0:RGB
tft_cmd(TFT_PIXELFORMAT, 1, (uint8_t[1]){0x05}); //0x05:RGB565, 0x06 RGB666
tft_cmd(TFT_DSIPLAY_INVER, 1, (uint8_t[1]){0x01}); // 1-dot
//tft_cmd(TFT_DISPLAYFUNC, 3, (uint8_t[3]){0x0A, 0x82, 0x27}); // ILI9342
tft_cmd(TFT_DISPLAYFUNC, 3, (uint8_t[3]){0x80, 0x02, 0x3B}); // ST7796
tft_cmd(TFT_DISP_OUTPUT_CTRL_ADJUST, 8, (uint8_t[8])
{0x40,
0x8A,
0x00,
0x00,
0x29, //Source eqaulizing period time= 22.5 us
0x19, //Timing for "Gate start"=25 (Tclk)
0xA5, //Timing for "Gate End"=37 (Tclk), Gate driver EQ function ON
0x33}); // ST7796
tft_cmd(TFT_POWERCONTROL2, 1, (uint8_t[1]){0x06}); // 0x05 :3.3V
tft_cmd(TFT_POWERCONTROL3, 1, (uint8_t[1]){0xA7});
tft_cmd(TFT_VCOMCONTROL1, 1, (uint8_t[1]){0x18});
sleep_ms(120);
tft_cmd(TFT_PGAMCOR, 14, (uint8_t[14]){ 0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b});
tft_cmd(TFT_NGAMCOR, 14, (uint8_t[14]){ 0xe0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2b, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b});
sleep_ms(120);
tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x3C}); // disable part 1
tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x69}); // disable part 2
tft_cmd(TFT_DISPLAYOFF, 0, NULL);
sleep_ms(120);
tft_cmd(TFT_DISPLAYON, 0, NULL);
sleep_ms(500);
}
void tft_init(PIO pio, uint sm, uint din_base, uint csx_dcx_sck_side_base_pin) {
tft_pio = pio;
tft_sm = sm;
#ifdef PICO_TFT_PARALLEL
in_out_base_pin = din_base;
set_base_pin = csx_dcx_sck_side_base_pin;
tft_pio_cmd_init(tft_pio, tft_sm, in_out_base_pin, set_base_pin, 70000000); //pio freq
#endif
#ifdef PICO_TFT_SERIAL
s_in_out_base_pin = din_base;
sideset_base_pin = csx_dcx_sck_side_base_pin;
tft_pio_cmd_init(tft_pio, tft_sm, s_in_out_base_pin, sideset_base_pin, 62500000/* 62.5M baud rate for SPI*/); //pio freq
#endif
tft_init_config();
}
pico_tft_lvgl.h
#ifndef __PICO_TFT_LVGL__
#define __PICO_TFT_LVGL__
#include "pico_tft.h"
#include "lvgl.h"
void tft_lvgl_draw_bitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t *bitmap);
void tft_lvgl_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p);
uint32_t pico_lvgl_get_tick_cb();
#endif
pico_tft_lvgl.c#include "pico_tft.h"
#include "pico_tft_lvgl.h"
#include "registers.h"
#include "stdio.h"
void tft_lvgl_draw_bitmap(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t *bitmap)
{
uint32_t total_pixels = (x2-x1+1) * (y2-y1+1)*2;
tft_set_address_window (x1, y1, x2, y2);
tft_cmd_dma(TFT_MEMORYWRITE, total_pixels, bitmap);
}
void tft_lvgl_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
tft_lvgl_draw_bitmap(
(uint16_t)(area->x1),
(uint16_t)(area->y1),
(uint16_t)(area->x2),
(uint16_t)(area->y2), (uint8_t*)color_p
);
lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/
}
/* tell how many milliseconds have elapsed since start up
for lvgl 9.x.x
*/
uint32_t pico_lvgl_get_tick_cb()
{
return (time_us_32()/1000);
}
pico_tft.pio.program tft_pio_parallel
; CSX, D/CX, WRX, RDX --> gpio 15, 14, 13, 12 (set pins)
.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
jmp start
.program tft_pio_serial
; CSX, D/CX(A0), SCL --> gpio 13, 12, 11 (side-set pins)
.side_set 3
.wrap_target
start:
pull side 0b100
mov x, osr side 0b000 ;command code, if 0x0, command nop, only data
jmp !x param side 0b000
set y,7 side 0b000
cmd_bit_loop:
out pins, 1 side 0b000 ;[2]
jmp y--, cmd_bit_loop side 0b001 ;[2]
param:
pull side 0b010
mov x, osr side 0b010 ;how many parameters
jmp !x, start side 0b010 ;no parameter return start
jmp x--, param_data side 0b010
param_data:
pull side 0b010 ; write data
set y,7 side 0b010
data_bit_loop:
out pins, 1 side 0b010 ;[2]
jmp y--, data_bit_loop side 0b011 ;[2]
jmp x--, param_data side 0b010
jmp start side 0b100
- XPT2046_touch
CMakeLists.txt
add_library(xpt2046_touch INTERFACE)
target_sources(xpt2046_touch INTERFACE
${CMAKE_CURRENT_LIST_DIR}/xpt2046.c
)
target_include_directories(xpt2046_touch INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/..
)
target_link_libraries(xpt2046_touch INTERFACE
hardware_spi
hardware_dma
)
xpt2046.h
#ifndef _XPT2046_H_
#define _XPT2046_H_
#include "pico/stdlib.h"
#include "lvgl.h"
#define XPT2046_IRQ_GPIO 11
#define XPT2046_MOSI 15 // 19
#define XPT2046_MISO 12 // 16
#define XPT2046_CS 13 // 17
#define XPT2046_CLK 14 // 18
#define XPT2046_SPI spi1
#define XPT2046_MIN_RAW_X 1350 //2000
#define XPT2046_MAX_RAW_X 31000 //30000
#define XPT2046_MIN_RAW_Y 2050 //1500
#define XPT2046_MAX_RAW_Y 31500 //29000
void xpt2046_init();
bool xpt2046_getXY(uint16_t *x, uint16_t *y);
void xpt2046_lvgl_read_cb(struct _lv_indev_drv_t * indev, lv_indev_data_t* data);
#endif
xpt2046.c#include "stdio.h"
#include "xpt2046.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "pico/stdlib.h"
#include "pico_tft.h"
uint32_t xpt2046_event=0;
uint8_t READ_X = 0xD0;
uint8_t READ_Y = 0x90;
bool xpt2046_getXY(uint16_t *x, uint16_t *y) {
uint8_t temp[2];
uint16_t raw_x, raw_y;
uint32_t avg_x = 0;
uint32_t avg_y = 0;
uint8_t nsamples = 0;
const uint8_t SAMPLES=16;
gpio_put(XPT2046_CS, false);
for(uint8_t i = 0; i < SAMPLES; i++, nsamples++)
{
if(gpio_get(XPT2046_IRQ_GPIO))
break;
spi_write_blocking(XPT2046_SPI, &READ_X, 1);
spi_read_blocking(XPT2046_SPI, 0x00, temp, 2);
raw_x = ((uint16_t)temp[0]) << 8 | (uint16_t)temp[1];
spi_write_blocking(XPT2046_SPI, &READ_Y, 1);
spi_read_blocking(XPT2046_SPI, 0x00, temp, 2);
raw_y = ((uint16_t)temp[0]) << 8 | (uint16_t)temp[1];
avg_x += raw_x;
avg_y += raw_y;
}
gpio_put(XPT2046_CS, true);
if(nsamples < SAMPLES)
return false;
raw_x = (avg_x / SAMPLES);
raw_y = (avg_y / SAMPLES);
//if(raw_x < XPT2046_MIN_RAW_X) raw_x = XPT2046_MIN_RAW_X;
//if(raw_x > XPT2046_MAX_RAW_X) raw_x = XPT2046_MAX_RAW_X;
if(raw_x < XPT2046_MIN_RAW_X || raw_x > XPT2046_MAX_RAW_X) return false;
//if(raw_y < XPT2046_MIN_RAW_Y) raw_y = XPT2046_MIN_RAW_Y;
//if(raw_y > XPT2046_MAX_RAW_Y) raw_y = XPT2046_MAX_RAW_Y;
if(raw_y < XPT2046_MIN_RAW_Y || raw_y > XPT2046_MAX_RAW_Y) return false;
*x = (raw_x - XPT2046_MIN_RAW_X) * TFT_WIDTH / (XPT2046_MAX_RAW_X - XPT2046_MIN_RAW_X);
*y = (raw_y - XPT2046_MIN_RAW_Y) * TFT_HEIGHT / (XPT2046_MAX_RAW_Y - XPT2046_MIN_RAW_Y);
return true;
}
void xpt2046_init() {
gpio_init(XPT2046_IRQ_GPIO);
gpio_init(XPT2046_MISO);
gpio_init(XPT2046_MOSI);
gpio_init(XPT2046_CLK);
gpio_init(XPT2046_CS);
gpio_set_dir(XPT2046_CS, GPIO_OUT);
gpio_set_dir(XPT2046_IRQ_GPIO, GPIO_OUT);
gpio_set_function(XPT2046_CLK,GPIO_FUNC_SPI);
gpio_set_function(XPT2046_CS,GPIO_FUNC_SIO);
gpio_set_function(XPT2046_MOSI,GPIO_FUNC_SPI);
gpio_set_function(XPT2046_MISO,GPIO_FUNC_SPI);
spi_init(XPT2046_SPI,250000);
}
void xpt2046_lvgl_read_cb(struct _lv_indev_drv_t * indev, lv_indev_data_t* data) {
uint16_t x,y;
if (xpt2046_getXY(&x,&y)) {
x = TFT_WIDTH-x;
data->point.x = x;
data->point.y = y;
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
- pico_lvgl
CMakeLists.txt
add_library(pico_lvgl INTERFACE)
target_sources(pico_lvgl INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_lvgl.c
)
add_subdirectory(pico_tft)
add_subdirectory(xpt2046_touch)
add_subdirectory(lvgl)
target_include_directories(pico_lvgl INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/pico_tft
${CMAKE_CURRENT_LIST_DIR}/xpt2046_touch
${CMAKE_CURRENT_LIST_DIR}/lvgl
)
target_link_libraries(pico_lvgl INTERFACE
hardware_pio
hardware_dma
pico_tft
xpt2046_touch
lvgl
)
pico_lvgl.c
#include "pico_lvgl.h"
bool lv_inc_timer_cb(repeating_timer_t *rt) {
lv_tick_inc(*((uint8_t*)rt->user_data));
return true;
}
void pico_lvgl_tft_init(PIO pio, uint sm, uint sdi_gpio, uint csx_dcx_sck_gpio) {
tft_init(pio , sm, sdi_gpio, csx_dcx_sck_gpio);
}
void pico_lvgl_tick_inc_timer_init(uint8_t tick_inc) {
static repeating_timer_t rt;
static uint8_t _tick_inc;
_tick_inc = tick_inc;
add_repeating_timer_ms(tick_inc, lv_inc_timer_cb, (void*) (&_tick_inc), &rt);
}
/*
tick_inc: to call the lv_tick_inc(x) function periodically in (x) milliseconds.
*/
void pico_lvgl_display_init(uint8_t tick_inc) {
// init timer
if (tick_inc > 10) tick_inc = 10;
if (tick_inc < 1) tick_inc = 1;
pico_lvgl_tick_inc_timer_init(tick_inc);
// lv_init
lv_init();
// set draw buffer
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[TFT_WIDTH * TFT_HEIGHT / 10]; /*Declare a buffer for 1/10 screen size*/
lv_disp_draw_buf_init(&draw_buf, buf1, NULL, TFT_WIDTH * TFT_HEIGHT / 10); /*Initialize the display buffer.*/
//setup display
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.flush_cb = tft_lvgl_disp_flush; /*Set your driver function*/
disp_drv.draw_buf = &draw_buf; /*Assign the buffer to the display*/
disp_drv.hor_res = TFT_WIDTH; /*Set the horizontal resolution of the display*/
disp_drv.ver_res = TFT_HEIGHT; /*Set the vertical resolution of the display*/
lv_disp_drv_register(&disp_drv); /*Finally register the driver*/
}
void pico_lvgl_xpt2046_init() {
// init xpt2046 hardware
xpt2046_init();
// setup touch screen input device
static lv_indev_drv_t indev_drv; /*Descriptor of a input device driver*/
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/
indev_drv.read_cb = xpt2046_lvgl_read_cb; /*Set your driver function*/
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
}
pico_lvgl.h
#ifndef __PICO_LVGL_H__
#define __PICO_LVGL_H__
#include "stdio.h"
#include "pico/stdio.h"
#include "hardware/pio.h"
#include "pico_tft.h"
#include "xpt2046.h"
#include "lvgl.h"
void pico_lvgl_tft_init(PIO pio, uint sm, uint sdi_gpio, uint csx_dcx_sck_gpio);
void pico_lvgl_xpt2046_init();
void pico_lvgl_display_init(uint8_t tick_inc);
#endif
- root
CMakeLists.txt
add_subdirectory(pico_lvgl)
target_link_libraries(project_name
pico_lvgl
)
- src資料夾、
- lvgl.h
- lv_conf.h(由lv_conf_template.h複製)
修改lv_conf.h的內容:
- /* clang-format off */
#if 1 /*Set it to "1" to enable content*/ - #define LV_COLOR_DEPTH 16
#define LV_COLOR_16_SWAP 1
CMakeLists.txt內容:
二、週期性呼叫 lv_tick_inc(x)bool lv_inc_timer_cb(repeating_timer_t *rt) {
lv_tick_inc(*((uint8_t*)rt->user_data));
return true;
}
void pico_lvgl_tick_inc_timer_init(uint8_t tick_inc) {
static repeating_timer_t rt;
static uint8_t _tick_inc;
_tick_inc = tick_inc;
add_repeating_timer_ms(tick_inc, lv_inc_timer_cb, (void*) (&_tick_inc), &rt);
}
四、製作draw buffer flush callback function, 將一個block資料寫到TFT Display memory中:
void tft_lvgl_draw_bitmap(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t *bitmap)
{
uint32_t total_pixels = (x2-x1+1) * (y2-y1+1)*2;
tft_set_address_window (x1, y1, x2, y2);
tft_cmd_dma(TFT_MEMORYWRITE, total_pixels, bitmap);
}
void tft_lvgl_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
tft_lvgl_draw_bitmap(
(uint16_t)(area->x1),
(uint16_t)(area->y1),
(uint16_t)(area->x2),
(uint16_t)(area->y2), (uint8_t*)color_p
);
lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/
}
五、以製作一組音樂播放器為展示範例:
CmakeList.txt
pico_tft.h
pico_tft.c
pico_tft_lvgl.h
pico_tft_lvgl.c
pico_tft.pio
option(LV_LVGL_H_INCLUDE_SIMPLE
"Use #include \"lvgl.h\" instead of #include \"../../lvgl.h\"" ON)
# Option to define LV_CONF_INCLUDE_SIMPLE, default: ON
option(LV_CONF_INCLUDE_SIMPLE
"Use #include \"lv_conf.h\" instead of #include \"../../lv_conf.h\"" ON)
add_library(lvgl INTERFACE)
file(GLOB_RECURSE SOURCES src/*.c src/*.S)
target_sources(lvgl INTERFACE
${SOURCES}
)
target_include_directories(lvgl INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/..
)
- pico_tft
add_library(pico_tft INTERFACE)
pico_generate_pio_header(pico_tft ${CMAKE_CURRENT_LIST_DIR}/pico_tft.pio)
target_sources(pico_tft INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_tft.c
${CMAKE_CURRENT_LIST_DIR}/pico_tft_lvgl.c
)
target_include_directories(pico_tft INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/..
)
target_link_libraries(pico_tft INTERFACE
hardware_pio
hardware_dma
)
pico_tft.h
#ifndef _TFT_H_
#define _TFT_H_
#define TFT_WIDTH 320
#define TFT_HEIGHT 480
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "pico_tft_lvgl.h"
void tft_init(PIO pio, uint sm, uint din_base, uint csx_dcx_sck_side_base_pin);
void tft_init_config();
void tft_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
void tft_set_address_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void tft_cmd(uint32_t cmd, uint32_t count, uint8_t *param);
void tft_cmd_dma(uint32_t cmd, uint32_t count, uint8_t *param);
uint16_t tft_color_565RGB(uint8_t R, uint8_t G, uint8_t B);
void tft_lv_draw_bitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t *bitmap);
void tft_pio_cmd_init(PIO pio, uint sm, uint out_base, uint set_base, uint32_t freq);
#endif
pico_tft.c
#include "stdio.h"
#include "stdlib.h"
#include "pico/stdlib.h"
#include "hardware/clocks.h"
#include "string.h"
#include "registers.h"
#include "pico_tft.pio.h"
#include "pico_tft.h"
#include "hardware/dma.h"
#define MAX_BYTE_TRANS (TFT_WIDTH*TFT_HEIGHT*2)
#define PICO_TFT_SERIAL
#define PICO_TFT_DMA
PIO tft_pio = pio1;
uint tft_sm=0;
uint in_out_base_pin=4;
uint set_base_pin=12;
uint sideset_base_pin=16;
uint s_in_out_base_pin=19;
int tft_dma_channel;
void tft_cmd(uint32_t cmd, uint32_t count, uint8_t *param)
{
pio_sm_restart(tft_pio, tft_sm);
#ifdef PICO_TFT_SERIAL
pio_sm_put_blocking(tft_pio, tft_sm, cmd << 24);
#endif
#ifdef PICO_TFT_PARALLEL
pio_sm_put_blocking(tft_pio, tft_sm, cmd);
#endif
pio_sm_put_blocking(tft_pio, tft_sm, count);
for (int i = 0; i < count; i++)
{
#ifdef PICO_TFT_SERIAL
pio_sm_put_blocking(tft_pio, tft_sm, param[i]<<24);
#endif
#ifdef PICO_TFT_PARALLEL
pio_sm_put_blocking(tft_pio, tft_sm, param[i]);
#endif
}
}
//#ifdef PICO_TFT_DMA
void tft_cmd_dma(uint32_t cmd, uint32_t count, uint8_t *param)
{
#ifdef PICO_TFT_SERIAL
tft_cmd(cmd, count, param);
return;
#endif
pio_sm_restart(tft_pio, tft_sm);
pio_sm_put_blocking(tft_pio, tft_sm, cmd);
pio_sm_put_blocking(tft_pio, tft_sm, count);
dma_channel_set_trans_count(tft_dma_channel, count >> DMA_SIZE_8, false);
dma_channel_set_read_addr(tft_dma_channel, param, false);
dma_channel_start(tft_dma_channel);
dma_channel_wait_for_finish_blocking(tft_dma_channel);
}
//#endif
void tft_pio_cmd_init(PIO pio, uint sm, uint in_out_base, uint set_sideset, uint32_t freq) {
uint offset=0;
pio_sm_config c;
#ifdef PICO_TFT_PARALLEL
offset = pio_add_program(pio, &tft_pio_parallel_program);
c = tft_pio_parallel_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_sideset+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_sideset, 4);
sm_config_set_out_shift(&c, true, false, 8);
sm_config_set_in_shift(&c, false, false, 8);
#endif
#ifdef PICO_TFT_SERIAL
offset = pio_add_program(pio, &tft_pio_serial_program);
c = tft_pio_serial_program_get_default_config(offset);
pio_gpio_init(pio, in_out_base);
for (int i=0; i < 3; i++) pio_gpio_init(pio, set_sideset+i);
pio_sm_set_consecutive_pindirs(pio, sm, in_out_base, 1, true);
pio_sm_set_consecutive_pindirs(pio, sm, set_sideset, 3, true);
sm_config_set_in_pins(&c, in_out_base);
sm_config_set_out_pins(&c, in_out_base, 1);
sm_config_set_sideset_pins(&c, set_sideset);
sm_config_set_out_shift(&c, false, false, 8);
sm_config_set_in_shift(&c, true, false, 8);
#endif
//sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
float div = clock_get_hz(clk_sys)/freq;
sm_config_set_clkdiv(&c, div);
//sm_config_set_clkdiv(&c, 1.25);
#ifdef PICO_TFT_DMA
/* DMA */
tft_dma_channel = dma_claim_unused_channel(true);
dma_channel_config dc = dma_channel_get_default_config(tft_dma_channel);
channel_config_set_write_increment(&dc, false);
channel_config_set_read_increment(&dc, true);
channel_config_set_dreq(&dc, pio_get_dreq(pio, sm, true));
channel_config_set_transfer_data_size(&dc, DMA_SIZE_8); //DMA_SIZE_8,16,32
dma_channel_configure(tft_dma_channel, &dc, (void*) (PIO1_BASE+PIO_TXF0_OFFSET),
NULL, MAX_BYTE_TRANS>> DMA_SIZE_8, false); //DMA_SIZE_8 or 16 or 32
/* DMA */
#endif
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
/* tft draw functions*/
uint16_t tft_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 tft_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);
tft_cmd(TFT_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);
tft_cmd(TFT_PAGEADDRSET, 4, addr );
tft_cmd(TFT_MEMORYWRITE, 0, NULL);
}
void tft_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);
tft_cmd(TFT_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);
tft_cmd(TFT_PAGEADDRSET, 4, addr );
}
/* put color at point*/
void tft_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
{
if ( x < 0 || x > TFT_WIDTH-1 || y < 0 || y > TFT_HEIGHT-1) {
printf("over range,x,y\n");
return;
}
tft_set_address_window(x,y,x,y);
tft_cmd(TFT_MEMORYWRITE, 2, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)color});
}
void tft_init_config() {
tft_cmd(TFT_SOFTRESET, 0, NULL);
sleep_ms(120);
tft_cmd(TFT_SLEEPOUT, 0, NULL);
sleep_ms(120);
tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0xC3}); //enable part 1
tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x96}); //enable part 2
tft_cmd(TFT_MADCTL, 1, (uint8_t[1]){0x88}); //MY,MX,MV,ML,BRG,MH,0,0(24), 0:RGB
tft_cmd(TFT_PIXELFORMAT, 1, (uint8_t[1]){0x05}); //0x05:RGB565, 0x06 RGB666
tft_cmd(TFT_DSIPLAY_INVER, 1, (uint8_t[1]){0x01}); // 1-dot
//tft_cmd(TFT_DISPLAYFUNC, 3, (uint8_t[3]){0x0A, 0x82, 0x27}); // ILI9342
tft_cmd(TFT_DISPLAYFUNC, 3, (uint8_t[3]){0x80, 0x02, 0x3B}); // ST7796
tft_cmd(TFT_DISP_OUTPUT_CTRL_ADJUST, 8, (uint8_t[8])
{0x40,
0x8A,
0x00,
0x00,
0x29, //Source eqaulizing period time= 22.5 us
0x19, //Timing for "Gate start"=25 (Tclk)
0xA5, //Timing for "Gate End"=37 (Tclk), Gate driver EQ function ON
0x33}); // ST7796
tft_cmd(TFT_POWERCONTROL2, 1, (uint8_t[1]){0x06}); // 0x05 :3.3V
tft_cmd(TFT_POWERCONTROL3, 1, (uint8_t[1]){0xA7});
tft_cmd(TFT_VCOMCONTROL1, 1, (uint8_t[1]){0x18});
sleep_ms(120);
tft_cmd(TFT_PGAMCOR, 14, (uint8_t[14]){ 0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b});
tft_cmd(TFT_NGAMCOR, 14, (uint8_t[14]){ 0xe0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2b, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b});
sleep_ms(120);
tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x3C}); // disable part 1
tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x69}); // disable part 2
tft_cmd(TFT_DISPLAYOFF, 0, NULL);
sleep_ms(120);
tft_cmd(TFT_DISPLAYON, 0, NULL);
sleep_ms(500);
}
void tft_init(PIO pio, uint sm, uint din_base, uint csx_dcx_sck_side_base_pin) {
tft_pio = pio;
tft_sm = sm;
#ifdef PICO_TFT_PARALLEL
in_out_base_pin = din_base;
set_base_pin = csx_dcx_sck_side_base_pin;
tft_pio_cmd_init(tft_pio, tft_sm, in_out_base_pin, set_base_pin, 70000000); //pio freq
#endif
#ifdef PICO_TFT_SERIAL
s_in_out_base_pin = din_base;
sideset_base_pin = csx_dcx_sck_side_base_pin;
tft_pio_cmd_init(tft_pio, tft_sm, s_in_out_base_pin, sideset_base_pin, 62500000/* 62.5M baud rate for SPI*/); //pio freq
#endif
tft_init_config();
}
pico_tft_lvgl.h
#ifndef __PICO_TFT_LVGL__
#define __PICO_TFT_LVGL__
#include "pico_tft.h"
#include "lvgl.h"
void tft_lvgl_draw_bitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t *bitmap);
void tft_lvgl_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p);
uint32_t pico_lvgl_get_tick_cb();
#endif
pico_tft_lvgl.c
#include "pico_tft.h"
#include "pico_tft_lvgl.h"
#include "registers.h"
#include "stdio.h"
void tft_lvgl_draw_bitmap(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t *bitmap)
{
uint32_t total_pixels = (x2-x1+1) * (y2-y1+1)*2;
tft_set_address_window (x1, y1, x2, y2);
tft_cmd_dma(TFT_MEMORYWRITE, total_pixels, bitmap);
}
void tft_lvgl_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
tft_lvgl_draw_bitmap(
(uint16_t)(area->x1),
(uint16_t)(area->y1),
(uint16_t)(area->x2),
(uint16_t)(area->y2), (uint8_t*)color_p
);
lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/
}
/* tell how many milliseconds have elapsed since start up
for lvgl 9.x.x
*/
uint32_t pico_lvgl_get_tick_cb()
{
return (time_us_32()/1000);
}
pico_tft.pio
.program tft_pio_parallel
; CSX, D/CX, WRX, RDX --> gpio 15, 14, 13, 12 (set pins)
.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
jmp start
.program tft_pio_serial
; CSX, D/CX(A0), SCL --> gpio 13, 12, 11 (side-set pins)
.side_set 3
.wrap_target
start:
pull side 0b100
mov x, osr side 0b000 ;command code, if 0x0, command nop, only data
jmp !x param side 0b000
set y,7 side 0b000
cmd_bit_loop:
out pins, 1 side 0b000 ;[2]
jmp y--, cmd_bit_loop side 0b001 ;[2]
param:
pull side 0b010
mov x, osr side 0b010 ;how many parameters
jmp !x, start side 0b010 ;no parameter return start
jmp x--, param_data side 0b010
param_data:
pull side 0b010 ; write data
set y,7 side 0b010
data_bit_loop:
out pins, 1 side 0b010 ;[2]
jmp y--, data_bit_loop side 0b011 ;[2]
jmp x--, param_data side 0b010
jmp start side 0b100
- XPT2046_touch
CMakeLists.txt
xpt2046.h
xpt2046.c
CMakeLists.txt
add_library(xpt2046_touch INTERFACE)
target_sources(xpt2046_touch INTERFACE
${CMAKE_CURRENT_LIST_DIR}/xpt2046.c
)
target_include_directories(xpt2046_touch INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/..
)
target_link_libraries(xpt2046_touch INTERFACE
hardware_spi
hardware_dma
)
xpt2046.h
#ifndef _XPT2046_H_
#define _XPT2046_H_
#include "pico/stdlib.h"
#include "lvgl.h"
#define XPT2046_IRQ_GPIO 11
#define XPT2046_MOSI 15 // 19
#define XPT2046_MISO 12 // 16
#define XPT2046_CS 13 // 17
#define XPT2046_CLK 14 // 18
#define XPT2046_SPI spi1
#define XPT2046_MIN_RAW_X 1350 //2000
#define XPT2046_MAX_RAW_X 31000 //30000
#define XPT2046_MIN_RAW_Y 2050 //1500
#define XPT2046_MAX_RAW_Y 31500 //29000
void xpt2046_init();
bool xpt2046_getXY(uint16_t *x, uint16_t *y);
void xpt2046_lvgl_read_cb(struct _lv_indev_drv_t * indev, lv_indev_data_t* data);
#endif
xpt2046.c
#include "stdio.h"
#include "xpt2046.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "pico/stdlib.h"
#include "pico_tft.h"
uint32_t xpt2046_event=0;
uint8_t READ_X = 0xD0;
uint8_t READ_Y = 0x90;
bool xpt2046_getXY(uint16_t *x, uint16_t *y) {
uint8_t temp[2];
uint16_t raw_x, raw_y;
uint32_t avg_x = 0;
uint32_t avg_y = 0;
uint8_t nsamples = 0;
const uint8_t SAMPLES=16;
gpio_put(XPT2046_CS, false);
for(uint8_t i = 0; i < SAMPLES; i++, nsamples++)
{
if(gpio_get(XPT2046_IRQ_GPIO))
break;
spi_write_blocking(XPT2046_SPI, &READ_X, 1);
spi_read_blocking(XPT2046_SPI, 0x00, temp, 2);
raw_x = ((uint16_t)temp[0]) << 8 | (uint16_t)temp[1];
spi_write_blocking(XPT2046_SPI, &READ_Y, 1);
spi_read_blocking(XPT2046_SPI, 0x00, temp, 2);
raw_y = ((uint16_t)temp[0]) << 8 | (uint16_t)temp[1];
avg_x += raw_x;
avg_y += raw_y;
}
gpio_put(XPT2046_CS, true);
if(nsamples < SAMPLES)
return false;
raw_x = (avg_x / SAMPLES);
raw_y = (avg_y / SAMPLES);
//if(raw_x < XPT2046_MIN_RAW_X) raw_x = XPT2046_MIN_RAW_X;
//if(raw_x > XPT2046_MAX_RAW_X) raw_x = XPT2046_MAX_RAW_X;
if(raw_x < XPT2046_MIN_RAW_X || raw_x > XPT2046_MAX_RAW_X) return false;
//if(raw_y < XPT2046_MIN_RAW_Y) raw_y = XPT2046_MIN_RAW_Y;
//if(raw_y > XPT2046_MAX_RAW_Y) raw_y = XPT2046_MAX_RAW_Y;
if(raw_y < XPT2046_MIN_RAW_Y || raw_y > XPT2046_MAX_RAW_Y) return false;
*x = (raw_x - XPT2046_MIN_RAW_X) * TFT_WIDTH / (XPT2046_MAX_RAW_X - XPT2046_MIN_RAW_X);
*y = (raw_y - XPT2046_MIN_RAW_Y) * TFT_HEIGHT / (XPT2046_MAX_RAW_Y - XPT2046_MIN_RAW_Y);
return true;
}
void xpt2046_init() {
gpio_init(XPT2046_IRQ_GPIO);
gpio_init(XPT2046_MISO);
gpio_init(XPT2046_MOSI);
gpio_init(XPT2046_CLK);
gpio_init(XPT2046_CS);
gpio_set_dir(XPT2046_CS, GPIO_OUT);
gpio_set_dir(XPT2046_IRQ_GPIO, GPIO_OUT);
gpio_set_function(XPT2046_CLK,GPIO_FUNC_SPI);
gpio_set_function(XPT2046_CS,GPIO_FUNC_SIO);
gpio_set_function(XPT2046_MOSI,GPIO_FUNC_SPI);
gpio_set_function(XPT2046_MISO,GPIO_FUNC_SPI);
spi_init(XPT2046_SPI,250000);
}
void xpt2046_lvgl_read_cb(struct _lv_indev_drv_t * indev, lv_indev_data_t* data) {
uint16_t x,y;
if (xpt2046_getXY(&x,&y)) {
x = TFT_WIDTH-x;
data->point.x = x;
data->point.y = y;
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
- pico_lvgl
add_library(pico_lvgl INTERFACE)
target_sources(pico_lvgl INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_lvgl.c
)
add_subdirectory(pico_tft)
add_subdirectory(xpt2046_touch)
add_subdirectory(lvgl)
target_include_directories(pico_lvgl INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/pico_tft
${CMAKE_CURRENT_LIST_DIR}/xpt2046_touch
${CMAKE_CURRENT_LIST_DIR}/lvgl
)
target_link_libraries(pico_lvgl INTERFACE
hardware_pio
hardware_dma
pico_tft
xpt2046_touch
lvgl
)
#include "pico_lvgl.h"
bool lv_inc_timer_cb(repeating_timer_t *rt) {
lv_tick_inc(*((uint8_t*)rt->user_data));
return true;
}
void pico_lvgl_tft_init(PIO pio, uint sm, uint sdi_gpio, uint csx_dcx_sck_gpio) {
tft_init(pio , sm, sdi_gpio, csx_dcx_sck_gpio);
}
void pico_lvgl_tick_inc_timer_init(uint8_t tick_inc) {
static repeating_timer_t rt;
static uint8_t _tick_inc;
_tick_inc = tick_inc;
add_repeating_timer_ms(tick_inc, lv_inc_timer_cb, (void*) (&_tick_inc), &rt);
}
/*
tick_inc: to call the lv_tick_inc(x) function periodically in (x) milliseconds.
*/
void pico_lvgl_display_init(uint8_t tick_inc) {
// init timer
if (tick_inc > 10) tick_inc = 10;
if (tick_inc < 1) tick_inc = 1;
pico_lvgl_tick_inc_timer_init(tick_inc);
// lv_init
lv_init();
// set draw buffer
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[TFT_WIDTH * TFT_HEIGHT / 10]; /*Declare a buffer for 1/10 screen size*/
lv_disp_draw_buf_init(&draw_buf, buf1, NULL, TFT_WIDTH * TFT_HEIGHT / 10); /*Initialize the display buffer.*/
//setup display
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.flush_cb = tft_lvgl_disp_flush; /*Set your driver function*/
disp_drv.draw_buf = &draw_buf; /*Assign the buffer to the display*/
disp_drv.hor_res = TFT_WIDTH; /*Set the horizontal resolution of the display*/
disp_drv.ver_res = TFT_HEIGHT; /*Set the vertical resolution of the display*/
lv_disp_drv_register(&disp_drv); /*Finally register the driver*/
}
void pico_lvgl_xpt2046_init() {
// init xpt2046 hardware
xpt2046_init();
// setup touch screen input device
static lv_indev_drv_t indev_drv; /*Descriptor of a input device driver*/
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/
indev_drv.read_cb = xpt2046_lvgl_read_cb; /*Set your driver function*/
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
}
#ifndef __PICO_LVGL_H__
#define __PICO_LVGL_H__
#include "stdio.h"
#include "pico/stdio.h"
#include "hardware/pio.h"
#include "pico_tft.h"
#include "xpt2046.h"
#include "lvgl.h"
void pico_lvgl_tft_init(PIO pio, uint sm, uint sdi_gpio, uint csx_dcx_sck_gpio);
void pico_lvgl_xpt2046_init();
void pico_lvgl_display_init(uint8_t tick_inc);
#endif
- root
CMakeLists.txt
add_subdirectory(pico_lvgl)
target_link_libraries(project_name
pico_lvgl
)
沒有留言:
張貼留言