// 色の定義
#define BLACK 0x0000
void setup(void) {
display.begin();
display.fillScreen(BLACK);
}
void loop() {
}
#define BLACK 0x0000
void setup(void) {
display.begin();
display.fillScreen(BLACK);
}
void loop() {
}
今回、調べるのはこの中の、
display.fillScreen(BLACK);
の部分。
以下は、これまでに確認している変数の一覧。
uint8_t _cs | 10(cs) |
uint8_t _rs | 8 (dc) |
uint8_t _sid | 0 |
uint8_t _sclk | 0 |
uint8_t _rst | 9(rst) |
int16_t _width | 96(WIDTH) |
int16_t _height | 64(HEIGHT) |
uint8_t rotation | 0 |
int16_t cursor_y | 0 |
int16_t cursor_x | 0 |
uint8_t textsize | 1 |
uint16_t textcolor | 0xFFFF |
uint16_t textbgcolor | 0xFFFF |
boolean wrap | true |
boolean _cp437 | false |
gfxFont | NULL |
"fillScreen()"は、"Adafruit_GFX.cpp"の中に見つかったので確認して行きます。
"fillRect()"も同じファイルの中に記載されているので、調べましょう。
順番に追いかけます。
"startWrite()"も同じファイル内に見つかりました。
ん。。何もないですね。
ちなみに、"endWrite()"も先に見ておきます。
似たようなものですね。これから先に出てくる場合はスルーで良さそうです。
次にfor文のループ箇所。
0〜96までのループで、"writeFastVLine()"を繰り返し実行するようです。
ここから先では実数があった方が良いのでループの数値iを48と固定しておきます。
writeからdrawに変わっただけの"drawFastVLine()"を調べます。
"startWrite()"、"endWrite()"の二つはスルーして、
"writeLine()"を確認。
ここで、コードを追って見ます。
"steep = abs(y1 - y0) > abs(x1 - x0)"
代入しているだけなのですが、気になるのは比較演算子">"かと思います。
比較演算子の条件が成立するとtrue(1)が返り、不成立だとfalse(blank)が返る単純なものです。
abs(63 - 0) > abs(48 - 48) = 63 > 0なので、"1"です。
以上により、
"if (steep) { ~ }"
の条件分岐が成立するので、
"_swap_int16_t(x0, y0);"
"_swap_int16_t(x1, y1);"
をマクロに当嵌め、
x0 = 0、y0 = 48、
x1 = 63、y1 = 48となります。
"if ( x0 > x1 ) { ~ }"の条件分岐は不成立なのでスルー。
dx = x1 - x0 = 63 - 0 = 63
dy = abs( y1 - y0 ) = abs( 48 - 48 ) = 0
err = dx / 2 = 63 / 2 = 31.5
"if ( y0 < y1 ) { ystep = 1; } else { ystep = -1; }"
( y0 < y1 ) = ( 48 < 48 )条件分岐は不成立なので、ystep = -1。
"for (; x0<=x1; x0++) { ~ }"
初期値はX0=0なので省略されていますが、X0が0〜63までのループ処理。
ループ内部では、
"if (steep) { writePixel(y0, x0, color); } else { writePixel(x0, y0, color); }"
条件分岐が成立するので、
"writePixel(y0, x0, color);"
が実行される。
"err -= dy"は、err = err - dy = 31.5 - 0 なので変化なし。
続く"if ( err < 0 ) { ~ }"も不成立になるのでスルー
以上で動作確認は終了。と言うことで、"writePixel()"を調査。
なんだか元どおりになってる気もしますが理由があるのでしょう。
この値をさらに"drawPixel()"で実行するわけですが、こちらは"Adafruit_SSD1331.cpp"の中で見つけられます。
コードを追いながら細かい箇所を調べて行きます。
まずは、
" if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return;"
との情報から、
次は、"switch (getRotation()) { ~ }"の"getRotation()"です。
これらも初期化処理で取得した"rotation"の値を返しているだけです。
breakキーワードはswitchステートメントを終了し、通常は各ケースの終わりに使用されます。breakステートメントがなければ、switchステートメントはブレークまたはswitchステートメントの終わりに達するまで、次の式の実行を続行します( "fall-through")。
case label1:
// ステートメント
break;
case label2:
// ステートメント
break;
default:
// ステートメント
}
label1, label2: 定数
使用できるデータ型: int, char
次に"Adafruit_SSD1331.h"で"gfx_swap()"のマクロを発見。
内容としては、先の"_swap_int16_t()"とほぼ同じです。
唯一異なるのは、こちらは"符号なし整数"の取り扱いになってる点でしょうか。
情報も集まったところで、コードの調査に戻ります。
"rotation"の値は、0なので"getRotation()"も0になり、"case"の条件に該当するものがありません。よって、"switch (getRotation()) { ~ }"はスルーとなります。
続く、"goTo(x, y);"での引数は前処理がスルーだったので、変化なく引き継がれます。とりあえず、"goTo()"で何が行われるのか見て見ましょう。
"goTo()"での処理は以上。
"*rsportreg |= rspin;"
"*csportreg &= ~ cspin;"
この辺りは、前回にも書きましたが再度確認。
7.2 コマンドデコーダ
このモジュールは、入力をD / C#ピンの入力に基づいてデータまたはコマンドとして解釈する必要があるかどうかを決定します。D/C#ピンがHIGHの場合、データはグラフィック表示データRAM(GDDRAM)に書き込まれます。D/C#がLOWの場合、D0〜D15の入力はコマンドとして解釈され、デコードされて対応するコマンドレジスタに書き込まれます。
以上を参考に確認すると、
"*rsportreg"ポインタ(D/C#)をHIGH、"*csportreg"ポインタ(CS#)をLOW
より、"Write data"に設定されていることがわかります。
次に来る"spiwrite(color >> 8);"では、ビットシフト演算子が出てきます。
行なっているのは、8ビット右にシフトさせているだけです。
color = 0x0000なので、8ビットシフトさせても0です。
正確には、0x00になりますが、あまり意味があるとも思えません。
実験的にcolor = 0xF81Fで試してみましょう。
"1111100000011111"を8ビット右にシフトさせるので、"11111000"となり、
0xF8になります。まぁ、2byteデータを1byteにしてるだけですかね。
その1byteの値を最初に送信し、その後に"spiwrite(color);"で2byte全体を送信しています。
で、最後に"*csportreg |= cspin;"で"*csportreg"ポインタをHIGHに戻して"Write data"を解除しています。
void Adafruit_GFX::fillScreen(uint16_t color) {
// 必要に応じてサブクラスで更新してください!
fillRect(0, 0, _width, _height, color);
}
// 必要に応じてサブクラスで更新してください!
fillRect(0, 0, _width, _height, color);
}
uint16_t color | 2 バイトの符号なし整数 color | 0x0000(BLACK) |
"fillRect()"も同じファイルの中に記載されているので、調べましょう。
void Adafruit_GFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) {
// 必要に応じてサブクラスで更新してください!
startWrite();
for (int16_t i=x; i<x+w; i++) {
writeFastVLine(i, y, h, color);
}
endWrite();
}
uint16_t color) {
// 必要に応じてサブクラスで更新してください!
startWrite();
for (int16_t i=x; i<x+w; i++) {
writeFastVLine(i, y, h, color);
}
endWrite();
}
int16_t x | 2 バイトの符号付き整数 x | 0 |
int16_t y | 2 バイトの符号付き整数 y | 0 |
int16_t w | 2 バイトの符号付き整数 w | 96(_width) |
int16_t h | 2 バイトの符号付き整数 h | 64(_height) |
uint16_t y | 2 バイトの符号なし整数 color | 0x0000(BLACK) |
順番に追いかけます。
"startWrite()"も同じファイル内に見つかりました。
void Adafruit_GFX::startWrite(){
// 必要に応じてサブクラスで上書きしてください!
}
// 必要に応じてサブクラスで上書きしてください!
}
ん。。何もないですね。
ちなみに、"endWrite()"も先に見ておきます。
void Adafruit_GFX::endWrite(){
// startWrite が定義されている場合は、サブクラスで上書きしてください!
}
// startWrite が定義されている場合は、サブクラスで上書きしてください!
}
似たようなものですね。これから先に出てくる場合はスルーで良さそうです。
次にfor文のループ箇所。
0〜96までのループで、"writeFastVLine()"を繰り返し実行するようです。
ここから先では実数があった方が良いのでループの数値iを48と固定しておきます。
// (x,y) は最上点; 不明な場合は、呼び出し側の関数はエンドポイントをソートするか、
// 代わりに writeLine() を呼び出す必要があります
void Adafruit_GFX::writeFastVLine(int16_t x, int16_t y,
int16_t h, uint16_t color) {
// startWriteが定義されている場合は、サブクラスで上書きしてください!
// writeLine(x, y, x, y+h-1, color); もしくは writeFillRect(x, y, 1, h, color); でも構いません。
drawFastVLine(x, y, h, color);
}
// 代わりに writeLine() を呼び出す必要があります
void Adafruit_GFX::writeFastVLine(int16_t x, int16_t y,
int16_t h, uint16_t color) {
// startWriteが定義されている場合は、サブクラスで上書きしてください!
// writeLine(x, y, x, y+h-1, color); もしくは writeFillRect(x, y, 1, h, color); でも構いません。
drawFastVLine(x, y, h, color);
}
int16_t x | 2 バイトの符号付き整数 x | 48(i) |
int16_t y | 2 バイトの符号付き整数 y | 0 |
int16_t h | 2 バイトの符号付き整数 h | 64(_height) |
uint16_t color | 2 バイトの符号なし整数 color | 0x0000(BLACK) |
writeからdrawに変わっただけの"drawFastVLine()"を調べます。
// (x,y) は最上点である。 不明な場合は、呼び出し側の関数がエンドポイントをソートするか、
// 代わりに drawLine() を呼び出す必要があります
void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y,
int16_t h, uint16_t color) {
// 必要に応じてサブクラスで更新してください!
startWrite();
writeLine(x, y, x, y+h-1, color);
endWrite();
}
// 代わりに drawLine() を呼び出す必要があります
void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y,
int16_t h, uint16_t color) {
// 必要に応じてサブクラスで更新してください!
startWrite();
writeLine(x, y, x, y+h-1, color);
endWrite();
}
int16_t x | 2 バイトの符号付き整数 x | 48(i) |
int16_t y | 2 バイトの符号付き整数 y | 0 |
int16_t h | 2 バイトの符号付き整数 h | 64(_height) |
uint16_t color | 2 バイトの符号なし整数 color | 0x0000(BLACK) |
"startWrite()"、"endWrite()"の二つはスルーして、
"writeLine()"を確認。
// Bresenham's algorithm - thx wikpedia
void Adafruit_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color) {
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
_swap_int16_t(x0, y0);
_swap_int16_t(x1, y1);
}
if (x0 > x1) {
_swap_int16_t(x0, x1);
_swap_int16_t(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t err = dx / 2;
int16_t ystep;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
for (; x0<=x1; x0++) {
if (steep) {
writePixel(y0, x0, color);
} else {
writePixel(x0, y0, color);
}
err -= dy;
if (err < 0) {
y0 += ystep;
err += dx;
}
}
}
void Adafruit_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color) {
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
_swap_int16_t(x0, y0);
_swap_int16_t(x1, y1);
}
if (x0 > x1) {
_swap_int16_t(x0, x1);
_swap_int16_t(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t err = dx / 2;
int16_t ystep;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
for (; x0<=x1; x0++) {
if (steep) {
writePixel(y0, x0, color);
} else {
writePixel(x0, y0, color);
}
err -= dy;
if (err < 0) {
y0 += ystep;
err += dx;
}
}
}
int16_t x0 | 2 バイトの符号付き整数 x0 | 48(i) |
int16_t y0 | 2 バイトの符号付き整数 y0 | 0 |
int16_t x1 | 2 バイトの符号付き整数 x1 | 48(i) |
int16_t y1 | 2 バイトの符号付き整数 y1 | 0+64(_height)-1 |
uint16_t color | 2 バイトの符号なし整数 color | 0x0000(BLACK) |
int16_t steep | 2 バイトの符号付き整数 steep | abs(y1 - y0) = 63 > abs(x1 - x0) = 0 |
int16_t dx | 2 バイトの符号付き整数 dx | x1 - x0 |
int16_t dy | 2 バイトの符号付き整数 dy | abs(y1 - y0) |
int16_t err | 2 バイトの符号付き整数 err | dx / 2 |
int16_t ystep | 2 バイトの符号付き整数 ystep |
これは基本的な関数ですが、念のため掲載しておきます。
次のマクロは、引数の二値を入れ替えるためだけのものです。
abs()
[Math]
説明
数値の絶対値を計算する
構文
abs(x)
パラメータ
x: 数値
戻り値
x: xが0以上の場合
-x:xが0より小さい場合
Notes and Warnings
abs()関数が実装されているため、角カッコ内の他の関数の使用は避けてください。結果が正しくない可能性があります。
abs(a++); // これを避ける - 不正な結果となります
abs(a); // これを代わりに使用する -
a++; // 他の[Math]関数は外に置くようにします
[Math]
説明
数値の絶対値を計算する
構文
abs(x)
パラメータ
x: 数値
戻り値
x: xが0以上の場合
-x:xが0より小さい場合
Notes and Warnings
abs()関数が実装されているため、角カッコ内の他の関数の使用は避けてください。結果が正しくない可能性があります。
abs(a++); // これを避ける - 不正な結果となります
abs(a); // これを代わりに使用する -
a++; // 他の[Math]関数は外に置くようにします
次のマクロは、引数の二値を入れ替えるためだけのものです。
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
ここで、コードを追って見ます。
"steep = abs(y1 - y0) > abs(x1 - x0)"
代入しているだけなのですが、気になるのは比較演算子">"かと思います。
比較演算子の条件が成立するとtrue(1)が返り、不成立だとfalse(blank)が返る単純なものです。
abs(63 - 0) > abs(48 - 48) = 63 > 0なので、"1"です。
以上により、
"if (steep) { ~ }"
の条件分岐が成立するので、
"_swap_int16_t(x0, y0);"
"_swap_int16_t(x1, y1);"
をマクロに当嵌め、
x0 = 0、y0 = 48、
x1 = 63、y1 = 48となります。
"if ( x0 > x1 ) { ~ }"の条件分岐は不成立なのでスルー。
dx = x1 - x0 = 63 - 0 = 63
dy = abs( y1 - y0 ) = abs( 48 - 48 ) = 0
err = dx / 2 = 63 / 2 = 31.5
"if ( y0 < y1 ) { ystep = 1; } else { ystep = -1; }"
( y0 < y1 ) = ( 48 < 48 )条件分岐は不成立なので、ystep = -1。
"for (; x0<=x1; x0++) { ~ }"
初期値はX0=0なので省略されていますが、X0が0〜63までのループ処理。
ループ内部では、
"if (steep) { writePixel(y0, x0, color); } else { writePixel(x0, y0, color); }"
条件分岐が成立するので、
"writePixel(y0, x0, color);"
が実行される。
"err -= dy"は、err = err - dy = 31.5 - 0 なので変化なし。
続く"if ( err < 0 ) { ~ }"も不成立になるのでスルー
以上で動作確認は終了。と言うことで、"writePixel()"を調査。
void Adafruit_GFX::writePixel(int16_t x, int16_t y, uint16_t color){
// startWriteが定義されている場合は、サブクラスで上書きしてください!
drawPixel(x, y, color);
}
// startWriteが定義されている場合は、サブクラスで上書きしてください!
drawPixel(x, y, color);
}
int16_t x | 2 バイトの符号付き整数 x | 48(i) |
int16_t y | 2 バイトの符号付き整数 y | 0 |
uint16_t color | 2 バイトの符号なし整数 color | 0x0000(BLACK) |
なんだか元どおりになってる気もしますが理由があるのでしょう。
この値をさらに"drawPixel()"で実行するわけですが、こちらは"Adafruit_SSD1331.cpp"の中で見つけられます。
void Adafruit_SSD1331::drawPixel(int16_t x, int16_t y, uint16_t color)
{
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return;
// 回転を確認し、必要に応じてピクセルを周りに移動する
switch (getRotation()) {
case 1:
gfx_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
gfx_swap(x, y);
y = HEIGHT - y - 1;
break;
}
goTo(x, y);
// データの設定
*rsportreg |= rspin;
*csportreg &= ~ cspin;
spiwrite(color >> 8);
spiwrite(color);
*csportreg |= cspin;
}
{
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return;
// 回転を確認し、必要に応じてピクセルを周りに移動する
switch (getRotation()) {
case 1:
gfx_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
gfx_swap(x, y);
y = HEIGHT - y - 1;
break;
}
goTo(x, y);
// データの設定
*rsportreg |= rspin;
*csportreg &= ~ cspin;
spiwrite(color >> 8);
spiwrite(color);
*csportreg |= cspin;
}
int16_t x | 2 バイトの符号付き整数 x | 48(i) |
int16_t y | 2 バイトの符号付き整数 y | 0 |
uint16_t color | 2 バイトの符号なし整数 color | 0x0000(BLACK) |
コードを追いながら細かい箇所を調べて行きます。
まずは、
" if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return;"
上のコードに含まれる関数を探していると、
"Adafruit_GFX.cpp"に"width()"、"height()"がありました。
初期化処理で取得した"_width"、"_height"の値を返しているだけですね。
"Adafruit_GFX.cpp"に"width()"、"height()"がありました。
// ディスプレイのサイズを返します(現在の回転ごとに)
int16_t Adafruit_GFX::width(void) const {
return _width;
}
int16_t Adafruit_GFX::height(void) const {
return _height;
}
int16_t Adafruit_GFX::width(void) const {
return _width;
}
int16_t Adafruit_GFX::height(void) const {
return _height;
}
初期化処理で取得した"_width"、"_height"の値を返しているだけですね。
との情報から、
- ( x < 0 ) = ( 48 < 0 ) : false
- ( x >= width() ) = ( 48 >= 96 ) : false
- ( y < 0 ) = ( 0 < 0 ) : false
- ( y >= height() ) = ( 0 >= 64 ) : false
次は、"switch (getRotation()) { ~ }"の"getRotation()"です。
uint8_t Adafruit_GFX::getRotation(void) const {
return rotation;
}
return rotation;
}
これらも初期化処理で取得した"rotation"の値を返しているだけです。
switch...case
[Control Structure]
説明
if文と同様に、switch caseはプログラマが様々な条件で実行すべき異なるコードを指定できるようにすることで、プログラムの流れを制御します。特に、switchステートメントは、変数の値をcaseステートメントで指定された値と比較します。変数の値と値が一致するcaseステートメントが見つかると、そのcaseステートメントのコードが実行されます。breakキーワードはswitchステートメントを終了し、通常は各ケースの終わりに使用されます。breakステートメントがなければ、switchステートメントはブレークまたはswitchステートメントの終わりに達するまで、次の式の実行を続行します( "fall-through")。
構文
switch (var) {case label1:
// ステートメント
break;
case label2:
// ステートメント
break;
default:
// ステートメント
}
パラメータ
var: さまざまなケースと比較する値
使用できるデータ型: int, char label1, label2: 定数
使用できるデータ型: int, char
戻り値
Nothing
次に"Adafruit_SSD1331.h"で"gfx_swap()"のマクロを発見。
#define gfx_swap(a, b) { uint16_t t = a; a = b; b = t; }
内容としては、先の"_swap_int16_t()"とほぼ同じです。
唯一異なるのは、こちらは"符号なし整数"の取り扱いになってる点でしょうか。
"rotation"の値は、0なので"getRotation()"も0になり、"case"の条件に該当するものがありません。よって、"switch (getRotation()) { ~ }"はスルーとなります。
続く、"goTo(x, y);"での引数は前処理がスルーだったので、変化なく引き継がれます。とりあえず、"goTo()"で何が行われるのか見て見ましょう。
"goTo()"は、"Adafruit_SSD1331.cpp"に記載されていました。
WIDTHとHEIGHTは値を設定している箇所が見当たらないので、詳細がわかりませんが、恐らくWIDTH:96/HEIGHT:64なのだと思います。
内容を見て行きます。まずは、
"if ( ( x >= WIDTH ) || ( y >= HEIGHT ) ) return;"
ですが、
"if ( ( 48 >= 96 ) || ( 0 >= 64 ) ) return;"
と解釈できるので、条件不成立となりスルー。
xとy座標を設定する処理で呼び出されている"writeCommand"の詳細は既に前回出ているので省略して、定数について調べます。
"writeCommand(SSD1331_CMD_SETCOLUMN);"
で、列アドレスの指定が可能な状態となります。
続いて、"writeCommand(x);"、"writeCommand(WIDTH-1);"で列の範囲(アドレス)を指定します。ここでは、48〜96が選択されます。
その後の"writeCommand(SSD1331_CMD_SETROW);"では、
行アドレスの指定が可能な状態として、
"writeCommand(y);"、"writeCommand(HEIGHT-1);"で行の範囲(アドレス)を指定します。0〜63が選択範囲。
void Adafruit_SSD1331::goTo(int x, int y) {
if ((x >= WIDTH) || (y >= HEIGHT)) return;
// xとy座標を設定する
writeCommand(SSD1331_CMD_SETCOLUMN);
writeCommand(x);
writeCommand(WIDTH-1);
writeCommand(SSD1331_CMD_SETROW);
writeCommand(y);
writeCommand(HEIGHT-1);
}
if ((x >= WIDTH) || (y >= HEIGHT)) return;
// xとy座標を設定する
writeCommand(SSD1331_CMD_SETCOLUMN);
writeCommand(x);
writeCommand(WIDTH-1);
writeCommand(SSD1331_CMD_SETROW);
writeCommand(y);
writeCommand(HEIGHT-1);
}
int x | 4 バイトの整数 x | 48(i) |
int y | 4 バイトの整数 y | 0 |
int16_t WIDTH | 2 バイトの符号付き整数 WIDTH | 96 ? |
int16_t HEIGHT | 2 バイトの符号付き整数 HEIGHT | 64 ? |
WIDTHとHEIGHTは値を設定している箇所が見当たらないので、詳細がわかりませんが、恐らくWIDTH:96/HEIGHT:64なのだと思います。
内容を見て行きます。まずは、
"if ( ( x >= WIDTH ) || ( y >= HEIGHT ) ) return;"
ですが、
"if ( ( 48 >= 96 ) || ( 0 >= 64 ) ) return;"
と解釈できるので、条件不成立となりスルー。
xとy座標を設定する処理で呼び出されている"writeCommand"の詳細は既に前回出ているので省略して、定数について調べます。
#define SSD1331_CMD_SETCOLUMN | 0x15 | ||||||||||||||||||||
x | 48 | ||||||||||||||||||||
WIDTH - 1 | 95 | ||||||||||||||||||||
列の開始アドレスと終了アドレスの設定 A[6:0] 00d-95d から開始アドレス B[6:0] 00d-95d の終了アドレス 9.1.1 列アドレスの設定 (15h) このコマンドは、表示データRAMの列開始アドレスと終了アドレスを指定します。このコマンドは、列アドレスポインタを列開始アドレスに設定します。このポインタは、グラフィック表示データRAM内の現在の読み出し/書き込み列アドレスを定義するために使用されます。コマンドA0hによって水平アドレスインクリメントモードがイネーブルされている場合、1カラムデータの読み書きを完了した後、次のカラムアドレスに自動的にインクリメントされます。列アドレスポインタが終了列アドレスへのアクセスを終了すると、開始列アドレスにリセットされる。 |
#define SSD1331_CMD_SETROW | 0x75 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
y | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HEIGHT - 1 | 63 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
行の開始アドレスと終了アドレスの設定 A[6:0] 00d-95d から開始アドレス B[6:0] 00d-95d の終了アドレス 9.1.2 行アドレスの設定 (75h) このコマンドは、表示データRAMの行開始アドレスと終了アドレスを指定します。このコマンドは、行アドレスポインタを行開始アドレスに設定します。このポインタは、グラフィック表示データRAM内の現在の読み出し/書き込み行アドレスを定義するために使用されます。コマンドA0hによって垂直アドレスインクリメントモードがイネーブルされている場合、1行データの読み書きを終了した後、次の行アドレスに自動的にインクリメントされます。 ロウアドレスポインタが終了ロウアドレスへのアクセスを終了すると、ロウアドレスポインタは開始ロウアドレスにリセットされる。 下の図は、例による列アドレスと行アドレスのポインタの移動方法を示しています。列開始アドレスが2に設定され、列終了アドレスが93に設定され、行開始アドレスが1に設定され、行終了アドレスが62に設定されます。水平アドレスインクリメントモードは、コマンドA0hによってイネーブルされます。この場合、グラフィック表示データRAM列のアクセス可能範囲は、列2から列93、行1から行62のみである。さらに、列アドレスポインタは2にセットされ、行アドレスポインタは1にセットされる。1ピクセルのデータの読み取り/書き込みを完了した後、次の読み取り/書き込み操作のために次のRAM位置にアクセスするために、列アドレスが自動的に1ずつ増加します(図21の実線)。列アドレスポインタが終了列93へのアクセスを終了すると、列アドレスポインタは列2にリセットされ、行アドレスは自動的に1だけ増加される(図21の赤文字)。終了行62および終了列93のRAM位置がアクセスされている間、行アドレスは1にリセットされて戻される(図21の青文字)。
|
"writeCommand(SSD1331_CMD_SETCOLUMN);"
で、列アドレスの指定が可能な状態となります。
続いて、"writeCommand(x);"、"writeCommand(WIDTH-1);"で列の範囲(アドレス)を指定します。ここでは、48〜96が選択されます。
その後の"writeCommand(SSD1331_CMD_SETROW);"では、
行アドレスの指定が可能な状態として、
"writeCommand(y);"、"writeCommand(HEIGHT-1);"で行の範囲(アドレス)を指定します。0〜63が選択範囲。
"goTo()"での処理は以上。
"*rsportreg |= rspin;"
"*csportreg &= ~ cspin;"
この辺りは、前回にも書きましたが再度確認。
Table 8 - シリアルインタフェースの制御端子 | ||||
---|---|---|---|---|
Function | E | R/W# | CS# | D/C# |
Write command | Tie low | Tie low | L | L |
Write data | Tie low | Tie low | L | H |
7.2 コマンドデコーダ
このモジュールは、入力をD / C#ピンの入力に基づいてデータまたはコマンドとして解釈する必要があるかどうかを決定します。D/C#ピンがHIGHの場合、データはグラフィック表示データRAM(GDDRAM)に書き込まれます。D/C#がLOWの場合、D0〜D15の入力はコマンドとして解釈され、デコードされて対応するコマンドレジスタに書き込まれます。
以上を参考に確認すると、
"*rsportreg"ポインタ(D/C#)をHIGH、"*csportreg"ポインタ(CS#)をLOW
より、"Write data"に設定されていることがわかります。
次に来る"spiwrite(color >> 8);"では、ビットシフト演算子が出てきます。
>>
[Bitwise Operators]
説明
右シフト演算子>>は、左オペランドのビットを右オペランドによって指定された位置の数だけ右にシフトさせます。
構文
パラメータ
変数: 使用できるデータ型: byte, int, long
number_of_bits: <= 32の数値。許可されるデータ型: int
コード例
ノートと警告
xをyビット(x >> y)だけ右にシフトし、xの最上位ビットが1の場合、動作はxの正確なデータ型に依存します。xがint型である場合、上で議論したように、最高ビットは符号ビットであり、xが負であるか否かを決定する。その場合、秘密の歴史上の理由から、符号ビットは下位ビットにコピーされます:
符号拡張と呼ばれるこの動作は、しばしば必要な動作ではありません。代わりに、ゼロを左からシフトすることをお勧めします。右シフトルールは符号なしのint式では異なりますので、タイプキャストを使用して左からコピーされているものを抑制することができます:
符号拡張を行わないように注意する場合は、2の累乗で除算する方法として右シフト演算子>>を使用できます。たとえば、次のようにします:
[Bitwise Operators]
説明
右シフト演算子>>は、左オペランドのビットを右オペランドによって指定された位置の数だけ右にシフトさせます。
構文
variable >> number_of_bits;
パラメータ
変数: 使用できるデータ型: byte, int, long
number_of_bits: <= 32の数値。許可されるデータ型: int
コード例
int a = 40; // binary: 0000000000101000
int b = a >> 3; // binary: 0000000000000101, or 5 in decimal
int b = a >> 3; // binary: 0000000000000101, or 5 in decimal
ノートと警告
xをyビット(x >> y)だけ右にシフトし、xの最上位ビットが1の場合、動作はxの正確なデータ型に依存します。xがint型である場合、上で議論したように、最高ビットは符号ビットであり、xが負であるか否かを決定する。その場合、秘密の歴史上の理由から、符号ビットは下位ビットにコピーされます:
int x = -16; // binary: 1111111111110000
int y = 3;
int result = x >> y; // binary: 1111111111111110
int y = 3;
int result = x >> y; // binary: 1111111111111110
符号拡張と呼ばれるこの動作は、しばしば必要な動作ではありません。代わりに、ゼロを左からシフトすることをお勧めします。右シフトルールは符号なしのint式では異なりますので、タイプキャストを使用して左からコピーされているものを抑制することができます:
int x = -16; // binary: 1111111111110000
int y = 3;
int result = (unsigned int)x >> y; // binary: 0001111111111110
int y = 3;
int result = (unsigned int)x >> y; // binary: 0001111111111110
符号拡張を行わないように注意する場合は、2の累乗で除算する方法として右シフト演算子>>を使用できます。たとえば、次のようにします:
int x = 1000;
int y = x >> 3; // integer division of 1000 by 8, causing y = 125.
int y = x >> 3; // integer division of 1000 by 8, causing y = 125.
行なっているのは、8ビット右にシフトさせているだけです。
color = 0x0000なので、8ビットシフトさせても0です。
正確には、0x00になりますが、あまり意味があるとも思えません。
実験的にcolor = 0xF81Fで試してみましょう。
"1111100000011111"を8ビット右にシフトさせるので、"11111000"となり、
0xF8になります。まぁ、2byteデータを1byteにしてるだけですかね。
その1byteの値を最初に送信し、その後に"spiwrite(color);"で2byte全体を送信しています。
で、最後に"*csportreg |= cspin;"で"*csportreg"ポインタをHIGHに戻して"Write data"を解除しています。
以上で画面描画の調査は終了です。
SSD1331を触る上での基本情報は集まったので、次回からライブラリを使わずにベタなコードでここまでの流れを再現して見たいと思います。
0 件のコメント:
コメントを投稿