prettyprint

2022年12月13日 星期二

[Raspberry Pi Pico (c-sdk)] Display: Ep 2 : PIO TFT LCD ILI9341 8-bit parallel C-code Driver

本文章介紹使用Raspberry Pi Pico PIO(Programmable IO)功能來寫作ILI3941 8-bit parallel模式的驅動程式。並且試著只改變StateMachine執行的Frequency,來比較LCD TLF ILI9341的顯示效能。


一、接腳:

  1. GPIO 11,10,9,8,7,6,5,4分別接DB7,DB6,DB5,DB4,DB3,DB2,DB1,DB0。並設定成StateMachine的out pins。
  2. GPIO 15,14,13,12分別接CS(CSX), RS(D/CX),WR(WRX),RD(RDX)。並設定成StateMachine的set pins。

二、ILI9341 Datasheet write sequence 與 PIO程式碼比對。


三、重要指令說明:

  1. 相對於PIO的ili9341_cmd:
    依序put 指定(cmd),多少參數(count),與資料(params)到TX_FIFO中,若指令為0x00(NOP)則只有輸入資料,count為0則指令不帶參數。

  2. 定址:2A, 2B, 2C
    column address 與page address分別需要4個參數。
    ili9341_draw_pixel 寫入一個RGB565格式的顏色點。
  3. 其他劃線、圓、方則由上面兩個函數衍生。

四、成果影片


五、程式碼

  •     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
  • 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 out_base_pin = 4;
uint in_base_pin = 4;
uint set_base_pin = 12;

static uint8_t p_count = 0;
static uint8_t d_count = 0;
static uint8_t data_recv = 0;
static uint8_t params[8];
static uint32_t data[8];

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 ili9431_pio_cmd_init(PIO pio, uint sm, uint 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, out_base + i);
    for (int i = 0; i < 4; i++)
        pio_gpio_init(pio, set_base + i);

    pio_sm_set_consecutive_pindirs(pio, sm, out_base, 8, true);
    pio_sm_set_consecutive_pindirs(pio, sm, set_base, 4, true);

    sm_config_set_out_pins(&c, out_base, 8);
    sm_config_set_set_pins(&c, set_base, 4);

    sm_config_set_out_shift(&c, true, false, 32);

    float div = clock_get_hz(clk_sys) / freq;
    sm_config_set_clkdiv(&c, div);

    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_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);

    ili9341_cmd(ILI9341_MEMORYWRITE, 0, NULL);
}

void ili9341_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
{
    if (x < 0 || x > SCREEN_WIDTH || y < 0 || y > SCREEN_HEIGHT)
        return;
    ili9341_set_address_window(x, y, x, y);
    ili9341_cmd(ILI9341_NOP, 2, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)color});
}
void ili9341_invert_display(bool invert)
{
    if (invert)
        ili9341_cmd(ILI9341_INVERTON, 0, NULL);
    else
        ili9341_cmd(ILI9341_INVERTOFF, 0, 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 > SCREEN_WIDTH)
        width = SCREEN_WIDTH - x;
    if (y + height > SCREEN_HEIGHT)
        height = SCREEN_HEIGHT - y;
    ili9341_set_address_window(x, y, x + width, y + height);
    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 & 0x00ff)});
}

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_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);

    for (uint16_t pixels = 0; pixels < total_pixels; pixels++)
    {
        ili9341_cmd(ILI9341_NOP, 1, (uint8_t[1]){(uint8_t)(bitmap->data[2 * pixels])});
        ili9341_cmd(ILI9341_NOP, 1, (uint8_t[1]){(uint8_t)(bitmap->data[2 * pixels + 1])});
    }
}
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});
    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]){0x40});             // MY,MX,MV,ML,BRG,MH,0,0
    ili9341_cmd(ILI9341_FRAMECONTROL, 2, (uint8_t[2]){0x00, 0x1B}); // Default 70Hz
    ili9341_cmd(ILI9341_DISPLAYFUNC, 4, (uint8_t[4]){0x0A, 0xA2, 0x27, 0x04});
    ili9341_cmd(ILI9341_GAMMASET, 1, (uint8_t[1]){0x01});

    ili9341_cmd(ILI9341_PGAMCOR, 15, (uint8_t[15]){0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00});
    ili9341_cmd(ILI9341_NGAMCOR, 15, (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, out_base_pin, set_base_pin, 70 * 1000000); // 70*
    ili9431_init_config();
}
  • ili9341.h

#ifndef  _ILI9431_H_
#define _ILI9431_H_

#define SCREEN_WIDTH   240
#define SCREEN_HEIGHT   320

#include "pico/stdlib.h"
#include "tft_string_lib/ili_string.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);
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 pio_sm_reinit(uint32_t freq);
#endif
  • main.c
#include <stdio.h>
#include "stdlib.h"
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "hardware/pio.h"
#include "ili9341.h"
#include "pico_img.h"
#include "string.h"

void demo() {
    ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, 0x0000);
    for (int j = 0; j < 320; j+=5) ili9341_draw_line(0, 0, 239, j, 0xffff);
    for (int i = 0; i < 240; i+=5) ili9341_draw_line(0, 0, i, 319, 0xffff);
    for (int i = 10;i < 150; i+=10) {
        ili9341_fill_rect(i, i, 100, 100, ili9341_color_565RGB(0x60+i, 0x70+i*2, 0x30+i));
    }
    ili9341_draw_bitmap(50,0, &pico_img);
    ili9341_invert_display(true);
    ili9341_invert_display(false);
}
int main()
{
    stdio_init_all();
    
    ili9341_init();

    ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, 0x0000);
    for (int j = 0; j < 320; j+=5) ili9341_draw_line(0, 0, 239, j, 0xffff);
    sleep_ms(2000);
    for (int i = 0; i < 240; i+=5) ili9341_draw_line(0, 0, i, 319, 0xffff);
    sleep_ms(2000);
    for (int i = 10;i < 150; i+=10) {
        ili9341_fill_rect(i, i, 100, 100, ili9341_color_565RGB(0x60+i, 0x70+i*2, 0x30+i));
    }
    sleep_ms(2000);
    ili9341_draw_bitmap(50,0, &pico_img);
    sleep_ms(2000);
    ili9341_invert_display(true);
    sleep_ms(2000);
    ili9341_invert_display(false);
    sleep_ms(2000);

    char buf[60];
    uint32_t freqs[4] = {1000000,2500000,  50000000, 70000000};
    for (int i = 0 ; i < 4; i++) {
        float div =125000000/freqs[i]; 
        //
        pio_sm_set_clkdiv(pio1, 0, div);
        pio_sm_clkdiv_restart(pio1, 0);
        
        ili9341_fill_rect(0,0, SCREEN_WIDTH, SCREEN_HEIGHT, 0x0000);
        absolute_time_t t1 = get_absolute_time();
        demo();
        absolute_time_t t2 = get_absolute_time();
        printf("Freq=%d\n",freqs[i]);
        printf("%d ms", absolute_time_diff_us(t1,t2)/1000);
        sleep_ms(5000);
    }

}

沒有留言:

張貼留言