The Display only requires six connections: 5V, GND, SCL, SDA as well as a connection for the multiplexer and the piezo speaker. It was designed to work directly with the I2C and SPI Education Shield, and as because of that, it does not have I2C pull up resistors on board. Depending on what you’re connecting to, it may be necessary to provide those resistors as well as series resistors on the SCL and SDA connections. For general connections to an Arduino or other development board, pull up resistors of 4.7K should be sufficient. If you need to use series resistors, 100Ω is fine.
For this tutorial, I’ve connected the I2C Display Add-on to an Arduino Uno using a breadboard, jumper wires and 4.7K pull up resistors.
A Larson Scanner, for those who aren’t familiar with it, is a simple display that has LEDs that chase back and forth, left to right to left. If you think of the nose of the car KITT from Knight Rider, or the eyes of the Cylons from the 80s version of Battlestar Galactica, that’s what a Larson Scanner is. In fact, it’s called a “Larson Scanner” because the creator of those two shows specifically, was Glen A. Larson.
So what we’re going to do, is have the top and bottom row of green LEDs act as a Larson Scanner on the I2C Display. And just because we don’t want to ignore the seven segment display, we’ll count how many times we’ve scanned and display it numerically.
As usual, we’ll want to start with a pretty descriptive set of declarations to reference the various registers of the AS1115, so that we’re not guessing at what our code does. There is a bit of configuration necessary to get the chip running as well, so a function to perform chip initialization will be necessary.
We’re going to want to update the display quite frequently, and since we’re updating two different things, the number display and the bar graph, it would get ugly fast if we put all of that into the main loop of our sketch. So we’ll want to create a generic display update function. Any glitches that occur won’t be buried in data, but displayed pretty visually, so if any I2C errors occur, we’ll want to capture those as well.
First things first… load the library by putting
#include <I2C.h> right at the top of your sketch. This is a common mistake to make and will cause all kinds of compile havoc if you forget it.
We need to create a description of the I2C address of our AS1115, and then each of the registers we’ll need to work with in this sketch, including the digit registers.
Finally we need to create a global variable to hold the amount of errors that might be occurring, and a simple global byte we can use to hold the returned error code (if any) from the I2C commands we’ll be using. There’s also the little variable that will hold how many times we’ve scanned left-right-left with the bar graph, so we can display it on the 7SEG.
Larson Scanner Declarations
constbyteAS1115_I2C=0x00;// Display Manager IC I2C Address
constbyteREGISTER_SHUTDOWN_MODE=0x0C;// Manages the shutdown features of the AS1115
constbyteREGISTER_DECODE=0x09;// Determines which digits are decoded
constbyteREGISTER_SCAN_LIMIT=0x0B;// Determines which digits are enabled
constbyteREGISTER_INTENSITY=0x0A;// Determins how bright the display is globally
constbyteDIGIT_0=0x01;// Left most seven segment digit
constbyteDIGIT_1=0x02;// Second seven segment digit
constbyteDIGIT_2=0x03;// Third seven segment digit
constbyteDIGIT_3=0x04;// Right most seven segment digit
constbyteBTM_RED=0x05;// Bottom Red bar graph multiplexed with L1/L2/L3
constbyteBTM_GRN=0x06;// Bottom Green bar graph
constbyteTOP_RED=0x07;// Top Red bar graph
constbyteTOP_GRN=0x08;// Top Green bar graph
longerrorCount=0;// A variable for holding how many errors occur
byteerrorStatus=0;// A variable to hold the I2C command error code
intscanCycles=0;// A variable to hold how many times the scanner cycles
We’re going to use a very simple error checking system that will simply increment the amount of errors occurring on the I2C bus and display that count on the Serial monitor. That way, if the display acts glitchy, we can open up the monitor and see just how bad it is, then start taking corrective action.
The way this works, is by wrapping every I2C command with an error checker. We’ll take errorStatus and set it equal to the returned error code from each I2C command. Then we’ll quickly check the value… if it’s zero, we do nothing, if it’s anything else, we’ll call the error handler.
The error handling function will increment the error count, then display it, along with the error code that was loaded into errorStatus (which is a global variable, so easy to access from any function in the sketch). As a good measure, we’ll reset errorStatus to 0 before we exit the handler, so we’re always in a known state when beginning an I2C command.
Larson Scanner errorHandler Function
Serial.print("Error Code: 0x");
Serial.print(" Error Count Is Now: ");
The Setup function is going to be pretty simple. First things first, get the Serial system initialized. Then Analog 0, which controls the multiplexing SPDT IC on the Display, needs to be driven high. That will enable the bottom row red LEDs and disable the L1/L2/L3 display on the 7SEG.
Because we’re using the customizable I2C library, and not the Wire library, we need to configure our I2C settings quickly. We need to initialize the buffers, disable internal pull up resistors, set our bus speed to 100kHz, and prepare a timeout value to keep our sketch from jamming if something fails to update the microcontroller appropriately.
Finally, we’ll make a call to the chip initialization function that will configure the AS1115 for operation.
Larson Scanner Setup Function
pinMode(A0,OUTPUT);// Configure the Display multiplexer pin as output
digitalWrite(A0,HIGH);// Enable the bottom row red LED, not L1/L2/L3
I2c.begin();// Initialize the I2C library
I2c.pullup(0);// Disable the internal pullup resistors
I2c.setSpeed(0);// Enable 100kHz I2C Bus Speed
I2c.timeOut(250);// Set a 250ms timeout before the bus resets
The chip initialization needs to perform at least one thing, which is to bring the chip out of the default shutdown state. Beyond that, we have to think through how many digits we want to display (scan limit), what format we intend to use to control them (decode mode), and how bright we want to display them (intensity), .
The scan limit doesn’t allow enable / disable of individual digits, but rather 0 or 0 and 1 or 0 and 1 and 2, etc. We need to show everything since we’re going to be using the top green LED bar graph, so we’ll need to write a 0x07 to REGISTER_SCAN_LIMIT.
Our decode method is a little tricky. We want to be able to send simple values to the 7SEG digits, but want to control the LEDs of the bar graph individually. That means Digits 0 through 3 need to use the decode fonts, but the bar graph needs a different setting. Each digit (and we’re thinking of each bar graph as a separate digit as well) is represented by a corresponding bit in REGISTER_DECODE, bit 0 = digit 0, bit 1 = digit 1, etc. Setting the bit means decode, clearing the bit means no decode. Using that logic, we need to write B00001111, hex value 0x0F to the register.
The intensity level is entirely subjective so it’s up to you how bright you want to make everything. Writing 0x00 to REGISTER_INTENSITY is the least bright and 0x0F is the most bright. You can just increase or reduce this value as you want. Remember you can also adjust each digits brightness individually, but we’re not going to cover that in this tutorial.
Finally, we need to bring the chip out of shutdown, by writing 0x01 to REGISTER_SHUTDOWN_MODE.
errorStatus=I2c.write(AS1115_I2C,REGISTER_DECODE,0x0F);// Set digits 0-3 to decode as fonts
errorStatus=I2c.write(AS1115_I2C,REGISTER_INTENSITY,0x08);// Set the intensity level; 0x00 = LOW, 0x0F = HIGH
errorStatus=I2c.write(AS1115_I2C,REGISTER_SHUTDOWN_MODE,0x01);// Begin normal operation
To keep everything clean, the main loop is just going to call the larson scanner function, and the scan cycle count display function.
Larson Scanner Main Loop
This isn’t going to be the fanciest of Larson Scanners, but merely a demo to get the board up and running for you. So the bit won’t accelerate / decelerate or fade or any of the real cool things that people can get it to do. What it will do is just chase a bit back and forth across both the green bar graphs.
To do that, we’ll need to simple for loops. The first one will send the bit to the left, by shifting the value 0x01 the amount of places to the left dictated by how far we are through the first loop.
The second one will send the bit to the right, by shifting the value 0x04 the amount of places to the right dictated by how far we are through the second loop.
The only tricky part of this, is not re-displaying the bits at the ends by duplicating them in the two loops. That’s why the first loop will go from bit 0 to bit 6 and the second loop goes from bit 7 to bit 1.
A delay has to be included to allow our eyes to see the actual movement. To slow and it looks clunky, too fast and it just blinks. I set a speed of 40 for the delay and that’s nice and zippy, but not too zippy.
At the end of the function, we increment how many times we’ve cycled through the scanner, until we get to 9999 when we reset it to zero, so we don’t start displaying funky characters on the display (it would overflow into the decode font characters you had selected).
// increment the scanCycles variable, unless it's already at 9999, then reset it
This one is very simple. Since we’re decoding the values we send to the first four digits of the display, all we have to do is send the actual value we want to show, right to the digits register. To do that, we just have to break a four digit number into it’s constituent places by using some division and some modulo math.