single repo

This commit is contained in:
Adolfo Reyna
2026-01-01 13:33:51 -05:00
parent 8b56187ef1
commit bc8ae91eba
334 changed files with 124529 additions and 2 deletions

Submodule pico-ssd1306 deleted from 2cec467a06

39
pico-ssd1306/.gitignore vendored Normal file
View File

@@ -0,0 +1,39 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Ide settings
.idea
.vscode
# MacOS
.DS_Store

View File

@@ -0,0 +1,13 @@
add_library(pico_ssd1306
ssd1306.cpp
frameBuffer/FrameBuffer.cpp
shapeRenderer/ShapeRenderer.cpp)
add_subdirectory(textRenderer)
target_link_libraries(pico_ssd1306
ssd1306_textRenderer
hardware_i2c
pico_stdlib
)
target_include_directories (pico_ssd1306 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

29
pico-ssd1306/LICENSE Normal file
View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, Harbys
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(text_example)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(text_example
main.cpp)
target_link_libraries(text_example
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(text_example)

View File

@@ -0,0 +1,39 @@
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "hardware/i2c.h"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main(){
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display.setOrientation(0);
// Draw text on display
// After passing a pointer to display, we need to tell the function what font and text to use
// Available fonts are listed in textRenderer's readme
// Last we tell this function where to anchor the text
// Anchor means top left of what we draw
drawText(&display, font_12x16, "TEST text", 0 ,0);
// Send buffer to the display
display.sendBuffer();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 KiB

View File

@@ -0,0 +1,3 @@
# This examples produces such a result
![output](output.jpg)

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(bitmap_example)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(bitmap_example
main.cpp)
target_link_libraries(bitmap_example
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(bitmap_example)

View File

@@ -0,0 +1,55 @@
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "hardware/i2c.h"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main() {
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display.setOrientation(0);
// Create a variable storing our bitmap image
unsigned char image[] = {
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000011, 0b11000000,
0b00000010, 0b01000000,
0b00000110, 0b11100000,
0b00001100, 0b00110000,
0b00111001, 0b00011100,
0b11101000, 0b01000111,
0b11100010, 0b00000111,
0b00111000, 0b01011100,
0b00001100, 0b10110000,
0b00000110, 0b01100000,
0b00000011, 0b01000000,
0b00000011, 0b11000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000
};
// Add image to buffer with anchor at point x: 10, y:10 and image width: 16 and height:16
display.addBitmapImage(10, 10, 16, 16, image);
// Send buffer to the display
display.sendBuffer();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 KiB

View File

@@ -0,0 +1,3 @@
# This examples produces such a result
![output](output.jpg)

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(line_example)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(line_example
main.cpp)
target_link_libraries(line_example
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(line_example)

View File

@@ -0,0 +1,31 @@
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/shapeRenderer/ShapeRenderer.h"
#include "hardware/i2c.h"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main(){
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Draw a line on display from 0, 0 to 127, 63
drawLine(&display, 0, 0 ,127, 63);
// Send buffer to the display
display.sendBuffer();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 KiB

View File

@@ -0,0 +1,3 @@
# This examples produces such a result
![output](output.jpg)

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(falling_dots)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(falling_dots
main.cpp)
target_link_libraries(falling_dots
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(falling_dots)

View File

@@ -0,0 +1,50 @@
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "hardware/i2c.h"
#pragma ide diagnostic ignored "EndlessLoop"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main() {
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display.setOrientation(0);
// variable to count on witch frame we are on
uint32_t frame_counter = 0;
// Infinite animation loop
while (1){
// Draw pixels spaced 8 px apart, one px lower every frame
for (uint8_t pass1 = 0; pass1 < 15; pass1 ++){
display.setPixel(pass1 * 8, frame_counter % 63);
}
// Send buffer to display
display.sendBuffer();
// Show the frame for 100ms
sleep_ms(100);
// Clear the buffer
display.clear();
// Increment frame counter
frame_counter ++;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 KiB

View File

@@ -0,0 +1,7 @@
# This examples produces such a result
![output](output1.jpg)
![output](output2.jpg)
![output](output3.jpg)

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(flag)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(flag
main.cpp)
target_link_libraries(flag
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(flag)

View File

@@ -0,0 +1,44 @@
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/shapeRenderer/ShapeRenderer.h"
#include "hardware/i2c.h"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main() {
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display.setOrientation(0);
// Draw an outline
drawRect(&display, 0, 0, 127, 63);
// Draw 2 rectangles
fillRect(&display, 0, 0, 63, 31);
fillRect(&display, 64, 32, 127, 63);
// Draw a line across the screen
drawLine(&display, 127, 0, 0, 63);
// Send buffer to the display
display.sendBuffer();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

View File

@@ -0,0 +1,5 @@
# This examples produces such a result
## Ignore yellow at the bottom (that's just my screen dying from hours of testing and development and some abuse)
![output](output1.jpg)

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(multiple_displays)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(multiple_displays
main.cpp)
target_link_libraries(multiple_displays
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(multiple_displays)

View File

@@ -0,0 +1,44 @@
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "hardware/i2c.h"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main() {
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3C and size of 128x32
SSD1306 display1 = SSD1306(i2c0, 0x3C, Size::W128xH32);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display2 = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display1.setOrientation(0);
display2.setOrientation(0);
// Draw text on display 1
drawText(&display1, font_16x32, "Disp 1", 0, 0);
// Draw text on display 2
drawText(&display2, font_16x32, "Disp 2", 0, 0);
// Send buffer to the displays
display1.sendBuffer();
display2.sendBuffer();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

View File

@@ -0,0 +1,3 @@
# This examples produces such a result
![output](output1.jpg)

View File

@@ -0,0 +1,53 @@
# Examples for pico ssd1306 library
## Core library
| Link | Description |
|----------------------------------------|-------------------------------------------------|
| [bitmap_image](bitmap_image) | Display a simple bitmap image |
| [falling_dots](falling_dots) | Animation of dots falling on screen |
| [write_mode](write_mode) | Example showing differences between write modes |
| [multiple_displays](multiple_displays) | 2 Displays connected to the same i2c controller |
## Shape Renderer Module
| Link | Description |
|------------------------|-------------------------------------------------------------------------------|
| [draw_line](draw_line) | Example of drawing a line across the screen |
| [flag](flag) | Drawing a "flag" on your display. Shows how to use different shape renderers. |
## Text Renderer Module
| Link | Description |
|--------------------------------------------|----------------------------------------|
| [basic_text](basic_text) | Drawing some sample text to the screen |
| [text_advanced](text_advanced) | Drawing normal and rotated text |
| [text_extended_ascii](text_extended_ascii) | Using extended ASCII chars |
## How to build an example
* Clone this project and go into one of the examples
```shell
git clone https://github.com/Harbys/pico-ssd1306.git
cd pico-ssd1306/examples/(choose_an_example)
```
* Copy pico_sdk_import.cmake from your pico-sdk
```shell
cp (path_to_pico_sdk)/external/pico_sdk_import.cmake .
```
* Set the PICO_SDK_PATH environment variable
```shell
export PICO_SDK_PATH="(path_to_pico_sdk)"
```
* Create a symlink to the whole library
```shell
ln -s ../../. pico-ssd1306
```
* Create a build directory and enter it
```shell
mkdir build
cd build
```
* Build
```shell
cmake .. && make
```
* Copy the uf2 file to your pico

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(text_advanced)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(text_advanced
main.cpp)
target_link_libraries(text_advanced
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(text_advanced)

View File

@@ -0,0 +1,40 @@
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "hardware/i2c.h"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main() {
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display.setOrientation(0);
// Draw unrotated text
drawText(&display, font_8x8, "Text Normal", 16, 0);
// Draw text rotated by 90 degrees
drawText(&display, font_5x8, "Text Rotated", 0, 0, WriteMode::ADD, Rotation::deg90);
// Send buffer to the display
display.sendBuffer();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

View File

@@ -0,0 +1,3 @@
# This examples produces such a result
![output](output1.jpg)

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(text_extended_ascii)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(text_extended_ascii
main.cpp)
target_link_libraries(text_extended_ascii
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(text_extended_ascii)

View File

@@ -0,0 +1,46 @@
// Define SSD1306_ASCII_FULL to use full ascii range (32 - 255)
#define SSD1306_ASCII_FULL
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "hardware/i2c.h"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main(){
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display.setOrientation(0);
// Draw text on display
// After passing a pointer to display, we need to tell the function what font and text to use
// Available fonts are listed in textRenderer's readme
// Last we tell this function where to anchor the text
// Anchor means top left of what we draw
// We use \x escape to use chars by their hex numeration according to the ASCII table
drawText(&display, font_5x8, "\x24 \xba \xb2", 0 ,0);
drawText(&display, font_8x8, "\x24 \xba \xb2", 0 ,10);
drawText(&display, font_12x16, "\x24 \xba \xb2", 0 ,20);
drawText(&display, font_16x32, "\x24 \xba \xb2", 0 ,36);
// Send buffer to the display
display.sendBuffer();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

View File

@@ -0,0 +1,3 @@
# This examples produces such a result
![output](output1.jpg)

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(write_mode)
pico_sdk_init()
add_subdirectory(pico-ssd1306)
add_executable(write_mode
main.cpp)
target_link_libraries(write_mode
hardware_i2c
pico_ssd1306)
pico_add_extra_outputs(write_mode)

View File

@@ -0,0 +1,65 @@
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/shapeRenderer/ShapeRenderer.h"
#include "hardware/i2c.h"
#pragma ide diagnostic ignored "EndlessLoop"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main() {
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(12, GPIO_FUNC_I2C);
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12);
gpio_pull_up(13);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
SSD1306 display = SSD1306(i2c0, 0x3D, Size::W128xH64);
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display.setOrientation(0);
// Fill left half of the screen
fillRect(&display, 0, 0, 63,63);
// Create a variable storing a bitmap image
unsigned char image[] = {
0b00000001, 0b10000000,
0b00000001, 0b10000000,
0b00000011, 0b11000000,
0b00000010, 0b01000000,
0b00000110, 0b11100000,
0b00001100, 0b00110000,
0b00111001, 0b00011100,
0b11101000, 0b01000111,
0b11100010, 0b00000111,
0b00111000, 0b01011100,
0b00001100, 0b10110000,
0b00000110, 0b01100000,
0b00000011, 0b01000000,
0b00000011, 0b11000000,
0b00000001, 0b10000000,
0b00000001, 0b10000000
};
// To see this example at work play with WriteMode
// Add will turn pixels on regardless of their state
// Subtract will turn pixels off regardless of their state
// Invert will swap state of selected pixels
display.addBitmapImage(63-8, 31-8, 16, 16, image, WriteMode::INVERT);
// Send buffer to the display
display.sendBuffer();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

View File

@@ -0,0 +1,12 @@
# This examples produces such a result
## Ignore yellow at the bottom (that's just my screen dying from hours of testing and development and some abuse)
### WriteMode::ADD
![output](output1.jpg)
### WriteMode::SUBTRACT
![output](output2.jpg)
### WriteMode::INVERT
![output](output3.jpg)

View File

@@ -0,0 +1,43 @@
#include "FrameBuffer.h"
FrameBuffer::FrameBuffer() {
this->buffer = new unsigned char[FRAMEBUFFER_SIZE];
}
FrameBuffer::~FrameBuffer() {
delete[] this->buffer;
}
void FrameBuffer::byteOR(int n, unsigned char byte) {
// return if index outside 0 - buffer length - 1
if (n > (FRAMEBUFFER_SIZE-1)) return;
this->buffer[n] |= byte;
}
void FrameBuffer::byteAND(int n, unsigned char byte) {
// return if index outside 0 - buffer length - 1
if (n > (FRAMEBUFFER_SIZE-1)) return;
this->buffer[n] &= byte;
}
void FrameBuffer::byteXOR(int n, unsigned char byte) {
// return if index outside 0 - buffer length - 1
if (n > (FRAMEBUFFER_SIZE-1)) return;
this->buffer[n] ^= byte;
}
void FrameBuffer::setBuffer(unsigned char *new_buffer) {
// free buffer memory to prevent memory leak
delete[] this->buffer;
this->buffer = new_buffer;
}
void FrameBuffer::clear() {
//zeroes out the buffer via memset function from string library
memset(this->buffer, 0, FRAMEBUFFER_SIZE);
}
unsigned char *FrameBuffer::get() {
return this->buffer;
}

View File

@@ -0,0 +1,54 @@
#ifndef SSD1306_FRAMEBUFFER_H
#define SSD1306_FRAMEBUFFER_H
#include <string.h>
/// \brief Set frame buffer to 1024 bytes, witch is 128*64 / 8
///
/// For 128x32 displays it's still 1024 due to how memory mapping works on ssd1306.
/// This is explained in readme.md
#define FRAMEBUFFER_SIZE 1024
/// \brief Framebuffer class contains a pointer to buffer and functions for interacting with it
class FrameBuffer {
unsigned char * buffer;
public:
/// Constructs frame buffer and allocates memory for buffer
FrameBuffer();
/// Destroys frame buffer and frees buffer memory
~FrameBuffer();
/// \brief Performs OR logical operation on selected and provided byte
///
/// ex. if byte in buffer at position n is 0b10001111 and provided byte is 0b11110000 the buffer at position n becomes 0b11111111
/// \param n - byte offset in buffer array to work on
/// \param byte - provided byte to make operation
void byteOR(int n, unsigned char byte);
/// \brief Performs AND logical operation on selected and provided byte
///
/// ex. if byte in buffer at position n is 0b10001111 and provided byte is 0b11110000 the buffer at position n becomes 0b10000000
/// \param n - byte offset in buffer array to work on
/// \param byte - provided byte to make operation
void byteAND(int n, unsigned char byte);
/// \brief Performs XOR logical operation on selected and provided byte
///
/// ex. if byte in buffer at position n is 0b10001111 and provided byte is 0b11110000 the buffer at position n becomes 0b0111111
/// \param n - byte offset in buffer array to work on
/// \param byte - provided byte to make operation
void byteXOR(int n, unsigned char byte);
/// Replaces pointer with one pointing to a different buffer
void setBuffer(unsigned char * new_buffer);
/// Zeroes out the buffer aka set buffer to all 0
void clear();
/// Returns a pointer to the buffer
unsigned char * get();
};
#endif //SSD1306_FRAMEBUFFER_H

BIN
pico-ssd1306/images/ex1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
pico-ssd1306/images/ex2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
pico-ssd1306/images/ex3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
pico-ssd1306/images/ex4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
pico-ssd1306/images/ex5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

75
pico-ssd1306/readme.md Normal file
View File

@@ -0,0 +1,75 @@
# SSD1306 OLED Library for RP2040
## 1. Importing the library
* Clone this project into your pico project
* Add this to your CMakeLists.txt
```cmake
add_subdirectory(pico-ssd1306)
target_link_libraries(your_project_name
pico_ssd1306
# you will also need hardware i2c library for communication with the display
hardware_i2c)
```
* Import library in your code
```c++
#include "pico-ssd1306/ssd1306.h"
```
## 2. Basic usage
```c++
i2c_init(I2C_PORT, 1000000); //Use i2c port with baud rate of 1Mhz
//Set pins for I2C operation
gpio_set_function(I2C_PIN_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C_PIN_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C_PIN_SDA);
gpio_pull_up(I2C_PIN_SCL);
//Create a new display object
pico_ssd1306::SSD1306 display = pico_ssd1306::SSD1306(I2C_PORT, 0x3D, pico_ssd1306::Size::W128xH64);
//create a vertical line on x: 64 y:0-63
for (int y = 0; y < 64; y++){
display.setPixel(64, y);
}
display.sendBuffer(); //Send buffer to device and show on screen
```
### Expected output:
![example1](images/ex1.png)
You may have noticed that this entire library is under pico_ssd1306 namespace to avoid conflicts, but if you don't have any
conflicts and don't want to write ```pico_ssd1306::``` all the time just add
```c++
using namespace pico_ssd1306;
```
to your file
## 3. Principles of operation
See [usage explanation](usage.md) for detailed information on how to use core of the lib, but in short:
* First Initialize i2c and pins for i2c communication
* Create a display object. This automatically send setup commands to the device and prepares it for operation
* Modify the buffer containing pixel data
* Send buffer to display
* Clear the buffer and repeat
## 4. Pixel Addressing
![pixel_addressing](images/ex5.png)
same is true for 128x32 displays, then y range is 0-31
## 5. Additional Modules
This library comes with additional modules for shape rendering and text rendering to make your life easier
### Importing Shape Renderer
```c++
#include "pico-ssd1306/shapeRenderer/ShapeRenderer.h"
```
See: [Shape Renderer readme](shapeRenderer/readme.md) for usage and details
### Importing Text Renderer
```c++
#include "pico-ssd1306/textRenderer/TextRenderer.h"
```
See: [Text Renderer readme](textRenderer/readme.md) for usage and details
## 6. Examples
See [examples](examples). Many of them have their own readmes. Many things are also explained in code comments.
## 7. Documentation
Documentation of all functions is [here](https://ssd1306.harbys.me)

View File

@@ -0,0 +1,80 @@
#include "ShapeRenderer.h"
void pico_ssd1306::drawLine(pico_ssd1306::SSD1306 *ssd1306, uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
pico_ssd1306::WriteMode mode) {
int x, y, dx, dy, dx0, dy0, px, py, xe, ye, i;
dx = x1 - x0;
dy = y1 - y0;
dx0 = fabs(dx);
dy0 = fabs(dy);
px = 2 * dy0 - dx0;
py = 2 * dx0 - dy0;
if (dy0 <= dx0) {
if (dx >= 0) {
x = x0;
y = y0;
xe = x1;
} else {
x = x1;
y = y1;
xe = x0;
}
ssd1306->setPixel(x, y, mode);
for (i = 0; x < xe; i++) {
x = x + 1;
if (px < 0) {
px = px + 2 * dy0;
} else {
if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) {
y = y + 1;
} else {
y = y - 1;
}
px = px + 2 * (dy0 - dx0);
}
ssd1306->setPixel(x, y, mode);
}
} else {
if (dy >= 0) {
x = x0;
y = y0;
ye = y1;
} else {
x = x1;
y = y1;
ye = y0;
}
ssd1306->setPixel(x, y, mode);
for (i = 0; y < ye; i++) {
y = y + 1;
if (py <= 0) {
py = py + 2 * dx0;
} else {
if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) {
x = x + 1;
} else {
x = x - 1;
}
py = py + 2 * (dx0 - dy0);
}
ssd1306->setPixel(x, y, mode);
}
}
}
void pico_ssd1306::drawRect(pico_ssd1306::SSD1306 *ssd1306, uint8_t x_start, uint8_t y_start, uint8_t x_end, uint8_t y_end,
pico_ssd1306::WriteMode mode) {
drawLine(ssd1306, x_start, y_start, x_end, y_start, mode);
drawLine(ssd1306, x_start, y_end, x_end, y_end, mode);
drawLine(ssd1306, x_start, y_start, x_start, y_end, mode);
drawLine(ssd1306, x_end, y_start, x_end, y_end, mode);
}
void pico_ssd1306::fillRect(pico_ssd1306::SSD1306 *ssd1306, uint8_t x_start, uint8_t y_start, uint8_t x_end, uint8_t y_end,
pico_ssd1306::WriteMode mode) {
for (uint8_t x = x_start; x <= x_end; x++) {
for (uint8_t y = y_start; y <= y_end; y++) {
ssd1306->setPixel(x, y, mode);
}
}
}

View File

@@ -0,0 +1,27 @@
#ifndef SSD1306_SHAPERENDERER_H
#define SSD1306_SHAPERENDERER_H
#include <math.h>
#include "../ssd1306.h"
namespace pico_ssd1306{
/// \brief Draws a line from x0, y0 to x1, y1.
/// It supports all drawing angles
/// \param ssd1306 - is the pointer to a SSD1306 object aka an initialised display
/// \param x0, y0, x1, y1 are the start and end coordinates between which the line will be drawn
/// \param mode - mode describes setting behavior. See WriteMode doc for more information
void drawLine (pico_ssd1306::SSD1306 *ssd1306, uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, pico_ssd1306::WriteMode mode = pico_ssd1306::WriteMode::ADD);
/// \brief Draws a 1px wide rectangle between x0, y0 and x1, y1
/// \param x_start, x_end, y_start, y_end - corner points for the rectangle
/// \param mode - mode describes setting behavior. See WriteMode doc for more information
void drawRect (pico_ssd1306::SSD1306 *ssd1306 , uint8_t x_start, uint8_t y_start, uint8_t x_end, uint8_t y_end, pico_ssd1306::WriteMode mode = pico_ssd1306::WriteMode::ADD);
/// \brief Fills a rectangle from x0, y0 to x1, y1
/// \param x_start, x_end, y_start, y_end - corner points for the rectangle
/// \param mode - mode describes setting behavior. See WriteMode doc for more information
void fillRect (pico_ssd1306::SSD1306 *ssd1306 , uint8_t x_start, uint8_t y_start, uint8_t x_end, uint8_t y_end, pico_ssd1306::WriteMode mode = pico_ssd1306::WriteMode::ADD);
}
#endif //SSD1306_SHAPERENDERER_H

View File

@@ -0,0 +1,27 @@
# Shape Renderer Module
## This module provides functions for drawing basic geometrical shapes with simple code
## 1. Importing
```c++
#include "pico-ssd1306/shapeRenderer/ShapeRenderer.h"
```
note that core library and hardware_i2c library's need to be imported to use this library so follow steps from section 1
of [readme.md](../readme.md)
## 2. Differences from core library
Calling functions from modules is different from core lib. You can't just do ```object.drawLine(...``` since modules don't
actually extend the SSD1306 class. You need to provide module functions with a pointer to a display object. So the first
argument of every module draw function is a pointer to a display.
To see this more clearly here is an example:
```c++
// Create a new display object
pico_ssd1306::SSD1306 display = pico_ssd1306::SSD1306(I2C_PORT, 0x3D, pico_ssd1306::Size::W128xH64);
// Draw a line from x: 0, y: 0 to x: 128, y: 64
// Notice how we first pass the address of display object to the function
pico_ssd1306::drawLine(&display, 0, 0, 128, 64);
```
## All functions are documented [here](https://ssd1306.harbys.me)

187
pico-ssd1306/ssd1306.cpp Normal file
View File

@@ -0,0 +1,187 @@
#include "ssd1306.h"
namespace pico_ssd1306 {
SSD1306::SSD1306(i2c_inst *i2CInst, uint16_t Address, Size size) {
// Set class instanced variables
this->i2CInst = i2CInst;
this->address = Address;
this->size = size;
this->width = 128;
if (size == Size::W128xH32) {
this->height = 32;
} else {
this->height = 64;
}
// display is not inverted by default
this->inverted = false;
// this is a list of setup commands for the display
uint8_t setup[] = {
SSD1306_DISPLAY_OFF,
SSD1306_LOWCOLUMN,
SSD1306_HIGHCOLUMN,
SSD1306_STARTLINE,
SSD1306_MEMORYMODE,
SSD1306_MEMORYMODE_HORZONTAL,
SSD1306_CONTRAST,
0xFF,
SSD1306_INVERTED_OFF,
SSD1306_MULTIPLEX,
63,
SSD1306_DISPLAYOFFSET,
0x00,
SSD1306_DISPLAYCLOCKDIV,
0x80,
SSD1306_PRECHARGE,
0x22,
SSD1306_COMPINS,
0x12,
SSD1306_VCOMDETECT,
0x40,
SSD1306_CHARGEPUMP,
0x14,
SSD1306_DISPLAYALL_ON_RESUME,
SSD1306_DISPLAY_ON
};
// send each one of the setup commands
for (uint8_t &command: setup) {
this->cmd(command);
}
// clear the buffer and send it to the display
// if not done display shows garbage data
this->clear();
this->sendBuffer();
}
void SSD1306::setPixel(int16_t x, int16_t y, WriteMode mode) {
// return if position out of bounds
if ((x < 0) || (x >= this->width) || (y < 0) || (y >= this->height)) return;
// byte to be used for buffer operation
uint8_t byte;
// display with 32 px height requires doubling of set bits, reason to this is explained in readme
// this shifts 1 to byte based on y coordinate
// remember that buffer is a one dimension array, so we have to calculate offset from coordinates
if (size == Size::W128xH32) {
y = (y << 1) + 1;
byte = 1 << (y & 7);
char byte_offset = byte >> 1;
byte = byte | byte_offset;
} else {
byte = 1 << (y & 7);
}
// check the write mode and manipulate the frame buffer
if (mode == WriteMode::ADD) {
this->frameBuffer.byteOR(x + (y / 8) * this->width, byte);
} else if (mode == WriteMode::SUBTRACT) {
this->frameBuffer.byteAND(x + (y / 8) * this->width, ~byte);
} else if (mode == WriteMode::INVERT) {
this->frameBuffer.byteXOR(x + (y / 8) * this->width, byte);
}
}
void SSD1306::sendBuffer() {
this->cmd(SSD1306_PAGEADDR); //Set page address from min to max
this->cmd(0x00);
this->cmd(0x07);
this->cmd(SSD1306_COLUMNADDR); //Set column address from min to max
this->cmd(0x00);
this->cmd(127);
// create a temporary buffer of size of buffer plus 1 byte for startline command aka 0x40
unsigned char data[FRAMEBUFFER_SIZE + 1];
data[0] = SSD1306_STARTLINE;
// copy framebuffer to temporary buffer
memcpy(data + 1, frameBuffer.get(), FRAMEBUFFER_SIZE);
// send data to device
i2c_write_blocking(this->i2CInst, this->address, data, FRAMEBUFFER_SIZE + 1, false);
}
void SSD1306::clear() {
this->frameBuffer.clear();
}
void SSD1306::setOrientation(bool orientation) {
// remap columns and rows scan direction, effectively flipping the image on display
if (orientation) {
this->cmd(SSD1306_CLUMN_REMAP_OFF);
this->cmd(SSD1306_COM_REMAP_OFF);
} else {
this->cmd(SSD1306_CLUMN_REMAP_ON);
this->cmd(SSD1306_COM_REMAP_ON);
}
}
void
SSD1306::addBitmapImage(int16_t anchorX, int16_t anchorY, uint8_t image_width, uint8_t image_height,
uint8_t *image,
WriteMode mode) {
uint8_t byte;
// goes over every single bit in image and sets pixel data on its coordinates
for (uint8_t y = 0; y < image_height; y++) {
for (uint8_t x = 0; x < image_width / 8; x++) {
byte = image[y * (image_width / 8) + x];
for (uint8_t z = 0; z < 8; z++) {
if ((byte >> (7 - z)) & 1) {
this->setPixel(x * 8 + z + anchorX, y + anchorY, mode);
}
}
}
}
}
void SSD1306::invertDisplay() {
this->cmd(SSD1306_INVERTED_OFF | !this->inverted);
inverted = !inverted;
}
void SSD1306::cmd(unsigned char command) {
// 0x00 is a byte indicating to ssd1306 that a command is being sent
uint8_t data[2] = {0x00, command};
i2c_write_blocking(this->i2CInst, this->address, data, 2, false);
}
void SSD1306::setContrast(unsigned char contrast) {
this->cmd(SSD1306_CONTRAST);
this->cmd(contrast);
}
void SSD1306::setBuffer(unsigned char * buffer) {
this->frameBuffer.setBuffer(buffer);
}
void SSD1306::turnOff() {
this->cmd(SSD1306_DISPLAY_OFF);
}
void SSD1306::turnOn() {
this->cmd(SSD1306_DISPLAY_ON);
}
}

134
pico-ssd1306/ssd1306.h Normal file
View File

@@ -0,0 +1,134 @@
#ifndef SSD1306_SSD1306_H
#define SSD1306_SSD1306_H
#include <string.h>
#include "hardware/i2c.h"
#include "frameBuffer/FrameBuffer.h"
namespace pico_ssd1306 {
/// Register addresses from datasheet
enum REG_ADDRESSES : unsigned char{
SSD1306_CONTRAST = 0x81,
SSD1306_DISPLAYALL_ON_RESUME = 0xA4,
SSD1306_DISPLAYALL_ON = 0xA5,
SSD1306_INVERTED_OFF = 0xA6,
SSD1306_INVERTED_ON = 0xA7,
SSD1306_DISPLAY_OFF = 0xAE,
SSD1306_DISPLAY_ON = 0xAF,
SSD1306_DISPLAYOFFSET = 0xD3,
SSD1306_COMPINS = 0xDA,
SSD1306_VCOMDETECT = 0xDB,
SSD1306_DISPLAYCLOCKDIV = 0xD5,
SSD1306_PRECHARGE = 0xD9,
SSD1306_MULTIPLEX = 0xA8,
SSD1306_LOWCOLUMN = 0x00,
SSD1306_HIGHCOLUMN = 0x10,
SSD1306_STARTLINE = 0x40,
SSD1306_MEMORYMODE = 0x20,
SSD1306_MEMORYMODE_HORZONTAL = 0x00,
SSD1306_MEMORYMODE_VERTICAL = 0x01,
SSD1306_MEMORYMODE_PAGE = 0x10,
SSD1306_COLUMNADDR = 0x21,
SSD1306_PAGEADDR = 0x22,
SSD1306_COM_REMAP_OFF = 0xC0,
SSD1306_COM_REMAP_ON = 0xC8,
SSD1306_CLUMN_REMAP_OFF = 0xA0,
SSD1306_CLUMN_REMAP_ON = 0xA1,
SSD1306_CHARGEPUMP = 0x8D,
SSD1306_EXTERNALVCC = 0x1,
SSD1306_SWITCHCAPVCC = 0x2,
};
/// \enum pico_ssd1306::Size
enum class Size {
/// Display size W128xH64
W128xH64,
/// Display size W128xH32
W128xH32
};
/// \enum pico_ssd1306::WriteMode
enum class WriteMode : const unsigned char{
/// sets pixel on regardless of its state
ADD = 0,
/// sets pixel off regardless of its state
SUBTRACT = 1,
/// inverts pixel, so 1->0 or 0->1
INVERT = 2,
};
/// \class SSD1306 ssd1306.h "pico-ssd1306/ssd1306.h"
/// \brief SSD1306 class represents i2c connection to display
class SSD1306 {
private:
i2c_inst *i2CInst;
uint16_t address;
Size size;
FrameBuffer frameBuffer;
uint8_t width, height;
bool inverted;
/// \brief Sends single 8bit command to ssd1306 controller
/// \param command - byte to be sent to controller
void cmd(unsigned char command);
public:
/// \brief SSD1306 constructor initialized display and sets all required registers for operation
/// \param i2CInst - i2c instance. Either i2c0 or i2c1
/// \param Address - display i2c address. usually for 128x32 0x3C and for 128x64 0x3D
/// \param size - display size. Acceptable values W128xH32 or W128xH64
SSD1306(i2c_inst *i2CInst, uint16_t Address, Size size);
/// \brief Set pixel operates frame buffer
/// x is the x position of pixel you want to change. values 0 - 127
/// y is the y position of pixel you want to change. values 0 - 31 or 0 - 63
/// \param x - position of pixel you want to change. values 0 - 127
/// \param y - position of pixel you want to change. values 0 - 31 or 0 - 63
/// \param mode - mode describes setting behavior. See WriteMode doc for more information
void setPixel(int16_t x, int16_t y, WriteMode mode = WriteMode::ADD);
/// \brief Sends frame buffer to display so that it updated
void sendBuffer();
/// \brief Adds bitmap image to frame buffer
/// \param anchorX - sets start point of where to put the image on the screen
/// \param anchorY - sets start point of where to put the image on the screen
/// \param image_width - width of the image in pixels
/// \param image_height - height of the image in pixels
/// \param image - pointer to uint8_t (unsigned char) array containing image data
/// \param mode - mode describes setting behavior. See WriteMode doc for more information
void addBitmapImage(int16_t anchorX, int16_t anchorY, uint8_t image_width, uint8_t image_height, uint8_t *image,
WriteMode mode = WriteMode::ADD);
/// \brief Manually set frame buffer. make sure it's correct size of 1024 bytes
/// \param buffer - pointer to a new buffer
void setBuffer(unsigned char *buffer);
/// \brief Flips the display
/// \param orientation - 0 for not flipped, 1 for flipped display
void setOrientation(bool orientation);
/// \brief Clears frame buffer aka set all bytes to 0
void clear();
/// \brief Inverts screen on hardware level. Way more efficient than setting buffer to all ones and then using WriteMode subtract.
void invertDisplay();
/// \brief Sets display contrast according to ssd1306 documentation
/// \param contrast - accepted values of 0 to 255 to set the contrast
void setContrast(unsigned char contrast);
/// \brief Turns display off
void turnOff();
/// \brief Turns display on
void turnOn();
};
}
#endif //SSD1306_SSD1306_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
add_library(ssd1306_textRenderer
TextRenderer.cpp
5x8_font.h
8x8_font.h
12x16_font.h
16x32_font.h
)
target_link_libraries(ssd1306_textRenderer
hardware_i2c
pico_stdlib
)

View File

@@ -0,0 +1,57 @@
#include "TextRenderer.h"
namespace pico_ssd1306 {
void drawText(pico_ssd1306::SSD1306 *ssd1306, const unsigned char *font, const char *text, uint8_t anchor_x,
uint8_t anchor_y, WriteMode mode, Rotation rotation) {
if(!ssd1306 || !font || !text) return;
uint8_t font_width = font[0];
uint16_t n = 0;
while (text[n] != '\0') {
switch (rotation) {
case Rotation::deg0:
drawChar(ssd1306, font, text[n], anchor_x + (n * font_width), anchor_y, mode, rotation);
break;
case Rotation::deg90:
drawChar(ssd1306, font, text[n], anchor_x, anchor_y + (n * font_width), mode, rotation);
break;
}
n++;
}
}
void drawChar(pico_ssd1306::SSD1306 *ssd1306, const unsigned char *font, char c, uint8_t anchor_x, uint8_t anchor_y,
WriteMode mode, Rotation rotation) {
if(!ssd1306 || !font || c < 32) return;
uint8_t font_width = font[0];
uint8_t font_height = font[1];
uint16_t seek = (c - 32) * (font_width * font_height) / 8 + 2;
uint8_t b_seek = 0;
for (uint8_t x = 0; x < font_width; x++) {
for (uint8_t y = 0; y < font_height; y++) {
if (font[seek] >> b_seek & 0b00000001) {
switch (rotation) {
case Rotation::deg0:
ssd1306->setPixel(x + anchor_x, y + anchor_y, mode);
break;
case Rotation::deg90:
ssd1306->setPixel(-y + anchor_x + font_height, x + anchor_y, mode);
break;
}
}
b_seek++;
if (b_seek == 8) {
b_seek = 0;
seek++;
}
}
}
}
}

View File

@@ -0,0 +1,40 @@
#ifndef SSD1306_TEXTRENDERER_H
#define SSD1306_TEXTRENDERER_H
#include "../ssd1306.h"
#include "5x8_font.h"
#include "8x8_font.h"
#include "12x16_font.h"
#include "16x32_font.h"
namespace pico_ssd1306{
/// \enum pico_ssd1306::Rotation
enum class Rotation{
/// deg0 - means no rotation
deg0,
/// deg 90 - means 90 deg rotation
deg90,
};
/// \brief Draws a single glyph on the screen
/// \param ssd1306 - pointer to a SSD1306 object aka initialised display
/// \param font - pointer to a font data array
/// \param c - char to be drawn
/// \param anchor_x, anchor_y - coordinates setting where to put the glyph
/// \param mode - mode describes setting behavior. See WriteMode doc for more information
/// \param rotation - either rotates the char by 90 deg or leaves it unrotated
void drawChar(pico_ssd1306::SSD1306 *ssd1306, const unsigned char * font, char c, uint8_t anchor_x, uint8_t anchor_y, WriteMode mode = WriteMode::ADD, Rotation rotation = Rotation::deg0);
/// \brief Draws text on screen
/// \param ssd1306 - pointer to a SSD1306 object aka initialised display
/// \param font - pointer to a font data array
/// \param text - text to be drawn
/// \param anchor_x, anchor_y - coordinates setting where to put the text
/// \param mode - mode describes setting behavior. See WriteMode doc for more information
/// \param rotation - either rotates the text by 90 deg or leaves it unrotated
void drawText(pico_ssd1306::SSD1306 *ssd1306, const unsigned char * font, const char * text, uint8_t anchor_x, uint8_t anchor_y, WriteMode mode = WriteMode::ADD, Rotation rotation = Rotation::deg0);
}
#endif //SSD1306_TEXTRENDERER_H

View File

@@ -0,0 +1,60 @@
# Text Renderer Module
## This module provides functions for drawing chars and text on ssd1306 displays
## 1. Importing
```c++
#include "pico-ssd1306/textRenderer/TextRenderer.h"
```
note that core library and hardware_i2c library's need to be imported to use this library so follow steps from section 1
of [readme.md](../readme.md)
## 2. Differences from core library
Calling functions from modules is different from core lib. You can't just do ```object.drawText(...``` since modules don't
actually extend the SSD1306 class. You need to provide module functions with a pointer to a display object. So the first
argument of every module draw function is a pointer to a display.
To see this more clearly here is an example:
```c++
// Create a new display object
pico_ssd1306::SSD1306 display = pico_ssd1306::SSD1306(I2C_PORT, 0x3D, pico_ssd1306::Size::W128xH64);
// Draw some text
// Notice how we first pass the address of display object to the function
drawText(&display, font_12x16, "TEST text", 0 ,0);
```
## 3. Available fonts
This module comes with 4 fonts to choose from
* font_5x8 - 5px wide, 8px high font
* font_8x8 - 8px wide, 8px high font
* font_12x16 - 12px wide, 16px high font
* font_16x32 - 16px wide, 32px high font
Basic fonts support basic ASCII set chars (32-127), if you want to extended ASCII define
```c++
#define SSD1306_ASCII_FULL
```
at the top of your file
#### Note that using extended ASCII range increases project size
### Creating your own fonts
doing so is not that hard
a font is just a large array of bytes
```c++
const unsigned char font_16x32[] = {
...
};
```
the first 2 bytes are font's width and height
```c++
const unsigned char font_16x32[] = {
0x10, 0x20, // font width, height
...
};
```
then followed by char data for ascii characters 32 - 126
Unlike bitmap images in the core library, fonts are scanned right to left, top to bottom
## All functions are documented [here](https://ssd1306.harbys.me)

149
pico-ssd1306/usage.md Normal file
View File

@@ -0,0 +1,149 @@
# Using the core library
## 1. Setting up the i2c ports
In order to communicate with the display you need to set up pico's i2c communication
### Add hardware_i2c library to CMakeLists.txt
```cmake
target_link_libraries(project_name
hardware_i2c)
```
### Add the ssd1306 library to your project
This is explained in [readme.md](readme.md) section 1
### Set up i2c
Pico has 2 i2c controllers. You can use any of these, just remember to pass according i2c instance in code
for this example pins 12 and 13 are used for sda and scl lines. These are connected to i2c0 controller. You can use any
pins capable of i2c communication but if they are connected to i2c1 controller you'll need to change the code when
initializing i2c communication and when creating a display object
#### imports:
you need
```c++
#include "hardware/i2c.h"
#include "pico-ssd1306/ssd1306.h"
```
i2c init and pin init:
```c++
i2c_init(i2c0, 1000000); // Init i2c0 and set baud rate to 1Mhz (max supported speed by pico)
// lower baud rates make the communication slower, and since this lib uses blocking writing,
// lower baud rates lower maximal achievable frame rate
gpio_set_function(12, GPIO_FUNC_I2C); // Set pins 12 and 13 for i2c role
gpio_set_function(13, GPIO_FUNC_I2C);
gpio_pull_up(12); // Pull up the pins for proper communication
gpio_pull_up(13);
```
## 2. Create a display object
When creating a display object you need to provide 3 arguments. Witch i2c controller you want to use (the one you
initialized above), display address (usually 0x3C or 0x3D with ssd1306) and display size. This library defines 2
available sizes Size::W128xH64 and Size::W128xH32
```c++
pico_ssd1306::SSD1306 display = pico_ssd1306::SSD1306(i2c0, 0x3D, pico_ssd1306::Size::W128xH64);
```
## 3. A few words on how this works
From now on you can use the display object to draw anything you want to the display.
Core of ssd1306 library exposes 2 functions for drawing (documentation of all functions is here [doxygen](https://ssd1306.harbys.me)).
```setPixel()``` and ```addBitmapImage()``` but just calling these won't draw anything on screen. These functions only affect the buffer.
To actually display something you need to first draw to buffer (by ```setPixel()``` or ```addBitmapImage()```) and then sending it to the
display with ```sendBuffer()```. Same is true for ```clear()``` function. It just clears the buffer, not the screen. so
calling ```clear()``` and ```sendBuffer()``` will actually clear the display.
## 4. Turning on a pixel
```setPixel()``` is pretty self-explanatory. It modifies the state of exactly 1 pixel. Just give it the x and y coordinates,
optionally change the write mode and done. If you're confused about write mode see [doxygen](https://ssd1306.harbys.me)
section about write mode. tldr write mode decides whether a pixel is to be set on, off or inverted (on to off and vice versa)
this example set pixel at x = 0 and y = 0 (top left corner) on
```c++
//Create a new display object
pico_ssd1306::SSD1306 display = pico_ssd1306::SSD1306(I2C_PORT, 0x3D, pico_ssd1306::Size::W128xH64);
display.setPixel(0, 0);
display.sendBuffer();
```
## 5. Displaying an image
### How to prepare an image for pico
Well you can't just shove a png file into c++ and expect it to work. We need a bitmap containing data on which pixels
to turn on. This library scans bytes in bit map left to right, top to bottom, so :
```c++
#define IMAGE_WIDTH 32
#define IMAGE_HEIGHT 32
uint8_t image[128] = {
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00111111, 0b11111111, 0b11111111, 0b11111100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100,
0b00100000, 0b00000001, 0b00000000, 0b00000100
};
```
contains such an image:
![example2](images/ex2.png)
It's best to keep width to a number divisible by 8. Otherwise, errors occur while shifting bytes and rendering fails.
Height does not have such a limitation.
### Bitmap to display
```addBitmapImage()``` function does pretty much all the work for us. we need to tell it what and where to draw and done.
So what do we need? x and y coordinated on where to anchor an image (anchor is top left point of the image), image and width, height.
```c++
display.addBitmapImage(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, image);
display.sendBuffer();
```
Should draw this image to display at point 0, 0
Entire display now should look like this:
![example2](images/ex3.png)
Also note that ```addBitmapImage()``` in default write mode doesn't turn pixels off, so overlaying images is possible, so:
```c++
display.addBitmapImage(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, image);
display.addBitmapImage(8, 0, IMAGE_WIDTH, IMAGE_HEIGHT, image);
display.sendBuffer();
```
will overlay the same image with 8 pixel to right image, producing:
![example2](images/ex4.png)
This function can take an optional argument of write mode, this is same as in ```setPixel()```
## 6. Additional Settings
### Things like setting contrast, flipping the display etc. are all documented [here](https://ssd1306.harbys.me)