在[Raspberry Pi Pico (c-sdk)] Display: Ep 2 : PIO TFT LCD ILI9341 8-bit parallel C-code Driver介紹使用PIO製作ILI9341 C Code驅動程式。本篇文章加入PIO DMA功能。比較在使用DMA與frame buffer的條件下,能達到多快的frame rate。
在原來的PIO init funtion中加入DMA。
加入一個新的function ili9341_cmd_dma()透夠DMA輸出整個frame(320*240)資料。
詳細成果請參考成果影片,詳細程式碼附於文末。
- 成果影片
- 程式碼:
測試主程式
- tft_frame.c
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/dma.h" #include "hardware/pio.h" #include "ili9341.h" #include "fonts/pico.h" #include "registers.h" #include "inttypes.h" #include "string.h" #include "stdlib.h" #include "time.h" int main() { stdio_init_all(); ili9341_init(); //sleep_ms(5000); ili9341_fill_rect(0,0,320,240, 0xf800); sleep_ms(1000); time_t t; srand((unsigned) time(&t)); uint8_t *spt; int x,y,width,color; int boxs[100][4]; //(x,y,width, color-h, cololr-l) uint8_t *buff = (uint8_t*) calloc(320*240*2, sizeof(uint8_t)); //frame buffer if (!buff) { printf("alloc memory error\n"); return 0; } //==== 100 random rectangle for (int i=0; i < 100; i++) { y=rand()%240; x=rand()%320; width=rand()%60; if (y+width >=240) y=240-y; if (x+width >=320) x=320-x; boxs[i][0]=x; boxs[i][1]=y; boxs[i][2]=width; boxs[i][3]=rand()%0xff; } int p; absolute_time_t t1 ,t2; int i; // repeat test times //======= printf("\n50 rect in a frame\nStarting... \n"); for (int rep=0; rep < 5; rep++) { t1= get_absolute_time(); for (i=0; i < 500; i++) { memset(buff, 0, 320*240*2); for (int j=0; j < 50; j++) { p = rand()%100; spt= (buff+boxs[p][0]*2+boxs[p][1]*640); for (int k =0; k < boxs[p][2]; k++){ memset((spt+k*640), boxs[p][3], boxs[p][2]*2); } } ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd_dma(ILI9341_MEMORYWRITE, 320*240*2, buff); } t2=get_absolute_time(); printf("\n%d frames(50 Boxs) in RAM) -- time:%"PRIu64"us\n",i, absolute_time_diff_us(t1, t2)); printf("Frame Rate: %ffps, pause 1 second to start next test\n", (float)i/(absolute_time_diff_us(t1, t2))*1000000); sleep_ms(1000); } //============================== printf("starting(with DMA)...\n"); //=============================== memcpy(buff, image_data_pico, 320*240*2); width=20; t1 = get_absolute_time(); for (i=0; i < 1000; i++) { if (i%50==0) memcpy(buff, image_data_pico, 320*240*2); color = rand()&0xff; x = rand()%300; y=rand()%220; spt= (buff+x*2+y*640); for (int k =0; k < 20; k++){ memset((spt+k*640),color, 40); } ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd_dma(ILI9341_MEMORYWRITE, 320*240*2, buff); } t2=get_absolute_time(); printf("\n%d (image frames in RAM) -- time:%"PRIu64"us\n",i,absolute_time_diff_us(t1, t2)); printf("Frame Rate: %ffps\n", (float)i/(absolute_time_diff_us(t1, t2))*1000000); //============================== t1 = get_absolute_time(); for (i=0; i < 60; i++) { ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd_dma(ILI9341_MEMORYWRITE, 320*240*2, (uint8_t*)image_data_pico); } t2=get_absolute_time(); printf("\n%d image frames in XIP Flash--time:%"PRIu64"us\n",i,absolute_time_diff_us(t1, t2)); printf("Frame Rate: %ffps\n", (float)i/(absolute_time_diff_us(t1, t2))*1000000); //====================================== printf("\nrepeat 5 times\n"); for (int i=0; i< 5; i++) { memcpy(buff, image_data_pico, 320*240*2); t1 = get_absolute_time(); ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd_dma(ILI9341_MEMORYWRITE, 320*240*2, buff); t2=get_absolute_time(); printf("1 frame(in RAM) --time:%"PRIu64"us",absolute_time_diff_us(t1, t2)); printf(" ,sleep 1 second\n"); sleep_ms(1000); t1 = get_absolute_time(); memset(buff, 0xf71f, 320*240*2); ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd_dma(ILI9341_MEMORYWRITE, 320*240*2, buff); t2=get_absolute_time(); printf("1 frame(memset color) --time:%"PRIu64"us",absolute_time_diff_us(t1, t2)); printf(" ,sleep 1 second\n"); sleep_ms(1000); } memcpy(buff, image_data_pico, 320*240*2); t1 = get_absolute_time(); for (i=0; i < 120; i++) { ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd_dma(ILI9341_MEMORYWRITE, 320*240*2, buff); } t2=get_absolute_time(); printf("\n%d 320*240 image frame in RAM-time:%"PRIu64"us\n",1, absolute_time_diff_us(t1, t2)); printf("Frame Rate: %ffps\n", (float)i/(absolute_time_diff_us(t1, t2))*1000000); printf("\ncomplete\n"); //======================================================================== printf("starting(without DMA)...\n"); //=============================== memcpy(buff, image_data_pico, 320*240*2); t1 = get_absolute_time(); for (i=0; i < 1000; i++) { if (i%50==0) memcpy(buff, image_data_pico, 320*240*2); color = rand()&0xff; x = rand()%300; y=rand()%220; spt= (buff+x*2+y*640); for (int k =0; k < 20; k++){ memset((spt+k*640),color, 40); } ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd(ILI9341_MEMORYWRITE, 320*240*2, buff); } t2=get_absolute_time(); printf("\n%d (image frames in RAM) -- time:%"PRIu64"us\n",i,absolute_time_diff_us(t1, t2)); printf("Frame Rate: %ffps\n", (float)i/(absolute_time_diff_us(t1, t2))*1000000); //============================== t1 = get_absolute_time(); for (i=0; i < 60; i++) { ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd(ILI9341_MEMORYWRITE, 320*240*2, (uint8_t*)image_data_pico); } t2=get_absolute_time(); printf("\n%d image frames in XIP Flash--time:%"PRIu64"us\n",i,absolute_time_diff_us(t1, t2)); printf("Frame Rate: %ffps\n", (float)i/(absolute_time_diff_us(t1, t2))*1000000); //====================================== printf("\nrepeat 5 times\n"); for (int i=0; i< 5; i++) { memcpy(buff, image_data_pico, 320*240*2); t1 = get_absolute_time(); ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd(ILI9341_MEMORYWRITE, 320*240*2, buff); t2=get_absolute_time(); printf("1 frame(in RAM) --time:%"PRIu64"us",absolute_time_diff_us(t1, t2)); printf(" ,sleep 1 second\n"); sleep_ms(1000); t1 = get_absolute_time(); memset(buff, 0xf71f, 320*240*2); ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd(ILI9341_MEMORYWRITE, 320*240*2, buff); t2=get_absolute_time(); printf("1 frame(memset color) --time:%"PRIu64"us",absolute_time_diff_us(t1, t2)); printf(" ,sleep 1 second\n"); sleep_ms(1000); } memcpy(buff, image_data_pico, 320*240*2); t1 = get_absolute_time(); for (i=0; i < 120; i++) { ili9341_set_address_window (0, 0, 319, 239); ili9341_cmd(ILI9341_MEMORYWRITE, 320*240*2, buff); } t2=get_absolute_time(); printf("\n%d 320*240 image frame in RAM-time:%"PRIu64"us\n",i, absolute_time_diff_us(t1, t2)); printf("Frame Rate: %ffps\n", (float)i/(float)(absolute_time_diff_us(t1, t2))*1000000); printf("\ncomplete\n"); while(1) { tight_loop_contents(); } return 0; }
- 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" #include "hardware/dma.h" #define MAX_BYTE_TRANS (320*240*2) PIO ili9341_pio = pio1; uint ili9341_sm=0; uint in_out_base_pin=4; uint set_base_pin=12; int ili9341_dma_channel; void ili9341_cmd(uint32_t cmd, uint32_t count, uint8_t *param) { pio_sm_restart(ili9341_pio, ili9341_sm); pio_sm_put_blocking(ili9341_pio, ili9341_sm, cmd); pio_sm_put_blocking(ili9341_pio, ili9341_sm, count); for (int i = 0; i < count; i++) { pio_sm_put_blocking(ili9341_pio, ili9341_sm, param[i]); } } void ili9341_cmd_dma(uint32_t cmd, uint32_t count, uint8_t *param) { pio_sm_restart(ili9341_pio, ili9341_sm); pio_sm_put_blocking(ili9341_pio, ili9341_sm, cmd); pio_sm_put_blocking(ili9341_pio, ili9341_sm, count); dma_channel_set_trans_count(ili9341_dma_channel, count >> DMA_SIZE_8, false); dma_channel_set_read_addr(ili9341_dma_channel, param, false); dma_channel_start(ili9341_dma_channel); dma_channel_wait_for_finish_blocking(ili9341_dma_channel); } 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_fifo_join(&c, PIO_FIFO_JOIN_TX); 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); /* DMA */ ili9341_dma_channel = dma_claim_unused_channel(true); dma_channel_config dc = dma_channel_get_default_config(ili9341_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(ili9341_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 */ 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, 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); } 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 ); } /* 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, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)color}); } 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, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)(color&0xff)}); } void ili9341_draw_bitmap(uint16_t x, uint16_t y, const tImage *bitmap) { uint32_t width = 0, height = 0; width = bitmap->width; height = bitmap->height; uint32_t total_pixels = width * height*2; ili9341_set_address_window (x, y, x + width-1, y + height-1); ili9341_cmd_dma(ILI9341_MEMORYWRITE, total_pixels, (uint8_t*)(bitmap->data)); } 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}); //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]){0x60}); //MY,MX,MV,ML,BRG,MH,0,0(40) ili9341_cmd(ILI9341_FRAMECONTROL, 2, (uint8_t[2]){0b00, 0x1B}); // Default 70Hz:0x1B ili9341_cmd(ILI9341_DISPLAYFUNC, 4, (uint8_t[4]){0x0A, 0x82, 0x27, 0x04}); //0a,a2,27,04 ili9341_cmd(ILI9341_GAMMASET, 1, (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, NULL); sleep_ms(150); ili9341_cmd(ILI9341_DISPLAYON, 0, NULL); sleep_ms(500); } void ili9341_init() { ili9431_pio_cmd_init(ili9341_pio, ili9341_sm, in_out_base_pin, set_base_pin, 70000000); //pio freq ili9431_init_config(); }
- ili9341.h
#ifndef _ILI9431_H_ #define _ILI9431_H_ #define TFT_WIDTH 320 #define TFT_HEIGHT 240 #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); void ili9341_set_address_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); void ili9341_cmd(uint32_t cmd, uint32_t count, uint8_t *param); void ili9341_cmd_dma(uint32_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_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); #endif
- ili9341.pio
.program ili9341_pio_cmd ; 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
register.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
沒有留言:
張貼留言