Shows how to use a display and the onboard SD Card. Requires a display module with an adapter

Dependencies:   DmTftLibrary SDFileSystem mbed

Files at this revision

API Documentation at this revision

Comitter:
displaymodule
Date:
Tue May 20 15:42:31 2014 +0000
Child:
1:64173e1ae223
Commit message:
First version

Changed in this revision

DmDrawBmpBase.cpp Show annotated file Show diff for this revision Revisions of this file
DmDrawBmpBase.h Show annotated file Show diff for this revision Revisions of this file
DmTftLibrary.lib Show annotated file Show diff for this revision Revisions of this file
SDFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
W25Q16BV.cpp Show annotated file Show diff for this revision Revisions of this file
W25Q16BV.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-src.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DmDrawBmpBase.cpp	Tue May 20 15:42:31 2014 +0000
@@ -0,0 +1,252 @@
+/**********************************************************************************************
+ Copyright (c) 2014 DisplayModule. All rights reserved.
+
+ Redistribution and use of this source code, part of this source code or any compiled binary
+ based on this source code is permitted as long as the above copyright notice and following
+ disclaimer is retained.
+
+ DISCLAIMER:
+ THIS SOFTWARE IS SUPPLIED "AS IS" WITHOUT ANY WARRANTIES AND SUPPORT. DISPLAYMODULE ASSUMES
+ NO RESPONSIBILITY OR LIABILITY FOR THE USE OF THE SOFTWARE.
+ ********************************************************************************************/
+
+#include "DmDrawBmpBase.h"
+
+bool DmDrawBmpBase::drawBitmap(DmTftBase& tft, uint16_t x, uint16_t y, readFunc func, uint32_t userData) {
+  _readFunc = func;
+  _userData = userData;
+  _readPos = 0;
+  
+  if (readBmpHeader()) {
+    if (IsValid565Bitmap()) {
+      return draw565Bitmap(tft, x, y);
+    }
+    if (IsValid888Bitmap()) {
+      return draw888Bitmap(tft, x, y);
+    }  
+  }
+  return false;
+}
+
+bool DmDrawBmpBase::draw888Bitmap(DmTftBase& tft, uint16_t x, uint16_t y) {
+  const uint8_t bytesPerPixel = 3;
+  uint32_t filePosition = _bitmapOffset; 
+  uint8_t red, green, blue;
+  uint16_t row, column;
+  uint16_t bytesPerRow = (bytesPerPixel*_width + 3) & ~3;
+  uint8_t buff[20*bytesPerPixel];
+  uint8_t buffPos = sizeof(buff);
+  
+  tft.select();
+  tft.setAddress(x, y, x+_width-1, y+_height-1);
+  tft.unSelect();
+  
+  for(row=0; row<_height; row++) {
+    _readPos = filePosition = _bitmapOffset + (_height - 1 -row ) * bytesPerRow;
+//    if(_imageFile.position() != filePosition) {
+//      tft.unSelect();
+//      _imageFile.seek(filePosition);
+//      tft.select();
+//      buffPos = sizeof(buff);
+//    }
+    buffPos = sizeof(buff);
+    
+    for(column=0; column<_width; column++) { 
+      if (buffPos >= sizeof(buff)) {
+        tft.unSelect();
+        _readFunc(_userData, buff, _readPos, sizeof(buff));
+        _readPos += sizeof(buff);
+//        _imageFile.read(buff, sizeof(buff));
+        tft.select();
+        buffPos = 0;
+      }
+          
+      blue = buff[buffPos++];
+      green = buff[buffPos++];
+      red = buff[buffPos++];
+
+      tft.sendData(Convert888to565(red, green, blue));
+    }
+  }
+  tft.unSelect();
+  return true;
+}
+
+bool DmDrawBmpBase::draw565Bitmap(DmTftBase& tft, uint16_t x, uint16_t y) {
+  const uint8_t bytesPerPixel = 2;
+  uint8_t buff[30*bytesPerPixel]; // Should be dividable by bytesPerPixel
+  uint8_t buffPos = sizeof(buff); 
+  uint16_t bytesPerRow = (bytesPerPixel * _width + 3) & ~3; // bytes Per Row including padding to 4 bytes boundary
+  uint16_t paddingSize = bytesPerRow - (bytesPerPixel * _width); // paddingSize for each row
+  uint16_t height = -_height; // Change if load bottom-top
+  uint16_t pixel;
+  uint16_t row, column;
+  _readPos = _bitmapOffset;
+  
+  //_imageFile.seek(_bitmapOffset);
+    
+  tft.select();
+  tft.setAddress(x, y, x+_width-1, y+height-1);
+  tft.unSelect();
+
+  for(row=0; row<height; row++) {
+    for(column=0; column<_width; column++) {
+      if (buffPos >= sizeof(buff)) {
+        tft.unSelect();
+        _readFunc(_userData, buff, _readPos, sizeof(buff));
+        _readPos += sizeof(buff);
+        //_imageFile.read(buff, sizeof(buff));
+        tft.select();
+        buffPos = 0;
+      }
+      pixel = buff[buffPos++] & 0xFF;
+      pixel |= buff[buffPos++] << 8;
+      tft.sendData(pixel);
+    }
+
+    if ( paddingSize > 0 ) { // Check if there is padding in the file     
+      if ((sizeof(buff) - buffPos) >= paddingSize) { // Most common case, the padding is in the buffer 
+        buffPos += paddingSize;
+      }
+      else { // Padding is not in the buffer, we have to load the buffer from file       
+        tft.unSelect();
+        _readFunc(_userData, buff, _readPos, sizeof(buff));
+        _readPos += sizeof(buff);
+//        _imageFile.read(buff, sizeof(buff));
+        tft.select();
+        buffPos = paddingSize-(sizeof(buff) - buffPos); // paddingSize (0-3) spaceLeftInBuffer (0-3)  where spaceLeftInBuffer < paddingSize
+      }
+    }
+  }
+  tft.unSelect();
+  
+  return true;
+}
+
+void DmDrawBmpBase::printBmpHeaderInfo() {
+  printf("Image size:         %d\n", _fileSize);
+  printf("Image offset:       %d\n", _bitmapOffset);
+  printf("Image size:         %d, %d\n", _width, _height);
+  printf("BitsPerPixel:       %d\n",_bitsPerPixel);
+  printf("Compression:        %d\n",_compression);
+  printf("Is 24-bit bmp:      %d\n", IsValid888Bitmap());  
+  printf("Is 16-bit 565 bmp:  %d\n", IsValid565Bitmap());  
+  printf("Has 565 color mask: %d\n", Is565ColorMask());  
+}
+
+bool DmDrawBmpBase::readBmpHeader() {
+  if (read16() !=0x4D42){ // read magic byte
+    return false;
+  }
+
+  _fileSize = read32();
+  read32(); // Value depends on application which created the image 
+  _bitmapOffset = read32();
+
+  // read DIB header
+  _headerSize = read32();
+  _width = readInt32();
+  _height = readInt32();
+
+  if (read16() != 1) { // number of color planes must be 1
+    return false;
+  }
+  
+  _bitsPerPixel = read16();
+  _compression = read32();
+  
+  if (_bitmapOffset == 66 || _bitmapOffset == 70) { // V3 or v2 format
+    //setPosition(54);
+    _readPos = 54;
+    _redMask = read32();
+    _greenMask = read32();
+    _blueMask = read32();
+  }
+  else {
+    _redMask = 0x00;
+    _greenMask = 0x00;
+    _blueMask = 0x00;
+  }
+  
+  if (!IsValid888Bitmap() && !IsValid565Bitmap())
+  {
+    return false;
+  }
+  
+  return true;
+}
+
+// In this context a valid bitmap
+// - Stored bottom to top
+// - 24-bit file
+// - No compression
+bool DmDrawBmpBase::IsValid888Bitmap() {
+  if (_height > 0 && _bitsPerPixel == 24 && _compression == 0)
+  {
+    return true;
+  }
+  return false;
+}
+
+// In this context a valid bitmap
+// - Stored top to bottom
+// - 16-bit file
+// - Compression 3 (BI_BITFIELDS)
+// - Have a 565 Colormask
+bool DmDrawBmpBase::IsValid565Bitmap() {
+  if (_height < 0 && _bitsPerPixel == 16 && _compression == 3 && Is565ColorMask())
+  {
+    return true;
+  }
+  return false;
+}
+
+bool DmDrawBmpBase::Is565ColorMask() {
+  if (_redMask == 0xF800 && _greenMask == 0x7E0 && _blueMask == 0x1F)
+  {
+    return true;
+  }
+  return false;
+}
+
+int32_t DmDrawBmpBase::readInt32() {
+  int32_t d;
+  uint16_t b;
+
+  b = read16();
+  d = read16();
+  d <<= 16;
+  d |= b;
+  return d;
+}  
+
+uint32_t DmDrawBmpBase::read32() {  
+  uint32_t d;
+  uint16_t b;
+
+  b = read16();
+  d = read16();
+  d <<= 16;
+  d |= b;
+  return d;
+}
+
+uint16_t DmDrawBmpBase::read16() {
+  //uint16_t d;
+  //uint8_t b;
+  uint8_t buff[2];
+  //b = _imageFile.read();
+  //d = _imageFile.read();
+  _readFunc(_userData, buff, _readPos, 2);
+  _readPos+=2;
+  //d <<= 8;
+  //d |= b;
+  //return d;
+  return (buff[1] << 8) | buff[0];
+}
+
+// http://stackoverflow.com/questions/2442576/how-does-one-convert-16-bit-rgb565-to-24-bit-rgb888
+uint16_t DmDrawBmpBase::Convert888to565(uint8_t red, uint8_t green, uint8_t blue){
+  return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DmDrawBmpBase.h	Tue May 20 15:42:31 2014 +0000
@@ -0,0 +1,57 @@
+/**********************************************************************************************
+ Copyright (c) 2014 DisplayModule. All rights reserved.
+
+ Redistribution and use of this source code, part of this source code or any compiled binary
+ based on this source code is permitted as long as the above copyright notice and following
+ disclaimer is retained.
+
+ DISCLAIMER:
+ THIS SOFTWARE IS SUPPLIED "AS IS" WITHOUT ANY WARRANTIES AND SUPPORT. DISPLAYMODULE ASSUMES
+ NO RESPONSIBILITY OR LIABILITY FOR THE USE OF THE SOFTWARE.
+ ********************************************************************************************/
+
+#ifndef DM_DRAW_BMP_BASE_h
+#define DM_DRAW_BMP_BASE_h
+
+#include "dm_platform.h"
+#include "DmTftBase.h"
+
+
+typedef bool (*readFunc)(uint32_t userData, uint8_t* data, int offset, int numBytes);
+
+class DmDrawBmpBase {
+public:
+  bool drawBitmap(DmTftBase& tft, uint16_t x, uint16_t y, readFunc func, uint32_t userData=0);
+protected:
+  bool draw565Bitmap(DmTftBase& tft, uint16_t x, uint16_t y);
+  bool draw888Bitmap(DmTftBase& tft, uint16_t x, uint16_t y);
+  //virtual void setPosition(uint32_t newPosition) = 0;
+  uint16_t read16();
+  
+  void printBmpHeaderInfo();
+  bool readBmpHeader();
+  bool IsValid888Bitmap();
+  bool IsValid565Bitmap();
+  bool Is565ColorMask();
+  uint16_t Convert888to565(uint8_t red, uint8_t green, uint8_t blue);
+
+  uint32_t read32();
+  int32_t readInt32();
+    
+  uint32_t _fileSize;
+  uint32_t _bitmapOffset;
+
+  uint32_t _headerSize;
+  int32_t _width, _height;
+  uint16_t _bitsPerPixel;
+  uint32_t _compression;
+  uint32_t _redMask;
+  uint32_t _greenMask;
+  uint32_t _blueMask;
+  
+  readFunc _readFunc;
+  uint32_t _userData;
+  uint32_t _readPos;
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DmTftLibrary.lib	Tue May 20 15:42:31 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/displaymodule/code/DmTftLibrary/#59be7fca4581
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem.lib	Tue May 20 15:42:31 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/SDFileSystem/#7b35d1709458
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/W25Q16BV.cpp	Tue May 20 15:42:31 2014 +0000
@@ -0,0 +1,252 @@
+// W25Q16BV.cpp
+
+#include"W25Q16BV.h"
+
+// CONSTRUCTOR 
+W25Q16BV::W25Q16BV(PinName mosi, PinName miso, PinName sclk, PinName cs) : _spi(mosi, miso, sclk), _cs(cs) {
+    _spi.format(SPI_NBIT, SPI_MODE);
+    _spi.frequency(SPI_FREQ);
+    chipDisable();
+
+    exitDeepPowerDown();
+    // The SPI Flash cannot safely be accessed the first 1-10 ms after power ON
+//    wait_us(WAIT_US_TPUW);
+}
+
+
+// READING
+int W25Q16BV::readByte(int addr) {
+    chipEnable();
+    _spi.write(R_INST);
+    _spi.write((addr >> 16) & 0xff);
+    _spi.write((addr >>  8) & 0xff);
+    _spi.write((addr      ) & 0xff);
+    int response = _spi.write(DUMMY_ADDR);
+    chipDisable();
+    return response;
+}
+int W25Q16BV::readByte(int a2, int a1, int a0) {
+   chipEnable();
+   _spi.write(R_INST);
+   _spi.write(a2);
+   _spi.write(a1);
+   _spi.write(a0);
+   int response = _spi.write(DUMMY_ADDR);
+    chipDisable();
+    return response;
+}
+void W25Q16BV::readStream(int addr, char* buf, int count) {
+    if (count < 1)
+        return;
+    chipEnable();
+    _spi.write(R_INST);
+    _spi.write((addr >> 16) & 0xff);
+    _spi.write((addr >>  8) & 0xff);
+    _spi.write((addr      ) & 0xff);
+    for (int i = 0; i < count; i++)
+        buf[i] =  _spi.write(DUMMY_ADDR);
+    chipDisable();
+}
+void W25Q16BV::readJEDEC(uint8_t* manId, uint8_t* memType, uint8_t* cap)
+{
+    chipEnable();
+    _spi.write(JDEC_INST);
+    *manId = _spi.write(DUMMY_ADDR);
+    *memType = _spi.write(DUMMY_ADDR);
+    *cap = _spi.write(DUMMY_ADDR);
+    chipDisable();
+}
+uint8_t W25Q16BV::readStatus1()
+{
+  uint8_t status;
+  chipEnable();
+  _spi.write(STATUS1_INST);
+  status = _spi.write(DUMMY_ADDR);
+  chipDisable();
+  return status;
+}
+uint8_t W25Q16BV::readStatus2()
+{
+  uint8_t status;
+  chipEnable();
+  _spi.write(STATUS2_INST);
+  status = _spi.write(DUMMY_ADDR);
+  chipDisable();
+  return status;
+}
+
+// WRITING
+void W25Q16BV::writeByte(int addr, int data) {
+    writeEnable();
+    chipEnable();
+    _spi.write(W_INST);
+    _spi.write((addr >> 16) & 0xff);
+    _spi.write((addr >>  8) & 0xff);
+    _spi.write((addr      ) & 0xff);
+    _spi.write(data);
+    chipDisable();
+    writeDisable();
+//    wait_us(WAIT_US_TBP);
+    waitWhileBusy();
+}
+void W25Q16BV::writeByte(int a2, int a1, int a0, int data) {
+    writeEnable();
+    chipEnable();
+    _spi.write(W_INST);
+    _spi.write(a2);
+    _spi.write(a1);
+    _spi.write(a0);
+    _spi.write(data);
+    chipDisable();
+    writeDisable();
+//    wait_us(WAIT_US_TBP);
+    waitWhileBusy();
+}
+#if 0
+void W25Q16BV::writeStream(int addr, char* buf, int count) {
+    if (count < 1)
+        return;
+    writeEnable();
+    chipEnable();
+    _spi.write(W_INST);
+    _spi.write((addr & ADDR_BMASK2) >> ADDR_BSHIFT2);
+    _spi.write((addr & ADDR_BMASK1) >> ADDR_BSHIFT1);
+    _spi.write((addr & ADDR_BMASK0) >> ADDR_BSHIFT0);
+    for (int i = 0; i < count; i++)
+        _spi.write(buf[i]);
+    chipDisable();
+    writeDisable();
+    wait(WAIT_TIME_MS);
+}
+#else
+void W25Q16BV::writeStream(int addr, char* buf, int count)
+{
+  int left = count;
+  int offset = 0;
+  int len = 0;
+  
+  if (count < 1) {
+    return;
+  }
+    
+  // find length of first page write
+  if ((addr / PAGE_SIZE) != ((addr + count) / PAGE_SIZE)) {
+    //spans across at least one boundary
+    len = PAGE_SIZE - (addr % PAGE_SIZE);
+  } else {
+    // ends inside same page => use normal length
+    len = count % PAGE_SIZE;
+  }
+  
+  //break up large write operation into several page write operations
+  while (left > 0) {
+    writeEnable();
+    chipEnable();
+    _spi.write(W_INST);
+    _spi.write(((addr + offset) >> 16) & 0xff);
+    _spi.write(((addr + offset) >>  8) & 0xff);
+    _spi.write(((addr + offset)      ) & 0xff);
+    for (int i = 0; i < len; i++) {
+      _spi.write(buf[offset + i]);
+    }
+    chipDisable();
+    //writeDisable();
+    
+    offset += len;
+    left -= len;
+    len = (left < PAGE_SIZE) ? left : PAGE_SIZE;
+    
+    //wait_us(WAIT_US_TPP);
+    waitWhileBusy();
+  }
+}
+#endif
+
+//ERASING
+void W25Q16BV::chipErase() {
+    writeEnable();
+    chipEnable();
+    _spi.write(C_ERASE_INST);
+    chipDisable();
+//    writeDisable();
+//    wait_us(WAIT_US_TCE);
+    waitWhileBusy();
+}
+bool W25Q16BV::blockErase(int startBlock, int num) {
+  if ((num < 1) || (startBlock < 0) || ((startBlock+num) > NUM_64KB_BLOCKS)) {
+    return false;
+  }
+  for (int i = 0; i < num; i++) {
+    writeEnable();
+    chipEnable();
+    _spi.write(B_ERASE_INST);
+    _spi.write(startBlock + i);
+    _spi.write(0);
+    _spi.write(0);
+    chipDisable();
+//    writeDisable();
+//    wait_us(WAIT_US_TBE);
+    waitWhileBusy();
+  }
+  return true;
+}
+bool W25Q16BV::sectorErase(int startSector, int num) {
+  if ((num < 1) || (startSector < 0) || ((startSector+num) > NUM_SECTORS)) {
+    return false;
+  }
+  int addr = startSector * SECTOR_SIZE;
+  for (int i = 0; i < num; i++) {
+    writeEnable();
+    chipEnable();
+    _spi.write(S_ERASE_INST);
+    _spi.write((addr >> 16) & 0xff);
+    _spi.write((addr >>  8) & 0xff);
+    _spi.write((addr      ) & 0xff);
+    chipDisable();
+//    writeDisable();
+//    wait_us(WAIT_US_TSE);
+    waitWhileBusy();
+    
+    addr += SECTOR_SIZE;
+  }
+  return true;
+}
+
+// Wakeup from deep power down (default state)
+void W25Q16BV::exitDeepPowerDown() {
+    chipEnable();
+    _spi.write(POWERUP_INST);
+    chipDisable();
+  wait_us(WAIT_US_TRES1);
+}
+
+void W25Q16BV::waitWhileBusy() {
+  uint8_t status = 0;
+  int i = 0;
+
+  do {
+    for (i = 0; i < 0x2000; i++);
+
+    status = readStatus1();
+  }
+  while ((status & STATUS_1_BUSY) != 0);
+}
+
+//ENABLE/DISABLE (private functions)
+void W25Q16BV::writeEnable() {
+    chipEnable();
+    _spi.write(WE_INST);
+    chipDisable();
+}
+void W25Q16BV::writeDisable() {
+    chipEnable();
+    _spi.write(WD_INST);
+    chipDisable();
+}
+void W25Q16BV::chipEnable() {
+    _cs = 0;
+}
+void W25Q16BV::chipDisable() {
+    _cs = 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/W25Q16BV.h	Tue May 20 15:42:31 2014 +0000
@@ -0,0 +1,87 @@
+// W25Q16BV.h
+
+#ifndef W25Q16BV_H
+#define W25Q16BV_H
+
+#include "mbed.h"
+//#include "BitBangedSPI.h"
+
+#define SPI_FREQ        1000000
+#define SPI_MODE        0
+#define SPI_NBIT        8
+
+#define POWERUP_INST    0xAB
+#define STATUS1_INST    0x05
+#define STATUS2_INST    0x35
+#define JDEC_INST       0x9F
+#define UNIQUE_INST     0x4B
+#define WE_INST         0x06
+#define WD_INST         0x04
+#define R_INST          0x03
+#define W_INST          0x02
+#define S_ERASE_INST    0x20  /* 4KB sector erase */
+#define B_ERASE_INST    0xD8  /* 64KB block erase */
+#define C_ERASE_INST    0x60
+
+#define DUMMY_ADDR      0x00
+
+#define WAIT_US_TRES1          5   /* Power Up:                 3us */
+//#define WAIT_US_TPUW       10000   /* Power Up Write Time:   1-10ms */
+//#define WAIT_US_TBP           50   /* Byte Program Time:    20-50us */
+//#define WAIT_US_TPP         3000   /* Page Program Time:    0.7-3ms */
+//#define WAIT_US_TSE       400000   /* Sector Erase Time:   30-400ms */
+//#define WAIT_US_TBE      1000000   /* 64KB Block Erase Time: 1000ms */
+//#define WAIT_US_TCE     10000000   /* Chip Erase Time:       3-10s  */
+
+//#define ADDR_BMASK2     0x00ff0000
+//#define ADDR_BMASK1     0x0000ff00
+//#define ADDR_BMASK0     0x000000ff
+
+//#define ADDR_BSHIFT2    16
+//#define ADDR_BSHIFT1    8
+//#define ADDR_BSHIFT0    0
+
+#define PAGE_SIZE          256
+#define SECTOR_SIZE       4096
+#define NUM_SECTORS        512
+#define NUM_64KB_BLOCKS     32
+
+#define STATUS_1_BUSY     0x01
+
+class W25Q16BV /*: public BitBangedSPI*/ {
+public:
+    W25Q16BV(PinName mosi, PinName miso, PinName sclk, PinName cs);
+    
+    int readByte(int addr);                                 // takes a 24-bit (3 bytes) address and returns the data (1 byte) at that location
+    int readByte(int a2, int a1, int a0);                   // takes the address in 3 separate bytes A[23,16], A[15,8], A[7,0]
+    void readStream(int addr, char* buf, int count);        // takes a 24-bit address, reads count bytes, and stores results in buf
+
+    void readJEDEC(uint8_t* manId, uint8_t* memType, uint8_t* cap);
+    uint8_t readStatus1();
+    uint8_t readStatus2();
+    
+    void writeByte(int addr, int data);                     // takes a 24-bit (3 bytes) address and a byte of data to write at that location
+    void writeByte(int a2, int a1, int a0, int data);       // takes the address in 3 separate bytes A[23,16], A[15,8], A[7,0]
+    void writeStream(int addr, char* buf, int count);       // write count bytes of data from buf to memory, starting at addr  
+    
+    void chipErase();                                       // erase all data on chip
+    bool blockErase(int startBlock, int num=1);             // erase all data in the specified number of 64KB blocks, return false if block number is invalid
+    bool sectorErase(int startSector, int num=1);           // erase all data in the specified number of  4KB sectors, return false if sector number is invalid
+    
+private:
+
+    void exitDeepPowerDown();
+    void waitWhileBusy();
+
+    void writeEnable();                                     // write enable
+    void writeDisable();                                    // write disable
+    void chipEnable();                                      // chip enable
+    void chipDisable();                                     // chip disable
+    
+//    BitBangedSPI _spi;
+    SPI _spi;
+    DigitalOut _cs;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue May 20 15:42:31 2014 +0000
@@ -0,0 +1,107 @@
+/**********************************************************************************************
+ Copyright (c) 2014 DisplayModule. All rights reserved.
+
+ Redistribution and use of this source code, part of this source code or any compiled binary
+ based on this source code is permitted as long as the above copyright notice and following
+ disclaimer is retained.
+
+ DISCLAIMER:
+ THIS SOFTWARE IS SUPPLIED "AS IS" WITHOUT ANY WARRANTIES AND SUPPORT. DISPLAYMODULE ASSUMES
+ NO RESPONSIBILITY OR LIABILITY FOR THE USE OF THE SOFTWARE.
+ ********************************************************************************************/
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include "mbed.h"
+
+#include "DmTftHX8353C.h"
+#include "DmTftS6D0164.h"
+#include "DmTftIli9325.h"
+#include "DmTftSsd2119.h"
+#include "DmTouch.h"
+
+#include "DmDrawBmpBase.h"
+#include "SDFileSystem.h"
+
+/******************************************************************************
+ * Typedefs and defines
+ *****************************************************************************/
+
+#define log(...) printf(__VA_ARGS__)
+//#define log(...)
+
+#define DM_PIN_SPI_MOSI   A0
+#define DM_PIN_SPI_MISO   D9
+#define DM_PIN_SPI_SCLK   A1
+
+#define DM_PIN_CS_TOUCH   D8
+#define DM_PIN_CS_TFT     A3
+#define DM_PIN_CS_SDCARD  D10
+
+/******************************************************************************
+ * Local variables
+ *****************************************************************************/
+
+DmTftHX8353C tft;  /* DM_TFT18_101 */
+//DmTftS6D0164 tft;  /* DM_TFT22_102 */
+//DmTftIli9325 tft;  /* DM_TFT28_103 and DM_TFT24_104 */
+//DmTftSsd2119 tft;   /* DM_TFT35_107 */
+
+//DmTouch touch(DmTouch::DM_TFT28_103, false); /* For LPC4088 QuickStart Board */
+//DmTouch touch(DmTouch::DM_TFT28_103);
+//DmTouch touch(DmTouch::DM_TFT24_104);
+//DmTouch touch(DmTouch::DM_TFT24_104, false); /* For LPC4088 QuickStart Board */
+//DmTouch touch(DmTouch::DM_TFT35_107);
+
+SDFileSystem sd(DM_PIN_SPI_MOSI, DM_PIN_SPI_MISO, DM_PIN_SPI_SCLK, DM_PIN_CS_SDCARD, "sd"); // mosi,miso,clk,cs
+
+DigitalInOut csTouch(DM_PIN_CS_TOUCH, PIN_OUTPUT, PullUp, 1);
+DigitalInOut csDisplay(DM_PIN_CS_TFT, PIN_OUTPUT, PullUp, 1);
+DigitalInOut csSDCard(DM_PIN_CS_SDCARD, PIN_OUTPUT, PullUp, 1);
+
+
+/******************************************************************************
+ * Global variables
+ *****************************************************************************/
+
+/******************************************************************************
+ * Local functions
+ *****************************************************************************/
+
+static bool sdcardReader(uint32_t userData, uint8_t* data, int offset, int numBytes) {
+  FILE* fp = (FILE*)userData;
+  fseek(fp, offset, SEEK_SET);
+  fread(data, 1, numBytes, fp);
+  return true;
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+
+int main() {
+  DmDrawBmpBase bmp;
+  const char* fname = "/sd/logop565.bmp";
+
+  log("init tft \r\n");
+  tft.init();
+  
+  while (true) {
+    log("Drawing from SD card (%s) ...\n", fname);
+    FILE *fp = fopen(fname, "r");
+    if (fp != NULL) {
+      bmp.drawBitmap(tft, 0, 0, sdcardReader, (uint32_t)fp);
+      fclose(fp);
+    } else {
+      log("failed to open file\n");
+      log("Skipping SDCard reading as it is unsupported\n");
+      tft.drawStringCentered(0, 0, tft.width(), tft.height(), "SD Card Unsupported!");
+    }
+
+    wait_ms(3000);
+    tft.clearScreen(RED);
+    wait_ms(1000);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-src.lib	Tue May 20 15:42:31 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/displaymodule/code/mbed-src/#3306e8fd8143