Louis Mayencourt
/
NRFBOY
MBED NRF51 Arduboy port
arduboy.hpp
- Committer:
- lmayencou
- Date:
- 2017-01-07
- Revision:
- 7:fb7e549d1cf6
- Parent:
- 1:c53e766082b4
File content as of revision 7:fb7e549d1cf6:
#define WIDTH 128 #define HEIGHT 32 #define WHITE 1 #define BLACK 0 #define COLUMN_ADDRESS_END (WIDTH - 1) & 0x7F #define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 0x07 #define _BV(b) (1UL << (b)) #define min(a,b) (((a)<(b))?(a):(b)) #define max(a,b) (((a)>(b))?(a):(b)) #define pgm_read_byte(a) 1 SPI _spi(p30, p31, p29); // mosi, miso, sclk DigitalOut _cs(p3); DigitalOut _dc(p4); DigitalOut _rst(p28); class Arduboy { public: Arduboy() { // frame management setFrameRate(60); frameCount = 0; lastFrameStart = 0; nextFrameStart = 0; lastFrameDurationMs = 0; post_render = false; } void begin() { // init SPI _spi.format(8,3); _spi.frequency(1000000); // init pin LCD _dc = 0; _cs = 0; _rst = 1; wait(1); _rst = 0; wait(10); _rst = 1; bootLCD(); } void Arduboy::bootLCD() { LCDCommandMode(); _spi.write(0xAE); // Display Off _spi.write(0XD5); // Set Display Clock Divisor v _spi.write(0xF0); // 0x80 is default _spi.write(0xA8); // Set Multiplex Ratio v _spi.write(0x3F); _spi.write(0xD3); // Set Display Offset v _spi.write(0x0); _spi.write(0x40); // Set Start Line (0) _spi.write(0x8D); // Charge Pump Setting v _spi.write(0x14); // Enable // why are we running this next pair twice? _spi.write(0x20); // Set Memory Mode v _spi.write(0x00); // Horizontal Addressing _spi.write(0xA1); // Set Segment Re-map (A0) | (b0001) _spi.write(0xC8); // Set COM Output Scan Direction _spi.write(0xDA); // Set COM Pins v _spi.write(0x12); _spi.write(0x81); // Set Contrast v _spi.write(0xCF); _spi.write(0xD9); // Set Precharge _spi.write(0xF1); _spi.write(0xDB); // Set VCom Detect _spi.write(0x40); _spi.write(0xA4); // Entire Display ON _spi.write(0xA6); // Set normal/inverse display _spi.write(0xAF); // Display On LCDCommandMode(); _spi.write(0x20); // set display mode _spi.write(0x00); // horizontal addressing mode _spi.write(0x21); // set col address _spi.write(0x00); _spi.write(COLUMN_ADDRESS_END); _spi.write(0x22); // set page address _spi.write(0x00); _spi.write(PAGE_ADDRESS_END); LCDDataMode(); } void LCDCommandMode() { _cs = 1; _dc = 0; _cs = 0; } void LCDDataMode() { _dc = 1; _cs = 0; }; void idle() {} /* Frame management */ void setFrameRate(uint8_t rate) { frameRate = rate; eachFrameMillis = 1000/rate; } bool everyXFrames(uint8_t frames) { return frameCount % frames == 0; } bool newFrame() { long now = time(NULL); uint8_t remaining; // post render if (post_render) { lastFrameDurationMs = now - lastFrameStart; frameCount++; post_render = false; } // if it's not time for the next frame yet if (now < nextFrameStart) { remaining = nextFrameStart - now; // if we have more than 1ms to spare, lets sleep // we should be woken up by timer0 every 1ms, so this should be ok if (remaining > 1) idle(); return false; } // pre-render // next frame should start from last frame start + frame duration nextFrameStart = lastFrameStart + eachFrameMillis; // If we're running CPU at 100%+ (too slow to complete each loop within // the frame duration) then it's possible that we get "behind"... Say we // took 5ms too long, resulting in nextFrameStart being 5ms in the PAST. // In that case we simply schedule the next frame to start immediately. // // If we were to let the nextFrameStart slide further and further into // the past AND eventually the CPU usage dropped then frame management // would try to "catch up" (by speeding up the game) to make up for all // that lost time. That would not be good. We allow frames to take too // long (what choice do we have?), but we do not allow super-fast frames // to make up for slow frames in the past. if (nextFrameStart < now) nextFrameStart = now; lastFrameStart = now; post_render = true; return post_render; } // This function is deprecated. // It should remain as is for backwards compatibility. // New code should use newFrame(). bool nextFrame() { long now = time(NULL); uint8_t remaining; if (post_render) { lastFrameDurationMs = now - lastFrameStart; frameCount++; post_render = false; } if (now < nextFrameStart) { remaining = nextFrameStart - now; if (remaining > 1) idle(); return false; } nextFrameStart = now + eachFrameMillis; lastFrameStart = now; post_render = true; return post_render; } /* Graphics */ void blank() { for (int a = 0; a < (HEIGHT * WIDTH) / 8; a++) _spi.write(0x00); } void clearDisplay() { this->fillScreen(0); } void drawPixel(int x, int y, uint8_t color) { #ifdef PIXEL_SAFE_MODE if (x < 0 || x > (WIDTH - 1) || y < 0 || y > (HEIGHT - 1)) { return; } #endif uint8_t row = (uint8_t)y / 8; if (color) { sBuffer[(row * WIDTH) + (uint8_t)x] |= _BV((uint8_t)y % 8); } else { sBuffer[(row * WIDTH) + (uint8_t)x] &= ~ _BV((uint8_t)y % 8); } } uint8_t getPixel(uint8_t x, uint8_t y) { uint8_t row = y / 8; uint8_t bit_position = y % 8; return (sBuffer[(row * WIDTH) + x] & _BV(bit_position)) >> bit_position; } static inline void swap(int16_t &a, int16_t &b) { int16_t t = a; a = b; b = t; } void drawCircle(int16_t x0, int16_t y0, int16_t r, uint8_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; drawPixel(x0, y0 + r, color); drawPixel(x0, y0 - r, color); drawPixel(x0 + r, y0, color); drawPixel(x0 - r, y0, color); while (x < y) { if (f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; drawPixel(x0 + x, y0 + y, color); drawPixel(x0 - x, y0 + y, color); drawPixel(x0 + x, y0 - y, color); drawPixel(x0 - x, y0 - y, color); drawPixel(x0 + y, y0 + x, color); drawPixel(x0 - y, y0 + x, color); drawPixel(x0 + y, y0 - x, color); drawPixel(x0 - y, y0 - x, color); } } void drawCircleHelper (int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint8_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; while (x < y) { if (f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; if (cornername & 0x4) { drawPixel(x0 + x, y0 + y, color); drawPixel(x0 + y, y0 + x, color); } if (cornername & 0x2) { drawPixel(x0 + x, y0 - y, color); drawPixel(x0 + y, y0 - x, color); } if (cornername & 0x8) { drawPixel(x0 - y, y0 + x, color); drawPixel(x0 - x, y0 + y, color); } if (cornername & 0x1) { drawPixel(x0 - y, y0 - x, color); drawPixel(x0 - x, y0 - y, color); } } } void fillCircle(int16_t x0, int16_t y0, int16_t r, uint8_t color) { drawFastVLine(x0, y0 - r, 2 * r + 1, color); fillCircleHelper(x0, y0, r, 3, 0, color); } void fillCircleHelper ( int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint8_t color ) { // used to do circles and roundrects! int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; while (x < y) { if (f >= 0) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; if (cornername & 0x1) { drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color); drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color); } if (cornername & 0x2) { drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color); drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color); } } } void drawLine (int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color) { // bresenham's algorithm - thx wikpedia bool steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { swap(x0, y0); swap(x1, y1); } if (x0 > x1) { swap(x0, x1); swap(y0, y1); } int16_t dx, dy; dx = x1 - x0; dy = abs(y1 - y0); int16_t err = dx / 2; int8_t ystep; if (y0 < y1) { ystep = 1; } else { ystep = -1; } for (; x0 <= x1; x0++) { if (steep) { drawPixel(y0, x0, color); } else { drawPixel(x0, y0, color); } err -= dy; if (err < 0) { y0 += ystep; err += dx; } } } void drawRect (int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color) { drawFastHLine(x, y, w, color); drawFastHLine(x, y + h - 1, w, color); drawFastVLine(x, y, h, color); drawFastVLine(x + w - 1, y, h, color); } void drawFastVLine (int16_t x, int16_t y, int16_t h, uint8_t color) { int end = y + h; for (int a = max(0, y); a < min(end, HEIGHT); a++) { drawPixel(x, a, color); } } void drawFastHLine (int16_t x, int16_t y, int16_t w, uint8_t color) { int end = x + w; for (int a = max(0, x); a < min(end, WIDTH); a++) { drawPixel(a, y, color); } } void fillRect (int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color) { // stupidest version - update in subclasses if desired! for (int16_t i = x; i < x + w; i++) { drawFastVLine(i, y, h, color); } } void fillScreen(uint8_t color) { // C version : if(color != 0) color = 0xFF; //change any nonzero argument to b11111111 and insert into screen array. for(int16_t i=0; i<1024; i++) { sBuffer[i] = color; } //sBuffer = (128*64) = 8192/8 = 1024 bytes. } uint8_t frameRate; uint16_t frameCount; uint8_t eachFrameMillis; long lastFrameStart; long nextFrameStart; bool post_render; uint8_t lastFrameDurationMs; protected: unsigned char sBuffer[(HEIGHT*WIDTH)/8]; };