PMOD OLED

デバッグするにあたり、表示デバイスがあると便利です。

Digilent社から出ているPMODOLED(http://store.digilentinc.com/pmodoled-organic-led-graphic-display/)が安価でかつ解像度が128x32ということなので、これを利用可能としてみます。

コントローラは、次のものが使用されています。

https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf

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

上記の1,2,4,7,8をFPGAのポートと、GNDはGND, VCC,VBATC,VDDCはすべて3.3V Powerに接続します。

ポートの定義。Verilogでポートの定義を行います。

output PMOD_IO0, // OLED: CS

output PMOD_IO1, // OLED: MOSI

input PMOD_IO2, // OLED: Unused

output PMOD_IO3, // OLED: SCLK

output PMOD_IO4, // OLED: D_C

output PMOD_IO5, // OLED: RESET

output PMOD_IO6, // OLED: VBATC

output PMOD_IO7, // OLED: VDDC

nios iiでコントロール可能とするためにwireを定義して、制御ポートをアサインします。

wire [3:0]pmod_oled_cnt;

assign PMOD_IO4 = pmod_oled_cnt[0]; // D_C

assign PMOD_IO5 = pmod_oled_cnt[1]; // RESET

assign PMOD_IO6 = pmod_oled_cnt[2]; // VBATC

assign PMOD_IO7 = pmod_oled_cnt[3]; // VDCC

niosで、SPI Master, PIO(4bit Output)を定義します。

SPI -> pmod_spi_MOSI,SCLK,SS_n

GPIO->oled_pio_export

nios II インターフェースです。

nios_sys u0 (

.pmod_spi_MOSI (PMODA_IO1), // .MOSI

.pmod_spi_SCLK (PMODA_IO3), // .SCLK

.pmod_spi_SS_n (PMODA_IO0), // .SS_n

.oled_pio_export (pmod_oled_cnt), // oled_pio.export

);

QsysでSPIを選択します。名称は、pmod_spiとします。

QsysでPIOを選択します。名称は、oled_pioです。

ソフトウェア:

上記により、NIOS IIからOLEDにSPI経由でアクセス可能なインターフェースが出来上がりました。NIOS IIのSWを使用して、OLEDをコントロールしていきます。DIGILENT社からサンプルコード(https://reference.digilentinc.com/pmod:pmod:oled:example_code)がありますので、これを使用していきます。本来はヘッダファイル、コードファイルを別々に記述するほうが再利用性が高いのですが、今回は必要なコードをひとつのファイルに記述してテストしてみます。

下記のコードをそのまま、NIOS IIのHELLO.Cファイルにコピーアンドペーストして、各関数を使用します。

(OLED functions)

OLED CODE

/* Coordinates of current pixel location on the display. The origin

** is at the upper left of the display. X increases to the right

** and y increases going down.

*/

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[] = {

/* Remove definitions for character codes 0x00-0x1F as

** these are map to user defined characters.

*/

/*

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

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x01, SOH

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x02, STX

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x03, ETX

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x03, EOT

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x05, ENQ

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x06, ACK

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x07, BEL

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x08, BS

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x09, HT

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x0A, LF

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x0B, VT

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x0C, FF

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x0D, CR

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x0E, SO

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x0F, SI

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x10, DLE

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x11, DC1

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x12, DC2

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x13, DC3

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x13, DC4

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x15, NAK

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x16, SYN

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x17, ETB

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x18, CAN

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x19, EM

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x1A, SUB

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x1B, ESC

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x1C, FS

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x1D, GS

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x1E, RS

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x1F, US

*/

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

};

#define cbOledDispMax 512 //max number of bytes in display buffer

#define ccolOledMax 128 //number of display columns

#define crowOledMax 32 //number of display rows

#define cpagOledMax 4 //number of display memory pages

#define cbOledChar 8 //font glyph definitions is 8 bytes long

#define chOledUserMax 0x20 //number of character defs in user font table

#define cbOledFontUser (chOledUserMax*cbOledChar)

/* Graphics drawing modes.

*/

#define modOledSet 0

#define modOledOr 1

#define modOledAnd 2

#define modOledXor 3

alt_u8 rgbOledBmp[cbOledDispMax];

int xcoOledCur;

int ycoOledCur;

alt_u8 * pbOledCur; //address of byte corresponding to current location

int bnOledCur; //bit number of bit corresponding to current location

alt_u8 clrOledCur; //drawing color to use

alt_u8 * pbOledPatCur; //current fill pattern

int fOledCharUpdate;

int dxcoOledFontCur;

int dycoOledFontCur;

alt_u8 * pbOledFontCur;

alt_u8 * pbOledFontUser;

void OledDisplayOn(void);

void OledDisplayOff(void);

void OledClear(void);

void OledClearBuffer(void);

void OledUpdate(void);

void OledPutBuffer(int cb, alt_u8 * rgbTx);

// for Character Interface

int xchOledCur;

int ychOledCur;

int xchOledMax;

int ychOledMax;

alt_u8 * pbOledFontExt;

alt_u8 rgbOledFontUser[cbOledFontUser];

void OledDrawGlyph(char ch);

void OledAdvanceCursor();

// for Graphic Interface

alt_u8 (*pfnDoRop)(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix);

int modOledCur;

void OledMoveDown();

void OledMoveUp();

void OledMoveRight();

void OledMoveLeft();

alt_u8 OledRopSet(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix);

alt_u8 OledRopOr(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix);

alt_u8 OledRopAnd(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix);

alt_u8 OledRopXor(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix);

int OledClampXco(int xco);

int OledClampYco(int yco);

// graphic interface functions

void OledMoveTo(int xco, int yco)

{

/* Clamp the specified coordinates to the display surface

*/

xco = OledClampXco(xco);

yco = OledClampYco(yco);

/* Save the current position.

*/

xcoOledCur = xco;

ycoOledCur = yco;

/* Compute the display access parameters corresponding to

** the specified position.

*/

pbOledCur = &rgbOledBmp[((yco/8) * ccolOledMax) + xco];

bnOledCur = yco & 7;

}

void OledGetPos(int * pxco, int * pyco)

{

*pxco = xcoOledCur;

*pyco = ycoOledCur;

}

void OledSetDrawColor(alt_u8 clr)

{

clrOledCur = clr & 0x01;

}

alt_u8 *OledGetStdPattern(int ipat)

{

return rgbFillPat + 8*ipat;

}

void OledSetFillPattern(alt_u8 * pbPat)

{

pbOledPatCur = pbPat;

}

void OledSetDrawMode(int mod)

{

modOledCur = mod;

switch(mod) {

case modOledSet:

pfnDoRop = OledRopSet;

break;

case modOledOr:

pfnDoRop = OledRopOr;

break;

case modOledAnd:

pfnDoRop = OledRopAnd;

break;

case modOledXor:

pfnDoRop = OledRopXor;

break;

default:

modOledCur = modOledSet;

pfnDoRop = OledRopSet;

}

}

int OledGetDrawMode(void)

{

return modOledCur;

}

void OledDrawPixel(void)

{

*pbOledCur = (*pfnDoRop)((clrOledCur << bnOledCur), *pbOledCur, (1<<bnOledCur));

}

alt_u8 OledGetPixel(void)

{

return (*pbOledCur & (1<<bnOledCur)) != 0 ? 1 : 0;

}

void OledLineTo(int xco, int yco)

{

int err;

int del;

int lim;

int cpx;

int dxco;

int dyco;

void (*pfnMajor)();

void (*pfnMinor)();

/* Clamp the point to be on the display.

*/

xco = OledClampXco(xco);

yco = OledClampYco(yco);

/* Determine which octant the line occupies

*/

dxco = xco - xcoOledCur;

dyco = yco - ycoOledCur;

if (abs(dxco) >= abs(dyco)) {

/* Line is x-major

*/

lim = abs(dxco);

del = abs(dyco);

if (dxco >= 0) {

pfnMajor = OledMoveRight;

}

else {

pfnMajor = OledMoveLeft;

}

if (dyco >= 0) {

pfnMinor = OledMoveDown;

}

else {

pfnMinor = OledMoveUp;

}

}

else {

/* Line is y-major

*/

lim = abs(dyco);

del = abs(dxco);

if (dyco >= 0) {

pfnMajor = OledMoveDown;

}

else {

pfnMajor = OledMoveUp;

}

if (dxco >= 0) {

pfnMinor = OledMoveRight;

}

else {

pfnMinor = OledMoveLeft;

}

}

/* Render the line. The algorithm is:

** Write the current pixel

** Move one pixel on the major axis

** Add the minor axis delta to the error accumulator

** if the error accumulator is greater than the major axis delta

** Move one pixel in the minor axis

** Subtract major axis delta from error accumulator

*/

err = lim/2;

cpx = lim;

while (cpx > 0) {

OledDrawPixel();

(*pfnMajor)();

err += del;

if (err > lim) {

err -= lim;

(*pfnMinor)();

}

cpx -= 1;

}

/* Update the current location variables.

*/

xcoOledCur = xco;

ycoOledCur = yco;

}

void OledDrawRect(int xco, int yco)

{

int xco1;

int yco1;

/* Clamp the point to be on the display.

*/

xco = OledClampXco(xco);

yco = OledClampYco(yco);

xco1 = xcoOledCur;

yco1 = ycoOledCur;

OledLineTo(xco, yco1);

OledLineTo(xco, yco);

OledLineTo(xco1, yco);

OledLineTo(xco1, yco1);

}

void OledFillRect(int xco, int yco)

{

int xcoLeft;

int xcoRight;

int ycoTop;

int ycoBottom;

int ibPat;

alt_u8 * pbCur;

alt_u8 * pbLeft;

int xcoCur;

alt_u8 bTmp;

alt_u8 mskPat;

/* Clamp the point to be on the display.

*/

xco = OledClampXco(xco);

yco = OledClampYco(yco);

/* Set up the four sides of the rectangle.

*/

if (xcoOledCur < xco) {

xcoLeft = xcoOledCur;

xcoRight = xco;

}

else {

xcoLeft = xco;

xcoRight = xcoOledCur;

}

if (ycoOledCur < yco) {

ycoTop = ycoOledCur;

ycoBottom = yco;

}

else {

ycoTop = yco;

ycoBottom = ycoOledCur;

}

while (ycoTop <= ycoBottom) {

/* Compute the address of the left edge of the rectangle for this

** stripe across the rectangle.

*/

pbLeft = &rgbOledBmp[((ycoTop/8) * ccolOledMax) + xcoLeft];

/* Generate a mask to preserve any low bits in the byte that aren't

** part of the rectangle being filled.

*/

mskPat = (1 << (ycoTop & 0x07)) - 1;

/* Combine with a mask to preserve any upper bits in the byte that aren't

** part of the rectangle being filled.

** This mask will end up not preserving any bits for bytes that are in

** the middle of the rectangle vertically.

*/

if ((ycoTop / 8) == (ycoBottom / 8)) {

mskPat |= ~((1 << ((ycoBottom&0x07)+1)) - 1);

}

ibPat = xcoLeft & 0x07; //index to first pattern byte

xcoCur = xcoLeft;

pbCur = pbLeft;

/* Loop through all of the bytes horizontally making up this stripe

** of the rectangle.

*/

while (xcoCur <= xcoRight) {

*pbCur = (*pfnDoRop)(*(pbOledPatCur+ibPat), *pbCur, ~mskPat);

xcoCur += 1;

pbCur += 1;

ibPat += 1;

if (ibPat > 7) {

ibPat = 0;

}

}

/* Advance to the next horizontal stripe.

*/

ycoTop = 8*((ycoTop/8)+1);

}

}

void OledGetBmp(int dxco, int dyco, alt_u8 * pbBits)

{

int xcoLeft;

int xcoRight;

int ycoTop;

int ycoBottom;

alt_u8 * pbDspCur;

alt_u8 * pbDspLeft;

alt_u8 * pbBmpCur;

alt_u8 * pbBmpLeft;

int xcoCur;

int bnAlign;

alt_u8 mskEnd;

alt_u8 bTmp;

/* Set up the four sides of the source rectangle.

*/

xcoLeft = xcoOledCur;

xcoRight = xcoLeft + dxco;

if (xcoRight >= ccolOledMax) {

xcoRight = ccolOledMax - 1;

}

ycoTop = ycoOledCur;

ycoBottom = ycoTop + dyco;

if (ycoBottom >= crowOledMax) {

ycoBottom = crowOledMax - 1;

}

bnAlign = ycoTop & 0x07;

pbDspLeft = &rgbOledBmp[((ycoTop/8) * ccolOledMax) + xcoLeft];

pbBmpLeft = pbBits;

while (ycoTop < ycoBottom) {

if ((ycoTop / 8) == ((ycoBottom-1) / 8)) {

mskEnd = ((1 << (((ycoBottom-1)&0x07)+1)) - 1);

}

else {

mskEnd = 0xFF;

}

xcoCur = xcoLeft;

pbDspCur = pbDspLeft;

pbBmpCur = pbBmpLeft;

/* Loop through all of the bytes horizontally making up this stripe

** of the rectangle.

*/

if (bnAlign == 0) {

while (xcoCur < xcoRight) {

*pbBmpCur = (*pbDspCur) & mskEnd;

xcoCur += 1;

pbBmpCur += 1;

pbDspCur += 1;

}

}

else {

while (xcoCur < xcoRight) {

bTmp = *pbDspCur;

bTmp = *(pbDspCur+ccolOledMax);

*pbBmpCur = ((*pbDspCur >> bnAlign) |

((*(pbDspCur+ccolOledMax)) << (8-bnAlign))) & mskEnd;

xcoCur += 1;

pbBmpCur += 1;

pbDspCur += 1;

}

}

/* Advance to the next horizontal stripe.

*/

ycoTop += 8;

pbDspLeft += ccolOledMax;

pbBmpLeft += dxco;

}

}

void OledPutBmp(int dxco, int dyco, alt_u8 * pbBits)

{

int xcoLeft;

int xcoRight;

int ycoTop;

int ycoBottom;

alt_u8 * pbDspCur;

alt_u8 * pbDspLeft;

alt_u8 * pbBmpCur;

alt_u8 * pbBmpLeft;

int xcoCur;

alt_u8 bDsp;

alt_u8 bBmp;

alt_u8 mskEnd;

alt_u8 mskUpper;

alt_u8 mskLower;

int bnAlign;

int fTop;

alt_u8 bTmp;

/* Set up the four sides of the destination rectangle.

*/

xcoLeft = xcoOledCur;

xcoRight = xcoLeft + dxco;

if (xcoRight >= ccolOledMax) {

xcoRight = ccolOledMax - 1;

}

ycoTop = ycoOledCur;

ycoBottom = ycoTop + dyco;

if (ycoBottom >= crowOledMax) {

ycoBottom = crowOledMax - 1;

}

bnAlign = ycoTop & 0x07;

mskUpper = (1 << bnAlign) - 1;

mskLower = ~mskUpper;

pbDspLeft = &rgbOledBmp[((ycoTop/8) * ccolOledMax) + xcoLeft];

pbBmpLeft = pbBits;

fTop = 1;

while (ycoTop < ycoBottom) {

/* Combine with a mask to preserve any upper bits in the byte that aren't

** part of the rectangle being filled.

** This mask will end up not preserving any bits for bytes that are in

** the middle of the rectangle vertically.

*/

if ((ycoTop / 8) == ((ycoBottom-1) / 8)) {

mskEnd = ((1 << (((ycoBottom-1)&0x07)+1)) - 1);

}

else {

mskEnd = 0xFF;

}

if (fTop) {

mskEnd &= ~mskUpper;

}

xcoCur = xcoLeft;

pbDspCur = pbDspLeft;

pbBmpCur = pbBmpLeft;

/* Loop through all of the bytes horizontally making up this stripe

** of the rectangle.

*/

if (bnAlign == 0) {

while (xcoCur < xcoRight) {

*pbDspCur = (*pfnDoRop)(*pbBmpCur, *pbDspCur, mskEnd);

xcoCur += 1;

pbDspCur += 1;

pbBmpCur += 1;

}

}

else {

while (xcoCur < xcoRight) {

bBmp = ((*pbBmpCur) << bnAlign);

if (!fTop) {

bBmp |= ((*(pbBmpCur - dxco) >> (8-bnAlign)) & ~mskLower);

}

bBmp &= mskEnd;

*pbDspCur = (*pfnDoRop)(bBmp, *pbDspCur, mskEnd);

xcoCur += 1;

pbDspCur += 1;

pbBmpCur += 1;

}

}

/* Advance to the next horizontal stripe.

*/

ycoTop = 8*((ycoTop/8)+1);

pbDspLeft += ccolOledMax;

pbBmpLeft += dxco;

fTop = 0;

}

}

void OledDrawChar(char ch)

{

alt_u8 * pbFont;

alt_u8 * pbBmp;

int ib;

if ((ch & 0x80) != 0) {

return;

}

if (ch < chOledUserMax) {

pbFont = pbOledFontUser + ch*cbOledChar;

}

else if ((ch & 0x80) == 0) {

pbFont = pbOledFontCur + (ch-chOledUserMax) * cbOledChar;

}

pbBmp = pbOledCur;

OledPutBmp(dxcoOledFontCur, dycoOledFontCur, pbFont);

xcoOledCur += dxcoOledFontCur;

}

void OledDrawString(char * sz)

{

while (*sz != '\0') {

OledDrawChar(*sz);

sz += 1;

}

}

alt_u8 OledRopSet(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix)

{

return (bDsp & ~mskPix) | (bPix & mskPix);

}

alt_u8 OledRopOr(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix)

{

return bDsp | (bPix & mskPix);

}

alt_u8 OledRopAnd(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix)

{

return bDsp & (bPix & mskPix);

}

alt_u8 OledRopXor(alt_u8 bPix, alt_u8 bDsp, alt_u8 mskPix)

{

return bDsp ^ (bPix & mskPix);

}

void OledMoveUp(void)

{

/* Go up one bit position in the current byte.

*/

bnOledCur -= 1;

/* If we have gone off the end of the current byte

** go up 1 page.

*/

if (bnOledCur < 0) {

bnOledCur = 7;

pbOledCur -= ccolOledMax;

/* If we have gone off of the top of the display,

** go back down.

*/

if (pbOledCur < rgbOledBmp) {

pbOledCur += ccolOledMax;

}

}

}

void OledMoveDown(void)

{

/* Go down one bit position in the current byte.

*/

bnOledCur += 1;

/* If we have gone off the end of the current byte,

** go down one page in the display memory.

*/

if (bnOledCur > 7) {

bnOledCur = 0;

pbOledCur += ccolOledMax;

/* If we have gone off the end of the display memory

** go back up a page.

*/

if (pbOledCur >= rgbOledBmp+cbOledDispMax) {

pbOledCur -= ccolOledMax;

}

}

}

void OledMoveLeft(void)

{

/* Are we at the left edge of the display already

*/

if (((pbOledCur - rgbOledBmp) & (ccolOledMax-1)) == 0) {

return;

}

/* Not at the left edge, so go back one byte.

*/

pbOledCur -= 1;

}

void OledMoveRight(void)

{

/* Are we at the right edge of the display already

*/

if (((pbOledCur-rgbOledBmp) & (ccolOledMax-1)) == (ccolOledMax-1)) {

return;

}

/* Not at the right edge, so go forward one byte

*/

pbOledCur += 1;

}

int OledClampXco(int xco)

{

if (xco < 0) {

xco = 0;

}

if (xco >= ccolOledMax) {

xco = ccolOledMax-1;

}

return xco;

}

int OledClampYco(int yco)

{

if (yco < 0) {

yco = 0;

}

if (yco >= crowOledMax) {

yco = crowOledMax-1;

}

return yco;

}

// character interface functions

void OledSetCursor(int xch, int ych)

{

/* Clamp the specified location to the display surface

*/

if (xch >= xchOledMax) {

xch = xchOledMax-1;

}

if (ych >= ychOledMax) {

ych = ychOledMax-1;

}

/* Save the given character location.

*/

xchOledCur = xch;

ychOledCur = ych;

/* Convert the character location to a frame buffer address.

*/

OledMoveTo(xch*dxcoOledFontCur, ych*dycoOledFontCur);

}

void OledGetCursor( int * pxch, int * pych)

{

*pxch = xchOledCur;

*pych = ychOledCur;

}

int OledDefUserChar(char ch, alt_u8 * pbDef)

{

alt_u8 * pb;

int ib;

if (ch < chOledUserMax) {

pb = pbOledFontUser + ch * cbOledChar;

for (ib = 0; ib < cbOledChar; ib++) {

*pb++ = *pbDef++;

}

return 1;

}

else {

return 0;

}

}

void OledSetCharUpdate(int f)

{

fOledCharUpdate = (f != 0) ? 1 : 0;

}

int OledGetCharUpdate(void)

{

return fOledCharUpdate;

}

void OledPutChar(char ch)

{

OledDrawGlyph(ch);

OledAdvanceCursor();

if (fOledCharUpdate) {

OledUpdate();

}

}

void OledPutString(char * sz)

{

while (*sz != '\0') {

OledDrawGlyph(*sz);

OledAdvanceCursor();

sz += 1;

}

if (fOledCharUpdate) {

OledUpdate();

}

}

void OledDrawGlyph(char ch)

{

alt_u8 * pbFont;

alt_u8 * pbBmp;

int ib;

if ((ch & 0x80) != 0) {

return;

}

if (ch < chOledUserMax) {

pbFont = pbOledFontUser + ch*cbOledChar;

}

else if ((ch & 0x80) == 0) {

pbFont = pbOledFontCur + (ch-chOledUserMax) * cbOledChar;

}

pbBmp = pbOledCur;

for (ib = 0; ib < dxcoOledFontCur; ib++) {

*pbBmp++ = *pbFont++;

}

}

void OledAdvanceCursor(void)

{

xchOledCur += 1;

if (xchOledCur >= xchOledMax) {

xchOledCur = 0;

ychOledCur += 1;

}

if (ychOledCur >= ychOledMax) {

ychOledCur = 0;

}

OledSetCursor(xchOledCur, ychOledCur);

}

// oled driver

void OledDisplayOn(void)

{

ssd1306_command(SSD1306_DISPLAYON);

}

void OledDisplayOff(void)

{

ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE

}

void OledClear(void)

{

OledClearBuffer();

OledUpdate();

}

void OledClearBuffer(void)

{

int ib;

alt_u8 * pb;

pb = rgbOledBmp;

/* Fill the memory buffer with 0.

*/

for (ib = 0; ib < cbOledDispMax; ib++) {

*pb++ = 0x00;

}

}

void OledUpdate(void)

{

int ipag;

int icol;

alt_u8 * pb;

pb = rgbOledBmp;

for (ipag = 0; ipag < cpagOledMax; ipag++) {

/* Set the page address

*/

ssd1306_command(0x22); //Set page command

ssd1306_command(ipag); //page number

/* Start at the left column

*/

ssd1306_command(0x00); //set low nybble of column

ssd1306_command(0x10); //set high nybble of column

/* Copy this memory page of display data.

*/

OledPutBuffer(ccolOledMax, pb);

pb += ccolOledMax;

}

}

void OledPutBuffer(int cb, alt_u8 * rgbTx)

{

int ib;

// alt_u8 bTmp;

/* Write/Read the data

*/

for (ib = 0; ib < cb; ib++) {

ssd1306_data(*rgbTx++);

}

}

下記コードはHWインターフェースの処理です。これも追加します。 (OLED Hardware Access Routine)

OLED Hardware Interface

/*---------------------------------------------------------------------------------------

* OLED Interface (128x32,spi)

*---------------------------------------------------------------------------------------

*/

#define OLED_PORT_DC 0

#define OLED_PORT_RESET 1

#define OLED_PORT_VBATC 2

#define OLED_PORT_VDDC 3

alt_u8 value_oled_port = 0;

void OLED_port_init(void)

{

IOWR(OLED_PIO_BASE, 0, value_oled_port);

}

// 0: data/command 0=command, 1=data

// 1: reset 0=Assert, 1=DeAssert

// 2: vbatc 1=active

// 3: vddc 1=active

void OLED_port_set(alt_u8 num)

{

value_oled_port |= (1 << num);

//printf("value_oled_port = 0x%0X\n", value_oled_port);

IOWR(OLED_PIO_BASE, 0, value_oled_port);

}

void OLED_port_clear(alt_u8 num)

{

value_oled_port &= ~(1 << num) & 0xF;

//printf("value_oled_port = 0x%0X\n", value_oled_port);

IOWR(OLED_PIO_BASE, 0, value_oled_port);

}

void OLED_spi_tx(alt_u8 spi_wrdata)

{

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

}

#define SSD1306_SETCONTRAST 0x81

#define SSD1306_DISPLAYALLON_RESUME 0xA4

#define SSD1306_DISPLAYALLON 0xA5

#define SSD1306_NORMALDISPLAY 0xA6

#define SSD1306_INVERTDISPLAY 0xA7

#define SSD1306_DISPLAYOFF 0xAE

#define SSD1306_DISPLAYON 0xAF

#define SSD1306_SETDISPLAYOFFSET 0xD3

#define SSD1306_SETCOMPINS 0xDA

#define SSD1306_SETVCOMDETECT 0xDB

#define SSD1306_SETDISPLAYCLOCKDIV 0xD5

#define SSD1306_SETPRECHARGE 0xD9

#define SSD1306_SETMULTIPLEX 0xA8

#define SSD1306_SETLOWCOLUMN 0x00

#define SSD1306_SETHIGHCOLUMN 0x10

#define SSD1306_SETSTARTLINE 0x40

#define SSD1306_MEMORYMODE 0x20

#define SSD1306_COLUMNADDR 0x21

#define SSD1306_PAGEADDR 0x22

#define SSD1306_COMSCANINC 0xC0

#define SSD1306_COMSCANDEC 0xC8

//#define SSD1306_SEGREMAP 0xA0

#define SSD1306_CHARGEPUMP 0x8D

#define SSD1306_EXTERNALVCC 0x01

#define SSD1306_SWITCHCAPVCC 0x02

#define SSD1306_SEGREMAP 0xA1

void ssd1306_command(alt_u8 c)

{

OLED_port_clear(OLED_PORT_DC);

OLED_spi_tx(c);

}

void ssd1306_data(alt_u8 c)

{

OLED_port_set(OLED_PORT_DC);

OLED_spi_tx(c);

}

OLED初期化処理 (OLED Initialize)

OLED Initialize

void OLED_DSP_Init(void)

{

int ib;

/* Init the parameters for the default font

*/

dxcoOledFontCur = cbOledChar;

dycoOledFontCur = 8;

pbOledFontCur = (alt_u8*)rgbOledFont0;

pbOledFontUser = rgbOledFontUser;

for (ib = 0; ib < cbOledFontUser; ib++) {

rgbOledFontUser[ib] = 0;

}

xchOledMax = ccolOledMax / dxcoOledFontCur;

ychOledMax = crowOledMax / dycoOledFontCur;

/* Set the default character cursor position.

*/

OledSetCursor(0, 0);

/* Set the default foreground draw color and fill pattern

*/

clrOledCur = 0x01;

pbOledPatCur = (alt_u8*)rgbFillPat;

OledSetDrawMode(modOledSet);

/* Default the character routines to automaticall

** update the display.

*/

fOledCharUpdate = 1;

}

void OLED_Init(void)

{

OLED_port_init();

OLED_port_set(OLED_PORT_VBATC);

OLED_port_set(OLED_PORT_VDDC);

OLED_port_set(OLED_PORT_RESET);

delay(1);

OLED_port_clear(OLED_PORT_DC);

OLED_port_clear(OLED_PORT_VDDC);

delay(1);

ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE

// bring reset low and then high

OLED_port_clear(OLED_PORT_RESET);

delay(5);

OLED_port_set(OLED_PORT_RESET);

delay(5);

// send the set charge pump and set pre-charge period commands

ssd1306_command(0x8D); // from univision datasheet, not in SSD1306 datasheet

ssd1306_command(0x14);

ssd1306_command(0xD9); // from univision datasheet, not in SSD1306 datasheet

ssd1306_command(0xF1);

// turn on vcc and wait 100ms

OLED_port_clear(OLED_PORT_VBATC);

delay(100);

// send the commands to invert the display

ssd1306_command(SSD1306_SEGREMAP);

ssd1306_command(SSD1306_COMSCANDEC);

// send the commands to select sequential COM configuration

ssd1306_command(SSD1306_SETCOMPINS);

ssd1306_command(0x20);

ssd1306_command(SSD1306_DISPLAYON);

}

void OLED_invertDisplay(alt_u8 i)

{

if (i) {

ssd1306_command(SSD1306_INVERTDISPLAY);

} else {

ssd1306_command(SSD1306_NORMALDISPLAY);

}

}

void OLED_dim(alt_u8 dim)

{

alt_u8 contrast;

if (dim) {

contrast = 0; // Dimmed display

} else {

contrast = 0xCF;

}

// the range of contrast to too small to be really useful

// it is useful to dim the display

ssd1306_command(SSD1306_SETCONTRAST);

ssd1306_command(contrast);

}

メイン作成例(Example Code)

OLED Examples

int main(int argc, char* argv[])

{

char display_buffer[32];

alt_u32 counter;

OLED_Init();

OLED_invertDisplay(0);

OLED_dim(0);

OLED_DSP_Init();

OledClear();

sprintf(display_buffer, "%s", "PMOD OLED Test");

OledSetCursor(0,0);

OledPutString(display_buffer);

counter = 0;

while (1) {

sprintf(display_buffer, "%012d", counter);

OledSetCursor(0,1);

OledPutString(display_buffer);

counter++;

}

return 0;

}