Display driver for Sharp's range of SPI-driven memory LCD's, leveraging the power of asynchronous transfers. Currently supports LS013B7DH03, but easily extendable to other models as well.

Dependents:   memLCD-Demo memLCD-Demo memLCD-Demo MemLCD-Temperature-Humidity-Demo ... more

Information

All examples in this repo are considered EXPERIMENTAL QUALITY, meaning this code has been created as one-off proof-of-concept and is suitable as a demonstration for experimental purposes only. This code will not be regularly maintained by Silicon Labs and there is no guarantee that these projects will work across all environments, SDK versions and hardware.

Memory LCD extension board

Caution

This library builds upon the asynchronous SPI interface provided by mbed, but not all platforms currently support this. So, make sure your platform is capable of asynchronous SPI (all Silicon Labs platforms are), otherwise you will get compiler errors!

Usage

The library is purposefully quite simple to use. To set it up, you initialize an SPI object and the required I/O pins, and call the library constructor with those. Make sure the display is powered on, enabled, and the inversion mode is set to external (EXTMODE is high).

setup

#define SCK     PD2
#define MOSI 	PD0

DigitalOut CS(PD3);
DigitalOut EXTCOM(PC4);

SPI displaySPI(MOSI, NC, SCK);
silabs::LS013B7DH03 display(&displaySPI, &CS, &EXTCOM);

You should also swap out the pin names for the relevant names on your platform.

After setup, you usually want to clear any static information left on the screen. To do that, you would call clearImmediate, and optionally provide a callback. The callback will get called when the clearing operation is complete and the display has been cleared. In this example, refreshCallback sets a global boolean 'refreshed' to true when called.

clearing the display

refreshed = false;
int result = display.clearImmediate(refreshCallback);
if(result == LS013B7DH03_OK) {
    while(refreshed == false) sleep();
} else {
    printf("Display error: %d", result);
}

Of course, instead of sleeping while the display is clearing, you could also just continue with the program, and check back whether the callback happened or not.

Then comes the fun part, actually writing stuff on the display! Since the display only supports one-way communication (i.e. you cannot read from it), it uses an internal frame buffer in RAM. So, to actually display something on the LCD, two steps need to happen: writing to the pixel buffer, and at the very end writing the pixel buffer to the LCD.

Pixelbuffer operations

The MemoryLCD library builds upon Simon Ford's TextDisplay library, which means you can use all of that functionality. Even printf is supported!

most important display operations

// Set one pixel at x=10, y=40 to the color White
display.pixel(10,40,White); 

// print "I love mbed" at the character position x=4, y=5
// i.e. starting on row 5, 4 characters from the left border
display.locate(4,5);
display.printf("I love mbed!");

// show a bitmap at a given location
// Constraints: bitmap is 8-bit, MSB first, 1 bit per pixel. Width, height and starting coordinates must be divisible by 8.
// This example: show a bitmap which is 128 pixels wide and high, and start at location 0,0.
display.showBMP(&mbed_logo, 128, 128, 0, 0);

//Draw a line (really a 1px wide rectangle) from (4,10) until (4,15)
display.fill(4, 10, 1, 5, White);

Updating the LCD

Updating the LCD (i.e. moving the framebuffer to the display) happens much the same way as clearing it: asynchronously. To start the update, you call update() on the display, optionally providing a callback to be called after the whole update has happened. In the example below, refreshCallback sets a global boolean 'refreshed' to true when called. Signature: void refreshCallback(void);

updating the display

refreshed = false;
int result = display.update(refreshCallback);
if(result == LS013B7DH03_OK) {
    while(refreshed == false) sleep();
} else {
    printf("Display error: %d", result);
}

Full example

example program

#include "LS013B7DH03.h"
#include "mbed_logo.h"
/******************** Define I/O *****************************/
DigitalOut myled(LED1);

#define SCK     PD2
#define MOSI 	PD0

DigitalOut CS(PD3);
DigitalOut EXTCOM(PC4);
DigitalOut EXTMODE(PD4);
DigitalOut DISP(PD5);

