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;
}