Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*********************************************************************
- This is an Arduino library for our Monochrome SHARP Memory Displays
- Pick one up today in the adafruit shop!
- ------> http://www.adafruit.com/products/1393
- These displays use SPI to communicate, 3 pins are required to
- interface
- Adafruit invests time and resources providing this open source code,
- please support Adafruit and open-source hardware by purchasing
- products from Adafruit!
- Written by Limor Fried/Ladyada for Adafruit Industries.
- BSD license, check license.txt for more information
- All text above, and the splash screen must be included in any redistribution
- *********************************************************************/
- #include "display.h"
- /**************************************************************************
- Sharp Memory Display Connector
- -----------------------------------------------------------------------
- Pin Function Notes
- === ============== ===============================
- 1 VIN 3.3-5.0V (into LDO supply)
- 2 3V3 3.3V out
- 3 GND
- 4 SCLK Serial Clock
- 5 MOSI Serial Data Input
- 6 CS Serial Chip Select
- 9 EXTMODE COM Inversion Select (Low = SW clock/serial)
- 7 EXTCOMIN External COM Inversion Signal
- 8 DISP Display On(High)/Off(Low)
- **************************************************************************/
- // TODO
- // - Transfer using SPI? How to use VCOM
- // - Transfer using DMA & SPI?
- #define DISPLAY_BIT_WRITECMD (0x80)
- #define DISPLAY_BIT_VCOM (0x40)
- #define DISPLAY_BIT_CLEAR (0x20)
- #define TOGGLE_VCOM do { _vcom = _vcom ? 0x00 : DISPLAY_BIT_VCOM; } while(0);
- // In theory we should get these values after the display pins has been setup
- // we used it because it is slightly faster as a define
- #define CLKPINMASK 1
- #define DATAPINMASK 1
- byte *framebuffer; // working frame buffer to render the game
- byte *displaysbuffer; // buffer that contain what was sent to the display
- void* _aligned_malloc(size_t size, size_t alignment)
- {
- size_t a = alignment - 1;
- void* raw = malloc(size + a);
- if (!raw)
- return NULL;
- void* ptr = (void*)((size_t(raw) + a) & ~a);
- return ptr;
- }
- /* ************* */
- /* CONSTRUCTORS */
- /* ************* */
- PlaydateDisplay::PlaydateDisplay() : Adafruit_GFX(400, 240) {
- _clk = DISPLAY_SCK;
- _mosi = DISPLAY_MOSI;
- _ss = DISPLAY_SS;
- }
- boolean PlaydateDisplay::setup(int fps_target) {
- // Set pin state before direction to make sure they start this way (no glitching)
- digitalWrite(_ss, HIGH);
- digitalWrite(_clk, LOW);
- digitalWrite(_mosi, HIGH);
- pinMode(_ss, OUTPUT);
- pinMode(_clk, OUTPUT);
- pinMode(_mosi, OUTPUT);
- clkport = portOutputRegister(digitalPinToPort(_clk));
- clkpinmask = digitalPinToBitMask(_clk);
- dataport = portOutputRegister(digitalPinToPort(_mosi));
- datapinmask = digitalPinToBitMask(_mosi);
- if (clkpinmask!=CLKPINMASK || datapinmask!=DATAPINMASK) {
- char printbuffer[256];
- sprintf(printbuffer, "CLKPINMASK or DATAPINMASK has incorrect value. CLKPINMASK should be %d. DATAPINMASK should be %d", (int)clkpinmask, (int)datapinmask);
- do { Serial.println(printbuffer); } while(1);
- }
- // Set the vcom bit to a defined state
- _vcom = DISPLAY_BIT_VCOM;
- framebuffer = (byte *)malloc((WIDTH * HEIGHT) / 8);
- if (!framebuffer) return false;
- displaysbuffer = (byte *)malloc((WIDTH * HEIGHT) / 8);
- if (!displaysbuffer) return false;
- memset(framebuffer, 0xFF, (WIDTH * HEIGHT) / 8);
- memset(displaysbuffer, 0xFF, (WIDTH * HEIGHT) / 8);
- m_lastRefresh = 0;
- m_frameLength = 1000000 / (unsigned long)(fps_target);
- return true;
- }
- /* *************** */
- /* PRIVATE METHODS */
- /* *************** */
- /**************************************************************************/
- /*!
- @brief Sends a single byte in pseudo-SPI.
- */
- /**************************************************************************/
- inline void PlaydateDisplay::write_data(int data) {
- if (data) write_data_1();
- else write_data_0();
- }
- inline void PlaydateDisplay::write_data_1() {
- *clkport &= ~CLKPINMASK;
- *dataport |= DATAPINMASK;
- *clkport |= CLKPINMASK;
- }
- inline void PlaydateDisplay::write_data_0() {
- *clkport &= ~CLKPINMASK;
- *dataport &= ~DATAPINMASK;
- *clkport |= CLKPINMASK;
- }
- inline void PlaydateDisplay::end_write_data() {
- *clkport &= ~CLKPINMASK;
- }
- void PlaydateDisplay::sendbyteZero()
- {
- // set 0
- *dataport &= ~datapinmask;
- // triger 8 clocks
- *clkport |= CLKPINMASK;
- *clkport &= ~CLKPINMASK;
- *clkport |= CLKPINMASK;
- *clkport &= ~CLKPINMASK;
- *clkport |= CLKPINMASK;
- *clkport &= ~CLKPINMASK;
- *clkport |= CLKPINMASK;
- *clkport &= ~CLKPINMASK;
- *clkport |= CLKPINMASK;
- *clkport &= ~CLKPINMASK;
- *clkport |= CLKPINMASK;
- *clkport &= ~CLKPINMASK;
- *clkport |= CLKPINMASK;
- *clkport &= ~CLKPINMASK;
- *clkport |= CLKPINMASK;
- *clkport &= ~CLKPINMASK;
- }
- FASTRUN void PlaydateDisplay::sendbyte(uint8_t data)
- {
- write_data(data&128);
- write_data(data&64);
- write_data(data&32);
- write_data(data&16);
- write_data(data&8);
- write_data(data&4);
- write_data(data&2);
- write_data(data&1);
- end_write_data();
- }
- FASTRUN void PlaydateDisplay::sendbyteLSB(uint8_t data)
- {
- write_data(data&1);
- write_data(data&2);
- write_data(data&4);
- write_data(data&8);
- write_data(data&16);
- write_data(data&32);
- write_data(data&64);
- write_data(data&128);
- end_write_data();
- }
- FASTRUN void PlaydateDisplay::sendU16(uint16_t data) {
- write_data(data&(1<<7));
- write_data(data&(1<<6));
- write_data(data&(1<<5));
- write_data(data&(1<<4));
- write_data(data&(1<<3));
- write_data(data&(1<<2));
- write_data(data&(1<<1));
- write_data(data&(1<<0));
- write_data(data&(1<<15));
- write_data(data&(1<<14));
- write_data(data&(1<<13));
- write_data(data&(1<<12));
- write_data(data&(1<<11));
- write_data(data&(1<<10));
- write_data(data&(1<<9));
- write_data(data&(1<<8));
- end_write_data();
- }
- void PlaydateDisplay::prepareSendingLine() {
- // Send the write command
- digitalWrite(_ss, HIGH);
- sendbyte(DISPLAY_BIT_WRITECMD | _vcom);
- TOGGLE_VCOM;
- }
- FASTRUN void PlaydateDisplay::sendLine(int line_number, byte* data) {
- uint16_t *u16data = (uint16_t*)data;
- sendbyteLSB(line_number);
- for (int i=0; i<(400/16); i++) {
- sendU16(*(u16data++));
- }
- sendbyteZero();
- }
- void PlaydateDisplay::stopSendingLine() {
- sendbyteZero();
- digitalWrite(_ss, LOW);
- }
- /* ************** */
- /* PUBLIC METHODS */
- /* ************** */
- /**************************************************************************/
- /*!
- @brief Draws a single pixel in image buffer
- @param[in] x
- The x position (0 based)
- @param[in] y
- The y position (0 based)
- */
- /**************************************************************************/
- void PlaydateDisplay::drawPixel(int16_t x, int16_t y, uint16_t color)
- {
- if((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return;
- if(color) {
- framebuffer[(y * WIDTH + x) / 8] |= 128>>(x&7);
- } else {
- framebuffer[(y * WIDTH + x) / 8] &= ~(128>>(x&7));
- }
- }
- /**************************************************************************/
- /*!
- @brief Gets the value (1 or 0) of the specified pixel from the buffer
- @param[in] x
- The x position (0 based)
- @param[in] y
- The y position (0 based)
- @return 1 if the pixel is enabled, 0 if disabled
- */
- /**************************************************************************/
- uint8_t PlaydateDisplay::getPixel(uint16_t x, uint16_t y)
- {
- if((x >= _width) || (y >= _height)) return 0; // <0 test not needed, unsigned
- return framebuffer[(y * WIDTH + x) / 8] & (128>>(x&7)) ? 1 : 0;
- }
- /**************************************************************************/
- /*!
- @brief Clears the screen
- */
- /**************************************************************************/
- void PlaydateDisplay::clearDisplay()
- {
- memset(framebuffer, 0xff, (WIDTH * HEIGHT) / 8);
- // Send the clear screen command rather than doing a HW refresh (quicker)
- digitalWrite(_ss, HIGH);
- sendbyte(_vcom | DISPLAY_BIT_CLEAR);
- sendbyteLSB(0x00);
- TOGGLE_VCOM;
- digitalWrite(_ss, LOW);
- }
- /**************************************************************************/
- /*!
- @brief Renders the contents of the pixel buffer on the LCD
- */
- /**************************************************************************/
- void PlaydateDisplay::refresh(void)
- {
- byte *line = framebuffer;
- // Send the write command
- digitalWrite(_ss, HIGH);
- sendbyte(DISPLAY_BIT_WRITECMD | _vcom);
- TOGGLE_VCOM;
- // Send image buffer
- for (int j=1; j<=240; j++, line+= (400/8)) {
- sendLine(j, line);
- }
- sendbyteZero();
- digitalWrite(_ss, LOW);
- m_lastRefresh = micros();
- }
- void PlaydateDisplay::partialRefresh(void)
- {
- byte *framebuffer_line = framebuffer;
- byte *displaybuffer_line = displaysbuffer;
- byte lineCount = 0;
- // Send the write command
- digitalWrite(_ss, HIGH);
- sendbyte(DISPLAY_BIT_WRITECMD | _vcom);
- TOGGLE_VCOM;
- // Send image buffer
- for (int j=1; j<=240; j++ ) {
- // determine if the line has been updated
- bool lineNeedRefresh = false;
- byte *fr = framebuffer_line;
- byte *dp = displaybuffer_line;
- for (int i=0; i<(400/8) && lineNeedRefresh==false; i++, fr++, dp++) {
- if (*fr!=*dp) {
- lineNeedRefresh = true;
- }
- }
- // force refresh
- // lineNeedRefresh = true;
- if (lineNeedRefresh) {
- // send the line number (from 1 to 240)
- sendLine(j, framebuffer_line);
- // copy line in display buffer
- memcpy(displaybuffer_line, framebuffer_line, 400/8);
- lineCount++;
- }
- // update line buffers
- framebuffer_line+= (400/8);
- displaybuffer_line+= (400/8);
- }
- sendbyteZero();
- digitalWrite(_ss, LOW);
- unsigned long currentFrameLength = micros() - m_lastRefresh;
- if ( m_frameLength>currentFrameLength )
- delayMicroseconds(m_frameLength-currentFrameLength);
- else
- Serial.println("! Performance under target FPS.");
- m_lastRefresh = micros();
- // Serial.print("Line updated: ");
- // Serial.println(lineCount);
- }
- void PlaydateDisplay::clear(byte color) {
- memset(framebuffer, color?0xFF:0x00, (WIDTH * HEIGHT) / 8);
- }
- void PlaydateDisplay::drawSprite(int x, int y, pdi *pSprite) {
- if (!pSprite) return;
- drawBitmap(x, y, pSprite->width, pSprite->height, pSprite->pBitmap, pSprite->pMask);
- //drawBitmap(x, y, pSprite->width, pSprite->height, pSprite->pBitmap, NULL);
- }
- void PlaydateDisplay::drawBitmap(int x, int y, uint16_t w, uint16_t h, byte *src, byte *mask) {
- byte *dest = framebuffer;
- int bx = 0;
- int by = 0;
- int bw = w;
- int bh = h;
- // sprite clipping
- if (x<0) {
- bx-= x;
- bw-= bx;
- }
- if (y<0) {
- by-= y;
- bh-= by;
- }
- if ((x+bw)>400) bw = 400-x;
- if ((y+bh)>240) bh = 240-y;
- // we return if there is nothing to display
- if (x>=400) return;
- if (y>=240) return;
- if (bw<=0) return;
- if (bh<=0) return;
- // prepare mask and bitshifting values
- bool isLeftCropped = ((bx&7)>0);
- byte srcShiftRight;
- if (isLeftCropped)
- srcShiftRight = 8-(bx&7);
- else
- srcShiftRight = x&7;
- byte srcShiftLeft = 8-srcShiftRight;
- byte destMaskLeft = 0xFF<<(8-(x&7));
- byte destMaskRight = ~destMaskLeft;
- uint16_t src_end_padding = w&7;
- uint16_t src_byte_width = (w/8) + (src_end_padding? 1 : 0);
- uint16_t destFirstByteIndex = (x+bx)/8;
- uint16_t destLastByteIndex = (x+bx+bw)/8;
- uint16_t srcFirstByteIndex = bx/8;
- uint16_t lastByteLeftover = (x+bx+bw) & 7;
- uint16_t lastByteMask = 0xFF>>lastByteLeftover;
- // move in the frame button where we should start
- dest+= (y+by)*(400/8) + destFirstByteIndex;
- src+= by*src_byte_width + srcFirstByteIndex;
- // Path with Mask
- if (mask!=NULL) {
- mask+= by*src_byte_width + (bx>>3);
- for (int j=0; j<bh; j++) {
- byte *destbyte = dest;
- byte *maskbyte = mask;
- byte *srcbyte = src;
- byte *destbyte_end = dest + (destLastByteIndex-destFirstByteIndex);
- // TODO opti, special case when mask==0 and mask==0xFF
- // because the dest byte have most of the time mask and mask+1
- // the test should be on a u16 -> (u16*)mask for 0x0000 and 0xFFFF
- if (isLeftCropped==false) {
- *destbyte&= destMaskLeft | (*maskbyte)>>srcShiftRight;
- *destbyte|= (*srcbyte)>>srcShiftRight;
- destbyte++;
- }
- while (destbyte<destbyte_end) {
- *destbyte&= (*(maskbyte))<<srcShiftLeft | (*(maskbyte+1))>>srcShiftRight;
- *destbyte|= (*(srcbyte))<<srcShiftLeft | (*(srcbyte+1))>>srcShiftRight;
- destbyte++;
- srcbyte++;
- maskbyte++;
- }
- // last byte to copy
- if (lastByteLeftover>0) {
- // happens when the sprite is bit aligned (srcShiftRight==0)
- if (srcShiftLeft==8) {
- *destbyte&= *(maskbyte+1) | lastByteMask;
- *destbyte|= *(srcbyte+1);
- }
- else if ( lastByteLeftover>srcShiftRight ) {
- *destbyte&= (*(maskbyte))<<srcShiftLeft | (*(maskbyte+1))>>srcShiftRight | lastByteMask;
- *destbyte|= (*(srcbyte))<<srcShiftLeft | (*(srcbyte+1))>>srcShiftRight;
- }
- else {
- *destbyte&= (*(maskbyte))<<srcShiftLeft | lastByteMask;
- *destbyte|= (*(srcbyte))<<srcShiftLeft;
- }
- }
- dest+= (400/8);
- src+= src_byte_width;
- mask+= src_byte_width;
- }
- return;
- }
- // the sprite is ideally bit aligned, let's speed up things
- if ( (x&7)==0 ) {
- uint16_t copyLen = bw/8;
- for (int j=0; j<bh; j++) {
- // fast copy
- memcpy(dest, src, copyLen);
- // copy bitmap leftovers if there is any
- if ((bw&7)>0) {
- srcShiftLeft = 8-(bw&7);
- destMaskRight = ~(0xFF<<srcShiftLeft);
- *(dest+copyLen) = *(src+copyLen) | ((*(dest+copyLen))&destMaskRight);
- }
- // next lines
- dest+= (400/8);
- src+= src_byte_width;
- }
- return;
- }
- // Default path
- for (int j=0; j<bh; j++) {
- byte *destbyte = dest;
- byte *srcbyte = src;
- byte *destbyte_end = dest + (destLastByteIndex-destFirstByteIndex);
- if (isLeftCropped==false) {
- *destbyte = ((*destbyte)&destMaskLeft) | (*srcbyte)>>srcShiftRight;
- destbyte++;
- }
- while (destbyte<destbyte_end) {
- *destbyte = (*(srcbyte))<<srcShiftLeft | (*(srcbyte+1))>>srcShiftRight;
- destbyte++;
- srcbyte++;
- }
- // last byte to copy
- if (lastByteLeftover>0) {
- if (lastByteLeftover>srcShiftRight)
- *destbyte = (*(srcbyte))<<srcShiftLeft | (*(srcbyte+1))>>srcShiftRight | ((*destbyte)&lastByteMask);
- else
- *destbyte = (*(srcbyte))<<srcShiftLeft | ((*destbyte)&lastByteMask);
- }
- dest+= (400/8);
- src+= src_byte_width;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement