Interfacing SSD1306 Based I2C 128x64 OLED Module With ATtiny85 Without Frame Buffer in Assembly Code

by Santanu1310 in Circuits > Electronics

3851 Views, 6 Favorites, 0 Comments

Interfacing SSD1306 Based I2C 128x64 OLED Module With ATtiny85 Without Frame Buffer in Assembly Code

20210606182558_IMG_3683.JPG

First of all, this is not another Arduino Sketch or Project. The Assembly Language fascinates me and I am using it to write codes for different Industrial control solutions. Previously, I did not work with any Graphic Display modules. Getting some leisure time due to lockdown, I started playing with this cheap OLED module. Initially, I started the project using ATmega32A but migrated to ATtiny85 to lower its power consumption for being able to run it through 4-20mA current loop found in the industries.

Supplies

The following components have been used to build the project:

  • Electronic Components:
  1. SSD1306 128x64 I2C OLED Display Module - 1 no.
  2. ATtiny85 in DIP-8 package - 1 no.
  3. 8-pin DIP IC base - 2 nos.
  4. 6mm Tactile Push-button - 4 nos.
  5. 3"x2" Generic PCB - 1 no.
  6. Resistor (4.7K) - 6 nos.
  7. Resistor (10K) - 1 no.
  8. 10-pin FRC Shrouded Male Header - 1 no.
  9. Male Single Row Straight Berg Strip (2.54mm) - 4 pins
  10. Female Single Row Straight Berg Strip (2.54mm) - 4 pins
  11. Male Single Row 90 degree Berg Strip (2.54mm) - 2 pins
  12. Single Strand Wire - 0.2 mtr. (approx.)
  • Tools:
  1. Multimeter
  2. Soldering Iron and Solder Wire
  3. Wire Cutters
  4. Tweezers
  5. USBasp AVR programmer
  • Software:
  1. AVR Studio 4
  2. MikroElectronika GLCD Font Creator ver 1.2
  3. PROGISP ver 1.72

The Hardware

Circuit Diagram.png
New Board.jpg
New Board Connection.jpg
20210530122908_IMG_3625.JPG
20210527_103250.jpg
20210524210134_IMG_3582.JPG

The circuit is built on a small 3"x2" Generic PCB. The ATtiny85 in the 8-pin PDIP package has only six I/O lines out of which, one pin is RESET, so, every I/O pin used in this project has been picked very carefully. The connection of the I/O lines can be grouped into three categories:

  • I2C Bus

The I2C Bus consists of 2 pins, (i) SDA line for data transmission and (ii) SCL line clock generation. Pin no. 3 is SDA and Pin no. 6 is SCL. Each line has been pulled up using a 4.7K resistor.

  • Input Keys

Due to the scarcity of I/O pins, instead of reading the Digital State (High or Low) of 4 input keys, they have been connected to a single ADC input pin as Voltage Divider Network. Each individual Key Press will produce a unique voltage level at the ADC pin. By comparing the ADC data with certain predefined levels, we can find out which key has been pressed. Simultaneous key-press cannot be detected here.

  • Analog I/O (Future provision)

Pin no. 5 and 7 are kept empty in this circuit to work with analog data for future use. Pin no. 5 can be utilized to generate PWM driven by the Timer/Counter 0 and Pin no. 7 can be utilized to read the analog voltage (other than the Input Keys) using ADC channel 1.

Bit Bang

StartStop.png
Bit Transfer.png

The ATtiny85 consists of a Universal Serial Interface (USI). Implementing SPI communication using the USI is easy but I got no luck with I2C. As per the datasheet, Clock Generation for the I2C must be implemented in Software. So, I went for full software implementation to organize the output pins according to my circuit needs. Here, the ATtiny85 is I2C Master and the SSD1306 module is I2C Slave.

Pin no. 2 is used as SDA and Pin no. 6 is used as SCL.

Brief theory of I2C:

Data transfer is always initiated by a Master Device. A High to Low transition on the SDA line while SCL line is High is defined to be a START condition. A START condition is always followed by the (unique) 7-bit Slave Address and then by a Data Direction Bit (R/W).

The Slave Device addressed now acknowledges to the Master by holding SDA Low for one clock cycle. If the Master does not receive any acknowledges, the transfer is terminated. Depending on the Data Direction bit, the Master or Slave now transmits 8-bit of data on the SDA line. The receiving device then acknowledges the data. Multiple bytes can be transferred in one direction before a repeated START or STOP condition is issued by the Master.

The transfer is terminated when the Master issues a STOP condition. A STOP condition is defined by a Low to High transition on the SDA line while the SCL is High.

Driving the SSD1306

20210606182259_IMG_3680.JPG
20210606182102_IMG_3679.JPG
SSD1306 I2C.png

In the case of SSD1306, the 7-bit Slave Address is 011110 SA0. SA0 is Slave Address bit. You will find two types of modules, one with the SA0 bit being 0 and the other with the SA0 bit being 1. Check the I2C Address Select resister connection on the PCB of the display module. For the display I used, it was 0. As the display module works in Write Only mode, the Data Direction or the Read/Write bit is always 0. So, the data to be sent after the START condition is either 0b0111 1000 (0x78) or 0b0111 1010 (0x7A).

After the slave device (SSD1306 module in this case) has been addressed, the device must be informed about the upcoming data bytes, whether it is Command stream or Bitmap stream. Sending 0x00 will treat the next bytes as commands and 0x40 will treat the next bytes as bitmaps.

The device needs to be initialized with a sequence of commands.

Frame Buffer

GDDRAM.png
GDDRAM Enlarged.png

To make a frame buffer for the 128 pixels wide by 64 pixels high display, you need at least 8192 bits or 1024 Bytes of RAM but the ATtiny85 has only 512 Bytes of SRAM.

The SSD1306 has a 128x64 bits of SRAM integrated into it and is called Graphics Display Data RAM (GDDRAM). So, the ATtiny85 will write directly onto the GDDRAM of the SSD1306 chip to be displayed on the small OLED panel.

In I2C/TWI mode, the GDDRAM of SSD1306 cannot be read (the I2C Read sub-routine has not been written for now to save some program space), so, you can only work with predefined graphic bitmaps and plotting graphic functions which require reading the existing data on the GDDRAM and modify it is not possible.

One additional DIP 8 pin IC base has been connected to add external I2C EEPROM or EERAM (e.g. Microchip 47L04/47C04/47L16/47C16) to the circuit if future applications demand extra memory.

Displaying Characters and Texts

Horizontal.png
image_2021-05-27_20-09-12.png

As graphical functions cannot be implemented into the SSD1305, the vertical positioning of texts is limited to 8 pages and the height of the fonts is multiples of 8.

To display characters and texts, I have used the Horizontal addressing mode. First, a boundary or Character Box (Column Start Address, Column End Address, Page Start Address, Page End Address) is defined according to the position, height and width of the character. In Horizontal Addressing Mode, the Column address pointer automatically increases after sending one bitmap byte and it will move to the next Page location when it reaches Column End Address. So, if you want to display an 8 bits wide by 16 bits (2 Bytes) high character, just throw 16 Bytes of the corresponding bitmap data stored in the program memory, continuously to the defined boundary. This way, one single character is being written. Bitmap images can also be displayed using this method.

Three types of Monospaced Fonts have been implemented in this project.

  1. 8w x 16h Roboto Mono (All ASCII characters)
  2. 16w x 24h Deja Vu Sans Mono (Only Numbers and Symbols i.e. ASCII 32 to 63 to save some Program Space)
  3. 6w x 8h Terminal (All ASCII characters)

The bitmap data of the Fonts have been obtained from the MikroElectronika GLCD Font Creator. Then, the data have been exported onto CSV files. Using Excel, the data have been organized as per the pointer movement sequence of Horizontal Addressing. After that, the files are opened again on Text editor and copied to the program along with commas.

Bitmap data of fonts occupied most of the program space, they are 3664 Bytes. The code is only 842 Bytes.

The Font in which characters are to be displayed is selected using a Font Style identifier.

Some Assembly Required

Registers and RAMs.png
Fonts.jpeg
I2C Start.png
I2C Stop.png
I2C Data Transfer.png
SSD13056 Data Command.jpeg
SSD13056 Init.jpeg
SSD13056 Col Page.jpeg
Character Box.png
Put Char.png
String.png
Number.png

The screenshots of I2C and OLED related codes have been posted. The ASM file also has been attached.

Downloads

Overall Work

20210524225141_IMG_3592.JPG
20210524225235_IMG_3594.JPG
20210524225309_IMG_3595.JPG
20210524225352_IMG_3596.JPG
20210524225423_IMG_3597.JPG

The project has been done up to this point. Applying 2.4 Volts from a pair of old alkaline batteries, the unit with minimum OLED contrast level and being clocked at 1MHz, is drawing only 3.5mA of current without any Sleep Mode. So, it can be further developed into 4-20mA Current Loop Powered Indicator device.

Ongoing Work

20210530124022_IMG_3634.JPG
20210530124830_IMG_3645.JPG

Currently, I am trying hard to implement Graphic functions on the SSD1306 without frame buffer in Assembly and got a little amount of success by (i) drawing a Dot at any X,Y coordinate on the screen and (ii) draw lines using Bresenham's Line Drawing Algorithm. You can also help me out. Thank you.