Hello Hobbyist, Today I’m going to demonstrate SSD1306 with Raspberry Pi Pico. Pico is recently launched and it’s libraries are not that developed. But today we are going to perform connecting this Oled to pico via SPI communication. All code and libraries are given so don’t worry. Let’s Begin then
Pi PICO:
Raspberry Pi Pico is development board which runs basically on MicroPython. It is similar to other microcontrollers like Arduino, NodeMCU and various other. But it has some more features as compared to them. It is built out of RP2040. It has 264KB of RAM and 2MB of FLASH space. You can also program PICO in C/C++ using arduino IDE. For that just include this link in your Arduino IDE preferences options. Then go to Boards manager and search Raspberry pi pico and download this Boards.
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index
Using this you can use all type of communication like SPI(2), I2C(2),UART, Serial. So is you are well good in python then you just try this board for sure. The library which you have to use for intalling firmware onto board can easily be done. By using Thonny IDE.
For updating the firmware or installing just press the button(BOOTSEL) on PICO and while pressing connect usb to pico. Once your board is detected in Thonny IDE you can release the button. Now click ‘Install or Upgrade Firmware” option available on bottom right.
Material Required:
- raspberry Pi Pico
- SSD1306 SPI OLED
- Jumper Wires
- Breadboard
- Thonny IDE installed on system
Circuit Design:
Raspberry PI |
7 Pin Oled Display |
+5 VOLT |
VCC |
GND |
GND |
GPIO 18 Pin |
SCK |
GPIO 19 Pin |
SDA |
GPIO 20 Pin |
RES |
GPIO 17 Pin |
DC |
GPIO 16 Pin |
CS |
Programming:
Open Thonny IDE and then Paste this code in a new file:
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
from micropython import const
import framebuf
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8D)
class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR,
0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO,
self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET,
0x00,
SET_COM_PIN_CFG,
0x02 if self.width > 2 * self.height else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV,
0x80,
SET_PRECHARGE,
0x22 if self.external_vcc else 0xF1,
SET_VCOM_DESEL,
0x30, # 0.83*Vcc
# display
SET_CONTRAST,
0xFF, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP,
0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01,
): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b"\x40", None] # Co=0, D/C#=1
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_data(self, buf):
self.write_list[1] = buf
self.i2c.writevto(self.addr, self.write_list)
class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)
Save this file in your pico with name ‘ssd1306.py’. This is not the main code but it helps up to run the main code or you can say that it is library which is used.
Now the main code work like this. First we import SPI and Pin functions from machine module. Then we import SSD1306_SPI function from library saved above (Use SSD1306_I2C for I2C communication). After it we import module which contains all processing that we gonna do to display things on oled. Alsowe import some time modules for delay in program.
from machine import Pin, SPI
from ssd1306 import SSD1306_SPI
import framebuf
from time import sleep
from utime import sleep_ms
Now we define to which SPI port we gonna hook our OLED i.e., SPI(0). PIns for SPI are 18,19,16,17; SCK, MOSI, MISO, CS respectively. Pins for dc and rst can be defined manually in SSD1306 OLED initialization. Also, we create an object ‘oled’ which stores the initialization of our OLED
spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))
oled = SSD1306_SPI(128, 64, spi, Pin(17),Pin(20), Pin(16))
after this in the while loop we apply to try and except method to make the loop continue until a keyboard interrupt is encountered. So display will show text ‘HELLO WORLD’ scrolling down and right at the same time until we give keyboard interrupt.
Code:
Here is the complete code. Just have a look and remember to save it with the name ‘main.py’ in pico so that it runs whenever pico gets restarted or powered on.
from machine import Pin, SPI
from ssd1306 import SSD1306_SPI
import framebuf
from time import sleep
from utime import sleep_ms
spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))
oled = SSD1306_SPI(128, 64, spi, Pin(17),Pin(20), Pin(16))
#oled = SSD1306_SPI(WIDTH, HEIGHT, spi, dc,rst, cs) use GPIO PIN NUMBERS
while True:
try:
for i in range(40):
for j in range(56):
oled.fill(0)
oled.show()
#sleep(1)
oled.text("HELLO WORLD",i,j)
oled.show()
sleep_ms(10)
except KeyboardInterrupt:
break
With this, we have successfully completed our tutorial on SSD1306 raspberry Pi Pico using SPI communication. I hope you liked it. If you come through and problem then let me know in the comment down below.
Works really well, after reformatting ‘SSD1306.py’ . Also works with Pico W. Images next!
Thank you VERY much. I had crises until I got this from you