Guest User

Untitled

a guest
Jan 18th, 2020
835
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*********************************************************************
  2. This is an Arduino library for our Monochrome SHARP Memory Displays
  3.  
  4.   Pick one up today in the adafruit shop!
  5.   ------> http://www.adafruit.com/products/1393
  6.  
  7. These displays use SPI to communicate, 3 pins are required to
  8. interface
  9.  
  10. Adafruit invests time and resources providing this open source code,
  11. please support Adafruit and open-source hardware by purchasing
  12. products from Adafruit!
  13.  
  14. Written by Limor Fried/Ladyada  for Adafruit Industries.
  15. BSD license, check license.txt for more information
  16. All text above, and the splash screen must be included in any redistribution
  17. *********************************************************************/
  18.  
  19. #include "display.h"
  20.  
  21. /**************************************************************************
  22.     Sharp Memory Display Connector
  23.     -----------------------------------------------------------------------
  24.     Pin   Function        Notes
  25.     ===   ==============  ===============================
  26.       1   VIN             3.3-5.0V (into LDO supply)
  27.       2   3V3             3.3V out
  28.       3   GND
  29.       4   SCLK            Serial Clock
  30.       5   MOSI            Serial Data Input
  31.       6   CS              Serial Chip Select
  32.       9   EXTMODE         COM Inversion Select (Low = SW clock/serial)
  33.       7   EXTCOMIN        External COM Inversion Signal
  34.       8   DISP            Display On(High)/Off(Low)
  35.  
  36.  **************************************************************************/
  37.  
  38. // TODO
  39. // - Transfer using SPI? How to use VCOM
  40. // - Transfer using DMA & SPI?
  41.  
  42. #define DISPLAY_BIT_WRITECMD   (0x80)
  43. #define DISPLAY_BIT_VCOM       (0x40)
  44. #define DISPLAY_BIT_CLEAR      (0x20)
  45. #define TOGGLE_VCOM             do { _vcom = _vcom ? 0x00 : DISPLAY_BIT_VCOM; } while(0);
  46.  
  47. // In theory we should get these values after the display pins has been setup
  48. // we used it because it is slightly faster as a define
  49. #define CLKPINMASK  1
  50. #define DATAPINMASK 1
  51.  
  52. byte *framebuffer;      // working frame buffer to render the game
  53. byte *displaysbuffer;   // buffer that contain what was sent to the display
  54.  
  55. void* _aligned_malloc(size_t size, size_t alignment)
  56. {
  57.     size_t a = alignment - 1;
  58.     void* raw = malloc(size + a);
  59.     if (!raw)
  60.         return NULL;
  61.  
  62.     void* ptr = (void*)((size_t(raw) + a) & ~a);
  63.     return ptr;
  64. }
  65.  
  66. /* ************* */
  67. /* CONSTRUCTORS  */
  68. /* ************* */
  69. PlaydateDisplay::PlaydateDisplay() : Adafruit_GFX(400, 240) {
  70.   _clk  = DISPLAY_SCK;
  71.   _mosi = DISPLAY_MOSI;
  72.   _ss   = DISPLAY_SS;
  73. }
  74.  
  75. boolean PlaydateDisplay::setup(int fps_target) {
  76.   // Set pin state before direction to make sure they start this way (no glitching)
  77.   digitalWrite(_ss, HIGH);
  78.   digitalWrite(_clk, LOW);
  79.   digitalWrite(_mosi, HIGH);
  80.  
  81.   pinMode(_ss, OUTPUT);
  82.   pinMode(_clk, OUTPUT);
  83.   pinMode(_mosi, OUTPUT);
  84.  
  85.   clkport     = portOutputRegister(digitalPinToPort(_clk));
  86.   clkpinmask  = digitalPinToBitMask(_clk);
  87.   dataport    = portOutputRegister(digitalPinToPort(_mosi));
  88.   datapinmask = digitalPinToBitMask(_mosi);
  89.  
  90.   if (clkpinmask!=CLKPINMASK || datapinmask!=DATAPINMASK) {
  91.       char printbuffer[256];
  92.       sprintf(printbuffer, "CLKPINMASK or DATAPINMASK has incorrect value. CLKPINMASK should be %d. DATAPINMASK should be %d", (int)clkpinmask, (int)datapinmask);
  93.       do { Serial.println(printbuffer); } while(1);
  94.   }
  95.  
  96.   // Set the vcom bit to a defined state
  97.     _vcom = DISPLAY_BIT_VCOM;
  98.  
  99.     framebuffer = (byte *)malloc((WIDTH * HEIGHT) / 8);
  100.     if (!framebuffer) return false;
  101.  
  102.     displaysbuffer = (byte *)malloc((WIDTH * HEIGHT) / 8);
  103.     if (!displaysbuffer) return false;
  104.  
  105.     memset(framebuffer, 0xFF, (WIDTH * HEIGHT) / 8);
  106.     memset(displaysbuffer, 0xFF, (WIDTH * HEIGHT) / 8);
  107.  
  108.     m_lastRefresh = 0;
  109.     m_frameLength = 1000000 / (unsigned long)(fps_target);
  110.  
  111.     return true;
  112. }
  113.  
  114.  
  115. /* *************** */
  116. /* PRIVATE METHODS */
  117. /* *************** */
  118.  
  119.  
  120. /**************************************************************************/
  121. /*!
  122.     @brief  Sends a single byte in pseudo-SPI.
  123. */
  124. /**************************************************************************/
  125. inline void PlaydateDisplay::write_data(int data) {
  126.     if (data)   write_data_1();
  127.     else        write_data_0();
  128. }
  129. inline void PlaydateDisplay::write_data_1() {
  130.     *clkport &= ~CLKPINMASK;
  131.     *dataport |=  DATAPINMASK;
  132.     *clkport |=  CLKPINMASK;
  133. }
  134. inline void PlaydateDisplay::write_data_0() {
  135.     *clkport &= ~CLKPINMASK;
  136.     *dataport &= ~DATAPINMASK;
  137.     *clkport |=  CLKPINMASK;
  138. }
  139. inline void PlaydateDisplay::end_write_data() {
  140.     *clkport &= ~CLKPINMASK;
  141. }
  142.  
  143. void PlaydateDisplay::sendbyteZero()
  144. {
  145.     // set 0
  146.     *dataport &= ~datapinmask;
  147.  
  148.     // triger 8 clocks
  149.     *clkport |=  CLKPINMASK;
  150.     *clkport &= ~CLKPINMASK;
  151.  
  152.     *clkport |=  CLKPINMASK;
  153.     *clkport &= ~CLKPINMASK;
  154.  
  155.     *clkport |=  CLKPINMASK;
  156.     *clkport &= ~CLKPINMASK;
  157.  
  158.     *clkport |=  CLKPINMASK;
  159.     *clkport &= ~CLKPINMASK;
  160.  
  161.     *clkport |=  CLKPINMASK;
  162.     *clkport &= ~CLKPINMASK;
  163.  
  164.     *clkport |=  CLKPINMASK;
  165.     *clkport &= ~CLKPINMASK;
  166.  
  167.     *clkport |=  CLKPINMASK;
  168.     *clkport &= ~CLKPINMASK;
  169.  
  170.     *clkport |=  CLKPINMASK;
  171.     *clkport &= ~CLKPINMASK;
  172. }
  173.  
  174.  
  175. FASTRUN void PlaydateDisplay::sendbyte(uint8_t data)
  176. {
  177.     write_data(data&128);
  178.     write_data(data&64);
  179.     write_data(data&32);
  180.     write_data(data&16);
  181.     write_data(data&8);
  182.     write_data(data&4);
  183.     write_data(data&2);
  184.     write_data(data&1);
  185.     end_write_data();
  186. }
  187.  
  188. FASTRUN void PlaydateDisplay::sendbyteLSB(uint8_t data)
  189. {
  190.     write_data(data&1);
  191.     write_data(data&2);
  192.     write_data(data&4);
  193.     write_data(data&8);
  194.     write_data(data&16);
  195.     write_data(data&32);
  196.     write_data(data&64);
  197.     write_data(data&128);
  198.     end_write_data();
  199. }
  200.  
  201. FASTRUN void PlaydateDisplay::sendU16(uint16_t data) {
  202.     write_data(data&(1<<7));
  203.     write_data(data&(1<<6));
  204.     write_data(data&(1<<5));
  205.     write_data(data&(1<<4));
  206.     write_data(data&(1<<3));
  207.     write_data(data&(1<<2));
  208.     write_data(data&(1<<1));
  209.     write_data(data&(1<<0));
  210.  
  211.     write_data(data&(1<<15));
  212.     write_data(data&(1<<14));
  213.     write_data(data&(1<<13));
  214.     write_data(data&(1<<12));
  215.     write_data(data&(1<<11));
  216.     write_data(data&(1<<10));
  217.     write_data(data&(1<<9));
  218.     write_data(data&(1<<8));
  219.  
  220.     end_write_data();
  221. }
  222.  
  223. void PlaydateDisplay::prepareSendingLine() {
  224.     // Send the write command
  225.     digitalWrite(_ss, HIGH);
  226.     sendbyte(DISPLAY_BIT_WRITECMD | _vcom);
  227.     TOGGLE_VCOM;
  228. }
  229.  
  230. FASTRUN void PlaydateDisplay::sendLine(int line_number, byte* data) {
  231.     uint16_t *u16data = (uint16_t*)data;
  232.     sendbyteLSB(line_number);
  233.     for (int i=0; i<(400/16); i++) {
  234.         sendU16(*(u16data++));
  235.     }
  236.     sendbyteZero();
  237.  
  238. }
  239.  
  240. void PlaydateDisplay::stopSendingLine() {
  241.     sendbyteZero();
  242.     digitalWrite(_ss, LOW);
  243. }
  244.  
  245.  
  246.  
  247. /* ************** */
  248. /* PUBLIC METHODS */
  249. /* ************** */
  250.  
  251. /**************************************************************************/
  252. /*!
  253.     @brief Draws a single pixel in image buffer
  254.  
  255.     @param[in]  x
  256.                 The x position (0 based)
  257.     @param[in]  y
  258.                 The y position (0 based)
  259. */
  260. /**************************************************************************/
  261. void PlaydateDisplay::drawPixel(int16_t x, int16_t y, uint16_t color)
  262. {
  263.   if((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return;
  264.  
  265.   if(color) {
  266.     framebuffer[(y * WIDTH + x) / 8] |= 128>>(x&7);
  267.   } else {
  268.     framebuffer[(y * WIDTH + x) / 8] &= ~(128>>(x&7));
  269.   }
  270. }
  271.  
  272. /**************************************************************************/
  273. /*!
  274.     @brief Gets the value (1 or 0) of the specified pixel from the buffer
  275.  
  276.     @param[in]  x
  277.                 The x position (0 based)
  278.     @param[in]  y
  279.                 The y position (0 based)
  280.  
  281.     @return     1 if the pixel is enabled, 0 if disabled
  282. */
  283. /**************************************************************************/
  284. uint8_t PlaydateDisplay::getPixel(uint16_t x, uint16_t y)
  285. {
  286.   if((x >= _width) || (y >= _height)) return 0; // <0 test not needed, unsigned
  287.  
  288.   return framebuffer[(y * WIDTH + x) / 8] & (128>>(x&7)) ? 1 : 0;
  289. }
  290.  
  291. /**************************************************************************/
  292. /*!
  293.     @brief Clears the screen
  294. */
  295. /**************************************************************************/
  296. void PlaydateDisplay::clearDisplay()
  297. {
  298.   memset(framebuffer, 0xff, (WIDTH * HEIGHT) / 8);
  299.   // Send the clear screen command rather than doing a HW refresh (quicker)
  300.   digitalWrite(_ss, HIGH);
  301.   sendbyte(_vcom | DISPLAY_BIT_CLEAR);
  302.   sendbyteLSB(0x00);
  303.   TOGGLE_VCOM;
  304.   digitalWrite(_ss, LOW);
  305. }
  306.  
  307. /**************************************************************************/
  308. /*!
  309.     @brief Renders the contents of the pixel buffer on the LCD
  310. */
  311. /**************************************************************************/
  312. void PlaydateDisplay::refresh(void)
  313. {
  314.     byte *line = framebuffer;
  315.  
  316.     // Send the write command
  317.     digitalWrite(_ss, HIGH);
  318.     sendbyte(DISPLAY_BIT_WRITECMD | _vcom);
  319.     TOGGLE_VCOM;
  320.  
  321.     // Send image buffer
  322.     for (int j=1; j<=240; j++, line+= (400/8)) {
  323.         sendLine(j, line);
  324.     }
  325.  
  326.     sendbyteZero();
  327.     digitalWrite(_ss, LOW);
  328.  
  329.     m_lastRefresh = micros();
  330. }
  331.  
  332. void PlaydateDisplay::partialRefresh(void)
  333. {
  334.     byte *framebuffer_line = framebuffer;
  335.     byte *displaybuffer_line = displaysbuffer;
  336.     byte lineCount = 0;
  337.  
  338.     // Send the write command
  339.     digitalWrite(_ss, HIGH);
  340.     sendbyte(DISPLAY_BIT_WRITECMD | _vcom);
  341.     TOGGLE_VCOM;
  342.  
  343.     // Send image buffer
  344.     for (int j=1; j<=240; j++ ) {
  345.         // determine if the line has been updated
  346.         bool lineNeedRefresh = false;
  347.         byte *fr = framebuffer_line;
  348.         byte *dp = displaybuffer_line;
  349.  
  350.         for (int i=0; i<(400/8) && lineNeedRefresh==false; i++, fr++, dp++) {
  351.             if (*fr!=*dp) {
  352.                 lineNeedRefresh = true;
  353.             }
  354.         }
  355.  
  356.         // force refresh
  357. //      lineNeedRefresh = true;
  358.  
  359.         if (lineNeedRefresh) {
  360.             // send the line number (from 1 to 240)
  361.             sendLine(j, framebuffer_line);
  362.  
  363.             // copy line in display buffer
  364.             memcpy(displaybuffer_line, framebuffer_line, 400/8);
  365.  
  366.             lineCount++;
  367.         }
  368.  
  369.         // update line buffers
  370.         framebuffer_line+= (400/8);
  371.         displaybuffer_line+= (400/8);
  372.     }
  373.  
  374.     sendbyteZero();
  375.     digitalWrite(_ss, LOW);
  376.  
  377.     unsigned long currentFrameLength = micros() - m_lastRefresh;
  378.  
  379.     if ( m_frameLength>currentFrameLength )
  380.         delayMicroseconds(m_frameLength-currentFrameLength);
  381.     else
  382.         Serial.println("! Performance under target FPS.");
  383.  
  384.     m_lastRefresh = micros();
  385.  
  386.  
  387. //  Serial.print("Line updated: ");
  388. //  Serial.println(lineCount);
  389. }
  390.  
  391.  
  392.  
  393. void PlaydateDisplay::clear(byte color) {
  394.     memset(framebuffer, color?0xFF:0x00, (WIDTH * HEIGHT) / 8);
  395. }
  396.  
  397. void PlaydateDisplay::drawSprite(int x, int y, pdi *pSprite) {
  398.     if (!pSprite) return;
  399.     drawBitmap(x, y, pSprite->width, pSprite->height, pSprite->pBitmap, pSprite->pMask);
  400.     //drawBitmap(x, y, pSprite->width, pSprite->height, pSprite->pBitmap, NULL);
  401. }
  402.  
  403. void PlaydateDisplay::drawBitmap(int x, int y, uint16_t w, uint16_t h, byte *src, byte *mask) {
  404.     byte *dest = framebuffer;
  405.  
  406.     int bx = 0;
  407.     int by = 0;
  408.     int bw = w;
  409.     int bh = h;
  410.  
  411.     // sprite clipping
  412.     if (x<0) {
  413.         bx-= x;
  414.         bw-= bx;
  415.     }
  416.     if (y<0) {
  417.         by-= y;
  418.         bh-= by;
  419.     }
  420.     if ((x+bw)>400) bw = 400-x;
  421.     if ((y+bh)>240) bh = 240-y;
  422.  
  423.     // we return if there is nothing to display
  424.     if (x>=400) return;
  425.     if (y>=240) return;
  426.     if (bw<=0) return;
  427.     if (bh<=0) return;
  428.  
  429.     // prepare mask and bitshifting values
  430.     bool isLeftCropped = ((bx&7)>0);
  431.     byte srcShiftRight;
  432.     if (isLeftCropped)
  433.         srcShiftRight = 8-(bx&7);
  434.     else
  435.         srcShiftRight = x&7;
  436.  
  437.     byte srcShiftLeft = 8-srcShiftRight;
  438.     byte destMaskLeft = 0xFF<<(8-(x&7));
  439.     byte destMaskRight = ~destMaskLeft;
  440.     uint16_t src_end_padding = w&7;
  441.     uint16_t src_byte_width = (w/8) + (src_end_padding? 1 : 0);
  442.  
  443.     uint16_t destFirstByteIndex = (x+bx)/8;
  444.     uint16_t destLastByteIndex = (x+bx+bw)/8;
  445.     uint16_t srcFirstByteIndex = bx/8;
  446.  
  447.     uint16_t lastByteLeftover = (x+bx+bw) & 7;
  448.     uint16_t lastByteMask = 0xFF>>lastByteLeftover;
  449.  
  450.     // move in the frame button where we should start
  451.     dest+= (y+by)*(400/8) + destFirstByteIndex;
  452.     src+= by*src_byte_width + srcFirstByteIndex;
  453.  
  454.     // Path with Mask
  455.     if (mask!=NULL) {
  456.         mask+=  by*src_byte_width + (bx>>3);
  457.  
  458.         for (int j=0; j<bh; j++) {
  459.             byte *destbyte = dest;
  460.             byte *maskbyte = mask;
  461.             byte *srcbyte = src;
  462.             byte *destbyte_end = dest + (destLastByteIndex-destFirstByteIndex);
  463.  
  464.             // TODO opti, special case when mask==0 and mask==0xFF
  465.             // because the dest byte have most of the time mask and mask+1
  466.             // the test should be on a u16 -> (u16*)mask for 0x0000 and 0xFFFF
  467.  
  468.             if (isLeftCropped==false) {
  469.                 *destbyte&= destMaskLeft | (*maskbyte)>>srcShiftRight;
  470.                 *destbyte|= (*srcbyte)>>srcShiftRight;
  471.                 destbyte++;
  472.             }
  473.  
  474.             while (destbyte<destbyte_end) {
  475.                 *destbyte&= (*(maskbyte))<<srcShiftLeft | (*(maskbyte+1))>>srcShiftRight;
  476.                 *destbyte|= (*(srcbyte))<<srcShiftLeft | (*(srcbyte+1))>>srcShiftRight;
  477.  
  478.                 destbyte++;
  479.                 srcbyte++;
  480.                 maskbyte++;
  481.             }
  482.  
  483.             // last byte to copy
  484.             if (lastByteLeftover>0) {
  485.                 // happens when the sprite is bit aligned (srcShiftRight==0)
  486.                 if (srcShiftLeft==8) {
  487.                     *destbyte&= *(maskbyte+1) | lastByteMask;
  488.                     *destbyte|= *(srcbyte+1);
  489.                 }
  490.                 else if ( lastByteLeftover>srcShiftRight ) {
  491.                     *destbyte&= (*(maskbyte))<<srcShiftLeft | (*(maskbyte+1))>>srcShiftRight | lastByteMask;
  492.                     *destbyte|= (*(srcbyte))<<srcShiftLeft | (*(srcbyte+1))>>srcShiftRight;
  493.                 }
  494.                 else {
  495.                     *destbyte&= (*(maskbyte))<<srcShiftLeft | lastByteMask;
  496.                     *destbyte|= (*(srcbyte))<<srcShiftLeft;
  497.                 }
  498.             }
  499.  
  500.             dest+= (400/8);
  501.             src+= src_byte_width;
  502.             mask+= src_byte_width;
  503.         }
  504.  
  505.         return;
  506.     }
  507.  
  508.     // the sprite is ideally bit aligned, let's speed up things
  509.     if ( (x&7)==0 ) {
  510.         uint16_t copyLen = bw/8;
  511.  
  512.         for (int j=0; j<bh; j++) {
  513.             // fast copy
  514.             memcpy(dest, src, copyLen);
  515.  
  516.             // copy bitmap leftovers if there is any
  517.             if ((bw&7)>0) {
  518.                 srcShiftLeft = 8-(bw&7);
  519.                 destMaskRight = ~(0xFF<<srcShiftLeft);
  520.                 *(dest+copyLen) = *(src+copyLen) | ((*(dest+copyLen))&destMaskRight);
  521.             }
  522.  
  523.             // next lines
  524.             dest+= (400/8);
  525.             src+= src_byte_width;
  526.         }
  527.  
  528.         return;
  529.     }
  530.  
  531.     // Default path
  532.     for (int j=0; j<bh; j++) {
  533.         byte *destbyte = dest;
  534.         byte *srcbyte = src;
  535.         byte *destbyte_end = dest + (destLastByteIndex-destFirstByteIndex);
  536.  
  537.         if (isLeftCropped==false) {
  538.             *destbyte = ((*destbyte)&destMaskLeft) | (*srcbyte)>>srcShiftRight;
  539.             destbyte++;
  540.         }
  541.  
  542.         while (destbyte<destbyte_end) {
  543.             *destbyte = (*(srcbyte))<<srcShiftLeft | (*(srcbyte+1))>>srcShiftRight;
  544.             destbyte++;
  545.             srcbyte++;
  546.         }
  547.  
  548.         // last byte to copy
  549.         if (lastByteLeftover>0) {
  550.             if (lastByteLeftover>srcShiftRight)
  551.                 *destbyte = (*(srcbyte))<<srcShiftLeft | (*(srcbyte+1))>>srcShiftRight | ((*destbyte)&lastByteMask);
  552.             else
  553.                 *destbyte = (*(srcbyte))<<srcShiftLeft | ((*destbyte)&lastByteMask);
  554.         }
  555.  
  556.         dest+= (400/8);
  557.         src+= src_byte_width;
  558.     }
  559. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×