LED Matrix PanelのESP32用ライブラリをフルスクラッチで作り、室内用と室外用にATH10を二つ接続。RTC(DS3231)をつないで室内外温湿度表示付き時計を作りました。
時計の操作(時刻合わせなど)はロータリスイッチを使い、暗くなったら表示も暗くするためにCdSを使っています。
フルカラーであることを示すために趣味の悪い色使いになっていますが、実際には落ち着いた色にしています。
AHT10はI2Cのカスケード接続です。ケーブルが短い室内はOKでしたが、1m程になる室外はエラーが出て安定しませんでした。フラットケーブルを使って、SDAとSCLの間にGND線を置くようにしたら安定しました。
I2CはRTCとも共有しています。
基板はKiCADで描いて作った自作基板です。基本的にHUB75への接続とRTCの接続でほとんどの使えるピンが埋まってしまうのでボタンをつけることができず、ロータリエンコーダで時刻設定などを行うことにしました。
ソースコードを全て載せると、ソースコードだらけになってしまうので、ポイントのところを抜粋して載せます。
YanelsオブジェクトはMatrix panelの幅x高さ の int16_t バッファint16_t buf[]を持ちます。
ドットの描画は B0RRRRGGGGGBBBBB と各色5bitずつで定義した int16_t を設定します。
バッファプレーンに点を描画する部分です。
void YPanels::drawDot(int16_t x, int16_t y, int16_t color, bool wraproll, bool alpha)
{
if (x < 0)
{
if (wraproll)
{
x = x + PANEL_WIDTH;
}
else
return;
}
else
{
if (x >= PANEL_WIDTH)
{
if (wraproll)
{
x = x - PANEL_WIDTH;
}
else
return;
}
else
{
if (y < 0)
{
if (wraproll)
{
y = y + PANEL_HEIGHT;
}
else
return;
}
else
{
if (y >= PANEL_HEIGHT)
{
if (wraproll)
{
y = y - PANEL_HEIGHT;
}
else
return;
}
}
}
if (alpha)
{
buf[y * PANEL_WIDTH + x] = buf[y * PANEL_WIDTH + x] | color;
}
else
{
buf[y * PANEL_WIDTH + x] = color;
}
}
}
Matrix Panelにバッファプレーンを転送する描画のキモになる部分、輝度bit毎に上から下までスキャンする。
int ganma[5] = {1, 16, 30, 60, 127}; //輝度bit 各諧調ページの光る時間を定義している
//bufの内容を書き出す currRowはスキャンごとの行、 pageは輝度bit
void writeColors()
{
for (byte col = 0; col < PANEL_WIDTH; col++)
{
bool rr1 = buf[currRow * PANEL_WIDTH + col] & (1 << page + 10);
bool rr2 = buf[(currRow + SCAN_COUNT) * PANEL_WIDTH + col] & (1 << page + 10);
bool gg1 = buf[currRow * PANEL_WIDTH + col] & (1 << page + 5);
bool gg2 = buf[(currRow + SCAN_COUNT) * PANEL_WIDTH + col] & (1 << page + 5);
bool bb1 = buf[currRow * PANEL_WIDTH + col] & (1 << page);
bool bb2 = buf[(currRow + SCAN_COUNT) * PANEL_WIDTH + col] & (1 << page);
//CLKをLOWにする
digitalWrite(CLK, LOW); // PB7=CLK
digitalWrite(R1, rr1);
digitalWrite(R2, rr2);
digitalWrite(G1, gg1);
digitalWrite(G2, gg2);
digitalWrite(B1, bb1);
digitalWrite(B2, bb2);
digitalWrite(CLK, HIGH); // PB7=CLK
}
}
//page と currRowを順次進める pageは輝度bit 0~4 currRowはスキャン 0~16
void nextSection()
{
currRow++;
if (currRow == SCAN_COUNT)
{
currRow = 0;
page++;
if (page == 5)
{
page = 0;
}
}
}
//currRowに従ってABCDをセットする
void setScan(byte row)
{
digitalWrite(AA, row & B00000001);
digitalWrite(BB, row & B00000010);
digitalWrite(CC, row & B00000100);
digitalWrite(DD, row & B00001000);
}
//page(輝度ビット)毎に光らせて消して次のスキャンを行う
void IRAM_ATTR onTimer()
{
portENTER_CRITICAL_ISR(&timerMux); // Increment the counter and set the time of ISR
portEXIT_CRITICAL_ISR(&timerMux);
xSemaphoreGiveFromISR(timerSemaphore, NULL);
timerAlarmDisable(timer);
digitalWrite(OE, HIGH); //出力を消す
nextSection();
setScan(currRow);
digitalWrite(LAT, LOW);
writeColors();
digitalWrite(LAT, HIGH);
digitalWrite(OE, LOW);
timerAlarmWrite(timer, ganma[page], true);
timerAlarmEnable(timer);
}
インスタンス生成後にタイマーをactivateする部分
void YPanels::begin() //タイマー初期化をコンストラクタに入れるとうまくいかない
{
timerSemaphore = xSemaphoreCreateBinary(); // Create semaphore to inform us when the timer has fired
timer = timerBegin(0, OE_PSCALE, true); // Use 1st timer of 4 (counted from zero). Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more info).
timerAttachInterrupt(timer, &onTimer, true); // Attach onTimer function to our timer.
timerAlarmWrite(timer, ganma[page], true); // Set alarm to call onTimer function every second (value in microseconds).Repeat the alarm (third parameter)
timerAlarmEnable(timer);
}
ちなみに、ピンアサインは以下の通り
define R1 25
#define G1 26
#define B1 27
#define R2 21
#define G2 22
#define B2 23
#define AA 12
#define BB 16
#define CC 17
#define DD 18
#define CLK 15
#define LAT 32
#define OE 33
投稿者の人気記事
-
eepoo
さんが
2021/02/21
に
編集
をしました。
(メッセージ: 初版)
-
eepoo
さんが
2021/02/21
に
編集
をしました。
-
eepoo
さんが
2021/02/23
に
編集
をしました。
-
eepoo
さんが
2021/02/26
に
編集
をしました。
-
eepoo
さんが
2021/03/13
に
編集
をしました。
ログインしてコメントを投稿する