Hi
Can anyone help me test this code, simply don't have the 2 OLED displays hardware do to it right now.
Transactional SPI setup with 2 NHD0420CW-Ax3 OLED displays.
1st OLED running on 8MHz.
2nd OLED running on 4MHz.
/*
* Demo_NHD0420CW-Ax3_SPI_HW.ino
*
* Tutorial sketch for use of character OLED slim display family by Newhaven with Arduino Uno, with
* using HW SPI library. Models: NHD0420CW-Ax3, NHD0220CW-Ax3, NHD0216CW-Ax3. Controller: US2066
* in this example, the display is connected to Arduino via SPI interface.
*
* Displays on the OLED alternately a 4-line message and a sequence of character "block".
* This sketch assumes the use of a 4x20 display; if different, modify the values of the two variables
* ROW_N e COLUMN_N.
* The sketch uses the minimum possible of Arduino's pins; if you intend to use also /RES or /CS lines,
* the related instructions are already present, it's sufficient to remove the comment markers.
*
* The circuit:
* OLED pin 1 (Vss) to Arduino pin ground
* OLED pin 2 (VDD) to Arduino pin 5V
* OLED pin 3 (REGVDD) to Arduino pin 5V
* OLED pin 4 to 6 to Vss ground
* OLED pin 7 (SCLK) to Arduino pin D13 (SCK)
* OLED pin 8 (SID) to Arduino pin D11 (MOSI)
* OLED pin 9 (SOD) to Arduino pin D12 (MISO) (optional, can be not connected)
* OLED pin 10 to 14 to Vss ground
* OLED pin 15 (/CS) to Vss ground (or to Arduino Pin D10(1st device) ~ Pin D9(2nd device), in case of use of more than one display)
* OLED pin 16 (/RES) to Arduino pin Reset or VDD 5V (or to Arduino pin D3, to control reset by sw)
* OLED pin 17 (BS0) to Vss ground
* OLED pin 18 (BS1) to Vss ground
* OLED pin 19 (BS2) to Vss ground
* OLED pin 20 (Vss) to Vss ground
*
* Original example created by Newhaven Display International Inc.
* Modified and adapted to Arduino Uno 30 Mar 2015 by Pasquale D'Antini
* Modified 19 May 2015 by Pasquale D'Antini
* Modified to use hardware SPI 1 April 2016 by Joakim Sandström
* HW SPI transaction 3 April 2016 by Cris O.
*
* This example code is in the public domain.
*/
// inslude the SPI library:
#include <SPI.h>
/*
* The SPI protocol allows for a range of transmission speeds ranging from 1Mhz to 100MHz.
* SPI slaves vary in the maximum speed at which they can reliably work.
* For a SPI chip rated up to 20 MHz, use 20000000.
* NOTE:
* Arduino Pro Mini (3.3 V / 8MHz) can only have SPI at 4 MHz as fastest speed, 1/2 the system clock speed.
* Arduino UNO (5 V / 16MHz) can only have SPI at 8 MHz as fastest speed, 1/2 the system clock speed.
*/
// Transactional SPI configuration
// Set up SPI transaction method for all devices
// Set up the speed, data order and data mode
SPISettings NHD0420CW_A(8000000, LSBFIRST, SPI_MODE3); // 8 MHz
SPISettings NHD0420CW_B(4000000, LSBFIRST, SPI_MODE3); // 4 MHz
/* Multiple SPI devices use the same SPI SCK, MISO and MOSI signals but each device will need it's own SS pin. */
// set the Slave Select Pins, using two SPI devices, A and B.
const int CS_A_Pin = 10; // (1st device)
const int CS_B_Pin = 9; // (2nd device)
/* Slave Select Signal:
* Any digital pin can be used for a SS (slave select) signal.
* The SPI library does not control the SS signals,
* because devices differ on when this is used,
* whether it is held low for multiple transfers or for each individual transfer,
* and so on. Control SS with digitalWrite().
* However, the SS pin must either be configured as an output,
* or if it is an input, it must remain low during the SPI transfer.
* Unconfigured pins default to input, and a pin with no signal can easily "float"
* to random voltages due to electrical noise.
* Always configure the SS pin as an output, or make sure it remains low.
*
* Most SPI devices are designed to work together with others,
* where SCK, MISO, and MOSI are shared. Each chip needs a separate SS signal.
* Only the selected chip will communicate. The others ignore SCK and MOSI,
* and avoid driving MISO when they are not selected.
*/
//const byte RES = 3; // Arduino's pin assigned to the Reset line (optional, can be always high)
// 1ST OLED
const byte ROW_N_1 = 4; // Number of display rows
const byte COLUMN_N_1 = 20; // Number of display columns
const byte TEXT_1[4][21] = {"1-Newhaven Display--",
"2-------Test-Oled-1-",
"3-16/20-Characters--",
"4!@#$%^&*()_+{}[]<>?"
}; // Strings to be displayed
byte new_line_1[4] = {0x80, 0xA0, 0xC0, 0xE0}; // DDRAM address for each line of the display
byte rows_1 = 0x08; // Display mode: 1/3 lines or 2/4 lines; default 2/4 (0x08)
// 2ND OLED
const byte ROW_N_2 = 4; // Number of display rows
const byte COLUMN_N_2 = 20; // Number of display columns
const byte TEXT_2[4][21] = {"1-Newhaven Display--",
"2-------Test-Oled-2-",
"3-16/20-Characters--",
"4!@#$%^&*()_+{}[]<>?"
}; // Strings to be displayed
byte new_line_2[4] = {0x80, 0xA0, 0xC0, 0xE0}; // DDRAM address for each line of the display
byte rows_2 = 0x08; // Display mode: 1/3 lines or 2/4 lines; default 2/4 (0x08)
// __
void command(byte c, byte device) // SUBROUTINE: PREPARES THE TRANSMISSION OF A COMMAND
{
switch (device) {
case 1: // (1st device)
SPI.beginTransaction(NHD0420CW_A);
// take the CS pin low to select the chip:
digitalWrite (CS_A_Pin, LOW); /* (most chips use LOW during the transfer) */
SPI.transfer(0x1F);
send_byte(c); // Transmits the byte
// take the CS pin high to de-select the chip:
digitalWrite (CS_A_Pin, HIGH);
SPI.endTransaction();
break;
case 2: // (2nd device)
SPI.beginTransaction(NHD0420CW_B);
// take the CS pin low to select the chip:
digitalWrite (CS_B_Pin, LOW); /* (most chips use LOW during the transfer) */
SPI.transfer(0x1F);
send_byte(c); // Transmits the byte
// take the CS pin high to de-select the chip:
digitalWrite (CS_B_Pin, HIGH);
SPI.endTransaction();
break;
case 3:
Serial.println("Error No device 3 specified");
break;
default:
Serial.print("Error Unknown Device:");
Serial.println(device);
break;
}
}
// _______________________________________________________________________________________
void data(byte d, byte device)
{
switch (device) {
case 1: // (1st device)
SPI.beginTransaction(NHD0420CW_A);
// take the CS pin low to select the chip:
digitalWrite (CS_A_Pin, LOW);
SPI.transfer(0x5F);
send_byte(d);
// take the CS pin high to de-select the chip:
digitalWrite (CS_A_Pin, HIGH);
SPI.endTransaction();
break;
case 2: // (2nd device)
SPI.beginTransaction(NHD0420CW_B);
// take the CS pin low to select the chip:
digitalWrite (CS_B_Pin, LOW); /* (most chips use LOW during the transfer) */
SPI.transfer(0x5F);
send_byte(d);
// take the CS pin high to de-select the chip:
digitalWrite (CS_B_Pin, HIGH);
SPI.endTransaction();
break;
case 3:
Serial.println("Error No device 3 specified");
break;
default:
Serial.print("Error Unknown Device:");
Serial.println(device);
break;
}
}
// _______________________________________________________________________________________
void send_byte(byte tx_b)
{
//Split the bytes into two and pad the last four bits with 0s
byte tx_b1 = tx_b & 0x0F;
byte tx_b2 = (tx_b >> 4) & 0x0F;
//Or together the bytes
int tx_int = (tx_b2 << 8) | tx_b1;
//transfer it
SPI.transfer16(tx_int);
}
// _______________________________________________________________________________________
void output(byte device) // SUBROUTINE: DISPLAYS THE FOUR STRINGS, THEN THE SAME IN REVERSE ORDER
{
// 1ST OLED ---------------------------------------------------------------------------------------
if (device == 1) {
unsigned long lastTime1 = 0;
unsigned long temp1;
byte r1 = 0; // Row index
byte c1 = 0; // Column index
command(0x01, device); // Clears display (and cursor home)
delay(2); // After a clear display, a minimum pause of 1-2 ms is required
for (r1 = 0; r1 < ROW_N_1; r1++) // One row at a time,
{
command(new_line_1[r1], device); // moves the cursor to the first column of that line
for (c1 = 0; c1 < COLUMN_N_1; c1++) // One character at a time,
{
data(TEXT_1[r1][c1], device); // displays the correspondig string
}
}
delay(2000); // Waits, only for visual effect purpose
lastTime1 = millis();
for (r1 = 0; r1 < ROW_N_1; r1++) // One row at a time,
{
command(new_line_1[r1], device); // moves the cursor to the first column of that line
for (c1 = 0; c1 < COLUMN_N_1; c1++) // One character at a time,
{
data(TEXT_1[3 - r1][c1], device); // displays the correspondig string (in reverse order)
}
}
temp1 = millis() - lastTime1;
Serial.print("OLED 1: ");
Serial.println(temp1);
}
// 2ND OLED -----------------------------------------------------------------------------------------
if (device == 2) {
unsigned long lastTime2 = 0;
unsigned long temp2;
byte r2 = 0; // Row index
byte c2 = 0; // Column index
command(0x01, device); // Clears display (and cursor home)
delay(2); // After a clear display, a minimum pause of 1-2 ms is required
for (r2 = 0; r2 < ROW_N_2; r2++) // One row at a time,
{
command(new_line_2[r2], device); // moves the cursor to the first column of that line
for (c2 = 0; c2 < COLUMN_N_2; c2++) // One character at a time,
{
data(TEXT_2[r2][c2], device); // displays the correspondig string
}
}
delay(2000); // Waits, only for visual effect purpose
lastTime2 = millis();
for (r2 = 0; r2 < ROW_N_2; r2++) // One row at a time,
{
command(new_line_2[r2], device); // moves the cursor to the first column of that line
for (c2 = 0; c2 < COLUMN_N_2; c2++) // One character at a time,
{
data(TEXT_2[3 - r2][c2], device); // displays the correspondig string (in reverse order)
}
}
temp2 = millis() - lastTime2;
Serial.print("OLED 2: ");
Serial.println(temp2);
}
}
// _______________________________________________________________________________________
void blocks(byte device) // SUBROUTINE: FILLS THE ENTIRE DISPLAY WITH THE CHARACTER "BLOCK"
{
// 1ST OLED ---------------------------------------------------------------------------------------
if (device == 1) {
byte r1 = 0; // Row index
byte c1 = 0; // Column index
command(0x01, device); // Clear display (and cursor home)
delay(2); // After a clear display, a minimum pause of 1-2 ms is required
for (r1 = 0; r1 < ROW_N_1; r1++) // One row at a time,
{
command(new_line_1[r1], device); // moves the cursor to the first column of that line
for (c1 = 0; c1 < COLUMN_N_1; c1++) // One character at a time,
{
data(0xDB, device); // displays the character 0xDB (block)
delay(50); // Waits, only for visual effect purpose
}
delay(500); // Waits, only for visual effect purpose
}
}
// 2ND OLED -----------------------------------------------------------------------------------------
if (device == 2) {
byte r2 = 0; // Row index
byte c2 = 0; // Column index
command(0x01, device); // Clear display (and cursor home)
delay(2); // After a clear display, a minimum pause of 1-2 ms is required
for (r2 = 0; r2 < ROW_N_2; r2++) // One row at a time,
{
command(new_line_2[r2], device); // moves the cursor to the first column of that line
for (c2 = 0; c2 < COLUMN_N_2; c2++) // One character at a time,
{
data(0xDB, device); // displays the character 0xDB (block)
delay(50); // Waits, only for visual effect purpose
}
delay(500); // Waits, only for visual effect purpose
}
}
}
// _______________________________________________________________________________________
void setup(void) // INITIAL SETUP
{
// pinMode(RES, OUTPUT); // Initializes Arduino pin for the Reset line (optional)
// digitalWrite(RES, HIGH); // Sets HIGH the Reset line of the display (optional, can be always high)
// set the Slave Select Pins as outputs:
pinMode (CS_A_Pin, OUTPUT); // Pin 10
pinMode (CS_B_Pin, OUTPUT); // Pin 9, optional 2ND device.
// initialize SPI:
SPI.begin();
/* Deprecated SPI configuration
Used by older versions of the SPI library, this method was not interrupt safe and
depended on your sketch doing low-level SPI configuration management.
SPI.setBitOrder(LSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV2);
SPI.setDataMode(SPI_MODE3);
*/
delayMicroseconds(200); // Waits 200 us for stabilization purpose
OledInit(1); // OLED init (1st device)
OledInit(2); // OLED init (2nd device)
Serial.begin(115200); //For performance measurement
}
// _______________________________________________________________________________________
void OledInit(byte device)
{
// OLED init
// 1ST OLED
if (ROW_N_1 == 2 || ROW_N_1 == 4)
rows_1 = 0x08; // Display mode: 2/4 lines
else
rows_1 = 0x00; // Display mode: 1/3 lines
// 2ND OLED
if (ROW_N_2 == 2 || ROW_N_2 == 4)
rows_2 = 0x08; // Display mode: 2/4 lines
else
rows_2 = 0x00; // Display mode: 1/3 lines
if (device == 1) {
command(0x22 | rows_1, device); // Function set: extended command set (RE=1), lines #
command(0x71, device); // Function selection A:
data(0x5C, device); // enable internal Vdd regulator at 5V I/O mode (def. value) (0x00 for disable, 2.8V I/O)
command(0x20 | rows_1, device); // Function set: fundamental command set (RE=0) (exit from extended command set), lines #
command(0x08, device); // Display ON/OFF control: display off, cursor off, blink off (default values)
command(0x22 | rows_1, device); // Function set: extended command set (RE=1), lines #
command(0x79, device); // OLED characterization: OLED command set enabled (SD=1)
command(0xD5, device); // Set display clock divide ratio/oscillator frequency:
command(0x70, device); // divide ratio=1, frequency=7 (default values)
command(0x78, device); // OLED characterization: OLED command set disabled (SD=0) (exit from OLED command set)
if (ROW_N_1 > 2)
command(0x09, device); // Extended function set (RE=1): 5-dot font, B/W inverting disabled (def. val.), 3/4 lines
else
command(0x08, device); // Extended function set (RE=1): 5-dot font, B/W inverting disabled (def. val.), 1/2 lines
command(0x06, device); // Entry Mode set - COM/SEG direction: COM0->COM31, SEG99->SEG0 (BDC=1, BDS=0)
command(0x72, device); // Function selection B:
data(0x0A, device); // ROM/CGRAM selection: ROM C, CGROM=250, CGRAM=6 (ROM=10, OPR=10)
command(0x79, device); // OLED characterization: OLED command set enabled (SD=1)
command(0xDA, device); // Set SEG pins hardware configuration:
command(0x10, device); // alternative odd/even SEG pin, disable SEG left/right remap (default values)
command(0xDC, device); // Function selection C:
command(0x00, device); // internal VSL, GPIO input disable
command(0x81, device); // Set contrast control:
command(0x7F, device); // contrast=127 (default value)
command(0xD9, device); // Set phase length:
command(0xF1, device); // phase2=15, phase1=1 (default: 0x78)
command(0xDB, device); // Set VCOMH deselect level:
command(0x40, device); // VCOMH deselect level=1 x Vcc (default: 0x20=0,77 x Vcc)
command(0x78, device); // OLED characterization: OLED command set disabled (SD=0) (exit from OLED command set)
command(0x20 | rows_1 , device); // Function set: fundamental command set (RE=0) (exit from extended command set), lines #
command(0x01, device); // Clear display
delay(2); // After a clear display, a minimum pause of 1-2 ms is required
command(0x80, device); // Set DDRAM address 0x00 in address counter (cursor home) (default value)
command(0x0C, device); // Display ON/OFF control: display ON, cursor off, blink off
delay(250); // Waits 250 ms for stabilization purpose after display on
}
if (device == 2) {
command(0x22 | rows_2, device); // Function set: extended command set (RE=1), lines #
/*i had to remove the comments cause of
The message exceeds the maximum allowed length (20000 characters).*/
command(0x71, device);
data(0x5C, device);
command(0x20 | rows_2, device);
command(0x08, device);
command(0x22 | rows_2, device);
command(0x79, device);
command(0xD5, device);
command(0x70, device);
command(0x78, device);
if (ROW_N_2 > 2)
command(0x09, device);
else
command(0x08, device);
command(0x06, device);
command(0x72, device);
data(0x0A, device);
command(0x79, device);
command(0xDA, device);
command(0x10, device);
command(0xDC, device);
command(0x00, device);
command(0x81, device);
command(0x7F, device);
command(0xD9, device);
command(0xF1, device);
command(0xDB, device);
command(0x40, device);
command(0x78, device);
command(0x20 | rows_2 , device);
command(0x01, device);
delay(2);
command(0x80, device);
command(0x0C, device);
delay(250);
}
if (ROW_N_1 == 2)
new_line_1[1] = 0xC0;
if (ROW_N_2 == 2)
new_line_2[1] = 0xC0;
}
// _______________________________________________________________________________________
void loop(void) // MAIN PROGRAM
{
output(1); // OLED 1 Execute subroutine "output"
output(2); // OLED 2
delay(2000); // Waits, only for visual effect purpose
blocks(1); // OLED 1 Execute subroutine "blocks"
blocks(2); // OLED 2
delay(2000); // Waits, only for visual effect purpose
}