SPI displaySPI(MOSI, NC, SCK);
silabs::LS013B7DH03 display(&displaySPI, &CS, &EXTCOM);

/******************** Define Timers *****************************/

LowPowerTicker timeKeeping;

/***************** Define global variables **********************/
#define INIT_SECONDS		17600

volatile uint32_t prevSeconds = INIT_SECONDS, seconds = INIT_SECONDS;
volatile bool refreshed = false;

/***************** Define callback handlers *********************/
void secondsCallback(void);
void refreshCallback(void);

void secondsCallback(void) {
	seconds++;
}

/**
 * Callback for refresh completion
 */
void refreshCallback(void) {
	refreshed = true;
}

/*************************** MAIN *******************************/
int main() {
    // Enable the LCD
	EXTMODE = 1;
	DISP = 1;

	// Start generating the 1Hz call for keeping time
	timeKeeping.attach(&secondsCallback, 1.0f);

	// Reset the LCD to a blank state. (All white)
	refreshed = false;
	display.clearImmediate(refreshCallback);
	while(refreshed == false) sleep();

	printf("Initialization done! \n");

	// Apply mbed logo bitmap to the pixel buffer
	display.showBMP((uint8_t*)mbed_enabled_logo, 128, 128, 0, 0);
	display.printf("I like MBED!");

	// Push update to the display
	refreshed = false;
	display.update(refreshCallback);

	// Sleep while doing the transmit
	while(refreshed == false) sleep();

	// Go into clock mode
	while(1) {
		sleep();

		// In clock mode, only update once per second
		if(prevSeconds != seconds) {
			display.locate(4,15);
			display.printf("%02d:%02d:%02d", (seconds / 1200) % 24, (seconds / 60) % 60, seconds % 60);
			if(refreshed == true) {
				prevSeconds = seconds;
				refreshed = false;
				display.update(refreshCallback);
			}
		}
	}
}

Datasheet

Datasheet for the LCD (hosted by Mouser)

