以前にATtint85にOLEDを繋ぐという投稿をした。
Arduinoプログラミング手法-(6)ATtiny85にOLEDを繋ぐ
OLED(128×64)のフレームバッファとして1024バイト必要であるのに対しATtiny85には512バイト(実質的には300バイト程度が上限)しかRAMがないのでピクセル合成が出来ない。
こういう場合、さっさと諦めてしまうのは普通の人、私は普通じゃない人(いい意味でですよ(笑))なのでなんとかできないものかと考えてしまう。
で、思いついたのが、ATtint85でも描画できる程度に描画領域を分割し、その描画領域を切り替えながら描画が完了するまで繰り替えすというロジックを考えてみた。て、いうか、それ普通に考えるだろう?って、突っ込みはなしで。(笑)
まずは、ATtiny85(digispark)にOLEDを接続し、
前回のSSD1306.hを改造し、次のコード(汚くてすいません)を試験的に追加し、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
#define PAGE_X_SHIFT 3 #define PAGE_Y_SHIFT 3 #define PAGE_X_BITS (7 - PAGE_X_SHIFT) #define PAGE_Y_BITS (6 - PAGE_Y_SHIFT) #if PAGE_X_SHIFT == 0 #define PAGENO(x, y) ((y) >> PAGE_Y_SHIFT) #elif PAGE_X_SHIFT <= PAGE_Y_SHIFT #define PAGENO(x, y) (((x) >> PAGE_X_BITS) + (((y) & ~(_BV(PAGE_Y_SHIFT) - 1)) >> (PAGE_Y_SHIFT - PAGE_X_SHIFT))) #else #define PAGENO(x, y) (((x) >> PAGE_X_BITS) + (((y) & ~(_BV(PAGE_Y_SHIFT) - 1)) << (PAGE_X_SHIFT - PAGE_Y_SHIFT))) #endif typedef struct { int x, y; } POINT; typedef struct { int left, top, right, bottom; } RECT; uint8_t _g_buf[_BV(PAGE_X_BITS + PAGE_Y_BITS ) >> 3]; uint8_t _g_msk[_BV(PAGE_X_SHIFT + PAGE_Y_SHIFT) >> 3]; uint8_t _g_flg[_BV(PAGE_X_SHIFT + PAGE_Y_SHIFT) >> 3]; uint8_t _g_pno; POINT _g_org; RECT _g_cur; bool intersectRect(int x0, int y0, int x1, int y1) { return (x0 < x1 ? (x0 >= _g_cur.right ) || (x1 <= _g_cur.left) : (x0 < _g_cur.left) || (x1 >= _g_cur.right )) || (y0 < y1 ? (y0 >= _g_cur.bottom) || (y1 <= _g_cur.top ) : (y0 < _g_cur.top ) || (y1 >= _g_cur.bottom)) ? false : true; } void setCurPage(uint8_t no) { _g_pno = no; _g_msk[no >> 3] |= _BV(no & 7); #if PAGE_X_SHIFT == 0 _g_cur.left = 0; _g_cur.top = no << PAGE_Y_BITS; #else _g_cur.left = (no & (_BV(PAGE_X_SHIFT) - 1)) << PAGE_X_BITS; #if PAGE_X_SHIFT <= PAGE_Y_BITS _g_cur.top = (no & ~(_BV(PAGE_X_SHIFT) - 1)) << (PAGE_Y_BITS - PAGE_X_SHIFT); #else _g_cur.top = (no & ~(_BV(PAGE_X_SHIFT) - 1)) >> (PAGE_X_SHIFT - PAGE_Y_BITS); #endif #endif _g_cur.right = _g_cur.left + _BV(PAGE_X_BITS); _g_cur.bottom = _g_cur.top + _BV(PAGE_Y_BITS); memset(_g_buf, 0, sizeof(_g_buf)); } void beginPaint() { memset(_g_msk, 0, sizeof(_g_msk)); _g_pno = 0xFF; } bool endPaint() { if (_g_pno != 0xFF) { // // flush page // cursor(_g_cur.left >> 3, _g_cur.top >> 3); ctrl(SSD1306_GDDRAM, 1); uint8_t *p = _g_buf; for (uint8_t x = 0; x < _BV(PAGE_X_BITS); ++x) data(*p++); // // select page // for (uint8_t i = 0; i < _BV(PAGE_X_SHIFT + PAGE_Y_SHIFT); ++i) { if (~_g_msk[i >> 3] & _g_flg[i >> 3] & _BV(i & 7)) { setCurPage(i); return true; } } memcpy(_g_flg, _g_msk, sizeof(_g_flg)); _g_pno = 0xFF; } return false; } bool setPixel(int x, int y) { if ((x >= 0) && (x < SSD1306_MAX_WIDTH) && (y >= 0) && (y < SSD1306_MAX_HEIGHT)) { uint8_t no = PAGENO(x, y); if (_g_pno == 0xFF) setCurPage(no); if (_g_pno == no) { _g_buf[x & (_BV(PAGE_X_BITS) - 1)] |= _BV(y & 7); return true; } _g_flg[no >> 3] |= _BV(no & 7); } return false; } void moveto(int x, int y) { _g_org.x = x; _g_org.y = y; } void lineto(int x, int y) { int x0 = _g_org.x; int y0 = _g_org.y; int dx = abs(x - x0); int dy = abs(y - y0); int sx = (x0 < x ? 1 : -1); int sy = (y0 < y ? 1 : -1); int er = dx - dy; while (dx >= dy ? x0 != x : y0 != y) { if (!setPixel(x0, y0)) { if (!intersectRect(x0, y0, x, y)) break; } int e2 = er + er; if (e2 > -dy) { er -= dy; x0 += sx; } if (e2 < dx) { er += dx; y0 += sy; } } _g_org.x = x; _g_org.y = y; } |
下記サイトを参考に、ワイヤーフレームのボックスを回してみると、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#include "USITWI.h" #include "SSD1306.h" #define OLED_ADDR 0x3C #define TWI_BUS USITWI<USITWI_400K> TWI_BUS bus; SSD1306<TWI_BUS, bus, OLED_ADDR> oled; int x = 0, y = 22<<9; void RotatingCube() { const int Delta = 9; // Approximation to 1 degree in radians * 2^9 int x9 = x>>9, y9 = y>>9, x10 = x>>10, y10 = y>>10; ※ oled.beginPaint(); do { // Top oled.moveto( x9 + 64, y10 + 52); oled.lineto( y9 + 64, -x10 + 52); oled.lineto(-x9 + 64, -y10 + 52); oled.lineto(-y9 + 64, x10 + 52); oled.lineto( x9 + 64, y10 + 52); oled.lineto( x9 + 64, y10 + 28); // Bottom oled.lineto( y9 + 64, -x10 + 28); oled.lineto(-x9 + 64, -y10 + 28); oled.lineto(-y9 + 64, x10 + 28); oled.lineto( x9 + 64, y10 + 28); // Sides oled.moveto( y9 + 64, -x10 + 52); oled.lineto( y9 + 64, -x10 + 28); oled.moveto(-x9 + 64, -y10 + 52); oled.lineto(-x9 + 64, -y10 + 28); oled.moveto(-y9 + 64, x10 + 52); oled.lineto(-y9 + 64, x10 + 28); } ※ while (oled.endPaint()); // Rotate cube x = x + (y9 * Delta); y = y - ((x>>9) * Delta); } void setup() { bus.begin(); oled.begin(); } void loop() { uint16_t cnt = 0; uint16_t t = millis(); while ((uint16_t)millis() - t < 1000) { RotatingCube(); ++cnt; bus.handle(); } oled.cursor(0, 0); oled.printInt<uint16_t>(cnt); oled.printStr(" FPS"); oled.flush(); } |
※今回の肝の部分。描画が完了するまで繰り返すロジック。
結果は、こんな感じで、33-34FPSくらいは出た。
この例ではメモリ量と速度のバランスが一番良いと思われた128分割で実行した結果で、フレームバッファは、1024/128=8バイトでしかないが、まぁ、そこそこのパフォーマンスは出てるといえる。
しか-し、別の問題が。以前の投稿でも書いたようにコードサイズの問題であるが、試験プログラムで既に5K程度になっており残りは1K程度しか残っていないのだ。これではライブラリだけでお腹一杯。アプリが入らなさそう...
大きなアプリから利用したい場合、ATTiny85をグラフィック・サーバーとして利用する手はあるし、簡単なアプリであれば単体でも使えるかもしれない...が、無理してATtiny85でなくてもいいんじゃね?と、何故か虚しさを感じるのは私だけだろうか...
せめてATtiny85の16K版があったらいいのに...
microchip様、以前悪口言ったのは謝ります。
だから私の願いを聞いてくださーい。<(_ _)>
終