├── Software_Installation_Command_List.txt ├── MemoryLCD.h ├── README.txt ├── examples └── demo │ └── demo.cpp └── MemoryLCD.cpp /Software_Installation_Command_List.txt: -------------------------------------------------------------------------------- 1 | Sample set of commands to get all necessary software running and the demo program compiled. 2 | This set of commands installs the software in a new directory called 'electronics'. 3 | 4 | cd ~ 5 | mkdir electronics 6 | cd electronics 7 | 8 | sudo aptitude install git 9 | git clone https://github.com/MakerDyne/Memory-LCD-for-Raspberry-Pi.git 10 | 11 | cd ~/electronics 12 | wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.25.tar.gz 13 | tar zxvf bcm2835-1.25.tar.gz 14 | cd bcm2835-1.25 15 | ./configure 16 | make 17 | sudo make check 18 | sudo make install 19 | 20 | cd ~/electronics/Memory-LCD-for-Raspberry-Pi/examples/demo/ 21 | g++ -c -g -Wall -funsigned-char demo.cpp ../../MemoryLCD.cpp 22 | g++ -o demo demo.o MemoryLCD.o -l bcm2835 -l pthread 23 | sudo ./demo 24 | (Ctrl+C to stop the demo) 25 | -------------------------------------------------------------------------------- /MemoryLCD.h: -------------------------------------------------------------------------------- 1 | #ifndef _MEMORY_LCD_LIB_H 2 | #define _MEMORY_LCD_LIB_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // Memory LCD pixel dimensions - ALTER ACCORDING TO YOUR PARTICULAR LCD MODEL 10 | #define LCDWIDTH (96) 11 | #define LCDHEIGHT (96) 12 | 13 | // Delay constants for LCD timing // (Datasheet values) 14 | #define PWRUP_DISP_DELAY 40 // (>30us) 15 | #define PWRUP_EXTCOMIN_DELAY 40 // (>30us) 16 | #define SCS_HIGH_DELAY 3 // (>3us) 17 | #define SCS_LOW_DELAY 1 // (>1us) 18 | #define INTERFRAME_DELAY 1 // (>1us) 19 | 20 | class MemoryLCD { 21 | public: 22 | MemoryLCD(char SCSpin, char DISPpin, char EXTCOMINpin, bool useEXTCOMIN); 23 | ~MemoryLCD(); 24 | // Write data direct to display 25 | void writeLineToDisplay(char lineNumber, char *line); 26 | void writeMultipleLinesToDisplay(char lineNumber, char numLines, char *lines); 27 | // Write data to line buffer 28 | void writePixelToLineBuffer(unsigned int pixel, bool isWhite); 29 | void writeByteToLineBuffer(char byteNumber, char byteToWrite); 30 | void copyByteWithinLineBuffer(char sourceByte, char destinationByte); 31 | void setLineBufferBlack(); 32 | void setLineBufferWhite(); 33 | // write data from line buffer to display 34 | void writeLineBufferToDisplay(char lineNumber); 35 | void writeLineBufferToDisplayRepeatedly(char lineNumber, char numLines); 36 | // Write data to frame buffer 37 | void writePixelToFrameBuffer(unsigned int pixel, char lineNumber, bool isWhite); 38 | void writeByteToFrameBuffer(char byteNumber, char lineNumber, char byteToWrite); 39 | void setFrameBufferBlack(); 40 | void setFrameBufferWhite(); 41 | // write data from frame buffer to display 42 | void writeFrameBufferToDisplay(); 43 | // clear functions 44 | void clearLineBuffer(); 45 | void clearFrameBuffer(); 46 | void clearDisplay(); 47 | // turn display on/off 48 | void turnOff(); 49 | void turnOn(); 50 | // return display parameters 51 | unsigned int getDisplayWidth(); 52 | unsigned int getDisplayHeight(); 53 | // software VCOM control - NOT YET PROPERLY IMPLEMENTED 54 | void softToggleVCOM(); 55 | private: 56 | char commandByte; 57 | char vcomByte; 58 | char clearByte; 59 | char paddingByte; 60 | char DISP; 61 | char SCS; 62 | char SI; 63 | char SCLK; 64 | char EXTCOMIN; 65 | bool enablePWM; 66 | char lineBuffer[LCDWIDTH/8]; 67 | char frameBuffer[LCDWIDTH*LCDHEIGHT/8]; 68 | static void *hardToggleVCOM(void *arg); 69 | char reverseByte(char b); 70 | }; 71 | 72 | #endif -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Author: Richard Leszczynski 2 | email: richard@makerdyne.com 3 | web: www.MakerDyne.com/memorylcds/ 4 | 5 | 6 | Basic class for Sharp Memory LCD control to accompany my Sharp Memory LCD Breakout Boards. 7 | 8 | IMPORTANT INFO: 9 | 10 | The Sharp Memory LCDs require their screens be refreshed at least once a second. The signal telling the display to refresh can either be supplied with a software command over SPI (set JP1 LOW on the rear of your breakout board) or with a square wave signal supplied to the EXTCOMIN pin (set JP1 HIGH). 11 | 12 | The class and its demos rely on the hardware method of display refreshing at present as proper software control has not been implemented. The demo outputs a square wave at 2Hz on pin "GPIO 25" of the Raspberry Pi. See the diagram (with the green background) of the GPIO pins for the pin names: 13 | 14 | http://elinux.org/RPi_Low-level_peripherals#General_Purpose_Input.2FOutput_.28GPIO.29 15 | 16 | A more reliable way of constantly providing a hardware EXTCOMIN signal to the Memory LCD while writing your code would be to connect the EXTCOMIN pin on the breakout board to the output of a 555 timer configured in astable mode to output a frequency between 2 Hz and 60 Hz. This will ensure that an EXTCOMIN signal always gets through to the display regardless of whether you're currently running your Memory LCD code or not. 17 | 18 | See here for how to wire a 555 in astable mode and for guidance on how to choose R & C values. 19 | 20 | http://www.ohmslawcalculator.com/555_astable.php 21 | 22 | I am using: 23 | C = 20 nF 24 | R1 = 2.7 MOhm 25 | R2 = 10 MOhm 26 | 27 | You need a frequency of between 2 Hz and 60 Hz and a duty cycle of between 50 and 70%. Other combinations of R & C values can provide that if you don't have the parts I've suggested. 28 | 29 | If you want to keep the Memory LCD connected to the Raspberry Pi for long periods without providing a square wave on EXTCOMIN, you should clear the display and turn it off by pulling the breakout board's DISP pin LOW. 30 | 31 | A note on connecting the Memory LCD breakout board to the Pi: I have had a powered-on Pi turn off upon inserting a breakout board into a breadboard that already had the necessary connections made with the Pi's GPIOs. 32 | 33 | HARDWARE SETUP: 34 | 35 | Again, refer to http://elinux.org/RPi_Low-level_peripherals#General_Purpose_Input.2FOutput_.28GPIO.29 36 | 37 | You must make the following connections between the Pi's GPIO pins and the breakout board header to make the demo run. You can alter the GPIO pins used in your own code for SCS, DIS and, EXTCOMIN by changing the pins given when the Memory LCD constructor is called. NB: SI and SCLK cannot be changed. 38 | 39 | Breakout Board --> Raspberry Pi 40 | DISP --> "GPIO 24" [can be changed] 41 | EXTCOMIN --> "GPIO 25" [can be changed] (or connect EXCOMIN to the output of a 555 timer) 42 | SCS --> "GPIO 23" [can be changed] 43 | SI --> "GPIO 10 (MOSI)" [Must be on this pin, is dedicated bcm2835 hardware SPI pin] 44 | SCLK --> "GPIO 11 (SCLK)" [Must be on this pin, is dedicated bcm2835 hardware SPI pin] 45 | Vin --> 5V or 3V3 depending on minimum Vin of your Memory LCD model [can be changed] 46 | GND --> GND [can be changed, multiple GND connections on the GPIO header] 47 | 48 | JUMPERS (on rear of breakout board) 49 | JP1 = HIGH 50 | JP2 = Set according to your model of LCD 51 | 52 | SOFTWARE INSTALLATION: 53 | 54 | Download the zip file from GitHub and extract it a directory of your choice within your HOME directory. 55 | 56 | If you use the commands: 57 | 58 | cd~ 59 | wget https://github.com/MakerDyne/Memory-LCD-for-Raspberry-Pi/archive/master.zip 60 | unzip master.zip 61 | 62 | alternatively, the wget and unzip commands can be replaced within 63 | 64 | git clone https://github.com/MakerDyne/Memory-LCD-for-Raspberry-Pi.github 65 | 66 | which will grab the code direct from github ('sudo aptitude install git' if you don't have git installed) 67 | 68 | you should end up with the following: 69 | 70 | /home/USER/Memory-LCD-for-Raspberry-Pi-master/ 71 | /home/USER/Memory-LCD-for-Raspberry-Pi-master/MemoryLCD.h 72 | /home/USER/Memory-LCD-for-Raspberry-Pi-master/MemoryLCD.cpp 73 | /home/USER/Memory-LCD-for-Raspberry-Pi-master/README.txt 74 | /home/USER/Memory-LCD-for-Raspberry-Pi-master/examples/demo/demo.cpp 75 | 76 | Now, in addition to the Memory LCD library for Raspberry Pi from my GitHub page, you also need to install the library for the Pi's Broadcom BCM 2835 chip that allows control of the GPIO pins. 77 | 78 | The library can be downloaded from http://www.airspayce.com/mikem/bcm2835/index.html 79 | 80 | Use the following commands to download and install it (NB: version number may change, check website for latest version before entering these commands): 81 | 82 | cd ~ 83 | wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.25.tar.gz 84 | tar zxvf bcm2835-1.25.tar.gz 85 | cd bcm2835-1.25 86 | ./configure 87 | make 88 | sudo make check 89 | sudo make install 90 | 91 | Now, before you compile and run the Memory LCD demo program, you must first open the MemoryLCD.h file and edit the LCDWIDTH and LCDHEIGHT defines such that they match the pixel dimensions of your particular LCD: 92 | 93 | #define LCDWIDTH 96 94 | #define LCDHEIGHT 96 95 | 96 | must be changed to match the dimensions of your particular model of Memory LCD in pixels. 97 | 98 | (The boards I am selling are either (WxH) 96x96 pixels, 128x128 pixels, or 400x240 pixels.) 99 | 100 | If everything has gone to plan, change to the demo directory within the Memory-LCD-for-Raspberry-Pi-master/ directory and compile, link and run the demo program 101 | 102 | cd ~/Memory-LCD-for-Raspberry-Pi-master/examples/demo/ 103 | g++ -c -g -Wall -funsigned-char demo.cpp ../../MemoryLCD.cpp 104 | g++ -o demo demo.o MemoryLCD.o -l bcm2835 -l pthread 105 | sudo ./demo 106 | 107 | If there were no compile or linking errors, and the pi is connected to the Memory LCD breakout board as described above, you should see the demo start with a side scrolling sinewave. 108 | 109 | Note the use of sudo to run the demo - any program wanting access to the GPIOs must run as root. -------------------------------------------------------------------------------- /examples/demo/demo.cpp: -------------------------------------------------------------------------------- 1 | #include "../../MemoryLCD.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | /* 9 | * The Raspberry Pi GPIO pins used for SPI are: 10 | * P1-19 (MOSI) 11 | * P1-21 (MISO) 12 | * P1-23 (CLK) 13 | * P1-24 (CE0) 14 | * P1-26 (CE1) 15 | */ 16 | 17 | // use the pin name/number, not the number based on physical header position 18 | char SCS = 23; // Use any pin except the dedicated SPI SS pins? 19 | char DISP = 24; // Use any non-specialised GPIO pin 20 | char EXTCOMIN = 25; // Use any non-specialised GPIO pin 21 | 22 | MemoryLCD memLcd(SCS, DISP, EXTCOMIN, true); 23 | 24 | unsigned int lcdWidth; 25 | unsigned int lcdHeight; 26 | char numRepetitions = 4; 27 | bool toggle = true; 28 | 29 | int main() { 30 | memLcd.clearDisplay(); 31 | lcdWidth = memLcd.getDisplayWidth(); 32 | lcdHeight = memLcd.getDisplayHeight(); 33 | sleep(2); 34 | 35 | while(1) { 36 | // print sinewave 37 | //float increment = 360.00/lcdWidth; // one number MUST have a decimal point here! 38 | float increment = 6.2831/lcdWidth; 39 | unsigned int sinValue = 0; 40 | for(float theta=0; theta<50.26; theta += 0.1256) { 41 | //for(unsigned int theta=0; theta<211; theta +=30) { 42 | for(unsigned int y=1; y<=lcdHeight; y++) { 43 | for(unsigned int x=1; x<=lcdWidth; x++) { 44 | sinValue = ( sin( theta+(x*increment) ) * lcdHeight/2 ) + lcdHeight/2; 45 | if(sinValue >= y && y > lcdHeight/2) { 46 | memLcd.writePixelToLineBuffer(x, 0); 47 | } 48 | if(sinValue <= y && y <= lcdHeight/2) { 49 | memLcd.writePixelToLineBuffer(x, 0); 50 | } 51 | } 52 | memLcd.writeLineBufferToDisplay(y); 53 | memLcd.clearLineBuffer(); 54 | } 55 | usleep(20000); 56 | memLcd.clearDisplay(); 57 | } 58 | 59 | 60 | // print expanding and contracting circles 61 | unsigned int originX = lcdWidth/2; 62 | unsigned int originY = lcdHeight/2; 63 | unsigned int expandingCircleRadius = (lcdHeight/2)*0.9; 64 | for(unsigned int repeat = 0; repeat < 2; repeat++) { 65 | for(unsigned int radius = 5; radius < expandingCircleRadius; radius++) { 66 | for(unsigned int y = originY - radius; y <= originY; y++) { 67 | // need to calculate left and right limits of the circle 68 | float theta = acos(float(abs(originY-y))/float(radius)); 69 | theta -= 1.5708; 70 | unsigned int xLength = cos(theta)*float(radius); 71 | for(unsigned int x = originX - xLength; x <= originX; x++) { 72 | memLcd.writePixelToLineBuffer(x, 0); 73 | memLcd.writePixelToLineBuffer(originX + (originX - x), 0); 74 | } 75 | memLcd.writeLineBufferToDisplay(y); 76 | memLcd.writeLineBufferToDisplay(originY + (originY - y)); 77 | memLcd.clearLineBuffer(); 78 | } 79 | usleep(20000); 80 | } 81 | for(unsigned int radius = expandingCircleRadius; radius > 2; radius--) { 82 | for(unsigned int y = originY - radius; y <= originY; y++) { 83 | // need to calculate left and right limits of the circle 84 | float theta = acos(float(abs(originY-y))/float(radius)); 85 | theta -= 1.5708; 86 | unsigned int xLength = cos(theta)*float(radius); 87 | for(unsigned int x = originX - xLength; x <= originX ; x++) { 88 | memLcd.writePixelToLineBuffer(x, 0); 89 | memLcd.writePixelToLineBuffer(originX + (originX - x), 0); 90 | } 91 | memLcd.writeLineBufferToDisplay(y); 92 | memLcd.writeLineBufferToDisplay(originY + (originY - y)); 93 | memLcd.clearLineBuffer(); 94 | } 95 | memLcd.writeLineBufferToDisplay(originY+radius); 96 | memLcd.writeLineBufferToDisplay(originY-radius); 97 | usleep(20000); 98 | } 99 | memLcd.clearDisplay(); 100 | memLcd.clearLineBuffer(); 101 | } 102 | 103 | 104 | // print circling circle 105 | numRepetitions = 4; 106 | unsigned int sweepRadius = (lcdHeight/2)*0.8; 107 | unsigned int sweepOriginX = lcdWidth/2; 108 | unsigned int sweepOriginY = lcdHeight/2; 109 | unsigned int circleRadius = 0.7*((lcdHeight/2)-sweepRadius); 110 | for(float rads=0; rads<6.2824*numRepetitions; rads += 0.04) { 111 | // calculate circle centre 112 | unsigned int circleOriginX = sweepOriginX + cos(rads)*sweepRadius; 113 | unsigned int circleOriginY = sweepOriginY + sin(rads)*sweepRadius; 114 | // draw circle about the centre 115 | for(unsigned int y = circleOriginY - circleRadius; y <= circleOriginY; y++) { 116 | // need to calculate left and right limits of the circle 117 | float theta = acos(float(std::abs(circleOriginY-y))/float(circleRadius)); 118 | theta -= 1.5708; 119 | unsigned int xLength = cos(theta)*float(circleRadius); 120 | for(unsigned int x = circleOriginX - xLength; x <= circleOriginX; x++) { 121 | memLcd.writePixelToLineBuffer(x, 0); 122 | memLcd.writePixelToLineBuffer(circleOriginX + (circleOriginX - x), 0); 123 | } 124 | memLcd.writeLineBufferToDisplay(y); 125 | memLcd.writeLineBufferToDisplay(circleOriginY + (circleOriginY - y)); 126 | memLcd.clearLineBuffer(); 127 | } 128 | usleep(15000); 129 | memLcd.clearDisplay(); 130 | } 131 | memLcd.clearDisplay(); 132 | memLcd.clearLineBuffer(); 133 | 134 | 135 | // print triangles 136 | numRepetitions = 4; 137 | toggle = false; 138 | for(char i=0; i< numRepetitions; i++) { 139 | for(unsigned int y=1; y<=lcdHeight; y++) { 140 | for(unsigned int x=1; x0; y--) { 147 | for(unsigned int x=lcdWidth; x>y+((lcdWidth-lcdHeight)/2); x--) { 148 | memLcd.writePixelToLineBuffer(x, toggle); 149 | } 150 | memLcd.writeLineBufferToDisplay(y); 151 | usleep(5000); 152 | } 153 | if(toggle) 154 | toggle = false; 155 | else 156 | toggle = true; 157 | } 158 | memLcd.clearDisplay(); 159 | memLcd.clearLineBuffer(); 160 | 161 | 162 | // Print chequerboard patterns 163 | numRepetitions = 8; 164 | for(char i=0; i 0 && whiteLine < lcdHeight+1) { 259 | memLcd.setLineBufferWhite(); 260 | memLcd.writeLineBufferToDisplay(whiteLine); 261 | } 262 | if(blackLine > 0 && blackLine < lcdHeight+1) { 263 | memLcd.setLineBufferBlack(); 264 | memLcd.writeLineBufferToDisplay(blackLine); 265 | } 266 | usleep(5000); 267 | } 268 | } 269 | // make sure you clear both the buffer and display before moving on to a new sequence 270 | memLcd.clearLineBuffer(); 271 | memLcd.clearDisplay(); 272 | } 273 | sleep(1); 274 | return 0; 275 | } -------------------------------------------------------------------------------- /MemoryLCD.cpp: -------------------------------------------------------------------------------- 1 | #include "MemoryLCD.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | MemoryLCD::MemoryLCD(char SCSpin, char DISPpin, char EXTCOMINpin, bool useEXTCOMIN) { 7 | bcm2835_init(); 8 | // Initialise private vars (constructor args) 9 | // Set DISPpin = 255, when you call the constructor if you want to save a pin 10 | // and not have hardware control of _DISP 11 | SCS = SCSpin; 12 | DISP = DISPpin; 13 | EXTCOMIN = EXTCOMINpin; 14 | enablePWM = useEXTCOMIN; 15 | 16 | bcm2835_gpio_fsel(SCS, BCM2835_GPIO_FSEL_OUTP); 17 | bcm2835_gpio_fsel(DISP, BCM2835_GPIO_FSEL_OUTP); 18 | bcm2835_gpio_fsel(EXTCOMIN, BCM2835_GPIO_FSEL_OUTP); 19 | 20 | // initialise private vars (others) 21 | commandByte = 0b10000000; 22 | vcomByte = 0b01000000; 23 | clearByte = 0b00100000; 24 | paddingByte = 0b00000000; 25 | 26 | // Introduce delay to allow MemoryLCD to reach 5V 27 | // (probably redundant here as Pi's boot is much longer than Arduino's power-on time) 28 | bcm2835_delayMicroseconds(800); // minimum 750us 29 | 30 | // setup separate thread to continuously output the EXTCOMIN signal for as long as the parent runs. 31 | // NB: this leaves the Memory LCD vulnerable if an image is left displayed after the program stops. 32 | pthread_t threadId; 33 | if(enablePWM) { 34 | if(pthread_create(&threadId, NULL, &hardToggleVCOM, (void *)EXTCOMIN)) { 35 | cout << "Error creating EXTCOMIN thread" << endl; 36 | } else { 37 | cout << "PWM thread started successfully" << endl; 38 | } 39 | } else { 40 | // TODO: setup timer driven interrupt instead? 41 | } 42 | 43 | // SETUP SPI 44 | // Datasheet says SPI clock must have <1MHz frequency (BCM2835_SPI_CLOCK_DIVIDER_256) 45 | // but it may work up to 4MHz (BCM2835_SPI_CLOCK_DIVIDER_128, BCM2835_SPI_CLOCK_DIVIDER_64) 46 | /* 47 | * The Raspberry Pi GPIO pins reserved for SPI once bcm2835_spi_begin() is called are: 48 | * P1-19 (MOSI) 49 | * P1-21 (MISO) 50 | * P1-23 (CLK) 51 | * P1-24 (CE0) 52 | * P1-26 (CE1) 53 | */ 54 | bcm2835_spi_begin(); 55 | // set MSB here - setting to LSB elsewhere doesn't work. So I'm manually reversing lineAddress bit order instead. 56 | bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); 57 | bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_128); // this is the 2 MHz setting 58 | bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); 59 | bcm2835_spi_chipSelect(BCM2835_SPI_CS_NONE); 60 | // Not sure if I can use the built-in bcm2835 Chip Select functions as the docs suggest it only 61 | // affects bcm2835_spi_transfer() calls so I'm setting it to inactive and setting up my own CS pin 62 | // as I want to use bcm2835_spi_writenb() to send data over SPI instead. 63 | 64 | // Set pin modes 65 | bcm2835_gpio_write(SCS, LOW); 66 | 67 | if(DISP != 255) { 68 | bcm2835_gpio_write(DISP, LOW); 69 | } 70 | if(enablePWM) { 71 | bcm2835_gpio_write(EXTCOMIN, LOW); 72 | } 73 | 74 | // Memory LCD startup sequence with recommended timings 75 | bcm2835_gpio_write(DISP, HIGH); 76 | bcm2835_delayMicroseconds(PWRUP_DISP_DELAY); 77 | 78 | bcm2835_gpio_write(EXTCOMIN, LOW); 79 | bcm2835_delayMicroseconds(PWRUP_EXTCOMIN_DELAY); 80 | 81 | clearLineBuffer(); 82 | } 83 | 84 | MemoryLCD::~MemoryLCD(void) { 85 | bcm2835_spi_end(); 86 | bcm2835_close(); 87 | } 88 | 89 | void MemoryLCD::writeLineToDisplay(char lineNumber, char *line) { 90 | writeMultipleLinesToDisplay(lineNumber, 1, line); 91 | } 92 | 93 | 94 | void MemoryLCD::writeMultipleLinesToDisplay(char lineNumber, char numLines, char *lines) { 95 | // this implementation writes multiple lines that are CONSECUTIVE (although they don't 96 | // have to be, as an address is given for every line, not just the first in the sequence) 97 | // data for all lines should be stored in a single array 98 | char * linePtr = lines; 99 | bcm2835_gpio_write(SCS, HIGH); 100 | bcm2835_delayMicroseconds(SCS_HIGH_DELAY); 101 | bcm2835_spi_writenb(&commandByte, 1); 102 | for(char x=0; x> 4 | (b & 0x0F) << 4; 293 | b = (b & 0xCC) >> 2 | (b & 0x33) << 2; 294 | b = (b & 0xAA) >> 1 | (b & 0x55) << 1; 295 | return b; 296 | } --------------------------------------------------------------------------------