prettyprint

2023年2月25日 星期六

[Raspberry Pi Pico (c-sdk)] Display: Ep 4 : ILI9341 TFT LCD 8-bit parallel - Frame(320*240) Rate above 120fps

 在[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




沒有留言:

張貼留言