/** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "pico/stdlib.h" #include "hardware/pio.h" #include "hardware/gpio.h" #include "hardware/interp.h" #include "st7789_lcd.pio.h" #include "raspberry_256x256_rgb565.h" #define SCREEN_WIDTH 240 #define SCREEN_HEIGHT 240 #define IMAGE_SIZE 256 #define LOG_IMAGE_SIZE 8 #define PIN_DIN 0 #define PIN_CLK 1 #define PIN_CS 2 #define PIN_DC 3 #define PIN_RESET 4 #define PIN_BL 5 #define SERIAL_CLK_DIV 1.f // Format: cmd length (including cmd byte), post delay in units of 5 ms, then cmd payload // Note the delays have been shortened a little static const uint8_t st7789_init_seq[] = { 1, 20, 0x01, // Software reset 1, 10, 0x11, // Exit sleep mode 2, 2, 0x3a, 0x55, // Set colour mode to 16 bit 2, 0, 0x36, 0x00, // Set MADCTL: row then column, refresh is bottom to top ???? 5, 0, 0x2a, 0x00, 0x00, 0x00, 0xf0, // CASET: column addresses from 0 to 240 (f0) 5, 0, 0x2b, 0x00, 0x00, 0x00, 0xf0, // RASET: row addresses from 0 to 240 (f0) 1, 2, 0x21, // Inversion on, then 10 ms delay (supposedly a hack?) 1, 2, 0x13, // Normal display on, then 10 ms delay 1, 2, 0x29, // Main screen turn on, then wait 500 ms 0 // Terminate list }; static inline void lcd_set_dc_cs(bool dc, bool cs) { sleep_us(1); gpio_put_masked((1u << PIN_DC) | (1u << PIN_CS), !!dc << PIN_DC | !!cs << PIN_CS); sleep_us(1); } static inline void lcd_write_cmd(PIO pio, uint sm, const uint8_t *cmd, size_t count) { st7789_lcd_wait_idle(pio, sm); lcd_set_dc_cs(0, 0); st7789_lcd_put(pio, sm, *cmd++); if (count >= 2) { st7789_lcd_wait_idle(pio, sm); lcd_set_dc_cs(1, 0); for (size_t i = 0; i < count - 1; ++i) st7789_lcd_put(pio, sm, *cmd++); } st7789_lcd_wait_idle(pio, sm); lcd_set_dc_cs(1, 1); } static inline void lcd_init(PIO pio, uint sm, const uint8_t *init_seq) { const uint8_t *cmd = init_seq; while (*cmd) { lcd_write_cmd(pio, sm, cmd + 2, *cmd); sleep_ms(*(cmd + 1) * 5); cmd += *cmd + 2; } } static inline void st7789_start_pixels(PIO pio, uint sm) { uint8_t cmd = 0x2c; // RAMWR lcd_write_cmd(pio, sm, &cmd, 1); lcd_set_dc_cs(1, 0); } int main() { stdio_init_all(); PIO pio = pio0; uint sm = 0; uint offset = pio_add_program(pio, &st7789_lcd_program); st7789_lcd_program_init(pio, sm, offset, PIN_DIN, PIN_CLK, SERIAL_CLK_DIV); gpio_init(PIN_CS); gpio_init(PIN_DC); gpio_init(PIN_RESET); gpio_init(PIN_BL); gpio_set_dir(PIN_CS, GPIO_OUT); gpio_set_dir(PIN_DC, GPIO_OUT); gpio_set_dir(PIN_RESET, GPIO_OUT); gpio_set_dir(PIN_BL, GPIO_OUT); gpio_put(PIN_CS, 1); gpio_put(PIN_RESET, 1); lcd_init(pio, sm, st7789_init_seq); gpio_put(PIN_BL, 1); // Other SDKs: static image on screen, lame, boring // Raspberry Pi Pico SDK: spinning image on screen, bold, exciting // Lane 0 will be u coords (bits 8:1 of addr offset), lane 1 will be v // coords (bits 16:9 of addr offset), and we'll represent coords with // 16.16 fixed point. ACCUM0,1 will contain current coord, BASE0/1 will // contain increment vector, and BASE2 will contain image base pointer #define UNIT_LSB 16 interp_config lane0_cfg = interp_default_config(); interp_config_set_shift(&lane0_cfg, UNIT_LSB - 1); // -1 because 2 bytes per pixel interp_config_set_mask(&lane0_cfg, 1, 1 + (LOG_IMAGE_SIZE - 1)); interp_config_set_add_raw(&lane0_cfg, true); // Add full accumulator to base with each POP interp_config lane1_cfg = interp_default_config(); interp_config_set_shift(&lane1_cfg, UNIT_LSB - (1 + LOG_IMAGE_SIZE)); interp_config_set_mask(&lane1_cfg, 1 + LOG_IMAGE_SIZE, 1 + (2 * LOG_IMAGE_SIZE - 1)); interp_config_set_add_raw(&lane1_cfg, true); interp_set_config(interp0, 0, &lane0_cfg); interp_set_config(interp0, 1, &lane1_cfg); interp0->base[2] = (uint32_t) raspberry_256x256; float theta = 0.f; float theta_max = 2.f * (float) M_PI; while (1) { theta += 0.02f; if (theta > theta_max) theta -= theta_max; int32_t rotate[4] = { cosf(theta) * (1 << UNIT_LSB), -sinf(theta) * (1 << UNIT_LSB), sinf(theta) * (1 << UNIT_LSB), cosf(theta) * (1 << UNIT_LSB) }; interp0->base[0] = rotate[0]; interp0->base[1] = rotate[2]; st7789_start_pixels(pio, sm); for (int y = 0; y < SCREEN_HEIGHT; ++y) { interp0->accum[0] = rotate[1] * y; interp0->accum[1] = rotate[3] * y; for (int x = 0; x < SCREEN_WIDTH; ++x) { uint16_t colour = *(uint16_t *) (interp0->pop[2]); st7789_lcd_put(pio, sm, colour >> 8); st7789_lcd_put(pio, sm, colour & 0xff); } } } }