PMOD OLEDRGB

DIGILENT社からOLED RGB版が発売されています。これは16bitカラーなので、より表現力が高まります。これを使用してみます。

(Digilent/PMOD OLEDRGB: http://store.digilentinc.com/pmodoledrgb-96-x-64-rgb-oled-with-16-bit-color-resolution/)

本ページでは、IntelのCyclone 10LP FPGAの評価ボードを使用してnios2上で動作可能なコードを作成します。

上記リンクのデータシートを確認すると、J1コネクタは次のようにアサインされています。

PMOD OLED RGBのリファレンスマニュアル

https://reference.digilentinc.com/reference/pmod/pmodoledrgb/reference-manual

PMOD OLED RGBに搭載されているコントローラのデータシート

https://cdn-shop.adafruit.com/datasheets/SSD1331_1.2.pdf

FPGAで制御するために下記のようにQSYS上にSPIとGPIOをセットアップします。

今回は制御用として次の2つのIPをセットアップしています。

PMOD_CNTRL

D/C, RESET, VCCEN,PMODEN制御用のGPIOとして4ビット出力のPIOをセットアップ。

PMOD_SPI

CS,SDIN,SCLK制御用にSPIをセットアップ。 Timing:Clock polarityとClock phaseを共に1にします。

PMOD OLED RGBに使用されているSSD1331のデータシート(https://cdn-shop.adafruit.com/datasheets/SSD1331_1.2.pdf)を確認すると、Clockの最小値は130nsなので、周波数は7.5MHz以下にします。

サンプルコード:

初期化後に、文字列を表示します。

OLED RGB 8bit color

#include "alt_types.h"

#include "sys/alt_stdio.h"

#include "io.h"

#include "system.h"

#include "sys/alt_cache.h"

#include "altera_avalon_pio_regs.h"

#include "altera_avalon_spi.h"

#include "altera_avalon_spi_regs.h"

#include "sys/alt_irq.h"

char led_dat = 0xF;

void led_on(int bit)

{

char pos[] = {0xe, 0xd, 0xb, 0x7};

led_dat &= pos[bit];

IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, led_dat);

}

void led_off(int bit)

{

char pos[] = {0x1, 0x2, 0x4, 0x8};

led_dat |= pos[bit];

IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, led_dat);

}

void led_update(void)

{

IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, led_dat);

}

void led_set(int nib)

{

led_dat = ~(nib & 0x0F);

IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, led_dat);

}

/*

pmod_d[7] Bidir PIN_B16 : PMOD#10 : OLEDRGB/Vdd Logic Voltage Control

pmod_d[6] Bidir PIN_C15 : PMOD#9 : OLEDRGB/Vcc Enable

pmod_d[5] Bidir PIN_F15 : PMOD#8 : OLEDRGB/Power Reset

pmod_d[4] Bidir PIN_C16 : PMOD#7 : OLEDRGB/Data Command Control

pmod_d[3] Bidir PIN_F16 : SPI_SCLK : PMOD#4 : OLEDRGB/SCK

pmod_d[2] Bidir PIN_D15 : SPI_MISO : PMOD#3 : OLEDRGB/NC

pmod_d[1] Bidir PIN_F13 : SPI_MOSI : PMOD#2 : OLEDRGB/MOSI

pmod_d[0] Bidir PIN_D16 : SPI_SSn : PMOD#1 : OLDERGB/CS

*/

char pmod_oledrgb_cntrl_dat = 0x0;

void pmod_oledrgb_cntrl_low(int bit)

{

char pos[] = {0xe, 0xd, 0xb, 0x7};

pmod_oledrgb_cntrl_dat &= pos[bit];

IOWR_ALTERA_AVALON_PIO_DATA(PMOD_CNTRL_BASE, pmod_oledrgb_cntrl_dat);

}

void pmod_oledrgb_cntrl_high(int bit)

{

char pos[] = {0x1, 0x2, 0x4, 0x8};

pmod_oledrgb_cntrl_dat |= pos[bit];

IOWR_ALTERA_AVALON_PIO_DATA(PMOD_CNTRL_BASE, pmod_oledrgb_cntrl_dat);

}

void pmod_oledrgb_cntrl_update(void)

{

IOWR_ALTERA_AVALON_PIO_DATA(PMOD_CNTRL_BASE, pmod_oledrgb_cntrl_dat);

}

#define BIT_POS_PMOD_OLEDRGB_DS 0

#define BIT_POS_PMOD_OLEDRGB_RESET 1

#define BIT_POS_PMOD_OLEDRGB_VCCEN 2

#define BIT_POS_PMOD_OLEDRGB_PMODEN 3

void pmod_oledrgb_ds(int low_high)

{

if (low_high)

pmod_oledrgb_cntrl_high(BIT_POS_PMOD_OLEDRGB_DS);

else

pmod_oledrgb_cntrl_low(BIT_POS_PMOD_OLEDRGB_DS);

}

void pmod_oledrgb_reset(int low_high)

{

if (low_high)

pmod_oledrgb_cntrl_high(BIT_POS_PMOD_OLEDRGB_RESET);

else

pmod_oledrgb_cntrl_low(BIT_POS_PMOD_OLEDRGB_RESET);

}

void pmod_oledrgb_vccen(int low_high)

{

if (low_high)

pmod_oledrgb_cntrl_high(BIT_POS_PMOD_OLEDRGB_VCCEN);

else

pmod_oledrgb_cntrl_low(BIT_POS_PMOD_OLEDRGB_VCCEN);

}

void pmod_oledrgb_pmoden(int low_high)

{

if (low_high)

pmod_oledrgb_cntrl_high(BIT_POS_PMOD_OLEDRGB_PMODEN);

else

pmod_oledrgb_cntrl_low(BIT_POS_PMOD_OLEDRGB_PMODEN);

}

void pmod_oledrgb_spi_tx(alt_u8 spi_wrdata)

{

alt_avalon_spi_command(PMOD_SPI_BASE, 0, 1, &spi_wrdata, 0, (alt_u8*)0, 0);

}

void pmod_oledrgb_spi_tx_2(alt_u8 spi_wrdata, alt_u8 spi_wrdata2)

{

alt_u8 spiwrdata[2];

spiwrdata[0] = spi_wrdata;

spiwrdata[1] = spi_wrdata2;

alt_avalon_spi_command(PMOD_SPI_BASE, 0, 2, spiwrdata, 2, (alt_u8*)0, 0);

}

void pmod_oledrgb_spi_tx_5(alt_u8 spi_wrdata, alt_u8 spi_wrdata2, alt_u8 spi_wrdata3, alt_u8 spi_wrdata4, alt_u8 spi_wrdata5)

{

alt_u8 spiwrdata[5];

spiwrdata[0] = spi_wrdata;

spiwrdata[1] = spi_wrdata2;

spiwrdata[2] = spi_wrdata3;

spiwrdata[3] = spi_wrdata4;

spiwrdata[4] = spi_wrdata5;

alt_avalon_spi_command(PMOD_SPI_BASE, 0, 5, spiwrdata, 5, (alt_u8*)0, 0);

}

void pmod_oledrgb_spi_tx_buf(alt_u8 *spi_wrdata, int len)

{

alt_avalon_spi_command(PMOD_SPI_BASE, 0, len, spi_wrdata, len, (alt_u8*)0, 0);

}

void pmod_oledrgb_dat_buf(alt_u8 *a, int len)

{

pmod_oledrgb_ds(1); // data mode

pmod_oledrgb_spi_tx_buf(a, len);

}

void pmod_oledrgb_cmd(alt_u8 a)

{

pmod_oledrgb_ds(0); // command mode

pmod_oledrgb_spi_tx(a);

}

void pmod_oledrgb_dat(alt_u8 a)

{

pmod_oledrgb_ds(1); // data mode

pmod_oledrgb_spi_tx(a);

}

const alt_u8 pmod_oledrgb_init_cmd[] =

{

0xa0, 0b00110010,

0xa1, 0x00,

0xa2, 0x00,

0xa4,

0xa8, 63,

0xad, 0b10001110,

0xb0, 0x1a,

0xb1, 0x74,

0xb3, 0xf0,

0x8a, 0x81,

0x8b, 0x82,

0x8c, 0x83,

0xbb, 0x3a,

0xbe, 0x3e,

0x87, 0x06,

0x15, 0x00, 95,

0x75, 0, 63,

0x81, 0xff,

0x82, 0xff,

0x83, 0xff,

};

void pmod_oledrgb_init(void)

{

int i;

pmod_oledrgb_cntrl_update();

pmod_oledrgb_ds(0); // command mode

pmod_oledrgb_reset(1);

pmod_oledrgb_vccen(0);

pmod_oledrgb_pmoden(1);

usleep(20000); // wait for 20msec

pmod_oledrgb_reset(0);

usleep(3);

pmod_oledrgb_reset(1);

usleep(100);

for (i=0; i<41; i++)

pmod_oledrgb_cmd(pmod_oledrgb_init_cmd[i]);

pmod_oledrgb_vccen(1);

usleep(25000); // wait for 25msec

pmod_oledrgb_cmd(0xaf);

usleep(125000); // wait for 125msec

};

