#include #include #include #include "pico/stdlib.h" #include "hardware/spi.h" #include "hardware/dma.h" #include "hardware/timer.h" #include "pfnfont.h" #define SPI_PORT spi0 #define PIN_MISO 16 #define PIN_CS 17 #define PIN_SCK 18 #define PIN_MOSI 19 #define TFT_DC 20 // LOW:Command | HIGH:Data #define TFT_RES 21 #define TFT_COMMAND false #define TFT_DATA true #define TFT_WIDTH 240 // LCD縦ピクセル数 #define TFT_HEIGHT 240 // LCD横ピクセル数 #define TFT_BYTES_PER_PIXEL 2 #define TFT_COLOR_BLACK 0x0000 #define TFT_COLOR_BLUE 0x001F #define TFT_COLOR_RED 0xF800 #define TFT_COLOR_GREEN 0x07E0 #define TFT_COLOR_CYAN 0x07FF #define TFT_COLOR_MAGENTA 0xF81F #define TFT_COLOR_YELLOW 0xFFE0 #define TFT_COLOR_WHITE 0xFFFF #define SSTCMD_SWRESET 0x01 #define SSTCMD_SLPOUT 0x11 #define SSTCMD_NORON 0x13 #define SSTCMD_INVOFF 0x20 #define SSTCMD_INVON 0x21 #define SSTCMD_DISPOFF 0x28 #define SSTCMD_DISPON 0x29 #define SSTCMD_MADCTL 0x36 #define SSTCMD_COLMOD 0x3A #define SSTCMD_INVCTR 0xB4 #define SSTCMD_CASET 0x2A #define SSTCMD_RASET 0x2B #define SSTCMD_RAMWR 0x2C typedef struct { uint8_t start_x; uint8_t start_y; uint8_t end_x; uint8_t end_y; } sst7735_rect; inline int sst7735_rect_len(sst7735_rect *r) { return (r->end_x-r->start_x) * (r->end_y - r->start_y); } static inline void sst7735_send_cmd(uint8_t cmd) { gpio_put(TFT_DC, TFT_COMMAND); spi_set_format (SPI_PORT, 8, SPI_CPOL_1 , SPI_CPHA_1,SPI_MSB_FIRST ); spi_write_blocking(SPI_PORT, &cmd, sizeof(uint8_t)); gpio_put(TFT_DC, TFT_DATA); } static inline void sst7735_send_data8(uint8_t *data, size_t len) { gpio_put(TFT_DC, TFT_DATA); spi_set_format (SPI_PORT, 8, SPI_CPOL_1 , SPI_CPHA_1,SPI_MSB_FIRST ); spi_write_blocking(SPI_PORT, data, len); // gpio_put(TFT_DC, TFT_DATA); } static inline void sst7735_send_data16(uint16_t *data, size_t len) { gpio_put(TFT_DC, TFT_DATA); spi_set_format (SPI_PORT, 16, SPI_CPOL_1 , SPI_CPHA_1,SPI_MSB_FIRST ); spi_write16_blocking(SPI_PORT, data, len); // gpio_put(TFT_DC, TFT_DATA); } static inline void sst7735_reset() { gpio_put(TFT_RES, true); sleep_ms(500); // 500ms gpio_put(TFT_RES, false); sleep_ms(100); // RESET pulse 100ms gpio_put(TFT_RES, true); sleep_ms(500); // 500ms sst7735_send_cmd(SSTCMD_SWRESET); // Software Reset sleep_ms(100); // delay 100ms } static inline void sst7735_init(void) { uint8_t cmd_param[4]; sst7735_send_cmd(SSTCMD_DISPOFF); // Display Off sleep_ms(500); sst7735_send_cmd(SSTCMD_SLPOUT); // Sleep Out sleep_ms(500); sst7735_send_cmd(SSTCMD_DISPON); // Display on sleep_ms(100); sst7735_send_cmd(SSTCMD_COLMOD); // Color mode cmd_param[0] = 0x55; // 16bit mode sst7735_send_data8(cmd_param, 1); sleep_ms(100); sst7735_send_cmd(SSTCMD_MADCTL); cmd_param[0] = 0x00; sst7735_send_data8(cmd_param, 1); // RGB 16bit mode sleep_ms(100); // sst7735_send_cmd(SSTCMD_NORON); sst7735_send_cmd(SSTCMD_INVON); } void sst7735_draw_image(uint16_t *image, sst7735_rect *rect) { uint8_t cmd_param[4] = {0,0,0,0}; cmd_param[1] = rect->start_x; cmd_param[3] = rect->end_x; sst7735_send_cmd(SSTCMD_CASET); // Column Address Set sst7735_send_data8(cmd_param, 4); cmd_param[1] = rect->start_y; cmd_param[3] = rect->end_y; sst7735_send_cmd(SSTCMD_RASET); // Row Address Set sst7735_send_data8(cmd_param, 4); sst7735_send_cmd(SSTCMD_RAMWR); // RAM RW mode sleep_us(100); sst7735_send_data16((uint16_t *)image, sst7735_rect_len(rect)); } void sst7735_clear(void) { uint16_t *temp_buf; sst7735_rect r = {start_x:0, start_y:0, end_x:TFT_WIDTH, end_y:TFT_HEIGHT}; temp_buf = (uint16_t *)calloc(TFT_HEIGHT * TFT_WIDTH, sizeof(uint16_t)); sst7735_draw_image(temp_buf, &r); free(temp_buf); } int dma_ch = -1; // DMAチャンネル dma_channel_config dma_conf; // DMA設定 volatile bool dma_in_prgress = false; // DMA転送中フラグ // ワンショットタイマー遅延呼び出し用 int64_t schedule_deferred_call(alarm_id_t id, void *user_data) { // DMA転送中フラグを落とす dma_in_prgress = false; return 0; } // DMA割り込み void dma_irq_handler() { dma_channel_acknowledge_irq0(dma_ch); // 遅延を入れないと死ぬ // ワンショットタイマーで20マイクロ秒後にフラグを落とす add_alarm_in_us(20, schedule_deferred_call, NULL, true); } uint8_t *string_line_buffer[2]; // 1行バッファ int buffer_selector = 0; // バッファセレクタ uint16_t back_color = TFT_COLOR_BLACK; // 背景色 uint16_t font_color = TFT_COLOR_WHITE; // 文字色 PFNFont *pfn; // PFNフォントオブジェクト // 1行バッファ内にフォントグリフを描くコールバック関数 void draw_font_in_buffer(font_gryph_t *gryph, int x, int y) { // uint16_tポインタとしてバッファを扱う uint16_t *line_buf = (uint16_t*)string_line_buffer[buffer_selector]; for(int r = 0; r < gryph->height; r++) { for(int c = 0; c < gryph->width; c++) { // 画面外描画をクリップ if ((x + c) >= TFT_WIDTH) continue; int bit_index = r * gryph->width + c; int byte_index = bit_index / 8; int bit_offset = bit_index % 8; if ((gryph->bitmap[byte_index] >> (7 - bit_offset)) & 1) { line_buf[r * TFT_WIDTH + (x + c)] = font_color; } else { // 背景塗りつぶし済み } } } } // 文字列描画用DMA設定関数 void configure_dma_channel(void) { dma_ch = dma_claim_unused_channel(true); dma_conf = dma_channel_get_default_config(dma_ch); channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_16); channel_config_set_read_increment(&dma_conf, true); channel_config_set_write_increment(&dma_conf, false); uint dreq = spi_get_dreq(SPI_PORT, true); channel_config_set_dreq(&dma_conf, dreq); // DMA割り込み設定 dma_channel_set_irq0_enabled(dma_ch, true); irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler); irq_set_enabled(DMA_IRQ_0, true); dma_channel_configure( dma_ch, &dma_conf, &spi_get_hw(SPI_PORT)->dr, // DATAレジスタ(SSPDR) string_line_buffer[buffer_selector],// Dummy(後で設定) 2, // Dummy false ); } // 文字をLCDにレンダリングする void render_string(const char *str, int x, int y) { int render_width = 0; // DMAが設定されていなければ何もしない if(dma_ch < 0) return; // 1行バッファのサイズ size_t line_buffer_size = TFT_WIDTH * pfn->get_max_font_height() * TFT_BYTES_PER_PIXEL; // バッファを背景色で塗りつぶし uint16_t* current_buffer = (uint16_t*)string_line_buffer[buffer_selector]; for (int i = 0; i < line_buffer_size / TFT_BYTES_PER_PIXEL; ++i) { current_buffer[i] = back_color; } // 文字をバッファに描画 render_width = pfn->draw_string(str, x, 0); if(render_width == 0 ) return; // 前のDMAを待つ while(dma_in_prgress); // SST77xxコマンド発行 uint8_t cmd_param[4] = {0,0,0,0}; cmd_param[1] = 0; cmd_param[3] = TFT_WIDTH; sst7735_send_cmd(SSTCMD_CASET); // Column Address Set sst7735_send_data8(cmd_param, 4); cmd_param[1] = y; cmd_param[3] = y + pfn->get_max_font_height(); sst7735_send_cmd(SSTCMD_RASET); // Row Address Set sst7735_send_data8(cmd_param, 4); sst7735_send_cmd(SSTCMD_RAMWR); // RAM RW mode sleep_us(50); gpio_put(TFT_DC, TFT_DATA); spi_set_format (SPI_PORT, 16, SPI_CPOL_1 , SPI_CPHA_1,SPI_MSB_FIRST ); dma_channel_set_read_addr(dma_ch, string_line_buffer[buffer_selector], false); dma_channel_set_trans_count(dma_ch, TFT_WIDTH * pfn->get_max_font_height(), true); dma_in_prgress = true; // バッファ反転 buffer_selector = !buffer_selector; } // フォントデータ __asm(\ ".section \".rodata\" \n" ".balign 4\n" ".global _pfnfont_data\n" ".global _pfnfont_data_len\n" "_pfnfont_data:\n" ".incbin \"shnmk16u.pfn\"\n" ".set _pfnfont_data_len, . - _pfnfont_data\n" ".section \".text\"\n" ); extern const uint8_t _pfnfont_data[]; extern uint32_t _pfnfont_data_len; int main() { stdio_init_all(); // SPI spi_init(SPI_PORT, 20*1000*1000); gpio_set_function(PIN_MISO, GPIO_FUNC_SPI); gpio_set_function(PIN_CS, GPIO_FUNC_SPI); gpio_set_function(PIN_SCK, GPIO_FUNC_SPI); gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI); spi_set_format (SPI_PORT, 8, SPI_CPOL_1 , SPI_CPHA_1,SPI_MSB_FIRST ); // TFT Pin Init gpio_init(TFT_DC); gpio_set_dir(TFT_DC, GPIO_OUT); gpio_init(TFT_RES); gpio_set_dir(TFT_RES, GPIO_OUT); // TFT INIT sst7735_reset(); sst7735_init(); sst7735_clear(); // 描画コールバック関数を渡してPFNFontオブジェクトを生成 pfn = new PFNFont((uint8_t *)_pfnfont_data, (size_t)&_pfnfont_data_len, draw_font_in_buffer); // フォントが正しく読み込めたかチェック if (pfn->isValid()) { printf("Font loaded successfully.\n"); size_t line_buffer_size = TFT_WIDTH * pfn->get_max_font_height() * TFT_BYTES_PER_PIXEL; string_line_buffer[0] = (uint8_t *)malloc(line_buffer_size); string_line_buffer[1] = (uint8_t *)malloc(line_buffer_size); // render_string()を使用する前にかならずconfigure_dma_channel()を呼ぶこと configure_dma_channel(); int x = 0, y = 0; while (true) { back_color = TFT_COLOR_BLACK; font_color = TFT_COLOR_WHITE; render_string("Picoで日本語表示!", x, y); font_color = TFT_COLOR_BLUE; y += pfn->get_max_font_height(); render_string("Picoで日本語表示!", x, y); font_color = TFT_COLOR_RED; y += pfn->get_max_font_height(); render_string("Picoで日本語表示!", x, y); font_color = TFT_COLOR_YELLOW; y += pfn->get_max_font_height(); render_string("Picoで日本語表示!", x, y); sleep_ms(500); sst7735_clear(); y = 0; } } else { printf("Font loading failed.\n"); delete pfn; while(true) tight_loop_contents(); } return 0; }