Advertisement
Guest User

Untitled

a guest
Jan 18th, 2020
1,839
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.64 KB | None | 0 0
  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. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement