Platform game written for the GHI/OutrageousCircuits RETRO game device. Navigate the caves collecting all the pickups and avoiding the creatures and haunted mine carts that patrol the caves. Oh and remember to watch out for the poisonous plants... This game demonstrates the ability to have multiple animated sprites where the sprites can overlap the background environment. See how the player moves past the fence and climbs the wall in the 3rd screen.

Dependencies:   mbed

LCD_ST7735/LCD_ST7735.cpp

Committer:
taylorza
Date:
2015-02-16
Revision:
16:f9227904afc4
Parent:
4:45ff7fc8a431

File content as of revision 16:f9227904afc4:

#include "mbed.h"
#include "Bitmap4bpp.h"
#include "LCD_ST7735.h"

const uint16_t LCD_ST7735::DefaultPalette[] = {
    0x0000, // 0  - Black
    0x0019, // 1  - Blue
    0xc800, // 2  - Red
    0xc819, // 3  - Magenta
    0x0660, // 4  - Green
    0x0679, // 5  - Cyan
    0xce60, // 6  - Yellow
    0xce79, // 7  - White        
    0x001f, // 8  - Bright Blue
    0xf800, // 9  - Bright Red
    0xf81f, // 10 - Bright Magenta
    0x07e0, // 11 - Bright Green
    0x07ff, // 12 - Bright Cyan
    0xffe0, // 13 - Bright Yellow
    0xffff, // 14 - Bright White
};

LCD_ST7735::LCD_ST7735(
    PinName backlightPin,
    PinName resetPin,
    PinName dsPin,
    PinName mosiPin,
    PinName misoPin,
    PinName clkPin,
    PinName csPin,
    PanelColorFilter colorFilter
    ) :
        _colorFilter(colorFilter),
        _backlight(backlightPin, 0),
        _reset(resetPin, 1),
        _ds(dsPin, 0),
        _cs(csPin, 1),
        _spi(mosiPin, misoPin, clkPin)        
{        
    _spi.format(8, 3);
    _spi.frequency(18000000);
    
    initDisplay();
    clearScreen();
    setForegroundColor(0xffff);
    setBackgroundColor(0x0000);    
    
    _palette = (uint16_t*)DefaultPalette;    
}

void LCD_ST7735::setOrientation(Orientation orientation, bool flip)
{
    const static uint8_t my = 0x80; 
    const static uint8_t mx = 0x40;
    const static uint8_t mv = 0x20;
    
    uint8_t madctlData = _colorFilter;
    switch(orientation)
    {
        case Rotate0:
            _width = 128;
            _height = 160;
            madctlData |= flip ? mx : 0;
            break;
            
        case Rotate90:
            _width = 160;
            _height = 128;
            madctlData |= flip ? my | mv | mx : mv | mx;
            break;
            
        case Rotate180:
            _width = 128;
            _height = 160;
            madctlData |= flip ? my : mx | my;
            break;
            
        case Rotate270:
            _width = 160;
            _height = 128;
            madctlData |= flip ? mv : mv | my;
            break;
    }
    write(CMD_MADCTL, (uint8_t[]){madctlData}, 1);
}

int LCD_ST7735::getWidth()
{
    return _width;
}
        
int LCD_ST7735::getHeight()
{
    return _height;
}

void LCD_ST7735::setBacklight(bool state)
{
    _backlight = state ? 1 : 0;
}

void LCD_ST7735::clearScreen(uint16_t color)
{
    clipRect(0, 0, _width - 1, _height - 1);
    beginBatchCommand(CMD_RAMWR);
    uint8_t colorHigh = color >> 8;
    uint8_t colorLow = color;
    for(int i = 0; i < 128 * 160 * 2; ++i)
    {
        writeBatchData(colorHigh, colorLow);
    }
    endBatchCommand();
}

void LCD_ST7735::setPixel(int x, int y, uint16_t color)
{
    write(CMD_CASET, (uint8_t[]){0, x, 0, x}, 4);    
    write(CMD_RASET, (uint8_t[]){0, y, 0, y}, 4);    
    write(CMD_RAMWR, color);
}

