Help further reduce overdriving display inputs

(and fixed a few spelling errors in comments)
- Use fast pin change macros for RESET, SS and DC.
- Implement MVSPI.packetBegin() and MVSPI.packetEnd() to allow sending
  multiple byte "packets" while SS remains enabled.
- Add 2 and 3 byte command() functions to send multiple command bytes in
  one packet.
- Modify clear() and display() functions to send commands and data in
  packets.
- Do the "wait for SPI transfer complete" just before sending the next
  byte (plus after the last or only byte) in a packet, so code can be
  executed while bytes are being sent.
- Disable SPI mode when not transmitting, so MOSI (and SCK) will go low.
- Keep DC low except when sending data bytes.
- Change MVSPI.transfer() to void since nothing can be received from
  the display.
This commit is contained in:
Scott Allen
2014-07-30 19:28:39 -04:00
parent fa09bdd7aa
commit 499594d9de
2 changed files with 167 additions and 142 deletions

View File

@@ -123,119 +123,99 @@ 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
// 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); // line #0
command(CHARGEPUMP, 0x14); // 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 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 +223,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 +232,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;
}
@@ -268,9 +246,14 @@ void MicroView::clear(uint8_t mode) {
for (int i=0;i<8; i++) {
setPageAddress(i);
setColumnAddress(0);
for (int j=0; j<0x80; j++) {
data(0);
MVSPI.packetBegin();
DCHIGH;
MVSPI.transfer(0);
for (int j=1; j<0x80; j++) {
MVSPI.wait();
MVSPI.transfer(0);
}
MVSPI.packetEnd();
}
}
else
@@ -290,9 +273,14 @@ void MicroView::clear(uint8_t mode, uint8_t c) {
for (int i=0;i<8; i++) {
setPageAddress(i);
setColumnAddress(0);
for (int j=0; j<0x80; j++) {
data(c);
MVSPI.packetBegin();
DCHIGH;
MVSPI.transfer(c);
for (int j=1; j<0x80; j++) {
MVSPI.wait();
MVSPI.transfer(c);
}
MVSPI.packetEnd();
}
}
else
@@ -315,11 +303,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.
@@ -332,9 +319,14 @@ void MicroView::display(void) {
for (i=0; i<6; i++) {
setPageAddress(i);
setColumnAddress(0);
for (j=0;j<0x40;j++) {
data(screenmemory[i*0x40+j]);
MVSPI.packetBegin();
DCHIGH;
MVSPI.transfer(screenmemory[i*0x40]);
for (j=1;j<0x40;j++) {
MVSPI.wait();
MVSPI.transfer(screenmemory[i*0x40+j]);
}
MVSPI.packetEnd();
}
}
@@ -847,14 +839,9 @@ size_t MicroView::write(uint8_t c) {
if (stop<start) // stop must be larger or equal to start
return;
scrollStop(); // need to disable scrolling before starting to avoid memory corrupt
command(RIGHTHORIZONTALSCROLL);
command(0x00);
command(start);
command(0x7); // scroll speed frames , TODO
command(stop);
command(0x00);
command(0xFF);
command(ACTIVATESCROLL);
command(RIGHTHORIZONTALSCROLL, 0);
command(start, 0x7, stop); // scroll speed frames , TODO
command(0x00, 0xFF, ACTIVATESCROLL);
}
/** \brief Vertical flip.
@@ -1639,31 +1626,27 @@ size_t MicroView::write(uint8_t c) {
/** \brief SPI Initialisation.
Setup SCK, MOSI pins for SPI transmission.
Setup SCK, MOSI, SS and DC pins for SPI transmission.
*/
void MVSPIClass::begin() {
// Set SS to high so a connected chip will be "deselected" by default
digitalWrite(SS, HIGH);
// When the SS pin is set as OUTPUT, it can be used as
// a general purpose output port (it doesn't influence
// SPI operations).
pinMode(SS, OUTPUT);
// Set SS to high so the display will be "deselected" by default
SSHIGH;
// Warning: if the SS pin ever becomes a LOW INPUT then SPI
// automatically switches to Slave, so the data direction of
// the SS pin MUST be kept as OUTPUT.
SPCR |= _BV(MSTR);
SPCR |= _BV(SPE);
// automatically switches to Slave.
// This should not occur with the MicroView as nothing can drive the pin.
// Set direction register for SCK and MOSI pin.
// MISO pin automatically overrides to INPUT.
// By doing this AFTER enabling SPI, we avoid accidentally
// clocking in a single bit since the lines go directly
// from "input" to SPI control.
// http://code.google.com/p/arduino/issues/detail?id=888
// Set DC low for command mode, where it should always default to unless
// data is being transmitted.
DCLOW;
// Set SCK and MOSI to be outputs and low when SPI is disabled.
digitalWrite(SCK, LOW);
digitalWrite(MOSI, LOW);
pinMode(SCK, OUTPUT);
pinMode(MOSI, OUTPUT);
// Set SPI master mode. Don't enable SPI at this time.
SPCR |= _BV(MSTR);
}
/** \brief End SPI. */
@@ -1671,7 +1654,36 @@ size_t MicroView::write(uint8_t c) {
SPCR &= ~_BV(SPE);
}
/** \brief Set SPI bit order.
/** \brief Set up SPI for transmitting
Pepare the SPI interface for transmitting on or more bytes of
commands and/or data.
*/
void MVSPIClass::packetBegin() {
// Enable SPI mode
SPCR |= _BV(SPE);
// Set SS low
SSLOW;
}
/** \brief End a SPI packet transmission
End a SPI packet transmission:
- Wait for the last byte to finish being transmitted.
- Set DC to command mode (even if already set that way, just in case).
- Set SS high.
- Disable SPI mode (causing SCK and MOSI to go low).
*/
void MVSPIClass::packetEnd() {
while (!(SPSR & _BV(SPIF)))
;
DCLOW;
SSHIGH;
SPCR &= ~_BV(SPE);
}
/** \brief Set SPI bit order.
Set SPI port bit order with LSBFIRST or MSBFIRST.
*/
@@ -1686,7 +1698,7 @@ size_t MicroView::write(uint8_t c) {
/** \brief Set SPI data mode.
Set the SPI daa mode: clock polarity and phase. mode - SPI_MODE0, SPI_MODE1, SPI_MODE2, SPI_MODE3.
Set the SPI data mode: clock polarity and phase. mode - SPI_MODE0, SPI_MODE1, SPI_MODE2, SPI_MODE3.
*/
void MVSPIClass::setDataMode(uint8_t mode)
{