diff --git a/MicroView.cpp b/MicroView.cpp index 6a12b4c..10488fd 100644 --- a/MicroView.cpp +++ b/MicroView.cpp @@ -60,18 +60,18 @@ Page buffer is required because in SPI mode, the host cannot read the SSD1306's static uint8_t screenmemory [] = { // LCD Memory organised in 64 horizontal pixel and 6 rows of byte // B B .............B ----- - // y y .............y \ - // t t .............t \ - // e e .............e \ - // 0 1 .............63 \ - // \ - // D0 D0.............D0 \ - // D1 D1.............D1 / ROW 0 - // D2 D2.............D2 / - // D3 D3.............D3 / - // D4 D4.............D4 / - // D5 D5.............D5 / - // D6 D6.............D6 / + // y y .............y | + // t t .............t | + // e e .............e | + // 0 1 .............63 | + // | + // D0 D0.............D0 | ROW 0 + // D1 D1.............D1 | + // D2 D2.............D2 | + // D3 D3.............D3 | + // D4 D4.............D4 | + // D5 D5.............D5 | + // D6 D6.............D6 | // D7 D7.............D7 ---- //SparkFun Electronics LOGO @@ -123,119 +123,116 @@ void MicroView::begin() { setColor(WHITE); setDrawMode(NORM); setCursor(0,0); + + RESETLOW; + // Enable 3.3V power to the display pinMode(OLEDPWR, OUTPUT); - digitalWrite(OLEDPWR,HIGH); - // Setting up SPI pins - pinMode(MOSI, OUTPUT); - pinMode(SCK, OUTPUT); - - //pinMode(DC, OUTPUT); - pinMode(RESET, OUTPUT); - pinMode(SS, INPUT); - digitalWrite(SS, HIGH); - - ssport = portOutputRegister(digitalPinToPort(SS)); - sspinmask = digitalPinToBitMask(SS); - ssreg = portModeRegister(digitalPinToPort(SS)); - - dcport = portOutputRegister(digitalPinToPort(DC)); - dcpinmask = digitalPinToBitMask(DC); - dcreg = portModeRegister(digitalPinToPort(DC)); - - digitalWrite(RESET, HIGH); - // VDD (3.3V) goes high at start, lets just chill for 5 ms + digitalWrite(OLEDPWR, HIGH); + + // Give some time for power to stabilise + delay(10); + + RESETHIGH; delay(5); - // bring reset low - digitalWrite(RESET, LOW); - + RESETLOW; + // Setup SPI frequency MVSPI.setClockDivider(SPI_CLOCK_DIV2); + // Initialise SPI MVSPI.begin(); - - // wait 10ms - delay(10); + + delay(5); + // bring out of reset - pinMode(RESET,INPUT_PULLUP); - //digitalWrite(RESET, HIGH); + RESETHIGH; + + delay(10); // Init sequence for 64x48 OLED module - command(DISPLAYOFF); // 0xAE - - command(SETDISPLAYCLOCKDIV); // 0xD5 - command(0x80); // the suggested ratio 0x80 - - command(SETMULTIPLEX); // 0xA8 - command(0x2F); - - command(SETDISPLAYOFFSET); // 0xD3 - command(0x0); // no offset - - command(SETSTARTLINE | 0x0); // line #0 - - command(CHARGEPUMP); // enable charge pump - command(0x14); - - command(NORMALDISPLAY); // 0xA6 - command(DISPLAYALLONRESUME); // 0xA4 - +// command(DISPLAYOFF); // 0xAE +// command(SETDISPLAYCLOCKDIV, 0x80); // 0xD5 / the suggested ratio 0x80 + command(SETMULTIPLEX, 0x2F); // 0xA8 +// command(SETDISPLAYOFFSET, 0x0); // 0xD3 / no offset +// command(SETSTARTLINE | 0x0); // 0x40 / line #0 + command(CHARGEPUMP, 0x14); // 0x8D / enable charge pump +// command(NORMALDISPLAY); // 0xA6 +// command(DISPLAYALLONRESUME); // 0xA4 command(SEGREMAP | 0x1); command(COMSCANDEC); +// command(SETCOMPINS, 0x12); // 0xDA + command(SETCONTRAST, 0x8F); // 0x81 + command(SETPRECHARGE, 0xF1); // 0xD9 + command(SETVCOMDESELECT, 0x40); // 0xDB + command(DISPLAYON); //--turn on oled panel - command(SETCOMPINS); // 0xDA - command(0x12); + clear(ALL); // Erase hardware memory inside the OLED controller to avoid random data in memory. - command(SETCONTRAST); // 0x81 - command(0x8F); - - command(SETPRECHARGE); // 0xd9 - command(0xF1); - - command(SETVCOMDESELECT); // 0xDB - command(0x40); - - command(DISPLAYON); //--turn on oled panel - clear(ALL); // Erase hardware memory inside the OLED controller to avoid random data in memory. Serial.begin(115200); } -/** \brief SPI command. - - Setup DC and SS pins, then send command via SPI to SSD1306 controller. +/** \brief Power off the OLED display. + + Reset display control signals and + prepare the SSD1306 controller for power off, + then power off the 3.3V regulator. +*/ +void MicroView::end() { + DCLOW; // Just in case + command(DISPLAYOFF); + command(CHARGEPUMP, 0x10); // Disable the charge pump + delay(150); // Wait for charge pump off + RESETLOW; + SSLOW; + MVSPI.end(); // Disable SPI mode + digitalWrite(OLEDPWR, LOW); // Power off the 3.3V regulator +} + +/** \brief Send 1 command byte. + + Send 1 command byte via SPI to SSD1306 controller. */ void MicroView::command(uint8_t c) { - // Hardware SPI - *dcreg |= dcpinmask; // Set DC pin to OUTPUT - *dcport &= ~dcpinmask; // DC pin LOW - - *ssreg |= sspinmask; // Set SS pin to OUTPUT - *ssport &= ~sspinmask; // SS LOW - + MVSPI.packetBegin(); MVSPI.transfer(c); + MVSPI.packetEnd(); +} - *ssport |= sspinmask; // SS HIGH - *ssreg &= ~sspinmask; // Set SS pin to INPUT - - *dcreg &= ~dcpinmask; // Set DC to INPUT to avoid high voltage over driving the OLED logic +/** \brief Send 2 command bytes. + + Send 2 command bytes via SPI to SSD1306 controller. +*/ +void MicroView::command(uint8_t c1, uint8_t c2) { + MVSPI.packetBegin(); + MVSPI.transfer(c1); + MVSPI.wait(); + MVSPI.transfer(c2); + MVSPI.packetEnd(); +} + +/** \brief Send 3 command bytes. + + Send 3 command bytes via SPI to SSD1306 controller. +*/ +void MicroView::command(uint8_t c1, uint8_t c2, uint8_t c3) { + MVSPI.packetBegin(); + MVSPI.transfer(c1); + MVSPI.wait(); + MVSPI.transfer(c2); + MVSPI.wait(); + MVSPI.transfer(c3); + MVSPI.packetEnd(); } /** \brief SPI data. - Setup DC and SS pins, then send data via SPI to SSD1306 controller. + Send 1 data byte via SPI to SSD1306 controller. */ void MicroView::data(uint8_t c) { - // Hardware SPI - *dcport |= dcpinmask; // DC HIGH - - *ssreg |= sspinmask; // Set SS pin to OUTPUT - *ssport &= ~sspinmask; // SS LOW - + MVSPI.packetBegin(); + DCHIGH; MVSPI.transfer(c); - - *ssport |= sspinmask; // SS HIGH - *ssreg &= ~sspinmask; // Set SS pin to INPUT - - *dcreg &= ~dcpinmask; // Set DC to INPUT to avoid high voltage over driving the OLED logic + MVSPI.packetEnd(); } /** \brief Set SSD1306 page address. @@ -243,8 +240,7 @@ void MicroView::data(uint8_t c) { Send page address command and address to the SSD1306 OLED controller. */ void MicroView::setPageAddress(uint8_t add) { - add=0xb0|add; - command(add); + command(SETPAGE|add); return; } @@ -253,8 +249,7 @@ void MicroView::setPageAddress(uint8_t add) { Send column address command and address to the SSD1306 OLED controller. */ void MicroView::setColumnAddress(uint8_t add) { - command((0x10|(add>>4))+0x02); - command((0x0f&add)); + command((SETHIGHCOLUMN|(add>>4))+0x02, SETLOWCOLUMN|(0x0f&add)); return; } @@ -263,19 +258,25 @@ void MicroView::setColumnAddress(uint8_t add) { To clear GDRAM inside the LCD controller, pass in the variable mode = ALL and to clear screen page buffer pass in the variable mode = PAGE. */ void MicroView::clear(uint8_t mode) { - // uint8_t page=6, col=0x40; if (mode==ALL) { - for (int i=0;i<8; i++) { - setPageAddress(i); - setColumnAddress(0); - for (int j=0; j<0x80; j++) { - data(0); - } + command(SETADDRESSMODE, 0); // Set horizontal addressing mode + command(SETCOLUMNBOUNDS, 0, LCDTOTALWIDTH - 1); // Set width + command(SETPAGEBOUNDS, 0, LCDTOTALPAGES - 1); // Set height + + MVSPI.packetBegin(); + DCHIGH; + MVSPI.transfer(0); + for (int i = 1; i < LCDTOTALWIDTH * LCDTOTALPAGES; i++) { + MVSPI.wait(); + MVSPI.transfer(0); } + MVSPI.packetEnd(); + + command(SETADDRESSMODE, 2); // Return display to page addressing mode } else { - memset(screenmemory,0,384); // (64 x 48) / 8 = 384 + memset(screenmemory, 0, LCDWIDTH * LCDPAGES); //display(); } } @@ -285,19 +286,25 @@ void MicroView::clear(uint8_t mode) { To clear GDRAM inside the LCD controller, pass in the variable mode = ALL with c character and to clear screen page buffer, pass in the variable mode = PAGE with c character. */ void MicroView::clear(uint8_t mode, uint8_t c) { - //uint8_t page=6, col=0x40; if (mode==ALL) { - for (int i=0;i<8; i++) { - setPageAddress(i); - setColumnAddress(0); - for (int j=0; j<0x80; j++) { - data(c); - } + command(SETADDRESSMODE, 0); // Set horizontal addressing mode + command(SETCOLUMNBOUNDS, 0, LCDTOTALWIDTH - 1); // Set width + command(SETPAGEBOUNDS, 0, LCDTOTALPAGES - 1); // Set height + + MVSPI.packetBegin(); + DCHIGH; + MVSPI.transfer(c); + for (int i = 1; i < LCDTOTALWIDTH * LCDTOTALPAGES; i++) { + MVSPI.wait(); + MVSPI.transfer(c); } + MVSPI.packetEnd(); + + command(SETADDRESSMODE, 2); // Return display to page addressing mode } else { - memset(screenmemory,c,384); // (64 x 48) / 8 = 384 + memset(screenmemory, c, LCDWIDTH * LCDPAGES); display(); } } @@ -315,11 +322,10 @@ void MicroView::invert(boolean inv) { /** \brief Set contrast. - OLED contract value from 0 to 255. Note: Contrast level is not very obvious. + OLED contrast value from 0 to 255. Note: Contrast level is not very obvious. */ void MicroView::contrast(uint8_t contrast) { - command(SETCONTRAST); // 0x81 - command(contrast); + command(SETCONTRAST, contrast); // 0x81 } /** \brief Transfer display memory. @@ -327,15 +333,22 @@ void MicroView::contrast(uint8_t contrast) { Bulk move the screen buffer to the SSD1306 controller's memory so that images/graphics drawn on the screen buffer will be displayed on the OLED. */ void MicroView::display(void) { - uint8_t i, j; - - for (i=0; i<6; i++) { - setPageAddress(i); - setColumnAddress(0); - for (j=0;j<0x40;j++) { - data(screenmemory[i*0x40+j]); - } + command(SETADDRESSMODE, 0); // Set horizontal addressing mode + command(SETCOLUMNBOUNDS, + LCDCOLUMNOFFSET, + LCDCOLUMNOFFSET + LCDWIDTH - 1); // Set width + command(SETPAGEBOUNDS, 0, LCDPAGES - 1); // Set height + + MVSPI.packetBegin(); + DCHIGH; + MVSPI.transfer(screenmemory[0]); + for (int i = 1; i < LCDWIDTH * LCDPAGES; i++) { + MVSPI.wait(); + MVSPI.transfer(screenmemory[i]); } + MVSPI.packetEnd(); + + command(SETADDRESSMODE, 2); // Restore to page addressing mode } //#if ARDUINO >= 100 @@ -847,14 +860,9 @@ size_t MicroView::write(uint8_t c) { if (stop= 100 @@ -120,6 +154,8 @@ public: // RAW LCD functions void command(uint8_t c); + void command(uint8_t c1, uint8_t c2); + void command(uint8_t c1, uint8_t c2, uint8_t c3); void data(uint8_t c); void setColumnAddress(uint8_t add); void setPageAddress(uint8_t add); @@ -178,10 +214,6 @@ public: void doCmd(uint8_t index); private: - //uint8_t cs; - //volatile uint8_t *mosiport, *sckport; - volatile uint8_t *ssport, *dcport, *ssreg, *dcreg; // use volatile because these are fixed location port address - uint8_t mosipinmask, sckpinmask, sspinmask, dcpinmask; uint8_t foreColor,drawMode,fontWidth, fontHeight, fontType, fontStartChar, fontTotalChar, cursorX, cursorY; uint16_t fontMapWidth; //unsigned char *fontsPointer[TOTALFONTS]; @@ -261,8 +293,17 @@ private: class MVSPIClass { public: +/** \brief Wait for SPI serial transfer complete. */ + inline static void wait(); + /** \brief Transfer data byte via SPI port. */ - inline static byte transfer(byte _data); + inline static void transfer(byte _data); + +/** \brief Set up to begin a SPI packet transmit */ + static void packetBegin(); + +/** \brief End a SPI packet transmit */ + static void packetEnd(); // SPI Configuration methods @@ -279,11 +320,13 @@ public: extern MVSPIClass MVSPI; -byte MVSPIClass::transfer(byte _data) { - SPDR = _data; +void MVSPIClass::wait() { while (!(SPSR & _BV(SPIF))) ; - return SPDR; +} + +void MVSPIClass::transfer(byte _data) { + SPDR = _data; } void MVSPIClass::attachInterrupt() { diff --git a/examples/MicroViewDemo/MicroViewDemo.ino b/examples/MicroViewDemo/MicroViewDemo.ino index de3d5ca..bef5cec 100644 --- a/examples/MicroViewDemo/MicroViewDemo.ino +++ b/examples/MicroViewDemo/MicroViewDemo.ino @@ -373,7 +373,7 @@ void loop() { uView.print(6); uView.setCursor(0,uView.getLCDHeight() /2-(uView.getFontHeight()/2)); uView.print(9); - uView.setCursor(uView.getLCDWidth()-uView.getFontWidth(),uView.getLCDWidth()/2-(uView.getFontHeight()/2)); + uView.setCursor(uView.getLCDWidth()-uView.getFontWidth(),uView.getLCDHeight()/2-(uView.getFontHeight()/2)); uView.print(3); uView.display(); // display the memory buffer drawn @@ -618,4 +618,4 @@ void loop() { uView.clear(PAGE); } - +