void LCD_ST7735::drawLine(int x1, int y1, int x2, int y2, uint16_t color)
{
    int dx = abs(x2 - x1);
    int dy = abs(y2 - y1);
    
    if (dx == 0) 
    {
        if (y1 > y2) swap(y1, y2);
        drawVertLine(x1, y1, y2, color);
        return;
    }
    else if(dy == 0)
    {
        if (x1 > x2) swap(x1, x2);
        drawHorizLine(x1, y1, x2, color);
        return;
    }
   
    int sx = (x1 < x2) ? 1 : -1;
    int sy = (y1 < y2) ? 1 : -1;
    int err = dx - dy;
    while(x1 != x2 || y1 != y2)
    {
        setPixel(x1, y1, color);
        int e2 = err << 1;
        if (e2 > -dy)
        {
            err -= dy;
            x1 += sx;            
        }
        if (e2 < dx)
        {
            err += dx;
            y1 += sy;
        }
    }
    setPixel(x2, y2, color);
}

void LCD_ST7735::swap(int &a, int &b)
{
    int t = a;
    a = b;
    b = t;
}

void LCD_ST7735::drawRect(int x1, int y1, int x2, int y2, uint16_t color)
{
    if (x1 > x2) swap(x1, x2);
    if (y1 > y2) swap(y1, y2);
    
    drawHorizLine(x1, y1, x2, color);
    drawHorizLine(x1, y2, x2, color);
    drawVertLine(x1, y1, y2, color);
    drawVertLine(x2, y1, y2, color);
}

void LCD_ST7735::drawCircle(int x, int y, int r, uint16_t color)
{
    int ix = r;
    int iy = 0;
    int err = 1 - r;
    
    while(ix >= iy)
    {
        setPixel(x + ix, y + iy, color);
        setPixel(x + iy, y + ix, color);
        setPixel(x - ix, y + iy, color);
        setPixel(x - iy, y + ix, color);
        setPixel(x - ix, y - iy, color);
        setPixel(x - iy, y - ix, color);
        setPixel(x + ix, y - iy, color);
        setPixel(x + iy, y - ix, color);
        iy++;
        if (err < 0)
        {
            err += 2 * iy + 1;
        }
        else
        {
            ix--;
            err += 2 * (iy - ix + 1);
        }
    }
}

void LCD_ST7735::drawEllipse(int x, int y, int rx, int ry, uint16_t color)
{
    int a2 = rx * rx;
    int b2 = ry * ry;
    int fa2 = 4 * a2;
    int fb2 = 4 * b2;
    
    int ix, iy, sigma;    
    for (ix = 0, iy = ry, sigma = 2 * b2 + a2 * (1 - 2 * ry); b2 * ix <= a2 * iy; ix++)
    {
        setPixel(x + ix, y + iy, color);
        setPixel(x - ix, y + iy, color);
        setPixel(x + ix, y - iy, color);
        setPixel(x - ix, y - iy, color);
        if (sigma >= 0)
        {
            sigma+= fa2 * (1 - iy);
            iy--;
        }
        sigma += b2 * ((4 * ix) + 6);
    }
    
    for (ix = rx, iy = 0, sigma = 2 * a2 + b2 * (1 - 2 * rx); a2 * iy <= b2 * ix; iy++)
    {
        setPixel(x + ix, y + iy, color);
        setPixel(x - ix, y + iy, color);
        setPixel(x + ix, y - iy, color);
        setPixel(x - ix, y - iy, color);
        if (sigma >= 0)
        {
            sigma+= fb2 * (1 - ix);
            ix--;
        }
        sigma += a2 * ((4 * iy) + 6);
    }
}
void LCD_ST7735::fillRect(int x1, int y1, int x2, int y2, uint16_t fillColor)
{
    clipRect(x1, y1, x2, y2);
    int c = ((x2-x1) * (y2-y1)) << 1;
    uint8_t colorHigh = fillColor >> 8;
    uint8_t colorLow = fillColor;
    beginBatchCommand(CMD_RAMWR);
    while(c--)
    {
        writeBatchData(colorHigh, colorLow);
    }
    endBatchCommand();
}

void LCD_ST7735::fillRect(int x1, int y1, int x2, int y2, uint16_t borderColor, uint16_t fillColor)
{
    if (x1 > x2) swap(x1, x2);
    if (y1 > y2) swap(y1, y2);
    
    drawRect(x1, y1, x2, y2, borderColor);
    clipRect(x1 + 1, y1 + 1, x2 - 1, y2 - 1);
    int c = ((x2-x1-2) * (y2-y1-2)) << 1;
    uint8_t colorHigh = fillColor >> 8;
    uint8_t colorLow = fillColor;
    beginBatchCommand(CMD_RAMWR);
    while(c--)
    {
        writeBatchData(colorHigh, colorLow);
    }
    endBatchCommand();
}

void LCD_ST7735::fillCircle(int x, int y, int r, uint16_t borderColor, uint16_t fillColor)
{
    int ix = r;
    int iy = 0;
    int err = 1 - r;
    
    while(ix >= iy)
    {
        setPixel(x - ix, y + iy, borderColor);
        setPixel(x + ix, y + iy, borderColor);        
        drawHorizLine(x - ix + 1, y + iy, x + ix - 1, fillColor);
        
        setPixel(x - iy, y + ix, borderColor);
        setPixel(x + iy, y + ix, borderColor);                
        drawHorizLine(x - iy + 1, y + ix, x + iy - 1, fillColor);
                
        setPixel(x - ix, y - iy, borderColor);
        setPixel(x + ix, y - iy, borderColor);
        drawHorizLine(x - ix + 1, y - iy, x + ix - 1, fillColor);
        
        setPixel(x - iy, y - ix, borderColor);        
        setPixel(x + iy, y - ix, borderColor);
        drawHorizLine(x - iy + 1, y - ix, x + iy - 1, fillColor);
        iy++;
        if (err < 0)
        {
            err += 2 * iy + 1;
        }
        else
        {
            ix--;
            err += 2 * (iy - ix + 1);
        }
    }
}

void LCD_ST7735::fillEllipse(int x, int y, int rx, int ry, uint16_t borderColor, uint16_t fillColor)
{
    int a2 = rx * rx;
    int b2 = ry * ry;
    int fa2 = 4 * a2;
    int fb2 = 4 * b2;
    
    int ix, iy, sigma;    
    for (ix = 0, iy = ry, sigma = 2 * b2 + a2 * (1 - 2 * ry); b2 * ix <= a2 * iy; ix++)
    {
        setPixel(x + ix, y + iy, borderColor);
        setPixel(x - ix, y + iy, borderColor);
        drawHorizLine(x - ix + 1, y + iy, x + ix - 1, fillColor);
        
        setPixel(x + ix, y - iy, borderColor);
        setPixel(x - ix, y - iy, borderColor);
        drawHorizLine(x - ix + 1, y - iy, x + ix - 1, fillColor);
        
        if (sigma >= 0)
        {
            sigma+= fa2 * (1 - iy);
            iy--;
        }
        sigma += b2 * ((4 * ix) + 6);
    }
    
    for (ix = rx, iy = 0, sigma = 2 * a2 + b2 * (1 - 2 * rx); a2 * iy <= b2 * ix; iy++)
    {
        setPixel(x + ix, y + iy, borderColor);
        setPixel(x - ix, y + iy, borderColor);
        drawHorizLine(x - ix + 1, y + iy, x + ix - 1, fillColor);
        
        setPixel(x + ix, y - iy, borderColor);
        setPixel(x - ix, y - iy, borderColor);
        drawHorizLine(x - ix + 1, y - iy, x + ix - 1, fillColor);
        if (sigma >= 0)
        {
            sigma+= fb2 * (1 - ix);
            ix--;
        }
        sigma += a2 * ((4 * iy) + 6);
    }
}

void LCD_ST7735::drawBitmap(int x, int y, const uint16_t *pbmp)
{
    int w = *pbmp++;
    int h = *pbmp++;
    
    drawBitmap(x, y, pbmp, 0, 0, w, h);
}

