|
@@ -0,0 +1,341 @@
|
|
|
+#include <stdio.h>
|
|
|
+#include <malloc.h>
|
|
|
+#include <memory.h>
|
|
|
+#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;
|
|
|
+}
|