Richard Parker / EALCD
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.
+        }      
+    }  
+}