void LCD_ST7735::drawBitmap(int x, int y, const uint16_t *pbmp, int srcX, int srcY, int srcWidth, int srcHeight)
{
    // Clip if out of screen    
    if ((x >= _width) || (x + srcWidth < 0) || 
        (y >= _height) || (y + srcHeight < 0))
    {
        return;
    }
    
    // Clip X
    if (x < 0) { srcX += -x; srcWidth += x; x = 0; }
    if (x + srcWidth >= _width) { srcWidth += _width - (x + srcWidth); }
    
    // Clip Y
    if (y  < 0) {srcY += -y; srcHeight += y; y = 0; }  
    if (y + srcHeight >= _height) { srcHeight += _height - (y + srcHeight); }
    
    int w = *pbmp++;
    int h = *pbmp++;
    
    clip(x, y, srcWidth, srcHeight);
    beginBatchCommand(CMD_RAMWR);    
    const uint16_t *p = pbmp + srcX + (srcY * w);
    for(int iy = 0; iy < srcHeight; ++iy)
    {
        for(int ix = 0; ix < srcWidth; ++ix)
        {
            writeBatchData(*(p + ix));
        }
        p += w;
    } 
    endBatchCommand();
}

void LCD_ST7735::drawBitmap(int x, int y, Bitmap4bpp &bmp, int srcX, int srcY, int srcWidth, int srcHeight)
{
    // Clip if out of screen    
    if ((x >= _width) || (x + srcWidth < 0) || 
        (y >= _height) || (y + srcHeight < 0))
    {
        return;
    }
    
    // Clip X
    if (x < 0) { srcX += -x; srcWidth += x; x = 0; }
    if (x + srcWidth >= _width) { srcWidth += _width - (x + srcWidth); }
    
    // Clip Y
    if (y  < 0) {srcY += -y; srcHeight += y; y = 0; }  
    if (y + srcHeight >= _height) { srcHeight += _height - (y + srcHeight); }
    
    int stride = bmp.getStride();
    
    bool oddStart = srcX & 0x01;
    bool oddWidth = srcWidth & 0x01;    
    bool oddEnd = oddStart ^ oddWidth;
    
    int startX = oddStart ? 1 : 0;
    int endX = (oddEnd ? srcWidth : srcWidth + 1) >> 1;    
    
    const uint8_t *p = bmp.getBitmapData() + (srcX >> 1) + (srcY * stride);    

    clip(x, y, srcWidth, srcHeight);
    beginBatchCommand(CMD_RAMWR);        
    for(int iy = 0; iy < srcHeight; ++iy, p += stride)
    {
        if (oddStart) writeBatchData(_palette[*p & 0x0f]);
        for(int ix = startX; ix < endX; ++ix)
        {
            uint8_t c = *(p + ix);
            writeBatchData(_palette[(c >> 4) & 0x0f]);
            writeBatchData(_palette[c & 0x0f]);
        }        
        if (oddEnd) writeBatchData(_palette[(*(p + endX) >> 4) & 0x0f]);
    } 
    endBatchCommand();
}

void LCD_ST7735::drawBitmap(int x, int y, const uint8_t *pbmp, int srcX, int srcY, int srcWidth, int srcHeight, uint16_t foregroundColor, uint16_t backgroundColor)
{
    // Clip if out of screen    
    if ((x >= _width) || (x + srcWidth < 0) || 
        (y >= _height) || (y + srcHeight < 0))
    {
        return;
    }
    
    // Clip X
    if (x < 0) { srcX += -x; srcWidth += x; x = 0; }
    if (x + srcWidth >= _width) { srcWidth += _width - (x + srcWidth); }
    
    // Clip Y
    if (y  < 0) {srcY += -y; srcHeight += y; y = 0; }  
    if (y + srcHeight >= _height) { srcHeight += _height - (y + srcHeight); }
    
    uint8_t fch = foregroundColor >> 8;
    uint8_t fcl = foregroundColor;
    uint8_t bch = backgroundColor >> 8;
    uint8_t bcl = backgroundColor;
    
    uint16_t w = (*(pbmp + 1) << 8) | (*(pbmp + 0));    
    pbmp += 4;
    
    int stride = w / 8;
    clip(x, y, srcWidth, srcHeight);
    
    int offset = (stride * srcY) + (srcX / 8);
    int startbits = srcX % 8;
    
    beginBatchCommand(CMD_RAMWR);
    for(int r = 0; r < srcHeight; ++r)
    {
        const uint8_t *p = pbmp + offset;
        
        uint8_t b = *p;
        for (int c = 0, shift = startbits; c < srcWidth; ++c, ++shift)
        {
            if (shift == 8) 
            {
                shift = 0;
                b = *++p;
            }
            
            if ((b << shift) & 0x80)                            
            {
                writeBatchData(fch, fcl);                
            }
            else
            {
                writeBatchData(bch, bcl);                
            }
        }   
        offset += stride;             
    }
    endBatchCommand();
}

void LCD_ST7735::setForegroundColor(uint16_t color)
{
    _foregroundColorHigh = color >> 8;
    _foregroundColorLow = color;
}

void LCD_ST7735::setBackgroundColor(uint16_t color)
{
    _backgroundColorHigh = color >> 8;
    _backgroundColorLow = color;
}
        
void LCD_ST7735::drawString(const uint8_t *pFont, int x, int y, const char *pString)
{
    char *p = (char*)pString;
    while(*p != 0)
    {
        drawChar(pFont, x, y, *p++);
        x += 8;
    }
}

void LCD_ST7735::selectDevice()
{
    _spi.prepareFastSPI();
}

void LCD_ST7735::drawVertLine(int x1, int y1, int y2, uint16_t color)
{
    clipRect(x1, y1, x1, y2);
    beginBatchCommand(CMD_RAMWR);
    int c = (y2 - y1) << 1;
    uint8_t colorHigh = color >> 8;
    uint8_t colorLow = color;
    for (int i = 0; i < c; ++i)
    {
        writeBatchData(colorHigh, colorLow);        
    }
    endBatchCommand();
}

void LCD_ST7735::drawHorizLine(int x1, int y1, int x2, uint16_t color)
{    
    clipRect(x1, y1, x2, y1);
    beginBatchCommand(CMD_RAMWR);
    int c = (x2 - x1) << 1;
    uint8_t colorHigh = color >> 8;
    uint8_t colorLow = color;
    for (int i = 0; i < c; ++i)
    {
        writeBatchData(colorHigh, colorLow);
    }
    endBatchCommand();
}

void LCD_ST7735::drawChar(const uint8_t *pFont, int x, int y, char c)
{
    const uint8_t *pChar = pFont + (c * 8);
    
    clip(x, y, 8, 8);
    beginBatchCommand(CMD_RAMWR);
    for(int r = 0; r < 8; ++r)
    {
        uint8_t b = pChar[r];
        for(int c = 0; c < 8; ++c)
        {
            if (b & 0x80)
            {
                writeBatchData(_foregroundColorHigh);
                writeBatchData(_foregroundColorLow);
            }
            else
            {
                writeBatchData(_backgroundColorHigh);
                writeBatchData(_backgroundColorLow);
            }
                
            b <<= 1;
        }
    }
    endBatchCommand();
}

void LCD_ST7735::initDisplay()
{
    selectDevice();
    reset();
    
    writeCommand(CMD_SLPOUT);
    
    write(CMD_FRMCTR1, (uint8_t[]){0x01, 0x2c, 0x2d}, 3);
    write(CMD_FRMCTR2, (uint8_t[]){0x01, 0x2c, 0x2d}, 3);
    write(CMD_FRMCTR3, (uint8_t[]){0x01, 0x2c, 0x2d, 0x01, 0x2c, 0x2d}, 6);
    
    write(CMD_INVCTR, (uint8_t[]){0x07}, 1);
    
    write(CMD_PWCTR1, (uint8_t[]){0xa2, 0x02, 0x84}, 3);
    write(CMD_PWCTR2, (uint8_t[]){0xc5}, 1);
    write(CMD_PWCTR3, (uint8_t[]){0x0a, 0x00}, 2);
    write(CMD_PWCTR4, (uint8_t[]){0x8a, 0x2a}, 2);
    write(CMD_PWCTR5, (uint8_t[]){0x8a, 0xee}, 2);
    
    write(CMD_VMCTR1, (uint8_t[]){0x0e}, 1);
    
    write(CMD_MADCTL, (uint8_t[]){0xc0 | _colorFilter}, 1);
    
    // Gama sequence
    write(CMD_GAMCTRP1, (uint8_t[])
        {
            0x0f, 0x1a,
            0x0f, 0x18,
            0x2f, 0x28,
            0x20, 0x22,
            0x1f, 0x1b,
            0x23, 0x37,
            0x00, 0x07,
            0x02, 0x10
        }, 16);
        
    write(CMD_GAMCTRN1, (uint8_t[])
        {
            0x0f, 0x1b,
            0x0f, 0x17,
            0x33, 0x2c,
            0x29, 0x2e,
            0x30, 0x30,
            0x39, 0x3f,
            0x00, 0x07,
            0x03, 0x10
        }, 16);
        
    write(CMD_CASET, (uint8_t[]){0x00, 0x00, 0x00, 0x7f}, 4);
    write(CMD_RASET, (uint8_t[]){0x00, 0x00, 0x00, 0x9f}, 4);
    
    write(CMD_EXTCTRL, (uint8_t[]){0x01}, 1);            
    
    // Disable RAM power save
    write(0xf6, (uint8_t[]){0x00}, 1);                    
    
    // 65k color mode
    write(CMD_COLMOD, (uint8_t[]){0x05}, 1);            
    
    // Enable display
    writeCommand(CMD_DISPON);            
    
    setBacklight(true);
}

