A simple library for SSH1106 controlled GLCDs

Dependents:   SSH1106_OLED

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SSH1106.cpp Source File

SSH1106.cpp

00001 /**
00002  * This is a simple library for SSH1106 controlled graphic LCD's.
00003  * Written for a cheap 1.3" OLED GLCD
00004  * See      http://www.dx.com/p/open-smart-1-8-128-64-lcd-display-breakout-module-w-blue-backlit-444694
00005  *
00006  * Written by:  Erik van de Coevering
00007  * With thanks to Tim Barr from whom I've reused some code
00008  * Use this code in whatever way you like, as long as it stays free of charge!
00009  */
00010 
00011 #include "SSH1106.h"
00012 
00013 SSH1106::SSH1106(SPI &lcd, DigitalOut &lcd_cs, DigitalOut &lcd_cd, DigitalOut &lcd_rst)
00014 {
00015     _lcd = &lcd;
00016     _lcd_cs = &lcd_cs;
00017     _lcd_cd = &lcd_cd;
00018     _lcd_rst = &lcd_rst;
00019 }
00020 
00021 void SSH1106::init()
00022 {
00023     _lcd_cs->write(1);
00024     _lcd->frequency(24000000);      // Abusing SPI to save some time.. Try a lower freq if this doesn't work
00025     _lcd->format(8,3);
00026     _lcd_cs->write(0);              // enable SPI
00027     _lcd_cd->write(0);              // COMMAND mode
00028 
00029     _lcd_rst->write(0);
00030     wait_ms(100);
00031     _lcd_rst->write(1);
00032 
00033     wait_ms(50);
00034 
00035     _lcd->write(0xAE);              // Display off
00036     _lcd->write(0x02);              // Set lower column address
00037     _lcd->write(0x10);              // Set higher column address
00038     _lcd->write(0x40);              // Set display start line
00039     _lcd->write(0xB0);              // Set page address
00040     _lcd->write(0x81);              // Set contrast to
00041     _lcd->write(0x80);              // 128
00042     _lcd->write(0xA1);              // Segment remap
00043     _lcd->write(0xA6);              // Inverse: normal (A7 = inverse)
00044     _lcd->write(0xA8);              // Multiplex ratio
00045     _lcd->write(0x3F);              // Duty = 1/32
00046     _lcd->write(0xAD);              // Charge pump enable
00047     _lcd->write(0x8B);              // External VCC
00048     _lcd->write(0x33);              // VPP: 9v
00049     _lcd->write(0xC8);              // Com scan direction
00050     _lcd->write(0xD3);              // Display offset
00051     _lcd->write(0x00);              // 0x20
00052     _lcd->write(0xD5);              // Osc division
00053     _lcd->write(0x80);
00054     _lcd->write(0xD9);              // Pre-charge period
00055     _lcd->write(0x1F);              // 0x22
00056     _lcd->write(0xDA);              // Set com pins
00057     _lcd->write(0x12);
00058     _lcd->write(0xDB);              // Set vcomh
00059     _lcd->write(0x40);
00060     _lcd->write(0xAF);              // Display ON
00061     _lcd_cs->write(1);
00062     _lcd_cd->write(1);
00063 }
00064 
00065 void SSH1106::setContrast(char contrast)
00066 {
00067     _lcd_cs->write(0);              // enable SPI
00068     _lcd_cd->write(0);              // command mode
00069     _lcd->write(0x81);              // command to set contrast
00070     _lcd->write(contrast);          // set contrast
00071     _lcd_cs->write(1);
00072     _lcd_cd->write(1);
00073 }
00074 
00075 void SSH1106::setCursor(char column, char line)
00076 {
00077     int i, j;
00078     column = column+2; // column+4
00079 
00080     i=(column&0xF0)>>4;
00081     j=column&0x0F;
00082     _lcd_cd->write(0);
00083     _lcd->write(0xb0+line);
00084     _lcd->write(0x10+i);
00085     _lcd->write(j);
00086     _lcd_cd->write(1);
00087 }
00088 
00089 void SSH1106::clear()
00090 {
00091     _lcd_cs->write(0);
00092     _lcd_cd->write(1);
00093 
00094     for(unsigned short j = 0; j < LCDPAGES; j++) {
00095         SSH1106::setCursor(0, j);
00096         for(unsigned short i = 0; i < LCDWIDTH ; i++) {
00097             _lcd->write(0x00);
00098         }
00099     }
00100 
00101     SSH1106::setCursor(0, 0);
00102 
00103     _lcd_cs->write(1);
00104 }
00105 
00106 void SSH1106::writeText2d(char column, char page, const char font_address[96][8], const char *text, int size)
00107 {
00108     _lcd_cs->write(0);
00109     SSH1106::setCursor(column, page);
00110     for(int i=0; i<size; i++) {
00111         for(int a=0; a<8; a++) {
00112             _lcd->write((font_address[(text[i]-32)][a]));
00113         }
00114     }
00115     _lcd_cs->write(1);
00116 }
00117 
00118 void SSH1106::writeText(char column, char page, const char *font_address, const char *text, const uint8_t size)
00119 {
00120     // Position of character data in memory array
00121     uint16_t pos_array;
00122     // temporary column, page address, and column_cnt are used
00123     // to stay inside display area
00124     uint8_t i,y, column_cnt = 0;
00125     uint8_t count = 0;
00126 
00127     // font information, needed for calculation
00128     uint8_t start_code, last_code, width, page_height, bytes_p_char;
00129 
00130     uint8_t *txtbuffer;
00131 
00132     start_code   = font_address[2];  // get first defined character
00133     last_code    = font_address[3];  // get last defined character
00134     width        = font_address[4];  // width in pixel of one char
00135     page_height  = font_address[6];  // page count per char
00136     bytes_p_char = font_address[7];  // bytes per char
00137 
00138     _lcd_cs->write(0);  // Enable SPI
00139     _lcd_cd->write(1);  // Data mode
00140 
00141     if(page_height + page > LCDPAGES) //stay inside display area
00142         page_height = LCDPAGES - page;
00143 
00144     // The string is displayed character after character. If the font has more then one page,
00145     // the top page is printed first, then the next page and so on
00146     for(y = 0; y < page_height; y++) {
00147         txtbuffer = &_lcdbuffer[page*LCDWIDTH + column];
00148         column_cnt = 0;                 // clear column_cnt start point
00149         i = 0;
00150         while(( i < size) && ((column_cnt + column) < LCDWIDTH)) {
00151             if(text[i] < start_code || (uint8_t)text[i] > last_code) //make sure data is valid
00152                 i++;
00153             else {
00154                 // calculate position of ASCII character in font array
00155                 // bytes for header + (ASCII - startcode) * bytes per char)
00156                 pos_array = 8 + (uint8_t)(text[i++] - start_code) * bytes_p_char;
00157 
00158                 // get the dot pattern for the part of the char to print
00159                 pos_array += y*width;
00160 
00161                 // stay inside display area
00162                 if((column_cnt + width + column) > LCDWIDTH)
00163                     column_cnt = LCDWIDTH-width;
00164 
00165                 // copy character data to buffer
00166                 memcpy (txtbuffer+column_cnt,font_address+pos_array,width);
00167             }
00168 
00169             column_cnt += width;
00170         }
00171         SSH1106::setCursor(column,page);  // set start position x and y
00172 
00173         do {
00174             _lcd->write(txtbuffer[count]);
00175             count++;
00176         } while ((count <= column_cnt));
00177     }
00178 
00179     _lcd_cs->write(1);  // Disable SPI
00180 
00181 }
00182 
00183 void SSH1106::drawBitmap(const char *data)
00184 {
00185     int cnt = 0;
00186     _lcd_cs->write(0);
00187     _lcd_cd->write(1);
00188     SSH1106::setCursor(0,0);
00189     for(int row=0; row<LCDPAGES; row++) {
00190         SSH1106::setCursor(0, row);
00191         for(int column=0; column<LCDWIDTH; column++) {
00192             _lcd->write(data[cnt]);
00193             cnt++;
00194         }
00195     }
00196     _lcd_cs->write(1);
00197 }
00198 
00199 void SSH1106::drawLineHor(char posx, char posy, char height, char width)
00200 {
00201     char page, offset, offset2;
00202     char buffer[2] = {0xFF, 0xFF};
00203 
00204     _lcd_cs->write(0);
00205     _lcd_cd->write(1);
00206 
00207     if(width+posx > LCDWIDTH) width = (LCDWIDTH-posx); // keep inside display area
00208 
00209     page = posy/8;
00210     offset = posy - (page*8);
00211     buffer[0] = buffer[0] >> (8-height);
00212     buffer[0] = buffer[0] << offset;
00213 
00214     if((offset + height) > 8) {
00215         offset2 = ((offset+height)-8);
00216         buffer[1] = buffer[1] - (0xFF << (offset2));
00217     }
00218 
00219     SSH1106::setCursor(posx, page);
00220 
00221     for(int i=0; i<width; i++) _lcd->write(buffer[0]);
00222 
00223     if(buffer[1] != 0xFF && (page+1) < 8) {         // only write if line takes up > 1 page & keep inside display area
00224         SSH1106::setCursor(posx, (page+1));
00225         for(int i=0; i<width; i++) _lcd->write(buffer[1]);
00226     }
00227     _lcd_cs->write(1);
00228 }
00229 
00230 void SSH1106::drawLineVert(char posx, char posy, char height, char width)
00231 {
00232     char page, pagecount, offset, offset2;
00233     
00234     _lcd_cs->write(0);
00235     _lcd_cd->write(1);
00236 
00237     page = posy/8;
00238     pagecount = height/8;
00239     offset2 = height - (pagecount*8);
00240 
00241     SSH1106::setCursor(posx, page);
00242     for(int i=0; i<width; i++) _lcd->write((0xFF>>offset));
00243 
00244     for(; pagecount > 1; pagecount--) {
00245         page++;
00246         SSH1106::setCursor(posx, page);
00247         for(int i=0; i<width; i++) _lcd->write(0xFF);
00248     }
00249 
00250     SSH1106::setCursor(posx, (page+1));
00251     for(int i=0; i<width; i++) _lcd->write((0xFF<<offset2));
00252 
00253     _lcd_cs->write(1);
00254 }
00255 
00256 void SSH1106::clearBuffer(void)
00257 {
00258     for(int i=0; i<(LCDWIDTH*LCDPAGES); i++) buff[i] = 0;
00259 }
00260 
00261 void SSH1106::update(void)
00262 {
00263     int cnt = 0;
00264     _lcd_cs->write(0);
00265     _lcd_cd->write(1);
00266     SSH1106::setCursor(0,0);
00267     for(int row=0; row<LCDPAGES; row++) {
00268         SSH1106::setCursor(0, row);
00269         for(int column=0; column<LCDWIDTH; column++) {
00270             _lcd->write(buff[cnt]);
00271             cnt++;
00272         }
00273     }
00274     _lcd_cs->write(1);
00275 }
00276 
00277 void SSH1106::drawbufferLineHor(char posx, char posy, char height, char width)
00278 {
00279     char page, offset, offset2;
00280     int cursor;
00281     char buffer[2] = {0xFF, 0xFF};
00282 
00283     if(width+posx > LCDWIDTH) width = (LCDWIDTH-posx); // keep inside display area
00284 
00285     page = posy/LCDPAGES;
00286     offset = posy - (page*LCDPAGES);
00287     buffer[0] = buffer[0] >> (8-height);
00288     buffer[0] = buffer[0] << offset;
00289 
00290     if((offset + height) > 8) {
00291         offset2 = ((offset+height)-8);
00292         buffer[1] = buffer[1] - (0xFF << (offset2));
00293     }
00294 
00295     cursor = posx + (page*LCDWIDTH);
00296 
00297     for(int i=0; i<width; i++) SSH1106::buff[cursor+i] |= buffer[0];
00298 
00299     if(buffer[1] != 0xFF && (page+1) < LCDPAGES) {         // only write if line takes up > 1 page & keep inside display area
00300         for(int i=0; i<width; i++) SSH1106::buff[cursor+i+LCDWIDTH] |= buffer[1];
00301     }
00302 }
00303 
00304 void SSH1106::drawbufferLineVert(char posx, char posy, char height, char width)
00305 {
00306     char page, pagecount, offset, offset2;
00307     int cursor;
00308 
00309     page = posy/LCDPAGES;
00310     pagecount = height/LCDPAGES;
00311     offset2 = height - (pagecount*LCDPAGES);
00312     cursor = posx + (page*LCDWIDTH); // LCDWIDTH
00313 
00314     for(int i=0; i<width; i++) SSH1106::buff[cursor+i] |= (0xFF>>offset);
00315 
00316     for(; pagecount > 1; pagecount--) {
00317         page++;
00318         cursor += LCDWIDTH;
00319         for(int i=0; i<width; i++) SSH1106::buff[cursor+i] |= 0xFF;
00320     }
00321 
00322     cursor += LCDWIDTH;
00323     for(int i=0; i<width; i++) SSH1106::buff[cursor+i] |= (0xFF >> offset2);
00324 }