4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SpresenseAdvent Calendar 2023

Day 14

SPRESENSEで7インチモニターを制御しようとした話(未完)

Last updated at Posted at 2023-12-13

7インチディスプレイの概要

ひょんなことから、7インチのディスプレイを入手しました。ディスプレイのコントローラはFTDI製でSPIで制御できるものです。

DSC_1806.JPG
https://ja.newhavendisplay.com/7-0-inch-premium-eve2-resistive-tft-module/

もともとはArduino系のボードで制御するものなのですが、SPRESENSEでも制御できるんじゃね?という軽い考えで試してみました。

ディスプレイのIO

データシートを見ると、コネクタは20ピンですが基本的にはSPIのみ。タッチもついているようなのですが、I2Cのラインはなさそうです。SPIは3.3V系。バックライトは3.3Vもしくは5.0V電源です。

image.png

ディスプレイに必要な電力

バックライトに必要な電力を見ると、3.3Vでは760mA(2.5W)、5.0Vでは440mA(2.2W)です。いずれにしてもUSBからの給電では厳しそうです。

image.png

SPRESENSEとの結線

SPRESENSEでは20ピンのフラットケーブルをDIPに変換してくれる変換基板があったのでそれを使ってみました。

Moechando FPC FFC変換基板 1.0mmピッチ 20ピン 適合FPC FFCフラットケーブル 
https://item.rakuten.co.jp/moechando/9z-e042-wsdi/?s-id=ph_pc_itemimage
image.png

ただ、あとで気づいたのですが裏面にはIPのピンヘッダがあるのでそちらを活用したほうが簡単ですね。かさばるのが嫌な場合は、上記FFC変換基板を活用するとよいと思います。

DSC_1890.JPG

結線は次のようにしました。バックライトの電源は安定化電源から供給します。GPIO0とGPIO1は念のため接続していますが、不要かもしれません。

image.png

実際に結線した様子はこちらになります。

DSC_1805.JPG

SPRESENSE の Arduinoコード

プログラマガイドを頼りに次のようなスケッチを書いてみました。

#include <SPI.h>
#include "FT81_GPU.h"

#define CS_PIN  (4)
#define PDN_PIN (8)

uint16_t    FT_DispWidth = 800;
uint16_t 	FT_DispHeight = 480;
uint16_t 	FT_DispHCycle =  928;
uint16_t 	FT_DispHOffset = 88;
uint16_t 	FT_DispHSync0 = 0;
uint16_t 	FT_DispHSync1 = 48;
uint16_t 	FT_DispVCycle = 525;
uint16_t 	FT_DispVOffset = 32;
uint16_t 	FT_DispVSync0 = 0;
uint16_t 	FT_DispVSync1 = 3;
uint16_t 	FT_DispPCLK = 2;
uint16_t 	FT_DispSwizzle = 0;
uint16_t 	FT_DispPCLKPol = 1;
uint16_t 	FT_DispCSpread = 0;
uint16_t 	FT_DispDither = 1;


uint8_t rd8(uint32_t addr) {
  uint8_t value;
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(addr >> 16);
  SPI.transfer(highByte(addr));
  SPI.transfer(lowByte(addr));
  SPI.transfer(0); //Dummy Read Byte
  value = SPI.transfer(0);
  digitalWrite(CS_PIN, HIGH);
  return value;
}

uint16_t rd16(uint32_t addr) {
  uint16_t value;
  uint16_t byte0;
  uint16_t byte1;
  
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(addr >> 16);
  SPI.transfer(highByte(addr));
  SPI.transfer(lowByte(addr));
  SPI.transfer(0); //Dummy Read Byte
  byte0 = SPI.transfer(0);
  byte1 = SPI.transfer(0);
  value = (byte1 << 8) | byte0;
  digitalWrite(CS_PIN, HIGH);
  return value;
}

uint32_t rd32(uint32_t addr) {
  uint32_t value;
  uint32_t byte0;
  uint32_t byte1;
  uint32_t byte2;
  uint32_t byte3;

  digitalWrite(CS_PIN, LOW);
  SPI.transfer(addr >> 16);
  SPI.transfer(highByte(addr));
  SPI.transfer(lowByte(addr));
  SPI.transfer(0); //Dummy Read Byte
  byte0 = SPI.transfer(0);
  byte1 = SPI.transfer(0);
  byte2 = SPI.transfer(0);
  byte3 = SPI.transfer(0);
  value = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0;
  digitalWrite(CS_PIN, HIGH);
  return value;
}

void wr8(uint32_t addr, uint8_t value) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(0x80 | (addr >> 16));
  SPI.transfer(highByte(addr));
  SPI.transfer(lowByte(addr));
  SPI.transfer(value);
  digitalWrite(CS_PIN, HIGH);
}

void wr16(uint32_t addr, uint16_t value) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(0x80 | (addr >> 16));
  SPI.transfer(highByte(addr));
  SPI.transfer(lowByte(addr));
  SPI.transfer(value & 0xFF);//LSB first
  SPI.transfer((value >> 8) & 0xFF);
  digitalWrite(CS_PIN, HIGH);
}

void wr32(uint32_t addr, uint32_t value) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(0x80 | (addr >> 16));
  SPI.transfer(highByte(addr));
  SPI.transfer(lowByte(addr));
  SPI.transfer(value & 0xFF);//LSB first
  SPI.transfer((value >>  8) & 0xFF);  // 2Byte
  SPI.transfer((value >> 16) & 0xFF);  // 3Byte
  SPI.transfer((value >> 24) & 0xFF);  // 4Byte
  digitalWrite(CS_PIN, HIGH);
}

void host_command(uint8_t cmd) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(cmd);
  SPI.transfer(0);
  SPI.transfer(0);
  digitalWrite(CS_PIN, HIGH);
}

void setup() {
  Serial.begin(115200);

  pinMode(PDN_PIN, OUTPUT);
  digitalWrite(PDN_PIN, HIGH);
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);  
  delay(100);

  /* MCU_SPI_CLK_Freq(<11MHz); //use the MCU SPI clock less than 11MHz */
  SPI.begin();
  SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
  Serial.println("SPI open");

  /* Initialization Sequence from Power Down using PD_N pin */
  digitalWrite(PDN_PIN, LOW);
  delay(100);
  digitalWrite(PDN_PIN, HIGH);
  delay(100);
  Serial.println("DISPLAY Power On");

  host_command(FT_GPU_ACTIVE_M);

  uint8_t chipid; 
  do {   
    chipid = rd8(REG_ID);
    delay(100);
  } while (chipid != 0x7C);
  Serial.println("chipid = " + String(chipid, HEX));

  Serial.println("DISPLAY Settings");
  wr16(REG_HCYCLE,  FT_DispHCycle);
  wr16(REG_HOFFSET, FT_DispHOffset);
  wr16(REG_HSYNC0,  FT_DispHSync0);
  wr16(REG_HSYNC1,  FT_DispHSync1);
  wr16(REG_VCYCLE,  FT_DispVCycle);
  wr16(REG_VOFFSET, FT_DispVOffset);
  wr16(REG_VSYNC0,  FT_DispVSync0);
  wr16(REG_VSYNC1,  FT_DispVSync1);
  wr8(REG_SWIZZLE,  FT_DispSwizzle);
  wr8(REG_PCLK_POL, FT_DispPCLKPol);
  wr16(REG_HSIZE,   FT_DispWidth);
  wr16(REG_VSIZE,   FT_DispHeight);
  wr16(REG_CSPREAD, FT_DispCSpread);
  wr16(REG_DITHER,  FT_DispDither);

  /* write first display list */ 
  /*
  wr32(RAM_DL+0, CLEAR_COLOR_RGB(0,0,0));
  wr32(RAM_DL+4, CLEAR(1,1,1)); 
  wr32(RAM_DL+8, DISPLAY());
  */

  wr8(REG_DLSWAP, DLSWAP_FRAME); //display list swap
  wr8(REG_GPIO_DIR,0x80 | rd8(REG_GPIO_DIR)); 
  wr8(REG_GPIO,0x080 | rd8(REG_GPIO)); //enable display bit
  wr8(REG_PCLK,5); //after this display is visible on the LCD
  delay(100);
  
  /* MCU_SPI_CLK_Freq(<30Mhz);//use the MCU SPI clock upto 30MHz */
  SPI.endTransaction();
  SPI.end();
  Serial.println("Change SPISettings");
  SPI.begin();
  SPI.beginTransaction(SPISettings(28000000, MSBFIRST, SPI_MODE0));

  do {   
    chipid = rd8(REG_ID);
    delay(100);
  } while (chipid != 0x7C);
  Serial.println("chipid = " + String(chipid, HEX));


  const static uint8_t CMD_SIZE = 4;
  const static uint16_t V_START = 110;
  const static uint16_t V_HIGHT = 60;
  int n = 0;
  int c = 0;
  wr32(RAM_DL + CMD_SIZE*(c++), CLEAR(1, 1, 1)); // clear screen 
  wr32(RAM_DL + CMD_SIZE*(c++), BEGIN(BITMAPS)); // start drawing bitmaps 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(220, V_START+V_HIGHT*n, 31, 'S')); // ascii S in font 31 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(244, V_START+V_HIGHT*n, 31, 'P')); // ascii P 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(268, V_START+V_HIGHT*n, 31, 'R')); // ascii R 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(292, V_START+V_HIGHT*n, 31, 'E')); // ascii E 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(316, V_START+V_HIGHT*n, 31, 'S')); // ascii S 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(340, V_START+V_HIGHT*n, 31, 'E')); // ascii E 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(364, V_START+V_HIGHT*n, 31, 'N')); // ascii N 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(388, V_START+V_HIGHT*n, 31, 'S')); // ascii S   
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(412, V_START+V_HIGHT*n, 31, 'E')); // ascii E 
  wr32(RAM_DL + CMD_SIZE*(c++), END()); 
  wr32(RAM_DL + CMD_SIZE*(c++), COLOR_RGB(22, 22, 412)); // change colour to dark blue 
  wr32(RAM_DL + CMD_SIZE*(c++), POINT_SIZE(320)); // set point size to 20 pixels in radius 
  wr32(RAM_DL + CMD_SIZE*(c++), BEGIN(FTPOINTS)); // start drawing points 
  wr32(RAM_DL + CMD_SIZE*(c++), VERTEX2II(192, V_START+23+V_HIGHT*n, 0, 0)); // red point 
  wr32(RAM_DL + CMD_SIZE*(c++), END()); 
  wr32(RAM_DL + CMD_SIZE*(c++), DISPLAY()); // display the image
  // wait for FIFO empty
  while(rd16(REG_CMD_READ) != rd16(REG_CMD_WRITE));

  Serial.println("Display Strings done");
  delay(1000);

  SPI.endTransaction();
  SPI.end();
  Serial.end();
}

void loop() {
  /* put your main code here, to run repeatedly: */
}

動作結果

プログラムを書き込んで見たところ、何度かリセットをして、SPIのモードをいじくっているうちにようやく表示されました。

DSC_1807.JPG

しかし、かなり動作が不安定です。これはソフトの問題というよりはハードの問題のようです。フレキシブルケーブルが長すぎるのか、私の半田がいまいちなのか…。近いうちにDIPのコネクタで再チャレンジしてみようと思います。

使ってみた感想

このディスプレイは、GPUというだけあって、フォントを内蔵していますし様々な描画命令も備えているようで、SPI経由で命令を伝えるだけでグラフィック処理ができます。タッチもサポートしているので、GUIを作るには便利そうですね。ライブラリを準備すれば、かなり高度なことができそうです。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?