Committer:
stevew817
Date:
Wed Aug 12 14:06:07 2015 +0000
Revision:
11:0f8ae10b308d
Parent:
10:231fa7861d1f
Implement Paul Staron's enhancements to the display driver classes, from https://developer.mbed.org/users/star297/code/MemoryLCD/rev/b64f87859c57

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Steven Cooreman 0:a0faa86660d4 1 /***************************************************************************//**
Steven Cooreman 0:a0faa86660d4 2 * @file BufferedDisplay.cpp
Steven Cooreman 0:a0faa86660d4 3 * @brief Buffered version of GraphicsDisplay
Steven Cooreman 0:a0faa86660d4 4 *******************************************************************************
Steven Cooreman 0:a0faa86660d4 5 * @section License
Steven Cooreman 0:a0faa86660d4 6 * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
Steven Cooreman 0:a0faa86660d4 7 *******************************************************************************
Steven Cooreman 0:a0faa86660d4 8 *
Steven Cooreman 0:a0faa86660d4 9 * Permission is granted to anyone to use this software for any purpose,
Steven Cooreman 0:a0faa86660d4 10 * including commercial applications, and to alter it and redistribute it
Steven Cooreman 0:a0faa86660d4 11 * freely, subject to the following restrictions:
Steven Cooreman 0:a0faa86660d4 12 *
Steven Cooreman 0:a0faa86660d4 13 * 1. The origin of this software must not be misrepresented; you must not
Steven Cooreman 0:a0faa86660d4 14 * claim that you wrote the original software.
Steven Cooreman 0:a0faa86660d4 15 * 2. Altered source versions must be plainly marked as such, and must not be
Steven Cooreman 0:a0faa86660d4 16 * misrepresented as being the original software.
Steven Cooreman 0:a0faa86660d4 17 * 3. This notice may not be removed or altered from any source distribution.
Steven Cooreman 0:a0faa86660d4 18 *
Steven Cooreman 0:a0faa86660d4 19 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
Steven Cooreman 0:a0faa86660d4 20 * obligation to support this Software. Silicon Labs is providing the
Steven Cooreman 0:a0faa86660d4 21 * Software "AS IS", with no express or implied warranties of any kind,
Steven Cooreman 0:a0faa86660d4 22 * including, but not limited to, any implied warranties of merchantability
Steven Cooreman 0:a0faa86660d4 23 * or fitness for any particular purpose or warranties against infringement
Steven Cooreman 0:a0faa86660d4 24 * of any proprietary rights of a third party.
Steven Cooreman 0:a0faa86660d4 25 *
Steven Cooreman 0:a0faa86660d4 26 * Silicon Labs will not be liable for any consequential, incidental, or
Steven Cooreman 0:a0faa86660d4 27 * special damages, or any other relief, or for any claim by any third party,
Steven Cooreman 0:a0faa86660d4 28 * arising from your use of this Software.
Steven Cooreman 0:a0faa86660d4 29 *
Steven Cooreman 0:a0faa86660d4 30 ******************************************************************************/
Steven Cooreman 0:a0faa86660d4 31
Steven Cooreman 0:a0faa86660d4 32 #include "BufferedDisplay.h"
Steven Cooreman 0:a0faa86660d4 33
Steven Cooreman 0:a0faa86660d4 34 #define SWAP8(a) ((((a) & 0x80) >> 7) | (((a) & 0x40) >> 5) | (((a) & 0x20) >> 3) | (((a) & 0x10) >> 1) | (((a) & 0x08) << 1) | (((a) & 0x04) << 3) | (((a) & 0x02) << 5) | (((a) & 0x01) << 7))
Steven Cooreman 0:a0faa86660d4 35
Steven Cooreman 0:a0faa86660d4 36 namespace silabs {
Steven Cooreman 0:a0faa86660d4 37
Steven Cooreman 0:a0faa86660d4 38 BufferedDisplay::BufferedDisplay(const char *name) : GraphicsDisplay(name) {
Steven Cooreman 0:a0faa86660d4 39 memset((uint8_t*)_pixelBuffer, White, sizeof(_pixelBuffer)); // init full frame buffer
Steven Cooreman 0:a0faa86660d4 40 memset((uint8_t*)_dirtyRows, 0xFF, sizeof(_dirtyRows)); // init dirty status
Steven Cooreman 0:a0faa86660d4 41 }
Steven Cooreman 0:a0faa86660d4 42
Steven Cooreman 0:a0faa86660d4 43 /**
Steven Cooreman 0:a0faa86660d4 44 * Override of GraphicsDisplay's pixel()
Steven Cooreman 0:a0faa86660d4 45 */
Steven Cooreman 0:a0faa86660d4 46
Steven Cooreman 0:a0faa86660d4 47 void BufferedDisplay::pixel(int x, int y, int colour) {
stevew817 7:6cf0aa7bc0fc 48 /* Apply constraint to x and y */
stevew817 7:6cf0aa7bc0fc 49 if(x < 0 || y < 0) return;
stevew817 9:2441ef131ab8 50 if(x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) return;
stevew817 7:6cf0aa7bc0fc 51
stevew817 7:6cf0aa7bc0fc 52 /*****************************************************************************************************************
stevew817 7:6cf0aa7bc0fc 53 * The display expects LSB input, while the SPI is configured for 8bit MSB transfers. Therefore, we should
stevew817 7:6cf0aa7bc0fc 54 * construct the framebuffer accordingly, so that an MSB transmission will put pixel 0 first on the wire.
stevew817 7:6cf0aa7bc0fc 55 *
stevew817 7:6cf0aa7bc0fc 56 * So the actual pixel layout in framebuffer (for 128x128) is as follows:
stevew817 7:6cf0aa7bc0fc 57 * { //Framebuffer
stevew817 7:6cf0aa7bc0fc 58 * { //Line 0
stevew817 7:6cf0aa7bc0fc 59 * {p0, p1, p2, p3, p4, p5, p6, p7}, //Line 0 byte 0 (byte 0)
stevew817 7:6cf0aa7bc0fc 60 * {p8, p9,p10,p11,p12,p13,p14,p15}, //Line 0 byte 1 (byte 1)
stevew817 7:6cf0aa7bc0fc 61 * ...
stevew817 7:6cf0aa7bc0fc 62 * {p120,p121,p122,p123,p124,p125,p126,p127} //Line 0 byte 15 (byte 15)
stevew817 7:6cf0aa7bc0fc 63 * },
stevew817 7:6cf0aa7bc0fc 64 * { //Line 1
stevew817 7:6cf0aa7bc0fc 65 * {p128,p129,p130,p131,p132,p133,p134,p135}, //Line 1 byte 0 (byte 16)
stevew817 7:6cf0aa7bc0fc 66 * ...
stevew817 7:6cf0aa7bc0fc 67 * },
stevew817 7:6cf0aa7bc0fc 68 * ...
stevew817 7:6cf0aa7bc0fc 69 * { //Line 127
stevew817 7:6cf0aa7bc0fc 70 * {...}, //Line 127 byte 0 (byte 2032)
stevew817 7:6cf0aa7bc0fc 71 * ...
stevew817 7:6cf0aa7bc0fc 72 * {...} //Line 127 byte 15 (byte 2047) = 128*128 bits
stevew817 7:6cf0aa7bc0fc 73 * }
stevew817 7:6cf0aa7bc0fc 74 * }
stevew817 7:6cf0aa7bc0fc 75 *
stevew817 7:6cf0aa7bc0fc 76 * This means that to calculate the actual bit position in the framebuffer byte, we need to swap the bit
stevew817 7:6cf0aa7bc0fc 77 * order of the lower three bits. So pixel 7 becomes bit offset 0, 6 -> 1, 5 -> 2, 4->3, 3->4, 2->5, 1->6 and 0->7.
stevew817 7:6cf0aa7bc0fc 78 *****************************************************************************************************************/
Steven Cooreman 0:a0faa86660d4 79 uint8_t swapx = 7 - ((unsigned int)x & 0x07);
Steven Cooreman 0:a0faa86660d4 80 x = ((unsigned int)x & 0xFFFFFFF8) | swapx;
Steven Cooreman 0:a0faa86660d4 81
stevew817 7:6cf0aa7bc0fc 82 /* Since we are dealing with 1-bit pixels, we can avoid having to do bitshift and comparison operations twice.
stevew817 7:6cf0aa7bc0fc 83 * Basically, do the comparison with the requested state and current state, and if it changed, do an XOR on the framebuffer pixel and set the line to dirty.
stevew817 7:6cf0aa7bc0fc 84 */
stevew817 10:231fa7861d1f 85 bool change = ((_pixelBuffer[((y * DISPLAY_WIDTH) + x) / DISPLAY_BUFFER_TYPE_SIZE] & (1 << (x & DISPLAY_BUFFER_TYPE_MASK))) != ((colour & 0x01) << (x & DISPLAY_BUFFER_TYPE_MASK)));
Steven Cooreman 0:a0faa86660d4 86 if(change) {
stevew817 10:231fa7861d1f 87 /* Pixel's value and requested value are different, so since it's binary, we can simply do an XOR */
stevew817 10:231fa7861d1f 88 _pixelBuffer[((y * DISPLAY_WIDTH) + x) / DISPLAY_BUFFER_TYPE_SIZE] ^= (1 << (x & DISPLAY_BUFFER_TYPE_MASK));
Steven Cooreman 0:a0faa86660d4 89
stevew817 7:6cf0aa7bc0fc 90 /* notify dirty status of this line */
stevew817 10:231fa7861d1f 91 _dirtyRows[y / DISPLAY_BUFFER_TYPE_SIZE] |= (1 << (y & DISPLAY_BUFFER_TYPE_MASK));
Steven Cooreman 0:a0faa86660d4 92 }
Steven Cooreman 0:a0faa86660d4 93 }
Steven Cooreman 0:a0faa86660d4 94
Steven Cooreman 0:a0faa86660d4 95 int BufferedDisplay::width() {
Steven Cooreman 0:a0faa86660d4 96 return DISPLAY_WIDTH;
Steven Cooreman 0:a0faa86660d4 97 }
Steven Cooreman 0:a0faa86660d4 98 int BufferedDisplay::height() {
Steven Cooreman 0:a0faa86660d4 99 return DISPLAY_HEIGHT;
Steven Cooreman 0:a0faa86660d4 100 }
Steven Cooreman 0:a0faa86660d4 101
Steven Cooreman 0:a0faa86660d4 102 /**
Steven Cooreman 0:a0faa86660d4 103 * Function to move bitmap into frame buffer
Steven Cooreman 0:a0faa86660d4 104 * arguments:
Steven Cooreman 0:a0faa86660d4 105 * * bitmap: pointer to uint8 array containing horizontal pixel data
stevew817 6:fe04073fe90c 106 * * bmpWidth: width of the bitmap in pixels (must be multiple of 8)
stevew817 6:fe04073fe90c 107 * * bmpHeight: height of the bitmap in pixels
stevew817 6:fe04073fe90c 108 * * startX: starting position to apply bitmap in horizontal direction (0 = leftmost) (must be multiple of 8)
stevew817 6:fe04073fe90c 109 * * startY: starting position to apply bitmap in vertical direction (0 = topmost)
Steven Cooreman 0:a0faa86660d4 110 */
Steven Cooreman 0:a0faa86660d4 111 void BufferedDisplay::showBMP(const uint8_t* bitmap, const uint32_t bmpWidth, const uint32_t bmpHeight, const uint32_t startX, const uint32_t startY) {
stevew817 8:39206d1e11f7 112 uint32_t bmpLine = 0, y = startY, bytesPerLine = ((bmpWidth >= (DISPLAY_WIDTH - startX)) ? (DISPLAY_WIDTH - startX) : bmpWidth) / 8;
Steven Cooreman 0:a0faa86660d4 113
Steven Cooreman 0:a0faa86660d4 114 /* Apply constraints */
Steven Cooreman 0:a0faa86660d4 115 if((bmpWidth & 0x07) != 0) return;
Steven Cooreman 0:a0faa86660d4 116 if((startX & 0x07) != 0) return;
stevew817 6:fe04073fe90c 117 if(startX >= DISPLAY_WIDTH) return;
stevew817 6:fe04073fe90c 118
stevew817 6:fe04073fe90c 119 //Superflouous due to for-loop check
stevew817 6:fe04073fe90c 120 //if((startY >= DISPLAY_HEIGHT) return;
Steven Cooreman 0:a0faa86660d4 121
stevew817 7:6cf0aa7bc0fc 122 /* Copy over bytes to the framebuffer, do not write outside framebuffer boundary */
stevew817 6:fe04073fe90c 123 for(; y < DISPLAY_HEIGHT; y++) {
stevew817 7:6cf0aa7bc0fc 124 /* Check that we are not writing more than the BMP height */
stevew817 8:39206d1e11f7 125 if(bmpLine >= bmpHeight) break;
stevew817 7:6cf0aa7bc0fc 126
stevew817 8:39206d1e11f7 127 /* Copy over one line (bmpLine) from the BMP file to the corresponding line (y) in the pixel buffer */
stevew817 6:fe04073fe90c 128 memcpy( (void*) &(((uint8_t*)_pixelBuffer)[((y * DISPLAY_WIDTH) + startX) / 8]),
stevew817 8:39206d1e11f7 129 (const void*) &(bitmap[bmpLine * (bmpWidth / 8)]),
stevew817 6:fe04073fe90c 130 bytesPerLine);
Steven Cooreman 0:a0faa86660d4 131
stevew817 7:6cf0aa7bc0fc 132 /* Set dirty status for the line we just overwrote */
stevew817 6:fe04073fe90c 133 _dirtyRows[y / DISPLAY_BUFFER_TYPE_SIZE] |= (1 << (y % DISPLAY_BUFFER_TYPE_SIZE));
stevew817 8:39206d1e11f7 134 bmpLine++;
Steven Cooreman 0:a0faa86660d4 135 }
Steven Cooreman 0:a0faa86660d4 136
Steven Cooreman 0:a0faa86660d4 137 return;
Steven Cooreman 0:a0faa86660d4 138 }
Steven Cooreman 0:a0faa86660d4 139 }