void pmod_oledrgb_clrscr(void)

{

int x, y;

for (x=0; x<96; x++)

for (y=0; y<64; y++)

pmod_oledrgb_dat(0);

}

void pmod_oledrgb_fillscr(alt_u8 cl)

{

int x, y;

for (x=0; x<96; x++)

for (y=0; y<64; y++)

pmod_oledrgb_dat(cl);

}

#define PMOD_OLEDRGB_BUFF_SIZE 6144

alt_u8 pmod_oledrgb_buff[PMOD_OLEDRGB_BUFF_SIZE];

void pmod_oledrgb_bf_clr(void)

{

int l;

for(l=0; l<PMOD_OLEDRGB_BUFF_SIZE; l++)

pmod_oledrgb_buff[l] = 0;

}

void pmod_oledrgb_bf_fil(alt_u8 c)

{

int l;

for(l=0; l<PMOD_OLEDRGB_BUFF_SIZE; l++)

pmod_oledrgb_buff[l] = c;

}

void pmod_oledrgb_update_bf(void)

{

pmod_oledrgb_dat_buf(pmod_oledrgb_buff, PMOD_OLEDRGB_BUFF_SIZE);

}

void pmod_oledrgb_update_bf2(void)

{

int x,y;

for (x=0; x<96; x++)

pmod_oledrgb_dat_buf(pmod_oledrgb_buff+(x*64), 64);

}

void pmod_oledrgb_update_bf1(void)

{

int l;

for(l=0; l<PMOD_OLEDRGB_BUFF_SIZE; l++)

pmod_oledrgb_dat(pmod_oledrgb_buff[l]);

}

//------------------------------------------------------------------------------------------------------------

const alt_u8 rgbFillPat[] = {

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x01

0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, // 0x02

0x11, 0x44, 0x00, 0x11, 0x44, 0x00, 0x11, 0x44, // 0x03

0x92, 0x45, 0x24, 0x92, 0x45, 0x24, 0x92, 0x45, // 0x04

0x49, 0x92, 0x24, 0x49, 0x92, 0x24, 0x49, 0x92, // 0x05

0x22, 0x11, 0x22, 0x00, 0x22, 0x11, 0x22, 0x00, // 0x06

0x11, 0x22, 0x11, 0x00, 0x11, 0x22, 0x11, 0x00 // 0x07

};

const alt_u8 rgbOledFont0[] = {

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x20, space

0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x00, // 0x21, !

0x00,0x00,0x03,0x00,0x03,0x00,0x00,0x00, // 0x22, "

0x64,0x3c,0x26,0x64,0x3c,0x26,0x24,0x00, // 0x23, #

0x26,0x49,0x49,0x7f,0x49,0x49,0x32,0x00, // 0x23, $

0x42,0x25,0x12,0x08,0x24,0x52,0x21,0x00, // 0x25, %

0x20,0x50,0x4e,0x55,0x22,0x58,0x28,0x00, // 0x26, &

0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00, // 0x27, '

0x00,0x00,0x1c,0x22,0x41,0x00,0x00,0x00, // 0x28, (

0x00,0x00,0x00,0x41,0x22,0x1c,0x00,0x00, // 0x29, )

0x00,0x15,0x15,0x0e,0x0e,0x15,0x15,0x00, // 0x2A, *

0x00,0x08,0x08,0x3e,0x08,0x08,0x00,0x00, // 0x2B, +

0x00,0x00,0x00,0x50,0x30,0x00,0x00,0x00, // 0x2C, ,

0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00, // 0x2D, -

0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00, // 0x2E, .

0x40,0x20,0x10,0x08,0x04,0x02,0x01,0x00, // 0x2F, /

0x00,0x3e,0x41,0x41,0x41,0x3e,0x00,0x00, // 0x30, 0

0x00,0x00,0x41,0x7f,0x40,0x00,0x00,0x00, // 0x31, 1

0x00,0x42,0x61,0x51,0x49,0x6e,0x00,0x00, // 0x32, 2

0x00,0x22,0x41,0x49,0x49,0x36,0x00,0x00, // 0x33, 3

0x00,0x18,0x14,0x12,0x7f,0x10,0x00,0x00, // 0x33, 4

0x00,0x27,0x49,0x49,0x49,0x71,0x00,0x00, // 0x35, 5

0x00,0x3c,0x4a,0x49,0x48,0x70,0x00,0x00, // 0x36, 6

0x00,0x43,0x21,0x11,0x0d,0x03,0x00,0x00, // 0x37, 7

0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x00, // 0x38, 8

0x00,0x06,0x09,0x49,0x29,0x1e,0x00,0x00, // 0x39, 9

0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00, // 0x3A, :

0x00,0x00,0x00,0x52,0x30,0x00,0x00,0x00, // 0x3B, //

0x00,0x00,0x08,0x14,0x14,0x22,0x00,0x00, // 0x3C, <

0x00,0x14,0x14,0x14,0x14,0x14,0x14,0x00, // 0x3D, =

0x00,0x00,0x22,0x14,0x14,0x08,0x00,0x00, // 0x3E, >

0x00,0x02,0x01,0x59,0x05,0x02,0x00,0x00, // 0x3F, ?

0x3e,0x41,0x5d,0x55,0x4d,0x51,0x2e,0x00, // 0x40, @

0x40,0x7c,0x4a,0x09,0x4a,0x7c,0x40,0x00, // 0x41, A

0x41,0x7f,0x49,0x49,0x49,0x49,0x36,0x00, // 0x42, B

0x1c,0x22,0x41,0x41,0x41,0x41,0x22,0x00, // 0x43, C

0x41,0x7f,0x41,0x41,0x41,0x22,0x1c,0x00, // 0x44, D

0x41,0x7f,0x49,0x49,0x5d,0x41,0x63,0x00, // 0x45, E

0x41,0x7f,0x49,0x09,0x1d,0x01,0x03,0x00, // 0x46, F

0x1c,0x22,0x41,0x49,0x49,0x3a,0x08,0x00, // 0x47, G

0x41,0x7f,0x08,0x08,0x08,0x7f,0x41,0x00, // 0x48, H

0x00,0x41,0x41,0x7F,0x41,0x41,0x00,0x00, // 0x49, I

0x30,0x40,0x41,0x41,0x3F,0x01,0x01,0x00, // 0x4A, J

0x41,0x7f,0x08,0x0c,0x12,0x61,0x41,0x00, // 0x4B, K

0x41,0x7f,0x41,0x40,0x40,0x40,0x60,0x00, // 0x4C, L

0x41,0x7f,0x42,0x0c,0x42,0x7f,0x41,0x00, // 0x4D, M

0x41,0x7f,0x42,0x0c,0x11,0x7f,0x01,0x00, // 0x4E, N

0x1c,0x22,0x41,0x41,0x41,0x22,0x1c,0x00, // 0x4F, O

0x41,0x7f,0x49,0x09,0x09,0x09,0x06,0x00, // 0x50, P

0x0c,0x12,0x21,0x21,0x61,0x52,0x4c,0x00, // 0x51, Q

0x41,0x7f,0x09,0x09,0x19,0x69,0x46,0x00, // 0x52, R

0x66,0x49,0x49,0x49,0x49,0x49,0x33,0x00, // 0x53, S

0x03,0x01,0x41,0x7f,0x41,0x01,0x03,0x00, // 0x54, T

0x01,0x3f,0x41,0x40,0x41,0x3f,0x01,0x00, // 0x55, U

0x01,0x0f,0x31,0x40,0x31,0x0f,0x01,0x00, // 0x56, V

0x01,0x1f,0x61,0x14,0x61,0x1f,0x01,0x00, // 0x57, W

0x41,0x41,0x36,0x08,0x36,0x41,0x41,0x00, // 0x58, X

0x01,0x03,0x44,0x78,0x44,0x03,0x01,0x00, // 0x59, Y

0x43,0x61,0x51,0x49,0x45,0x43,0x61,0x00, // 0x5A, Z

0x00,0x00,0x7f,0x41,0x41,0x00,0x00,0x00, // 0x5B, [

0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x00, // 0x5C,

0x00,0x00,0x41,0x41,0x7f,0x00,0x00,0x00, // 0x5D, ]

0x00,0x04,0x02,0x01,0x01,0x02,0x04,0x00, // 0x5E, ^

0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x00, // 0x5F, _

0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00, // 0x60, `

0x00,0x34,0x4a,0x4a,0x4a,0x3c,0x40,0x00, // 0x61, a

0x00,0x41,0x3f,0x48,0x48,0x48,0x30,0x00, // 0x62. b

0x00,0x3c,0x42,0x42,0x42,0x24,0x00,0x00, // 0x63, c

0x00,0x30,0x48,0x48,0x49,0x3f,0x40,0x00, // 0x63, d

0x00,0x3c,0x4a,0x4a,0x4a,0x2c,0x00,0x00, // 0x65, e

0x00,0x00,0x48,0x7e,0x49,0x09,0x00,0x00, // 0x66, f

0x00,0x26,0x49,0x49,0x49,0x3f,0x01,0x00, // 0x67, g

0x41,0x7f,0x48,0x04,0x44,0x78,0x40,0x00, // 0x68, h

0x00,0x00,0x44,0x7d,0x40,0x00,0x00,0x00, // 0x69, i

0x00,0x00,0x40,0x44,0x3d,0x00,0x00,0x00, // 0x6A, j

0x41,0x7f,0x10,0x18,0x24,0x42,0x42,0x00, // 0x6B, k

0x00,0x40,0x41,0x7f,0x40,0x40,0x00,0x00, // 0x6C, l

0x42,0x7e,0x02,0x7c,0x02,0x7e,0x40,0x00, // 0x6D, m

0x42,0x7e,0x44,0x02,0x42,0x7c,0x40,0x00, // 0x6E, n

0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00, // 0x6F, o

0x00,0x41,0x7f,0x49,0x09,0x09,0x06,0x00, // 0x70, p

0x00,0x06,0x09,0x09,0x49,0x7f,0x41,0x00, // 0x71, q

0x00,0x42,0x7e,0x44,0x02,0x02,0x04,0x00, // 0x72, r

0x00,0x64,0x4a,0x4a,0x4a,0x36,0x00,0x00, // 0x73, s

0x00,0x04,0x3f,0x44,0x44,0x20,0x00,0x00, // 0x73, t

0x00,0x02,0x3e,0x40,0x40,0x22,0x7e,0x40, // 0x75, u

0x02,0x0e,0x32,0x40,0x32,0x0e,0x02,0x00, // 0x76, v

0x02,0x1e,0x62,0x18,0x62,0x1e,0x02,0x00, // 0x77, w

0x42,0x62,0x14,0x08,0x14,0x62,0x42,0x00, // 0x78, x

0x01,0x43,0x45,0x38,0x05,0x03,0x01,0x00, // 0x79, y

0x00,0x46,0x62,0x52,0x4a,0x46,0x62,0x00, // 0x7A, z

0x00,0x00,0x08,0x36,0x41,0x00,0x00,0x00, // 0x7B, {

0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00, // 0x7C, |

0x00,0x00,0x00,0x41,0x36,0x08,0x00,0x00, // 0x7D, }

0x00,0x18,0x08,0x08,0x10,0x10,0x18,0x00, // 0x7E, ~

0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55 // 0x7F, DEL

};

void font_write(alt_u8 chrno, alt_u8 cl, alt_u8 buf[8][8])

{

alt_u8 *p;

int i,j;

alt_u8 c;

alt_u8 pos[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};

p = &rgbOledFont0[(chrno-0x20)*8];

for (i=0; i<8; i++) {

c = *(p+i);

for (j=0; j<8; j++) {

if (c & pos[j])

buf[7-j][i] = cl;

else

buf[7-j][i] = 0;

}

}

}

/*

Buffer:

0 1 2 3 ... 8.... 95

96 97 98 99 ... ..... 191

192

*/

//pmod_oledrgb_buff

void pmod_oledrgb_str_print(alt_u8 x, alt_u8 y, alt_u8 *str, alt_u8 cl)

{

alt_u8 c;

int i, len;

int k, l, p;

alt_u8 buf[8][8];

int pos_x, pos_y;

len = strlen(str);

for (i=0; i<len; i++) {

c = *(str+i);

font_write(c, cl, buf);

pos_x = x+(8*i);

pos_y = y*96*8;

for (k=0; k<8; k++) { // y

printf("\n");

for (l=0; l<8; l++) { // x

pmod_oledrgb_buff[pos_x+l+pos_y+(k*96)] = buf[k][l];

}

}

}

}

int main()

{

char display_buffer[32];

alt_putstr("Hello from Nios II!\n");

int count=0;

led_update();

pmod_oledrgb_init();

pmod_oledrgb_clrscr();

usleep(1000000);

/* Event loop never exits. */

pmod_oledrgb_str_print(0, 0, "Test", 0xff);

pmod_oledrgb_str_print(0, 1, "PWR:3.3V", 0x0f);

pmod_oledrgb_str_print(0, 2, "CLK:150MHz", 0xf0);

pmod_oledrgb_str_print(0, 3, "SPI:7.5MHz", 0x3c);

while (1)

{

// pmod_oledrgb_bf_fil(count & 0xFF);

pmod_oledrgb_update_bf1();

led_set(count & 0x0F);

// usleep(100000);

count++;

//printf("count=%d\n", count);

}

return 0;

}


テスト画面: