Introduction: Connecting Arduino Uno to Crystalfontz 16x2 OLED With Only 4 Wires!

Picture of Connecting Arduino Uno to Crystalfontz 16x2 OLED With Only 4 Wires!

These modules are pretty nice. They are the same size as our CFAH1602C series 16x2 Character LCD modules, and thinner as a bonus. The OLED's contrast is fantastic, and they have a crisp look that is hard to beat. Another nice thing is that they have 4 software-selectable character sets, so you won't be stuck wanting for the omega or mu symbol.

Check them out here!

https://www.crystalfontz.com/product/cfal1602c-16x...

Step 1: What You'll Need

Picture of What You'll Need

Since I had an Arduino Uno handy, I decided to port the SPI version of the AVR code for this OLED to the Arduino. Here are all the parts you will need:

  1. Crystalfontz CFAL1602C OLED in your preferred color.
  2. Connection wires. I used some standard 0.1" headers I had around, and a pack of WRJMPY40 jumper wires.
  3. An Arduino Uno R3

Step 2: Convert OLED to SPI

Picture of Convert OLED to SPI

You will need to order your CFAL1602C pre-confgured for SPI, or follow the instructions in the CFAL1602C datasheet to make the conversion.

Step 3: Connect SPI Lines to OLED

Picture of Connect SPI Lines to OLED

Once that is done, use some of the jumper wires to connect to the pins used for SPI on the CFAL1602C.

Step 4: Connect 5v Power and Ground

Picture of Connect 5v Power and Ground

Then connect the 5v power and ground pins to the Arduino.

Step 5: Complete the Connections of the OLED's SPI Pins to the Arduino

Picture of Complete the Connections of the OLED's SPI Pins to the Arduino

  • SS: Slave Select
  • MOSI: Master (Arduino) Out, Slave (OLED) In
  • MISO: Master (Arduino) In, Slave (OLED) Out
  • SCK: Spi ClocK

Step 6: Connect Arduino Uno to PC and Open Sketch.

Picture of Connect Arduino Uno to PC and Open Sketch.

You then just need to connect the Arduino to your PC and open the sketch CFAL1602C_SPI_Arduino.ino. Upload and you will be rewarded with OLED goodness.


//===========================================================================
// // Code written for Arduino Uno // // CRYSTALFONTZ CFAL1602C-B CHARACTER OLED IN SPI MODE // // ref: https://www.crystalfontz.com/product/cfal1602c-16x2-character-oled // // 2015 - 10 - 22 Brent A. Crosby //=========================================================================== //This is free and unencumbered software released into the public domain. // //Anyone is free to copy, modify, publish, use, compile, sell, or //distribute this software, either in source code form or as a compiled //binary, for any purpose, commercial or non-commercial, and by any //means. // //In jurisdictions that recognize copyright laws, the author or authors //of this software dedicate any and all copyright interest in the //software to the public domain. We make this dedication for the benefit //of the public at large and to the detriment of our heirs and //successors. We intend this dedication to be an overt act of //relinquishment in perpetuity of all present and future rights to this //software under copyright law. // //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, //EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF //MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. //IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR //OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, //ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR //OTHER DEALINGS IN THE SOFTWARE. // //For more information, please refer to //=========================================================================== //Software SPI (10-bit transfers, difficult to do using the hardware SPI) #define SPIPORT (PORTB) #define SPITOGGLE (PINB) // PB5 (0x20) is SCK (output) green OLED pin 12 #define SPI_SCK_PIN (13) #define SCK_MASK (0x20) #define CLR_SCK (PORTB &= ~(SCK_MASK)) #define SET_SCK (PORTB |= (SCK_MASK)) // PB4 (0x10) is MISO (input) blue OLED pin 13 //(reference only, it is an input) #define SPI_MISO_PIN (12) #define MISO_MASK (0x10) #define CLR_MISO (PORTB &= ~(MISO_MASK)) #define SET_MISO (PORTB |= (MISO_MASK)) // PB3 (0x08) is MOSI (output) violet OLED pin 14 #define SPI_MOSI_PIN (11) #define MOSI_MASK (0x08) #define CLR_MOSI (PORTB &= ~(MOSI_MASK)) #define SET_MOSI (PORTB |= (MOSI_MASK)) // DB2 (0x04) is SS (output) gray OLED pin 16 #define SPI_SS_PIN (10) #define SS_MASK (0x04) #define CLR_SS (PORTB &= ~(SS_MASK)) #define SET_SS (PORTB |= (SS_MASK))

#define DATA (1) #define COMMAND (0) //=========================================================================== void write_to_OLED_SPI(uint8_t destination,uint8_t data) { //Bits sent in this order: // data(1)/command(0) flag // read(1)/write(0) flag // data.7 (msb) // data.6 // data.5 // data.4 // data.3 // data.2 // data.1 // data.0 (lsb) #if(1) //Pretty fast software SPI 10-bit transfer code //Several ideas taken from the fastest non-assembly implementation at: //http://nerdralph.blogspot.com/2015/03/fastest-avr-software-spi-in-west.html // // The SS pin is low for ~3.8uS, the clock frequency is ~ 2.6MHz // 47x faster than above

//Pre-calculate the value that will drive clk and data low in one cycle. //(Note that this is not interrupt safe if an ISR is writing to the same port) register uint8_t force_clk_and_data_low;

//Select the chip CLR_SS;

//Pre-calculate the value that will drive clk and data low in one //operation. force_clk_and_data_low = (SPIPORT & ~(MOSI_MASK | SCK_MASK) );

//Set clock and data low SPIPORT = force_clk_and_data_low; //Set the data(1)/command(0) flag if(DATA==destination) { SPITOGGLE = MOSI_MASK; } //Use a toggle to bring the clock up SPITOGGLE = SCK_MASK;

//Set clock and data low SPIPORT = force_clk_and_data_low; //MOSI is already 0, for read(1)/write(0) flag as write //Use a toggle to bring the clock up SPITOGGLE = SCK_MASK;

//Now clock the 8 bits of data out SPIPORT = force_clk_and_data_low; if(data & 0x80) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x40) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x20) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x10) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x08) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x04) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x02) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x01) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK;

//Release the chip SET_SS; #else //Straight-forward software SPI 10-bit transfer code, perhaps //easier to understand, possibly more portable. Certainly slower. // // The SS pin is low for ~180uS, the clock frequency is ~57KHz // 47x slower than above

//Select the chip, starting a 10-bit SPI transfer digitalWrite(SPI_SS_PIN, LOW);

//(bit 0) Set the data(1)/command(0) flag if(DATA==destination) digitalWrite(SPI_MOSI_PIN, HIGH); else digitalWrite(SPI_MOSI_PIN, LOW); //Clock it in. digitalWrite(SPI_SCK_PIN, LOW); digitalWrite(SPI_SCK_PIN, HIGH);

//(bit 1) Clear the read(1)/write(0) flag to write, clock it in. digitalWrite(SPI_MOSI_PIN, LOW); //Clock it in. digitalWrite(SPI_SCK_PIN, LOW); digitalWrite(SPI_SCK_PIN, HIGH);

//(bits 2-9) Push each of the 8 data bits out. for(uint8_t mask=0x80;mask;mask>>=1) { //Set the MOSI pin high or low depending on if our mask //corresponds to a 1 or 0 in the data. if(mask&data) { digitalWrite(SPI_MOSI_PIN, HIGH); } else { digitalWrite(SPI_MOSI_PIN, LOW); } //Clock it in. digitalWrite(SPI_SCK_PIN, LOW); digitalWrite(SPI_SCK_PIN, HIGH); }

//Release the chip, ending the 10-bit SPI transfer digitalWrite(SPI_SS_PIN, HIGH); #endif } //=========================================================================== void position_cursor(uint8_t column, uint8_t line) { //Set CGRAM Address, RS=0,R/W=0 // 7654 3210 // 1AAA AAAA // 0x00 to 0x27 => Line 1 // 0x40 to 0x67 => Line 2 write_to_OLED_SPI(COMMAND,0x80 | (line?0x40:0x00) | (column & 0x3F)); } //=========================================================================== // According to the WS0010 data sheet, only the clear display time has // an appreciable execution time of 6mS. All others are listed at 0. void clear_display(void) { //Display Clear RS=0,R/W=0 // 7654 3210 // 0000 0001 write_to_OLED_SPI(COMMAND,0x01); _delay_ms(6); } //=========================================================================== void initialize_display() { //Refer to WS0010 data sheet, page 21 // https://www.crystalfontz.com/products/document/3176/WS0010.pdf

//The display controller requests: // "Wait for power stabilization 500ms: _delay_ms(500);

//Function set, RS=0,R/W=0 // 7654 3210 // 0011 NFFT // N = lines: N=1 is 2 lines // F = Font: 0 = 5x8, 1 = 5x10 // FT = Font Table: // FT=00 is English/Japanese ~"standard" for character LCDs // FT=01 is Western European I fractions, circle-c some arrows // FT=10 is English/Russian // FT=11 is Western European II my favorite, arrows, Greek letters write_to_OLED_SPI(COMMAND,0x3B);

//Graphic vs character mode setting, RS=0,R/W=0 // 7654 3210 // 0001 GP11 // G = Mode: 1=graphic, 0=character // C = Power: 1=0n, 0=off write_to_OLED_SPI(COMMAND,0x17);

//Display On/Off Control RS=0,R/W=0 // 7654 3210 // 0000 1DCB // D = Display On // C = Cursor On // B = Cursor Blink write_to_OLED_SPI(COMMAND,0x0E);

//Display Clear RS=0,R/W=0 // 7654 3210 // 0000 0001 clear_display();

//Display home write_to_OLED_SPI(COMMAND,0x02);

//Entry Mode Set RS=0,R/W=0 // 7654 3210 // 0000 01IS // I = Increment/or decrement // S = Shift(scroll) data on line write_to_OLED_SPI(COMMAND,0x06);

//Display Clear RS=0,R/W=0 // 7654 3210 // 0000 0001 clear_display(); } //=========================================================================== void setup() { //General setup, port directions.

// PB5 (0x20) is SCK (output) green OLED pin 12 pinMode(SPI_SCK_PIN, OUTPUT); // PB4 (0x10) is MISO (input) blue OLED pin 13 //(reference only, it is an input) pinMode(SPI_MISO_PIN, INPUT); // PB3 (0x08) is MOSI (output) violet OLED pin 14 pinMode(SPI_MOSI_PIN, OUTPUT); // DB2 (0x04) is SS (output) gray OLED pin 16 pinMode(SPI_SS_PIN, OUTPUT); } //=========================================================================== void loop() { //Simple demo loop, shows a screen of text, including high order characters //( see page 10 of https://www.crystalfontz.com/controllers/WinstarDisplay/WS0010/378 ) // //Then there is a screen with solid characters on the left, and 50% //checkerboard characters on the right.

// Initialize the display initialize_display();

//Display text screen // 0123456789012345 char line1[17]=">}CRYSTALFONTZ{<"; line1[ 0]=0xF6; // right triange; line1[ 1]=0xC7; // right triange; line1[14]=0xC8; // left triange; line1[15]=0xF7; // left triange;

// 0123456789012345 char line2[17]="OLED:aaaaaaaaaaS"; line2[ 5]=0xE0; //beta line2[ 6]=0xA8; //function line2[ 7]=0xB8; //divide line2[ 8]=0xDA; //sigma line2[ 9]=0xEA; //mu (micro) line2[10]=0xDF; //alpha line2[11]=0xDE; //omega line2[12]=0xCF; //circle C line2[13]=0x1A; //about equal line2[14]=0xED; //pi

// write to the first line position_cursor(0,0); for(int col=0;col<16;col++) { write_to_OLED_SPI(DATA,line1[col]); }

// write to the second line position_cursor(0,1); for(int col=0;col<16;col++) { write_to_OLED_SPI(DATA,line2[col]); }

//Show the text message for a bit _delay_ms(1000);

//Use the custom characters (CGRAM) to make the //left half solid, the right half checkerboard

