Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: screen/EALCD.cpp
- Revision:
- 0:839ecbf5cb2a
- Child:
- 1:f04bcaea1d60
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/screen/EALCD.cpp Thu Feb 11 12:21:18 2010 +0000 @@ -0,0 +1,621 @@ +// Copyright 2010 Richard Parker + +#include "mbed.h" +#include "EALCD.h" + +// Register addresses. +#define EALCD_DEVICE_CODE_ADDRESS 0x00 +#define EALCD_TOUCH_FREQ 10000 +#define EALCD_LCD_FREQ 20000000 + +EALCD::EALCD( PinName LED_PWM, + PinName LCD_RS, + PinName LCD_SO, + PinName LCD_SI, + PinName LCD_SCL, + PinName LCD_CS, + PinName TCH_CS) +: _backlight(LED_PWM), + _comm(LCD_SI, LCD_SO, LCD_SCL), + _rs(LCD_RS), + _cs(LCD_CS), + _tch_cs(TCH_CS) +{ + // Make sure not selecting either chip. + _cs = 1; + _tch_cs = 1; + + // Set the frequency of the PWM backlight to have a period ~400us. + _backlight.period_us(400); + // By default 40% on. + setBrightness(0.4); + + // Set up comms, 8 bit interface. + _comm.format(8); + _comm.frequency(EALCD_LCD_FREQ); + + // Now actual initialise the connection. + _initialise(); +} + +EALCD::~EALCD() +{ +} + +void EALCD::setBrightness(float percentage) +{ + // Check the value is within the correct range. + if (percentage < 0) + { + percentage = 0; + } + else if (percentage > 1) + { + percentage = 1; + } + + _backlight = 1-percentage; +} + +void EALCD::_writeToDisplay(unsigned short data) +{ + // Set RS which is connected to DC and so when high is to data. + _rs = 1; + // Activate the chip (active low). + _cs = 0; + + // Write the msb data. + _comm.write(data >> 8); + // Write the lsb data. + _comm.write(data & 0xff); + + // Deactivate the chip (active low). + _cs = 1; +} + +void EALCD::_writeToRegister(unsigned short addr, unsigned short data) +{ + // Command phase. + // Set RS which is connected to DC and so when low is to command. + _rs = 0; + // Activate the chip (active low). + _cs = 0; + + // Write the msb address. + _comm.write(addr >> 8); + // Write the lsb address. + _comm.write(addr & 0xff); + + // Deactivate the chip (active low). + _cs = 1; + + // Data phase. + // Set RS which is connected to DC and so when high is data. + _rs = 1; + // Activate the chip (active low). + _cs = 0; + + // Write the msb data. + _comm.write(data >> 8); + // Write the lsb data. + _comm.write(data & 0xff); + + // Deactivate the chip (active low). + _cs = 1; + + // Restore index to GRAM by accessing GRAM register. + // Set RS which is connected to DC and so when low is to command. + _rs = 0; + // Activate the chip (active low). + _cs = 0; + + // Write the msb address. + _comm.write(0x00); + // Write the lsb address. + _comm.write(0x22); + + // Deactivate the chip (active low). + _cs = 1; +} + +void EALCD::_initialise() +{ + // Start up sequence. See page 72 of SSD1289 V1.3 datasheet for reasons. + _writeToRegister(0x07, 0x0021); + // Oscillator on. + _writeToRegister(0x00, 0x0001); + _writeToRegister(0x07, 0x0723); + // Exit sleep mode + _writeToRegister(0x10, 0x0000); + // Pause 200ms (need at least 30ms). + wait(0.2); + _writeToRegister(0x07, 0x0033); + // Setup screen orientation and colour mode etc. + _writeToRegister(0x11, 0x6828); + // LCD driving waveform. + _writeToRegister(0x02, 0x0600); + _writeToRegister(0x0f, 0x0000); + // Driver output control. + _writeToRegister(0x01, 0x2b3f); + _writeToRegister(0x0b, 0x5308); + // Set refresh rate to 70Hz. + _writeToRegister(0x25, 0xa000); + +} + +void EALCD::_moveTo(short x, short y) +{ + // Limit movement. + if (x < 0) + { + x = 0; + } + + if (y < 0) + { + y = 0; + } + + if (x >= width()) + { + x = width()-1; + } + + if (y >= height()) + { + y = height()-1; + } + + // Masking values to prevent too large. + x = x & 0x1ff; + _writeToRegister(0x4f, x); + + // Mask to get down to a sensible number. + y = y & 0xff; + // -1 because pixel at top of screen is 239 (as 0 indexed therefore 240 high). + y = height() - 1 - y; + _writeToRegister(0x4e, y); +} + +void EALCD::_window(short x, short y, unsigned short w, unsigned short h) +{ + // If start off screen then no need to even try drawing. + if ((x >= width()) || (y >= height())) + { + return; + } + + // Limit to not go off screen. + if ((x + w) > width()) + { + w = width()-x; + } + + if ((y + h) > height()) + { + h = height()-y; + } + + // Move x and y to correct location. + if (x < 0) + { + x = 0; + } + + if (y < 0) + { + y = 0; + } + + y = height() - 1 - y; + + // Vertical. + unsigned short hsa = y - h + 1; + unsigned short hea = y; + hea = hea << 8; + + _writeToRegister(0x44, hsa | hea); + + // Horizontal + unsigned short vsa = x; + unsigned short vea = x + w - 1; + + _writeToRegister(0x45, vsa); + _writeToRegister(0x46, vea); +} + +void EALCD::_getTouch(unsigned short& x, unsigned short& y, unsigned short& z1, unsigned short& z2) +{ + // As about to change clock freq and so data is not valid for screen disable. + _cs = 1; + + x = 0; + y = 0; + z1 = 0; + z2 = 0; + + // Set comm frequency to access touch screen. + _comm.frequency(EALCD_TOUCH_FREQ); + + // Read the x value. + // Activate the chip (active low). + _tch_cs = 0; + + // 0x83 = Set start bit high, power mode is 2'b11 (always on). + // 0x50 = Set address to 3'b101 + _comm.write(0x83 | 0x50 | 0x00); + // Read two bytes in, the last 3 bits are uninteresting (12 bit reading). + x = _comm.write(0x00) << 8; + x = x | _comm.write(0x00); + x = x << 1; + x = x >> 4; + + // Deactivate the chip (active low). + _tch_cs = 1; + + // Read the y value. + // Activate the chip (active low). + _tch_cs = 0; + + // 0x83 = Set start bit high, power mode is 2'b11 (always on). + // 0x50 = Set address to 3'b001 + _comm.write(0x83 | 0x10 | 0x00); + // Read two bytes in, the last 3 bits are uninteresting (12 bit reading). + y = _comm.write(0x00) << 8; + y = y | _comm.write(0x00); + y = y << 1; + y = y >> 4; + + // Deactivate the chip (active low). + _tch_cs = 1; + + // Read the z1 value. + // Activate the chip (active low). + _tch_cs = 0; + + // 0x83 = Set start bit high, power mode is 2'b11 (always on). + // 0x50 = Set address to 3'b011 + _comm.write(0x83 | 0x30 | 0x00); + // Read two bytes in, the last 3 bits are uninteresting (12 bit reading). + z1 = _comm.write(0x00) << 8; + z1 = z1 | _comm.write(0x00); + z1 = z1 << 1; + z1 = z1 >> 4; + + // Deactivate the chip (active low). + _tch_cs = 1; + + // Read the z2 value. + // Activate the chip (active low). + _tch_cs = 0; + + // 0x83 = Set start bit high, power mode is 2'b11 (always on). + // 0x50 = Set address to 3'b100 + _comm.write(0x83 | 0x40 | 0x00); + // Read two bytes in, the last 3 bits are uninteresting (12 bit reading). + z2 = _comm.write(0x00) << 8; + z2 = z2 | _comm.write(0x00); + z2 = z2 << 1; + z2 = z2 >> 4; + + // Deactivate the chip (active low). + _tch_cs = 1; + + // Set comm frequency to access lcd. + _comm.frequency(EALCD_LCD_FREQ); +} + +void EALCD::_drawPoint(short x, short y, unsigned short c) +{ + // No need to draw if off screen. + if ((x >= width()) + || + (y >= height()) + || + (x < 0) + || + (y < 0)) + { + return; + } + + // Move to the position on the screen to place the pixel. + _moveTo(x, y); + + // Draw the pixel with the current pen value. + _writeToDisplay(c); +} + +void EALCD::_swap(short& i, short& j) +{ + short temp = i; + i = j; + j = temp; +} + +void EALCD::_draw4EllipsePoints(short cX, short cY, short x, short y, bool filled) +{ + if (filled == true) + { + // line in quadrant 2 to 1. + _drawLine(cX-x, cY+y, cX+x, cY+y, _brush.color().rawValue()); + // Point in quadrant 3 to 4. + _drawLine(cX-x, cY-y, cX+x, cY-y, _brush.color().rawValue()); + } else { + // Point in quadrant 1. + _drawPoint(cX+x, cY+y, _pen.color().rawValue()); + // Point in quadrant 2. + _drawPoint(cX-x, cY+y, _pen.color().rawValue()); + // Point in quadrant 3. + _drawPoint(cX-x, cY-y, _pen.color().rawValue()); + // Point in quadrant 4. + _drawPoint(cX+x, cY-y, _pen.color().rawValue()); + } +} + +void EALCD::_drawEllipse(short x, short y, unsigned short w, unsigned short h, bool filled) +{ + // Algorithm taken from http://homepage.smc.edu/kennedy_john/belipse.pdf + short xRadius = w/2; + short yRadius = h/2; + short cX = x + xRadius; + short cY = y + yRadius; + + int xChange = yRadius*yRadius*(1 - 2*xRadius); + int yChange = xRadius*xRadius; + int ellipseError = 0; + int twoASquare = 2*xRadius*xRadius; + int twoBSquare = 2*yRadius*yRadius; + int stoppingX = twoBSquare*xRadius; + int stoppingY = 0; + + x = xRadius; + y = 0; + + while (stoppingX >= stoppingY) + { + // 1st set of points, y' > -1. + _draw4EllipsePoints(cX, cY, x, y, filled); + y += 1; + stoppingY += twoASquare; + ellipseError += yChange; + yChange += twoASquare; + if ((2*ellipseError + yChange) > 0) + { + x -= 1; + stoppingX -= twoBSquare; + ellipseError += xChange; + xChange += twoBSquare; + } + } + + // 1st point set is done; start the 2nd set of points. + x = 0; + y = yRadius-1; + xChange = yRadius*yRadius; + yChange = xRadius*xRadius*(1 - 2*yRadius); + ellipseError = 0; + stoppingX = 0; + stoppingY = twoASquare*yRadius; + + while (stoppingX <= stoppingY) + { + // 2nd set of points, y' < -1. + _draw4EllipsePoints(cX, cY, x, y, filled); + x += 1; + stoppingX += twoBSquare; + ellipseError += xChange; + xChange += twoBSquare; + + if ((2*ellipseError + yChange) > 0) + { + y -= 1; + stoppingY -= twoASquare; + ellipseError += yChange; + yChange += twoASquare; + } + } +} + +void EALCD::_drawLine(short x0, short y0, short x1, short y1, unsigned short c) +{ + // Check if can be done faster. + if (y0 == y1) + { + // Not on screen so don't draw. + if ((y0 >= height()) || (y0 < 0)) + { + return; + } + + // Horizontal line. + unsigned short sx = (x0 < x1) ? x0 : x1; + unsigned short length = abs(x1 - x0); + + // Make sure not going off edge of screen. + if (sx+length > width()) + { + length = width()-sx; + } + + // Move to start of line and just draw. + _moveTo(sx, y0); + for (int i = 0; i < length; i++) + { + _writeToDisplay(c); + } + + return; + } + + // This code is from http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm - the fast section i.e. + // relies on only integer maths. + + bool steep = (abs(y1 - y0) > abs(x1 - x0)); + + if (steep == true) + { + _swap(x0, y0); + _swap(x1, y1); + } + + if (x0 > x1) + { + _swap(x0, x1); + _swap(y0, y1); + } + + int deltaX = x1 - x0; + int deltaY = abs(y1 - y0); + int error = deltaX / 2; + int yStep; + int y = y0; + int x = 0; + + if (y0 < y1) + { + yStep = 1; + } else { + yStep = -1; + } + + // Now actually loop and draw the line. + for (x = x0; x < x1; x++) + { + // Channge drawing if drawing horizontal or vertical. + if (steep == true) + { + _drawPoint(y, x, c); + } else { + _drawPoint(x, y, c); + } + + // Calculate the error to decide to step or not. + error = error - deltaY; + + if (error < 0) + { + y = y + yStep; + error = error + deltaX; + } + } +} + +void EALCD::clearScreen() +{ + // First move to the top left. + _window(0, 0, width(), height()); + _moveTo(0, 0); + + // Now fill in relying on the addresses to be automatically updated. + for(int i = 0; i < (width()*height()); i++) + { + _writeToDisplay(_brush.color().rawValue()); + } +} + +void EALCD::drawPoint(short x, short y) +{ + // Draw the pixel with the current pen value. + _drawPoint(x, y, _pen.color().rawValue()); +} + +void EALCD::drawLine(short x0, short y0, short x1, short y1) +{ + _drawLine(x0, y0, x1, y1, _pen.color().rawValue()); +} + +void EALCD::drawRect(short x, short y, unsigned short w, unsigned short h) +{ + // Draw the top horizontal line. + drawLine(x, y, x + w - 1, y); + + // Draw the bottom horizontal line. + drawLine(x, y + h - 1, x + w - 1, y + h - 1); + + // Draw the left vertical line. + drawLine(x, y, x, y + h - 1); + + // Draw the right vertical line. + drawLine(x + w - 1, y, x + w - 1, y + h); +} + +void EALCD::drawFilledRect(short x, short y, unsigned short w, unsigned short h) +{ + if (x < 0) + { + x = 0; + } + + if (y < 0) + { + y = 0; + } + + // Set the window so that automatically wrap and go inside the box. + _window(x, y, w, h); + _moveTo(x, y); + + for (int i = 0; i < (w*h); i++) + { + _writeToDisplay(_brush.color().rawValue()); + } + + drawRect(x, y, w, h); +} + + +void EALCD::drawEllipse(short x, short y, unsigned short w, unsigned short h) +{ + _drawEllipse(x, y, w, h, false); +} + +void EALCD::drawFilledEllipse(short x, short y, unsigned short w, unsigned short h) +{ + _drawEllipse(x, y, w, h, true); + _drawEllipse(x, y, w, h, false); +} + +void EALCD::drawImage(unsigned short x, unsigned short y, EAImage& img) +{ + // Don't try painting an invalid image. + if (img.isValid() == false) + { + return; + } + + // Set the position to draw. + img.setX(x); + img.setY(y); + + // Draw the image. + img.paint(*this); +} + +void EALCD::drawText(unsigned short x, unsigned short y, char* text) +{ + // Don't try painting with an invalid font or no text + if ((_font.isValid() == false) || (text == NULL)) + { + return; + } + + EAImage img; + EAFont::EACharacter detail; + + img.load(_font.path()); + + for (int i = 0; i < strlen(text); i++) + { + if (_font.getCharacter(text[i], detail) == true) + { + img.setX(x+detail.xOffset); + img.setY(y+detail.yOffset); + img.paint(*this, detail.x, detail.y, detail.width, detail.height); + + x += detail.xAdvance; + } else { + // Character is unrecognised. + } + } +}