void LCD_ST7735::reset()
{
    _reset = 0;
    wait_us(100);
    _reset = 1;
    wait_us(100);
}

void LCD_ST7735::clip(int x, int y, int w, int h)
{
    clipRect(x, y, (x + w) - 1, (y + h) - 1);
}

void LCD_ST7735::clipRect(int x1, int y1, int x2, int y2)
{
    uint8_t x1l = (uint8_t)x1;
    //uint8_t x1h = (uint8_t)(x1 >> 8);
    uint8_t x2l = (uint8_t)x2;
    //uint8_t x2h = (uint8_t)(x2 >> 8);
    write(CMD_CASET, (uint8_t[]){0, x1l, 0, x2l}, 4);    
    
    uint8_t y1l = (uint8_t)y1;
    //uint8_t y1h = (uint8_t)(y1 >> 8);
    uint8_t y2l = (uint8_t)y2;
    //uint8_t y2h = (uint8_t)(y2 >> 8);
    write(CMD_RASET, (uint8_t[]){0, y1l, 0, y2l}, 4);    
}
        
void LCD_ST7735::writeCommand(uint8_t cmd)
{
    _cs = 0;
    _ds = 0;    
    _spi.fastWrite(cmd);
    _spi.waitWhileBusy();
    _spi.clearRx();
    _cs = 1;
}

void LCD_ST7735::write(uint8_t cmd, uint8_t data[], int dataLen)
{
    _cs = 0;
    _ds = 0;    
    _spi.fastWrite(cmd);
    _spi.waitWhileBusy();
    if (data != NULL & dataLen > 0)
    {
        _ds = 1;        
        for(int i = 0; i < dataLen; ++i)
        {            
            _spi.fastWrite(data[i]); 
        }        
        _spi.waitWhileBusy();
        _ds = 0; 
    }    
    _spi.clearRx();
    _cs = 1;
}

void LCD_ST7735::write(uint8_t cmd, uint16_t data)
{
    _cs = 0; 
    _ds = 0;    
    _spi.fastWrite(cmd);       
    _spi.waitWhileBusy();
    _ds = 1;            
    _spi.fastWrite(data >> 8);
    _spi.fastWrite(data);
    _spi.waitWhileBusy();
    _spi.clearRx();
    _ds = 0;     
    _cs = 1;
}

void LCD_ST7735::beginBatchCommand(uint8_t cmd)
{
    _cs = 0;
    _ds = 0;        
    _spi.fastWrite(cmd);   
    _spi.waitWhileBusy();
    _ds = 1;
}

void LCD_ST7735::writeBatchData(uint8_t data)
{
    _spi.fastWrite(data);
}

void LCD_ST7735::writeBatchData(uint8_t dataHigh, uint8_t dataLow)
{
    _spi.fastWrite(dataHigh);
    _spi.fastWrite(dataLow);
}


void LCD_ST7735::writeBatchData(uint16_t data)
{
    _spi.fastWrite(data >> 8);
    _spi.fastWrite(data); 
}

void LCD_ST7735::endBatchCommand()
{
    _spi.waitWhileBusy();
    _spi.clearRx();
    _ds = 0; 
    _cs = 1; 
}