//Set CGRAM [0], to solid write_to_OLED_SPI(COMMAND,0x40 | (0 <<3)); for(int i=1;i<=8;i++) write_to_OLED_SPI(DATA,0xFF); //Set CGRAM [1], checkerboard write_to_OLED_SPI(COMMAND,0x40 | (1 <<3)); for(int i=1;i<=4;i++) { write_to_OLED_SPI(DATA,0xAA); write_to_OLED_SPI(DATA,0x55); }

// write to the first line position_cursor(0,0); for(int col=0;col<8;col++) { write_to_OLED_SPI(DATA,0); // CGRAM[0] } for(int col=8;col<16;col++) { write_to_OLED_SPI(DATA,1); // CGRAM[1] }

// write to the second line position_cursor(0,1); for(int col=0;col<8;col++) { write_to_OLED_SPI(DATA,0); // CGRAM[0] } for(int col=8;col<16;col++) { write_to_OLED_SPI(DATA,1); // CGRAM[1] }

//Show the pattern for a bit _delay_ms(1000); } //===========================================================================

Step 7: Optimizing the Code.

Picture of Optimizing the Code.

This code transfers at a clock frequency of about ~57KHz, and it takes about ~180uS, to complete one 10-bit write, you won't die from old age waiting for it.

But the old assembly-coding EE in me wanted to see if it could go a little faster . Starting with some optimized Arduino Software SPI examples, I was able to do a bit better:

//Pretty fast software SPI 10-bit transfer code
//Several ideas taken from the fastest non-assembly implementation at: //http://nerdralph.blogspot.com/2015/03/fastest-avr-software-spi-in-west.html // // The SS pin is low for ~3.8uS, the clock frequency is ~ 2.6MHz // 47x faster than above

//Pre-calculate the value that will drive clk and data low in one cycle. //(Note that this is not interrupt safe if an ISR is writing to the same port) register uint8_t force_clk_and_data_low;

//Select the chip CLR_SS;

//Pre-calculate the value that will drive clk and data low in one //operation. force_clk_and_data_low = (SPIPORT & ~(MOSI_MASK | SCK_MASK) );

//Set clock and data low SPIPORT = force_clk_and_data_low; //Set the data(1)/command(0) flag if(DATA==destination) { SPITOGGLE = MOSI_MASK; } //Use a toggle to bring the clock up SPITOGGLE = SCK_MASK;

//Set clock and data low SPIPORT = force_clk_and_data_low; //MOSI is already 0, for read(1)/write(0) flag as write //Use a toggle to bring the clock up SPITOGGLE = SCK_MASK;

//Now clock the 8 bits of data out SPIPORT = force_clk_and_data_low; if(data & 0x80) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x40) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x20) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x10) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x08) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x04) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x02) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK; SPIPORT = force_clk_and_data_low; if(data & 0x01) SPITOGGLE = MOSI_MASK; SPITOGGLE = SCK_MASK;

//Release the chip SET_SS;

That speeds up things by a factor of 47 times, bringing a transfer from 180uS to 3.8us. The equivalent clock frequency from 247KHz to 2.6MHz. Very nice!

Of course either routine will work . . . the "proper Arduino" should be more portable. Have fun connecting an OLED to your Arduino, feel free post your comments or questions.

Step 8: Have Questions? Need More Info?

Have questions or problems? Post questions to our forum: https://forum.crystalfontz.com/showthread.php/7388-Connect-Arduino-Uno-to-a-CFAL1602C-SPI-16x2-Character-OLED-(only-4-wires-needed)

Comments

BrianK11 (author)2016-02-11

THANK YOU!!! I've been fighting with this thing for several hours now trying to get it to work in the default 6800 mode. this was so easy! thanks again!

About This Instructable

1,276views

17favorites

License:

Bio: We manufacturer LCDs, perfect for integrating into consumer products.
More by crystalfontz:Connecting Arduino Uno to Crystalfontz 16x2 OLED with only 4 wires!Adding a Character Display to Raspberry PiConnect Arduino to Crystalfontz CFA533 with I2C
Add instructable to: