Using HD44780 type two row OLED display via SPI bus

beginner

OLED display connected to Aery32

Update! Article updated to be consistent with the framework version 0.4.1.

In this article I will quickly describe how to connect OLED display to Aery32 devboard using serial peripheral interface bus (SPI). Only 6 pieces of jumper wires are needed, 4 for the SPI bus and 2 for the power (3.3V and GND). For the display module I chose NHD‐0220DZW‐AG5 (2 lines x 20 characters) that has Hitachi HD44780 LCD controller. The selection criterion was that HD44780 display controllers are very common and that the display was working at 3.3V supply voltage.

By default NHD‐0220DZW‐AG5 MPU interface is set to work in parallel mode, so I had to change the jumper setting at the bottom side of the display module to enable SPI interface. Hereby, I removed the jumper resistors marked with the red rectangles, jumped JCS with 0R resistor (marked by light green) and changed the position of the H_PS_L jumper to the position marked by the orange ractangle. Afterwards I noticed that jumper J68 would not have to be removed, so you can spare your soldering iron for that.

NHD‐0220DZW‐AG5 jumper selection for SPI

Connections table
Aery32 Dipslay module
PA10, CS PIN16
PA11, MISO PIN13
PA12, MOSI PIN14
PA13, SCK PIN12

Initialize the SPI bus

First SPI bus has to be initialized, so let's include SPI module into our main.cpp. We will also need aery32/delay.h and aery32/string.h.

#include "board.h"
#include <aery32/gpio.h>
#include <aery32/spi.h>
#include <aery32/delay.h>
#include <aery32/string.h>

I have chosen to connect the display to the SPI0 with NPCS0 (slave select 0), so I have to initialize those GPIO pins before initializing SPI0 itself. The SPI pins are PA10, 11, 12 and 13, that have to be assinged to peripheral function A. This I checked from the UC3A1's datasheet page 45.

using namespace aery;

#define SPI0_GPIO_MASK      ((1 << 10) | (1 << 11) | (1 << 12) | (1 << 13))

#define DISPLAY_SPI         spi0
#define DISPLAY_SPI_NPCS    0
#define DISPLAY_SPI_MODE    SPI_MODE3

int main(void)
{
    init_board();
    gpio_init_pins(porta, SPI0_GPIO_MASK, GPIO_FUNCTION_A);

    // the main function continues ...

Then init and enable SPI0 as a master, and its NPCS0 pin as it was defined above.

spi_init_master(DISPLAY_SPI);
spi_setup_npcs(DISPLAY_SPI, DISPLAY_SPI_NPCS, DISPLAY_SPI_MODE, 10);
spi_enable(DISPLAY_SPI);

Show strings on the display

I would like to write to the display with puts() like function, so let's make a function for that

int display_puts(const char *buf)
{
    return nputs(buf, strlen(buf), display_putchar);
}

That was easy, but it needs display_putchar() function that we do not have yet. So how do we write characters to the display? Yes, by writing bytes to the SPI bus that is connected to the display.

void display_putchar(char c)
{
    display_wait();
    spi_transmit(DISPLAY_SPI, DISPLAY_SPI_NPCS, 0x200|c);
    return c;
}

The transmitted byte c is bitwise OR'ed to set RS and R/W bits of the display instruction appropriately. Whatever, we can now write to the display like this

display_puts("Hello Aery32 devs!");

Easy!

Instructing the display

Above we created the display_puts() function and were very happy with that, but forgot to start our display–Oops! Somehow we have to be able to instruct the display. The HD44780 display driver has moderately large instruction set and its initialization sequence is also several lines long. But we won't let that discouraged us from making this work! So here's the instruction sequence to be placed after the SPI0 initialization in main().

display_instruct(HD44780_DL8BIT|HD44780_FONTBL_WE1);
display_instruct(HD44780_DISPLAY_OFF);
display_instruct(HD44780_CLEAR_DISPLAY);
display_instruct(HD44780_RETURN_HOME);
display_instruct(HD44780_EMODE_INCREMENT);
display_instruct(HD44780_CURSOR_ONBLINK);

And here comes the large paste that shows the defined instructions and the display_instruct() function. Hopefully you are ready for this ;)

#define HD44780_CLEAR_DISPLAY       0x01
#define HD44780_RETURN_HOME         0x02

#define HD44780_EMODE_INCREMENT     0x06
#define HD44780_EMODE_DECREMENT     0x04
#define HD44780_EMODE_INCRNSHIFT    0x07
#define HD44780_EMODE_DECRNSHIFT    0x05

#define HD44780_DISPLAY_ON          0x0C
#define HD44780_DISPLAY_OFF         0x08
#define HD44780_CURSOR_ON           0x0E
#define HD44780_CURSOR_ONBLINK      0x0F

#define HD44780_LSHIFT_CURSOR       0x10
#define HD44780_RSHIFT_CURSOR       0x14
#define HD44780_LSHIFT_DISPLAY      0x18
#define HD44780_RSHIFT_DISPLAY      0x1C

#define HD44780_DL4BIT              0x28
#define HD44780_DL8BIT              0x38
#define HD44780_FONTBL_ENJA         0x28
#define HD44780_FONTBL_WE1          0x29
#define HD44780_FONTBL_ENRU         0x2A
#define HD44780_FONTBL_WE2          0x2B

#define HD44780_DDRAM_ADDR          0x80
#define HD44780_CGRAM_ADDR          0x40

#define HD44780_BUSYBIT_MASK        0x80

bool display_isbusy(void)
{
    uint16_t rd; /* read data */

    aery_spi_transmit(DISPLAY_SPI, DISPLAY_SPI_NPCS, 0x100, false);
    rd = aery_spi_transmit(DISPLAY_SPI, DISPLAY_SPI_NPCS, 0x100);
    return ((HD44780_BUSYBIT_MASK << 2) & rd) != 0;
}

void display_wait(void)
{
    while (display_isbusy())
        delay_us(600);
}

void display_instruct(uint16_t instruction)
{
    display_wait();
    aery_spi_transmit(DISPLAY_SPI, DISPLAY_SPI_NPCS, instruction);
}

Btw. here is the entire program. ❑

blog comments powered by Disqus