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;
}
テスト画面: