1 プラットフォームの状態
ハードウェア: nrf52840
ソフトウェア: sdk17.0
2 QSPI の概要
QSPI ペリフェラルは、SPI を使用した外部フラッシュ デバイスとの通信をサポートします
QSPI ペリフェラルの主な機能を以下に示します。
• シングル/デュアル/クワッド SPI 入出力
• 2 ~ 32 MHz の設定可能なクロック周波数
• 外部フラッシュ メモリへのシングル ワード読み取り/書き込みアクセス
• ブロック読み取りおよび書き込み用 EasyDMA による転送
• EasyDMA 読み取り速度最大 16 MB/秒
• 外部フラッシュ メモリからプログラムを直接実行する Execution in Place (XIP)
Nordic nRF52/nRF53 シリーズでは、QSPI は 2 つの異なるモードをサポートできます。
カスタム命令を送信するためのフラッシュ/XIP 操作 (フラッシュ プロトコルに従う必要があります)
(データを送信するために QSPI の 1 線を使用する場合に限定されます)。
命令セットでは、外部フラッシュ デバイスを使用したオペコードをサポートします。
1.1 LCD 用 QSPI インターフェイス
基本的に、QSPI インターフェイスには 6 つの GPIO ピンがあります。
使用した画面: hannstar 360×360、GalaxyCore コントローラー解像度 (テスト用)。
2コード設定
2.1 初期化
QSPI モジュールを初期化して、
6 つの GPIO ピンすべてを高駆動出力として設定します。
#define LCD_QSPI_RESET_PIN NRF_GPIO_PIN_MAP(0,27)
#define LCD_QSPI_CSN_PIN NRF_GPIO_PIN_MAP(0,26)
#define LCD_QSPI_SCK_PIN NRF_GPIO_PIN_MAP(0,02)
#define LCD_QSPI_IO0_PIN NRF_GPIO_PIN_MAP(1,15)
#define LCD_QSPI_IO1_PIN NRF_GPIO_PIN_MAP(1,14)
#define LCD_QSPI_IO2_PIN NRF_GPIO_PIN_MAP(1,13)
#define LCD_QSPI_IO3_PIN NRF_GPIO_PIN_MAP(1,12)
static void config_qspi_pin_high_drive(void)
{
nrf_gpio_cfg(
LCD_QSPI_SCK_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_SENSE_HIGH);
nrf_gpio_cfg(
LCD_QSPI_CSN_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(
LCD_QSPI_IO0_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(
LCD_QSPI_IO1_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(
LCD_QSPI_IO2_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(
LCD_QSPI_IO3_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
}
LCD デモ 320×240 @ nRF52840 の後で、QSPI インターフェイスを使用して、解像度 360 x 360 @ nRF52840 などのより大きな LCD ディスプレイを駆動する方法について説明したいと思います。
QSPI クアッド シリアル ペリフェラル インターフェイス
QSPI ペリフェラルは、SPI を使用した外部フラッシュ メモリ デバイスとの通信をサポートします。
QSPI ペリフェラルの主な機能を以下に示します。
• シングル/デュアル/クワッド SPI 入出力
• 2 ~ 32 MHz の設定可能なクロック周波数
• 外部フラッシュ メモリへのシングル ワード読み取り/書き込みアクセス
• ブロック読み取りおよび書き込み用 EasyDMA による転送
• EasyDMA 読み取り速度は最大 16 MB/秒
• Execution in Place (XIP) により外部フラッシュ メモリから直接プログラムを実行
Nordic nRF52/nRF53 シリーズでは、QSPI は 2 つの異なるモードをサポートできます。
カスタム命令を送信するためのフラッシュ/XIP 操作 (フラッシュ プロトコルに従う必要があります)
(データを送信するために QSPI の 1 線を使用する場合に限定されます)。
命令セットでは、外部フラッシュ デバイスを使用したオペコードをサポートします。
LCD 用 QSPI インターフェイス
基本的に、QSPI インターフェイスには 6 つの GPIO ピンがあります。
このブログでは、ディスプレイ メーカーの HannStar 360×360 および GalaxyCore コントローラーの解像度を使用してテストします。
QSPI モジュールを初期化して、
6 つの GPIO ピンすべてを高駆動出力として設定します。
#define LCD_QSPI_RESET_PIN NRF_GPIO_PIN_MAP(0,27)
#define LCD_QSPI_CSN_PIN NRF_GPIO_PIN_MAP(0,26)
#define LCD_QSPI_SCK_PIN NRF_GPIO_PIN_MAP(0,02)
#define LCD_QSPI_IO0_PIN NRF_GPIO_PIN_MAP(1,15)
#define LCD_QSPI_IO1_PIN NRF_GPIO_PIN_MAP(1,14)
#define LCD_QSPI_IO2_PIN NRF_GPIO_PIN_MAP(1,13)
#define LCD_QSPI_IO3_PIN NRF_GPIO_PIN_MAP(1,12)
static void config_qspi_pin_high_drive(void)
{
nrf_gpio_cfg(
LCD_QSPI_SCK_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_CONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_SENSE_HIGH);
nrf_gpio_cfg(
LCD_QSPI_CSN_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(
LCD_QSPI_IO0_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(
LCD_QSPI_IO1_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(
LCD_QSPI_IO2_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg(
LCD_QSPI_IO3_PIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_H0H1,
NRF_GPIO_PIN_NOSENSE);
}
QSPI モジュールを初期化し、24 ビット アドレス/32MHz クロック用に構成します。
void qspi_lcd_GC9c01_init(void)
{
if (!m_is_qspi_init)
{
uint32_t err_code;
nrf_drv_qspi_config_t qspi_lcd_config = NRF_DRV_QSPI_DEFAULT_CONFIG;
//config.phy_if.sck_freq = NRF_QSPI_FREQ_32MDIV1; //NRF_QSPI_FREQ_32MDIV8; //32MHz
qspi_lcd_config.phy_if.sck_freq = NRF_QSPI_FREQ_32MDIV1; //NRF_QSPI_FREQ_32MDIV8; //32MHz
//qspi_lcd_config.phy_if.sck_freq = NRF_QSPI_FREQ_32MDIV8;//NRF_QSPI_FREQ_32MDIV8; //8MHz
qspi_lcd_config.pins.csn_pin = LCD_QSPI_CSN_PIN;
qspi_lcd_config.pins.sck_pin = LCD_QSPI_SCK_PIN;
qspi_lcd_config.pins.io0_pin = LCD_QSPI_IO0_PIN;
qspi_lcd_config.pins.io1_pin = LCD_QSPI_IO1_PIN;
qspi_lcd_config.pins.io2_pin = LCD_QSPI_IO2_PIN;
qspi_lcd_config.pins.io3_pin = LCD_QSPI_IO3_PIN;
err_code = nrf_drv_qspi_init(&qspi_lcd_config, qspi_lcd_handler, NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("QSPI LCD Init");
NRF_QSPI->IFCONFIG0 |= (QSPI_IFCONFIG0_PPSIZE_512Bytes << QSPI_IFCONFIG0_PPSIZE_Pos);
m_is_qspi_init = true;
}
else
{
NRF_LOG_ERROR("QSPI LCD has already initialized!");
APP_ERROR_CHECK(-1);
}
}
2.2 読み取りおよび書き込み操作
LCD上での操作は基本的に2つあります。
LCD コマンド コード (LCD 初期化コードなど)。
LCD データ書き込み (LCD 画面へのデータの書き込み)。
(1) LCD コマンドモード
(2) LCD ライトコマンド
static uint32_t drv_GC9c01_bus_writeCmd(const uint8_t cmd)
{
return send_qspi_cinstr_w0d(cmd);
}
static uint32_t drv_GC9c01_bus_writeCmdByte(const uint8_t cmd, const uint8_t data)
{
uint32_t err_code = NRF_SUCCESS;
err_code = send_qspi_cinstr_w1d(cmd, data);
return err_code;
}
(3)LCD初期化コード
static void configure_lcd()
{
drv_GC9c01_bus_writeCmd(0xfe);
drv_GC9c01_bus_writeCmd(0xef);
drv_GC9c01_bus_writeCmdByte(0x80, 0x11);
drv_GC9c01_bus_writeCmdByte(0x81, 0x70);
drv_GC9c01_bus_writeCmdByte(0x82, 0x09);
drv_GC9c01_bus_writeCmdByte(0x83, 0x03);
drv_GC9c01_bus_writeCmdByte(0x84, 0x20);
drv_GC9c01_bus_writeCmdByte(0x85, 0x42);
drv_GC9c01_bus_writeCmdByte(0x86, 0xfc);
drv_GC9c01_bus_writeCmdByte(0x87, 0x09);
drv_GC9c01_bus_writeCmdByte(0x89, 0x10);
drv_GC9c01_bus_writeCmdByte(0x8a, 0x4f);
drv_GC9c01_bus_writeCmdByte(0x8c, 0x59);
drv_GC9c01_bus_writeCmdByte(0x8d, 0x51);
drv_GC9c01_bus_writeCmdByte(0x8e, 0xae);
drv_GC9c01_bus_writeCmdByte(0x8f, 0xf3);
drv_GC9c01_bus_writeCmdByte(0x36, 0x00);
drv_GC9c01_bus_writeCmdByte(0x3a, 0x05);
drv_GC9c01_bus_writeCmdByte(0xec, 0x77);
uint8_t parmater1_buffer[] = {
0x01, 0x80, 0x00, 0x00, 0x00, 0x00};
send_qspi_cinstr_long_frame_word(0x74, parmater1_buffer, 6);
drv_GC9c01_bus_writeCmdByte(0x98, 0x3e);
drv_GC9c01_bus_writeCmdByte(0x99, 0x3e);
drv_GC9c01_bus_writeCmdByte(0xc3, 0x2A);
drv_GC9c01_bus_writeCmdByte(0xc4, 0x18);
send_qspi_cinstr_w1d_2data(0xa1,0x01,0x04);
send_qspi_cinstr_w1d_2data(0xa2,0x01,0x04);
drv_GC9c01_bus_writeCmdByte(0xa9, 0x1c);
send_qspi_cinstr_w1d_2data(0xa5,0x11,0x09);
drv_GC9c01_bus_writeCmdByte(0xb9, 0x8a);
drv_GC9c01_bus_writeCmdByte(0xa8, 0x5e);
drv_GC9c01_bus_writeCmdByte(0xa7, 0x40);
drv_GC9c01_bus_writeCmdByte(0xaf, 0x73);
drv_GC9c01_bus_writeCmdByte(0xae, 0x44);
drv_GC9c01_bus_writeCmdByte(0xad, 0x38);
drv_GC9c01_bus_writeCmdByte(0xa3, 0x5d);
drv_GC9c01_bus_writeCmdByte(0xc2, 0x02);
drv_GC9c01_bus_writeCmdByte(0xc5, 0x11);
drv_GC9c01_bus_writeCmdByte(0xc6, 0x0e);
drv_GC9c01_bus_writeCmdByte(0xc7, 0x13);
drv_GC9c01_bus_writeCmdByte(0xc8, 0x0d);
drv_GC9c01_bus_writeCmdByte(0xcb, 0x02);
send_qspi_cinstr_w1d_2data(0x7c,0xb6,0x26);
drv_GC9c01_bus_writeCmdByte(0xac, 0x24);
drv_GC9c01_bus_writeCmdByte(0xf6, 0x80);
send_qspi_cinstr_w1d_2data(0xb5,0x09,0x09);
send_qspi_cinstr_w1d_4data(0x60, 0x38,0x0b,0x5b,0x56);
send_qspi_cinstr_w1d_4data(0x63, 0x3a,0xe0,0x5b,0x56);
uint8_t parmater2_buffer[] = {
0x38, 0x0d, 0x72, 0xdd, 0x5b, 0x56};
send_qspi_cinstr_long_frame_word(0x64, parmater2_buffer, 6);
uint8_t parmater3_buffer[] = {
0x38, 0x11, 0x72, 0xe1, 0x5b, 0x56};
send_qspi_cinstr_long_frame_word(0x66, parmater3_buffer, 6);
uint8_t parmater4_buffer[] = {
0x3b, 0x08, 0x08, 0x00, 0x08, 0x29,0x5b};
send_qspi_cinstr_long_frame_word(0x68, parmater4_buffer, 7);
uint8_t parmater5_buffer[] = {
0x00,0x00,0x00,0x07,0x01,0x13,0x11,0x0b, \
0x09,0x16,0x15,0x1d,0x1e,0x00,0x00,0x00, \
0x00,0x00,0x00,0x1e,0x1d,0x15,0x16,0x0a, \
0x0c,0x12,0x14,0x02,0x08,0x00,0x00,0x00};
send_qspi_cinstr_long_frame_word(0x6e, parmater5_buffer, 32);
drv_GC9c01_bus_writeCmdByte(0xbe, 0x11);
uint8_t parmater6_buffer[] = {
0xcc, 0x0c, 0xcc, 0x84, 0xcc, 0x04,0x50};
send_qspi_cinstr_long_frame_word(0x6c, parmater6_buffer, 7);
drv_GC9c01_bus_writeCmdByte(0x7d, 0x72);
drv_GC9c01_bus_writeCmdByte(0x7e, 0x38);
uint8_t parmater7_buffer[] = {
0x02,0x03,0x09,0x05,0x0c,0x06,0x09,0x05,0x0c,0x06};
send_qspi_cinstr_long_frame_word(0x70, parmater7_buffer, 10);
send_qspi_cinstr_w1d_4data(0x90, 0x06,0x06,0x05,0x06);
send_qspi_cinstr_w1d_3data(0x93, 0x45,0xff,0x00);
uint8_t parmater8_buffer[] = {
0x45, 0x09, 0x08, 0x08, 0x26, 0x2a};
send_qspi_cinstr_long_frame_word(0xf0, parmater8_buffer, 6);
uint8_t parmater9_buffer[] = {
0x43, 0x70, 0x72, 0x36, 0x37, 0x6f};
send_qspi_cinstr_long_frame_word(0xf1, parmater9_buffer, 6);
uint8_t parmater10_buffer[] = {
0x45, 0x09, 0x08, 0x08, 0x26, 0x2a};
send_qspi_cinstr_long_frame_word(0xf2, parmater10_buffer, 6);
uint8_t parmater11_buffer[] = {
0x43, 0x70, 0x72, 0x36, 0x37, 0x6f};
send_qspi_cinstr_long_frame_word(0xf3, parmater11_buffer, 6);
drv_GC9c01_bus_writeCmd(0xfe);
drv_GC9c01_bus_writeCmd(0xee);
// User Command Set (UCS = CMD1)------------------------------------------
// drv_GC9c01_bus_writeCmdByte(GC9c01_CMD_WRITE_CMD_MODE_PAGE, 0x00);
//tearing effect line on--------------------------------------------------
// drv_GC9c01_bus_writeCmdByte(GC9c01_CMD_TEARING_EFFECT_ON, 0x00);
// Interface Pixel Format-------------------------------------------------
// drv_GC9c01_bus_writeCmdByte(GC9c01_CMD_INTERFACE_PIXEL_FORMAT, 0x55);// 0x55 -> 565
// Set_DSPI Mode----------------------------------------------------------
// drv_GC9c01_bus_writeCmdByte(GC9c01_CMD_SET_DSPI_MODE, 0x80);
// Write display brightness-----------------------------------------------
//GC9c01_drv_SetBrightness(0xCC);
// Sleep-out and Display on-----------------------------------------------
GC9c01_DRV_SLEEP_OUT();
nrf_delay_ms(120);
GC9c01_DRV_DISPLAY_ON();
nrf_delay_ms(20);
GC9c01_drv_setColRowPosition(0, 0);
NRF_LOG_INFO("After %s", __func__);
}
2.3 LCDデータの書き込み
LCD データの書き込みには Opcode = 0x32 PP4O を使用します。
static uint32_t gc9c01_bus_lcd_write_buffer(uint8_t *p_tx_buffer, uint32_t len, uint32_t addr)
{
uint32_t err_code = 0;
m_finished = false;
err_code = nrf_drv_qspi_write(p_tx_buffer, len, addr);
if(err_code != NRFX_SUCCESS)
{
// NRF_LOG_INFO("error code= 0x%x",err_code);
}
WAIT_FOR_PERIPH();
return err_code;
}
static void BMP_picture_show(uint32_t delay, const uint16_t *p_data)
{
uint32_t err_code;
uint32_t * p_color;
uint32_t i,k;
uint32_t tran_size,now_size=0;
uint32_t remain_size = GC9c01_DRV_ALL_SIZE;
//NRF_LOG_INFO("%s block_dx = %d, Color=%04x", __func__, a block_dx, color);
//static uint8_t __ALIGN(4) bank_buf[QSPI_PAG_SIZE];
tran_size = QSPI_PAG_SIZE;
memcpy(&m_buffer_tx[0], (uint8_t *)&p_data[0]+tran_size, tran_size);
err_code = gc9c01_bus_lcd_write_buffer(m_buffer_tx, QSPI_PAG_SIZE, 0x002C00);
remain_size -= tran_size;
now_size=tran_size;
while (remain_size)
{
if (remain_size > QSPI_PAG_SIZE)
{
tran_size = QSPI_PAG_SIZE;
}
else
{
tran_size = remain_size;
}
memcpy(m_buffer_tx, (uint8_t *)&p_data[0]+now_size, tran_size);
now_size += tran_size;
err_code = gc9c01_bus_lcd_write_buffer(&m_buffer_tx[0], tran_size, 0x003C00);
remain_size -= tran_size;
}
nrf_delay_ms(delay);
}
結果を示す
参照リンクアクセスアドレス