Files
TweinStein/RTOS_MotorBoard/SSD1306.c

1634 lines
61 KiB
C
Raw Normal View History

2026-06-12 02:55:04 -07:00
// SSD1306.c
// Runs on MSPM0
// Use I2C to send an 8-bit data/commands to the SSD1306 128 by 64 pixel oLED
// pixel LCD to display text, images, or other information.
// Jonathan and Daniel Valvano
// June 10, 2024
// Derived from: Adafruit_SSD1306.cpp
// Font table, initialization, and other functions based
// off of Nokia_5110_Example from Spark Fun:
// 7-17-2011
// Spark Fun Electronics 2011
// Nathan Seidle
// http://dlnmh9ip6v2uc.cloudfront.net/datasheets/LCD/Monochrome/Nokia_5110_Example.pde
/* This example accompanies the book
"Embedded Systems: Introduction to Robotics,
Jonathan W. Valvano, ISBN: 9781074544300, copyright (c) 2020
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
Simplified BSD License (FreeBSD License)
Copyright (c) 2021, Jonathan Valvano, 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.
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 OWNER 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.
The views and conclusions contained in the software and documentation are
those of the authors and should not be interpreted as representing official
policies, either expressed or implied, of the FreeBSD Project.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "file.h"
#include <ti/devices/msp/msp.h>
#include "../inc/LaunchPad.h"
#include "../inc/Clock.h"
#include "../inc/I2C.h"
#include "../inc/SSD1306.h"
// search for I2C SSD1306 on Amazon.com
// https://www.amazon.com/s?k=SSD1306
// VCC 3.3V power to OLED
// GND ground
// SCL PB2 I2C clock
// SDA PB3 I2C data
//
// rough estimate of execution times (runs a little faster with 1.5k pullup from SDA to 3.3V, and no glitch)
// with 1.5k
// SSD1306_OutClear 25.7ms lears entire display
// SSD1306_OutBuffer 24.7ms sends 1024 bytes, 128 by 64 pixels to OLED
// SSD1306_DrawBMP 261us (10 by 16 size), doesn't output, just fills buffer
// SSD1306_OutChar 206us does output to OLED
// SSD1306_OutUDec 1042us outputs 5 characters to OLED
// SSD1306_ClearBuffer 932us empties buffer, not OLED output
#define SSD1306ADDR 0x3C
// Some displays have a typo on OLED has a 0-ohm resistor selection
// that says "IIC ADDRESS SELECT 0x78 0x7A"
// should have said "IIC ADDRESS SELECT 0x3C 0x3D"
// *************************** Screen dimensions ***************************
#define WIDTH 128
#define HEIGHT 64
#define CR 0x0D
uint8_t buffer[WIDTH*HEIGHT/8]; // 8192 bits or 1024 bytes
int vccstate;
// vccstate = SSD1306_SWITCHCAPVCC for normal internal 3.3V power
// SSD1306_EXTERNALVCC for separate external power
int rotation;
// private function prototypes
void ssd1306drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color);
void ssd1306drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color);
#define SSD1306_MEMORYMODE 0x20 ///< See datasheet
#define SSD1306_COLUMNADDR 0x21 ///< See datasheet
#define SSD1306_PAGEADDR 0x22 ///< See datasheet
#define SSD1306_SETCONTRAST 0x81 ///< See datasheet
#define SSD1306_CHARGEPUMP 0x8D ///< See datasheet
#define SSD1306_SEGREMAP 0xA0 ///< See datasheet
#define SSD1306_DISPLAYALLON_RESUME 0xA4 ///< See datasheet
#define SSD1306_DISPLAYALLON 0xA5 ///< Not currently used
#define SSD1306_NORMALDISPLAY 0xA6 ///< See datasheet
#define SSD1306_INVERTDISPLAY 0xA7 ///< See datasheet
#define SSD1306_SETMULTIPLEX 0xA8 ///< See datasheet
#define SSD1306_DISPLAYOFF 0xAE ///< See datasheet
#define SSD1306_DISPLAYON 0xAF ///< See datasheet
#define SSD1306_COMSCANINC 0xC0 ///< Not currently used
#define SSD1306_COMSCANDEC 0xC8 ///< See datasheet
#define SSD1306_SETDISPLAYOFFSET 0xD3 ///< See datasheet
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 ///< See datasheet
#define SSD1306_SETPRECHARGE 0xD9 ///< See datasheet
#define SSD1306_SETCOMPINS 0xDA ///< See datasheet
#define SSD1306_SETVCOMDETECT 0xDB ///< See datasheet
#define SSD1306_SETLOWCOLUMN 0x00 ///< Not currently used
#define SSD1306_SETHIGHCOLUMN 0x10 ///< Not currently used
#define SSD1306_SETSTARTLINE 0x40 ///< See datasheet
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 ///< Init rt scroll
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 ///< Init left scroll
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 ///< Init diag scroll
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A ///< Init diag scroll
#define SSD1306_DEACTIVATE_SCROLL 0x2E ///< Stop scroll
#define SSD1306_ACTIVATE_SCROLL 0x2F ///< Start scroll
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 ///< Set scroll range
// This table contains the hex values that represent pixels
// for a font that is 6 pixels wide and 8 pixels high (last column 0 for separation)
static const uint8_t ASCII[][6] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // 20
,{0x00, 0x00, 0x5f, 0x00, 0x00, 0x00} // 21 !
,{0x00, 0x07, 0x00, 0x07, 0x00, 0x00} // 22 "
,{0x14, 0x7f, 0x14, 0x7f, 0x14, 0x00} // 23 #
,{0x24, 0x2a, 0x7f, 0x2a, 0x12, 0x00} // 24 $
,{0x23, 0x13, 0x08, 0x64, 0x62, 0x00} // 25 %
,{0x36, 0x49, 0x55, 0x22, 0x50, 0x00} // 26 &
,{0x00, 0x05, 0x03, 0x00, 0x00, 0x00} // 27 '
,{0x00, 0x1c, 0x22, 0x41, 0x00, 0x00} // 28 (
,{0x00, 0x41, 0x22, 0x1c, 0x00, 0x00} // 29 )
,{0x14, 0x08, 0x3e, 0x08, 0x14, 0x00} // 2a *
,{0x08, 0x08, 0x3e, 0x08, 0x08, 0x00} // 2b +
,{0x00, 0x50, 0x30, 0x00, 0x00, 0x00} // 2c ,
,{0x08, 0x08, 0x08, 0x08, 0x08, 0x00} // 2d -
,{0x00, 0x60, 0x60, 0x00, 0x00, 0x00} // 2e .
,{0x20, 0x10, 0x08, 0x04, 0x02, 0x00} // 2f /
,{0x3e, 0x51, 0x49, 0x45, 0x3e, 0x00} // 30 0
,{0x00, 0x42, 0x7f, 0x40, 0x00, 0x00} // 31 1
,{0x42, 0x61, 0x51, 0x49, 0x46, 0x00} // 32 2
,{0x21, 0x41, 0x45, 0x4b, 0x31, 0x00} // 33 3
,{0x18, 0x14, 0x12, 0x7f, 0x10, 0x00} // 34 4
,{0x27, 0x45, 0x45, 0x45, 0x39, 0x00} // 35 5
,{0x3c, 0x4a, 0x49, 0x49, 0x30, 0x00} // 36 6
,{0x01, 0x71, 0x09, 0x05, 0x03, 0x00} // 37 7
,{0x36, 0x49, 0x49, 0x49, 0x36, 0x00} // 38 8
,{0x06, 0x49, 0x49, 0x29, 0x1e, 0x00} // 39 9
,{0x00, 0x36, 0x36, 0x00, 0x00, 0x00} // 3a :
,{0x00, 0x56, 0x36, 0x00, 0x00, 0x00} // 3b ;
,{0x08, 0x14, 0x22, 0x41, 0x00, 0x00} // 3c <
,{0x14, 0x14, 0x14, 0x14, 0x14, 0x00} // 3d =
,{0x00, 0x41, 0x22, 0x14, 0x08, 0x00} // 3e >
,{0x02, 0x01, 0x51, 0x09, 0x06, 0x00} // 3f ?
,{0x32, 0x49, 0x79, 0x41, 0x3e, 0x00} // 40 @
,{0x7e, 0x11, 0x11, 0x11, 0x7e, 0x00} // 41 A
,{0x7f, 0x49, 0x49, 0x49, 0x36, 0x00} // 42 B
,{0x3e, 0x41, 0x41, 0x41, 0x22, 0x00} // 43 C
,{0x7f, 0x41, 0x41, 0x22, 0x1c, 0x00} // 44 D
,{0x7f, 0x49, 0x49, 0x49, 0x41, 0x00} // 45 E
,{0x7f, 0x09, 0x09, 0x09, 0x01, 0x00} // 46 F
,{0x3e, 0x41, 0x49, 0x49, 0x7a, 0x00} // 47 G
,{0x7f, 0x08, 0x08, 0x08, 0x7f, 0x00} // 48 H
,{0x00, 0x41, 0x7f, 0x41, 0x00, 0x00} // 49 I
,{0x20, 0x40, 0x41, 0x3f, 0x01, 0x00} // 4a J
,{0x7f, 0x08, 0x14, 0x22, 0x41, 0x00} // 4b K
,{0x7f, 0x40, 0x40, 0x40, 0x40, 0x00} // 4c L
,{0x7f, 0x02, 0x0c, 0x02, 0x7f, 0x00} // 4d M
,{0x7f, 0x04, 0x08, 0x10, 0x7f, 0x00} // 4e N
,{0x3e, 0x41, 0x41, 0x41, 0x3e, 0x00} // 4f O
,{0x7f, 0x09, 0x09, 0x09, 0x06, 0x00} // 50 P
,{0x3e, 0x41, 0x51, 0x21, 0x5e, 0x00} // 51 Q
,{0x7f, 0x09, 0x19, 0x29, 0x46, 0x00} // 52 R
,{0x46, 0x49, 0x49, 0x49, 0x31, 0x00} // 53 S
,{0x01, 0x01, 0x7f, 0x01, 0x01, 0x00} // 54 T
,{0x3f, 0x40, 0x40, 0x40, 0x3f, 0x00} // 55 U
,{0x1f, 0x20, 0x40, 0x20, 0x1f, 0x00} // 56 V
,{0x3f, 0x40, 0x38, 0x40, 0x3f, 0x00} // 57 W
,{0x63, 0x14, 0x08, 0x14, 0x63, 0x00} // 58 X
,{0x07, 0x08, 0x70, 0x08, 0x07, 0x00} // 59 Y
,{0x61, 0x51, 0x49, 0x45, 0x43, 0x00} // 5a Z
,{0x00, 0x7f, 0x41, 0x41, 0x00, 0x00} // 5b [
,{0x02, 0x04, 0x08, 0x10, 0x20, 0x00} // 5c '\'
,{0x00, 0x41, 0x41, 0x7f, 0x00, 0x00} // 5d ]
,{0x04, 0x02, 0x01, 0x02, 0x04, 0x00} // 5e ^
,{0x40, 0x40, 0x40, 0x40, 0x40, 0x00} // 5f _
,{0x00, 0x01, 0x02, 0x04, 0x00, 0x00} // 60 `
,{0x20, 0x54, 0x54, 0x54, 0x78, 0x00} // 61 a
,{0x7f, 0x48, 0x44, 0x44, 0x38, 0x00} // 62 b
,{0x38, 0x44, 0x44, 0x44, 0x20, 0x00} // 63 c
,{0x38, 0x44, 0x44, 0x48, 0x7f, 0x00} // 64 d
,{0x38, 0x54, 0x54, 0x54, 0x18, 0x00} // 65 e
,{0x08, 0x7e, 0x09, 0x01, 0x02, 0x00} // 66 f
,{0x0c, 0x52, 0x52, 0x52, 0x3e, 0x00} // 67 g
,{0x7f, 0x08, 0x04, 0x04, 0x78, 0x00} // 68 h
,{0x00, 0x44, 0x7d, 0x40, 0x00, 0x00} // 69 i
,{0x20, 0x40, 0x44, 0x3d, 0x00, 0x00} // 6a j
,{0x7f, 0x10, 0x28, 0x44, 0x00, 0x00} // 6b k
,{0x00, 0x41, 0x7f, 0x40, 0x00, 0x00} // 6c l
,{0x7c, 0x04, 0x18, 0x04, 0x78, 0x00} // 6d m
,{0x7c, 0x08, 0x04, 0x04, 0x78, 0x00} // 6e n
,{0x38, 0x44, 0x44, 0x44, 0x38, 0x00} // 6f o
,{0x7c, 0x14, 0x14, 0x14, 0x08, 0x00} // 70 p
,{0x08, 0x14, 0x14, 0x18, 0x7c, 0x00} // 71 q
,{0x7c, 0x08, 0x04, 0x04, 0x08, 0x00} // 72 r
,{0x48, 0x54, 0x54, 0x54, 0x20, 0x00} // 73 s
,{0x04, 0x3f, 0x44, 0x40, 0x20, 0x00} // 74 t
,{0x3c, 0x40, 0x40, 0x20, 0x7c, 0x00} // 75 u
,{0x1c, 0x20, 0x40, 0x20, 0x1c, 0x00} // 76 v
,{0x3c, 0x40, 0x30, 0x40, 0x3c, 0x00} // 77 w
,{0x44, 0x28, 0x10, 0x28, 0x44, 0x00} // 78 x
,{0x0c, 0x50, 0x50, 0x50, 0x3c, 0x00} // 79 y
,{0x44, 0x64, 0x54, 0x4c, 0x44, 0x00} // 7a z
,{0x00, 0x08, 0x36, 0x41, 0x00, 0x00} // 7b {
,{0x00, 0x00, 0x7f, 0x00, 0x00, 0x00} // 7c |
,{0x00, 0x41, 0x36, 0x08, 0x00, 0x00} // 7d }
,{0x10, 0x08, 0x08, 0x10, 0x08, 0x00}, // 7e ~
// ,{0x78, 0x46, 0x41, 0x46, 0x78, 0x00} // 7f DEL
{0x1f, 0x24, 0x7c, 0x24, 0x1f, 0x00}, // 7f UT sign
{0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x00}, // char = 128
{0x3A, 0x40, 0x40, 0x20, 0x7A, 0x00}, // char = 129
{0x38, 0x54, 0x54, 0x55, 0x59, 0x00}, // char = 130
{0x21, 0x55, 0x55, 0x79, 0x41, 0x00}, // char = 131
{0x21, 0x54, 0x54, 0x78, 0x41, 0x00}, // char = 132
{0x21, 0x55, 0x54, 0x78, 0x40, 0x00}, // char = 133
{0x20, 0x54, 0x55, 0x79, 0x40, 0x00}, // char = 134
{0x0C, 0x1E, 0x52, 0x72, 0x12, 0x00}, // char = 135
{0x39, 0x55, 0x55, 0x55, 0x59, 0x00}, // char = 136
{0x39, 0x54, 0x54, 0x54, 0x59, 0x00}, // char = 137
{0x39, 0x55, 0x54, 0x54, 0x58, 0x00}, // char = 138
{0x00, 0x00, 0x45, 0x7C, 0x41, 0x00}, // char = 139
{0x00, 0x02, 0x45, 0x7D, 0x42, 0x00}, // char = 140
{0x00, 0x01, 0x45, 0x7C, 0x40, 0x00}, // char = 141
{0xF0, 0x29, 0x24, 0x29, 0xF0, 0x00}, // char = 142
{0xF0, 0x28, 0x25, 0x28, 0xF0, 0x00}, // char = 143
{0x7C, 0x54, 0x55, 0x45, 0x00, 0x00}, // char = 144
{0x20, 0x54, 0x54, 0x7C, 0x54, 0x00}, // char = 145
{0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x00}, // char = 146
{0x32, 0x49, 0x49, 0x49, 0x32, 0x00}, // char = 147
{0x32, 0x48, 0x48, 0x48, 0x32, 0x00}, // char = 148
{0x32, 0x4A, 0x48, 0x48, 0x30, 0x00}, // char = 149
{0x3A, 0x41, 0x41, 0x21, 0x7A, 0x00}, // char = 150
{0x3A, 0x42, 0x40, 0x20, 0x78, 0x00}, // char = 151
{0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x00}, // char = 152
{0x39, 0x44, 0x44, 0x44, 0x39, 0x00}, // char = 153
{0x3D, 0x40, 0x40, 0x40, 0x3D, 0x00}, // char = 154
{0x3C, 0x24, 0xFF, 0x24, 0x24, 0x00}, // char = 155
{0x48, 0x7E, 0x49, 0x43, 0x66, 0x00}, // char = 156
{0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0x00}, // char = 157
{0xFF, 0x09, 0x29, 0xF6, 0x20, 0x00}, // char = 158
{0xC0, 0x88, 0x7E, 0x09, 0x03, 0x00}, // char = 159
{0x20, 0x54, 0x54, 0x79, 0x41, 0x00}, // char = 160
{0x00, 0x00, 0x44, 0x7D, 0x41, 0x00}, // char = 161
{0x30, 0x48, 0x48, 0x4A, 0x32, 0x00}, // char = 162
{0x38, 0x40, 0x40, 0x22, 0x7A, 0x00}, // char = 163
{0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x00}, // char = 164
{0x7D, 0x0D, 0x19, 0x31, 0x7D, 0x00}, // char = 165
{0x26, 0x29, 0x29, 0x2F, 0x28, 0x00}, // char = 166
{0x26, 0x29, 0x29, 0x29, 0x26, 0x00}, // char = 167
{0x30, 0x48, 0x4D, 0x40, 0x20, 0x00}, // char = 168
{0x38, 0x08, 0x08, 0x08, 0x08, 0x00}, // char = 169
{0x08, 0x08, 0x08, 0x08, 0x38, 0x00}, // char = 170
{0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x00}, // char = 171
{0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00}, // char = 172
{0x00, 0x00, 0x7B, 0x00, 0x00, 0x00}, // char = 173
{0x08, 0x14, 0x2A, 0x14, 0x22, 0x00}, // char = 174
{0x22, 0x14, 0x2A, 0x14, 0x08, 0x00}, // char = 175
{0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00}, // char = 176
{0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x00}, // char = 177
{0x00, 0x00, 0x00, 0xFF, 0x00, 0x00}, // char = 178
{0x10, 0x10, 0x10, 0xFF, 0x00, 0x00}, // char = 179
{0x14, 0x14, 0x14, 0xFF, 0x00, 0x00}, // char = 180
{0x10, 0x10, 0xFF, 0x00, 0xFF, 0x00}, // char = 181
{0x10, 0x10, 0xF0, 0x10, 0xF0, 0x00}, // char = 182
{0x14, 0x14, 0x14, 0xFC, 0x00, 0x00}, // char = 183
{0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00}, // char = 184
{0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00}, // char = 185
{0x14, 0x14, 0xF4, 0x04, 0xFC, 0x00}, // char = 186
{0x14, 0x14, 0x17, 0x10, 0x1F, 0x00}, // char = 187
{0x10, 0x10, 0x1F, 0x10, 0x1F, 0x00}, // char = 188
{0x14, 0x14, 0x14, 0x1F, 0x00, 0x00}, // char = 189
{0x10, 0x10, 0x10, 0xF0, 0x00, 0x00}, // char = 190
{0x00, 0x00, 0x00, 0x1F, 0x10, 0x00}, // char = 191
{0x10, 0x10, 0x10, 0x1F, 0x10, 0x00}, // char = 192
{0x10, 0x10, 0x10, 0xF0, 0x10, 0x00}, // char = 193
{0x00, 0x00, 0x00, 0xFF, 0x10, 0x00}, // char = 194
{0x10, 0x10, 0x10, 0x10, 0x10, 0x00}, // char = 195
{0x10, 0x10, 0x10, 0xFF, 0x10, 0x00}, // char = 196
{0x00, 0x00, 0x00, 0xFF, 0x14, 0x00}, // char = 197
{0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00}, // char = 198
{0x00, 0x00, 0x1F, 0x10, 0x17, 0x00}, // char = 199
{0x00, 0x00, 0xFC, 0x04, 0xF4, 0x00}, // char = 200
{0x14, 0x14, 0x17, 0x10, 0x17, 0x00}, // char = 201
{0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00}, // char = 202
{0x00, 0x00, 0xFF, 0x00, 0xF7, 0x00}, // char = 203
{0x14, 0x14, 0x14, 0x14, 0x14, 0x00}, // char = 204
{0x14, 0x14, 0xF7, 0x00, 0xF7, 0x00}, // char = 205
{0x14, 0x14, 0x14, 0x17, 0x14, 0x00}, // char = 206
{0x10, 0x10, 0x1F, 0x10, 0x1F, 0x00}, // char = 207
{0x14, 0x14, 0x14, 0xF4, 0x14, 0x00}, // char = 208
{0x10, 0x10, 0xF0, 0x10, 0xF0, 0x00}, // char = 209
{0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00}, // char = 210
{0x00, 0x00, 0x00, 0x1F, 0x14, 0x00}, // char = 211
{0x00, 0x00, 0x00, 0xFC, 0x14, 0x00}, // char = 212
{0x00, 0x00, 0xF0, 0x10, 0xF0, 0x00}, // char = 213
{0x10, 0x10, 0xFF, 0x10, 0xFF, 0x00}, // char = 214
{0x14, 0x14, 0x14, 0xFF, 0x14, 0x00}, // char = 215
{0x10, 0x10, 0x10, 0x1F, 0x00, 0x00}, // char = 216
{0x00, 0x00, 0x00, 0xF0, 0x10, 0x00}, // char = 217
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}, // char = 218
{0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00}, // char = 219
{0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}, // char = 220
{0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00}, // char = 221
{0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00}, // char = 222
{0x38, 0x44, 0x44, 0x38, 0x44, 0x00}, // char = 223
{0x7C, 0x2A, 0x2A, 0x3E, 0x14, 0x00}, // char = 224
{0x7E, 0x02, 0x02, 0x06, 0x06, 0x00}, // char = 225
{0x02, 0x7E, 0x02, 0x7E, 0x02, 0x00}, // char = 226
{0x63, 0x55, 0x49, 0x41, 0x63, 0x00}, // char = 227
{0x38, 0x44, 0x44, 0x3C, 0x04, 0x00}, // char = 228
{0x40, 0x7E, 0x20, 0x1E, 0x20, 0x00}, // char = 229
{0x06, 0x02, 0x7E, 0x02, 0x02, 0x00}, // char = 230
{0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x00}, // char = 231
{0x1C, 0x2A, 0x49, 0x2A, 0x1C, 0x00}, // char = 232
{0x4C, 0x72, 0x01, 0x72, 0x4C, 0x00}, // char = 233
{0x30, 0x4A, 0x4D, 0x4D, 0x30, 0x00}, // char = 234
{0x30, 0x48, 0x78, 0x48, 0x30, 0x00}, // char = 235
{0xBC, 0x62, 0x5A, 0x46, 0x3D, 0x00}, // char = 236
{0x3E, 0x49, 0x49, 0x49, 0x00, 0x00}, // char = 237
{0x7E, 0x01, 0x01, 0x01, 0x7E, 0x00}, // char = 238
{0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x00}, // char = 239
{0x44, 0x44, 0x5F, 0x44, 0x44, 0x00}, // char = 240
{0x40, 0x51, 0x4A, 0x44, 0x40, 0x00}, // char = 241
{0x40, 0x44, 0x4A, 0x51, 0x40, 0x00}, // char = 242
{0x00, 0x00, 0xFF, 0x01, 0x03, 0x00}, // char = 243
{0xE0, 0x80, 0xFF, 0x00, 0x00, 0x00}, // char = 244
{0x08, 0x08, 0x6B, 0x6B, 0x08, 0x00}, // char = 245
{0x36, 0x12, 0x36, 0x24, 0x36, 0x00}, // char = 246
{0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00}, // char = 247
{0x00, 0x00, 0x18, 0x18, 0x00, 0x00}, // char = 248
{0x00, 0x00, 0x10, 0x10, 0x00, 0x00}, // char = 249
{0x30, 0x40, 0xFF, 0x01, 0x01, 0x00}, // char = 250
{0x00, 0x1F, 0x01, 0x01, 0x1E, 0x00}, // char = 251
{0x00, 0x19, 0x1D, 0x17, 0x12, 0x00}, // char = 252
{0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00}, // char = 253
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // char = 254
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // char = 255
};
#define ssd1306_swap(a, b) \
(((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation
void ssd1306command(uint8_t c) {
I2C_Send2(SSD1306ADDR,0,c);
}
void ssd1306command1(uint8_t c) {
I2C_Send2(SSD1306ADDR,0,c);
}
void ssd1306commandList(const uint8_t *c, uint32_t n) {
// added 0 in front of each list
I2C_Send(SSD1306ADDR,(uint8_t *)c,n);
}
// ALLOCATE & INIT DISPLAY -------------------------------------------------
/*!
@brief Allocate RAM for image buffer, initialize peripherals and pins.
@param vcs
VCC selection. Pass SSD1306_SWITCHCAPVCC to generate the display
voltage (step up) from the 3.3V source, or SSD1306_EXTERNALVCC
otherwise. Most situations with Adafruit SSD1306 breakouts will
want SSD1306_SWITCHCAPVCC.
@param addr
I2C address of corresponding SSD1306 display (or pass 0 to use
default of 0x3C for 128x32 display, 0x3D for all others).
SPI displays (hardware or software) do not use addresses, but
this argument is still required (pass 0 or any value really,
it will simply be ignored). Default if unspecified is 0.
@param reset
If true, and if the reset pin passed to the constructor is
valid, a hard reset will be performed before initializing the
display. If using multiple SSD1306 displays on the same bus, and
if they all share the same reset pin, you should only pass true
on the first display being initialized, false on all others,
else the already-initialized displays would be reset. Default if
unspecified is true.
@param periphBegin
If true, and if a hardware peripheral is being used (I2C or SPI,
but not software SPI), call that peripheral's begin() function,
else (false) it has already been done in one's sketch code.
Cases where false might be used include multiple displays or
other devices sharing a common bus, or situations on some
platforms where a nonstandard begin() function is available
(e.g. a TwoWire interface on non-default pins, as can be done
on the ESP8266 and perhaps others).
@return true on successful allocation/init, false otherwise.
Well-behaved code should check the return value before
proceeding.
@note MUST call this function before any drawing or updates!
*/
// Init sequence, added 0 for command
static const uint8_t init1[] = {
0, // D/C = 0 for command
SSD1306_DISPLAYOFF, // 0xAE
SSD1306_SETDISPLAYCLOCKDIV, // 0xD5
0x80, // the suggested ratio 0x80
SSD1306_SETMULTIPLEX }; // 0xA8
static const uint8_t init2[] = {
0, // D/C = 0 for command
SSD1306_SETDISPLAYOFFSET, // 0xD3
0x0, // no offset
SSD1306_SETSTARTLINE | 0x0, // line #0
SSD1306_CHARGEPUMP }; // 0x8D
static const uint8_t init3[] = {
0, // D/C = 0 for command
SSD1306_MEMORYMODE, // 0x20
0x00, // 0x0 act like Nokia 5110
SSD1306_SEGREMAP | 0x1,
SSD1306_COMSCANDEC };
static const uint8_t init4b[] = {
0, // D/C = 0 for command
SSD1306_SETCOMPINS, // 0xDA
0x12,
SSD1306_SETCONTRAST }; // 0x81
static const uint8_t init5[] = {
0, // D/C = 0 for command
SSD1306_SETVCOMDETECT, // 0xDB
0x40,
SSD1306_DISPLAYALLON_RESUME, // 0xA4
SSD1306_NORMALDISPLAY, // 0xA6
SSD1306_DEACTIVATE_SCROLL,
SSD1306_DISPLAYON }; // Main screen turn on
// vccstate = SSD1306_SWITCHCAPVCC for normal internal 3.3V power
// SSD1306_EXTERNALVCC for separate external power
int SSD1306_Init(int vccst) {
// volatile uint32_t delay;
Clock_Delay1ms(300);
I2C_Init(); // 400kHz
vccstate = vccst;
// RESET = 0; // reset the LCD to a known state, RESET low
// for(delay=0; delay<10; delay=delay+1);// delay minimum 100 ns
// RESET = 1; // hold RESET high
SSD1306_ClearBuffer();
ssd1306commandList(init1, sizeof(init1));
ssd1306command1(HEIGHT - 1);
ssd1306commandList(init2, sizeof(init2));
ssd1306command1((vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0x14);
ssd1306commandList(init3, sizeof(init3));
ssd1306commandList(init4b, sizeof(init4b));
ssd1306command1((vccstate == SSD1306_EXTERNALVCC) ? 0x9F : 0xCF);
ssd1306command1(SSD1306_SETPRECHARGE); // 0xd9
ssd1306command1((vccstate == SSD1306_EXTERNALVCC) ? 0x22 : 0xF1);
ssd1306commandList(init5, sizeof(init5));
SSD1306_OutBuffer();
SSD1306_SetCursor(0, 0);
return true; // Success
}
// DRAWING FUNCTIONS -------------------------------------------------------
/*!
@brief Set/clear/invert a single pixel. This is also invoked by the
Adafruit_GFX library in generating many higher-level graphics
primitives.
@param x
Column of display -- 0 at left to (screen width - 1) at right.
@param y
Row of display -- 0 at top to (screen height - 1) at bottom.
@param color
Pixel color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERSE.
@return None (void).
@note Changes buffer contents only, no immediate effect on display.
Follow up with a call to SSD1306_OutBuffer(), or with other
graphics commands as needed by one's own application.
*/
void SSD1306_DrawPixel(int16_t x, int16_t y, uint16_t color) {
if((x >= 0) && (x < WIDTH) && (y >= 0) && (y < HEIGHT)) {
// Pixel is in-bounds. Rotate coordinates if needed.
switch(rotation) {
case 1:
ssd1306_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
ssd1306_swap(x, y);
y = HEIGHT - y - 1;
break;
}
switch(color) {
case SSD1306_WHITE: buffer[x + (y/8)*WIDTH] |= (1 << (y&7)); break;
case SSD1306_BLACK: buffer[x + (y/8)*WIDTH] &= ~(1 << (y&7)); break;
case SSD1306_INVERSE: buffer[x + (y/8)*WIDTH] ^= (1 << (y&7)); break;
}
}
}
static int32_t DeltaX,MaxX,MinX; // MaxX must be greater than MinX
static int32_t DeltaY,MaxY,MinY; // MaxY must be greater than MinY
static uint16_t PlotColor;
void SSD1306_SetPlot(int32_t minX, int32_t maxX, int32_t minY, int32_t maxY, uint16_t color){
if(minX >= maxX) return;// MaxX must be greater than MinX
if(minY >= maxY) return;// MaxY must be greater than MinY
if(color > SSD1306_INVERSE) return;
MaxX = maxX;
MinX = minX;
DeltaX = MaxX-MinX;
MaxY = maxY;
MinY = minY;
DeltaY = MaxY-MinY;
PlotColor = color;
}
void SSD1306_DrawPoint(int32_t x, int32_t y){
if((x<MinX)||(x>MaxX)||(y<MinY)||(y>MaxY)) return; // out of bounds
SSD1306_DrawPixel(((x-MinX)*(WIDTH-1))/DeltaX,
((MaxY-y)*(HEIGHT-1))/DeltaY,PlotColor);
//MaxX at right
//MaxY at top
}
// color is white or black, not invert
void SSD1306_DrawChar(int16_t x, int16_t y, char letter, uint16_t color){int mask;
uint16_t dot;
uint32_t k = (uint32_t)(letter & 0xFF); // convert to unsigned regardless of whether char is signed or unsigned
if(k < 0x20) return;
for(int i=0;i<5;i++){
mask = 0x01;
for(int j=0; j<7; j++){
if(ASCII[k-0x20][i]&mask){
dot = color;
}else{
dot = color^1;
}
SSD1306_DrawPixel(x+i,y+j,dot);
mask=mask<<1;
}
}
}
// color is white or black, not invert
void SSD1306_DrawString(int16_t x, int16_t y, char *pt, uint16_t color){
while(*pt){
SSD1306_DrawChar(x,y,*pt,color);
x = x+6; // skip a line
pt++;
}
}
/*!
@brief Clear contents of display buffer (set all pixels to off).
@return None (void).
@note Changes buffer contents only, no immediate effect on display.
Follow up with a call to SSD1306_OutBuffer(), or with other
graphics commands as needed by one's own application.
*/
void SSD1306_ClearBuffer(void) {int i;
for(i=0; i<WIDTH*HEIGHT/8; i++){
buffer[i] = 0;
}
SSD1306_SetCursor(0, 0);
}
/*!
@brief Clear the LCD by writing zeros to the entire screen and
reset the cursor to (0,0) (top left corner of screen).
@return None (void).
*/
void SSD1306_OutClear(void) {
SSD1306_ClearBuffer();
SSD1306_OutBuffer();
}
/*!
@brief Draw a raw bitmap to the RAM buffer.
@param xpos Horizontal position of bottom left corner of image, columns from the left edge.<br>
must be less than 128<br>
0 is on the left; 126 is near the right
@param ypos Vertical position of bottom left corner of image, rows from the top edge.<br>
must be less than 64<br>
2 is near the top; 63 is at the bottom
@param ptr Pointer to a 16-color BMP image.
@param threshold Grayscale colors above this number make corresponding pixel 'on'.<br>
0 to 14<br>
0 is fine for ships, explosions, projectiles, and bunkers
@param color
Pixel color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERSE.
@return None (void).
@note Bitmaps defined above were created for the LM3S1968 or
LM3S8962's 4-bit grayscale OLED display. More recently
they are created using Microsoft Paint (or your
favorite drawing program), saved as a 16-color bitmap,
and converted from binary to text using BmpConvert.exe.
Then copy and paste this text into the program, which
will create a constant containing the image that will
be loaded into ROM at compile time. These images will
still contain their header data and may contain padding
to preserve 4-byte alignment. This function takes a
bitmap in the previously described format and puts its
image data in the proper location in the buffer so the
image will appear on the screen after the next call to<br>
SSD1306_OutBuffer();<br>
The interface and operation of this process is modeled
after RIT128x96x4_BMP(x, y, image);
*/
void SSD1306_DrawBMP(uint8_t xpos, uint8_t ypos, const uint8_t *ptr, uint8_t threshold, uint16_t color){
int32_t width = ptr[18], height = ptr[22], i, j;
uint16_t screenx, screeny;
uint8_t mask;
// check for clipping
if((height <= 0) || // bitmap is unexpectedly encoded in top-to-bottom pixel order
((width%2) != 0) || // must be even number of columns
((xpos + width) > WIDTH) || // right side cut off
(ypos < (height - 1)) || // top cut off
(ypos > HEIGHT)){ // bottom cut off
return;
}
if(threshold > 14){
threshold = 14; // only full 'on' turns pixel on
}
// bitmaps are encoded backwards, so start at the bottom left corner of the image
screeny = ypos/8;
screenx = xpos + WIDTH*screeny;
mask = ypos%8; // row 0 to 7
mask = 0x01<<mask; // now stores a mask 0x01 to 0x80
j = ptr[10]; // byte 10 contains the offset where image data can be found
for(i=1; i<=(width*height/2); i=i+1){
// the left pixel is in the upper 4 bits
if(((ptr[j]>>4)&0xF) > threshold){
switch(color) {
case SSD1306_WHITE: buffer[screenx] |= mask; break;
case SSD1306_BLACK: buffer[screenx] &= ~mask; break;
case SSD1306_INVERSE: buffer[screenx] ^= mask; break;
}
} else{
switch(color) {
case SSD1306_WHITE: buffer[screenx] &= ~mask; break;
case SSD1306_BLACK: buffer[screenx] |= mask; break;
case SSD1306_INVERSE: break;
}
}
screenx = screenx + 1;
// the right pixel is in the lower 4 bits
if((ptr[j]&0xF) > threshold){
buffer[screenx] |= mask;
} else{
buffer[screenx] &= ~mask;
}
screenx = screenx + 1;
j = j + 1;
if((i%(width/2)) == 0){ // at the end of a row
if(mask > 0x01){
mask = mask>>1;
} else{
mask = 0x80;
screeny = screeny - 1;
}
screenx = xpos + WIDTH*screeny;
// bitmaps are 32-bit word aligned
switch((width/2)%4){ // skip any padding
case 0: j = j + 0; break;
case 1: j = j + 3; break;
case 2: j = j + 2; break;
case 3: j = j + 1; break;
}
}
}
}
/*!
@brief Draw a horizontal line. This is also invoked by the Adafruit_GFX
library in generating many higher-level graphics primitives.
@param x
Leftmost column -- 0 at left to (screen width - 1) at right.
@param y
Row of display -- 0 at top to (screen height -1) at bottom.
@param w
Width of line, in pixels.
@param color
Line color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERT.
@return None (void).
@note Changes buffer contents only, no immediate effect on display.
Follow up with a call to SSD1306_OutBuffer(), or with other
graphics commands as needed by one's own application.
*/
void SSD1306_DrawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
int bSwap = false;
switch(rotation) {
case 1:
// 90 degree rotation, swap x & y for rotation, then invert x
bSwap = true;
ssd1306_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
// 180 degree rotation, invert x and y, then shift y around for height.
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
x -= (w-1);
break;
case 3:
// 270 degree rotation, swap x & y for rotation,
// then invert y and adjust y for w (not to become h)
bSwap = true;
ssd1306_swap(x, y);
y = HEIGHT - y - 1;
y -= (w-1);
break;
}
if(bSwap) ssd1306drawFastVLineInternal(x, y, w, color);
else ssd1306drawFastHLineInternal(x, y, w, color);
}
void ssd1306drawFastHLineInternal(
int16_t x, int16_t y, int16_t w, uint16_t color) {
if((y >= 0) && (y < HEIGHT)) { // Y coord in bounds?
if(x < 0) { // Clip left
w += x;
x = 0;
}
if((x + w) > WIDTH) { // Clip right
w = (WIDTH - x);
}
if(w > 0) { // Proceed only if width is positive
uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x],
mask = 1 << (y & 7);
switch(color) {
case SSD1306_WHITE: while(w--) { *pBuf++ |= mask; }; break;
case SSD1306_BLACK: mask = ~mask; while(w--) { *pBuf++ &= mask; }; break;
case SSD1306_INVERSE: while(w--) { *pBuf++ ^= mask; }; break;
}
}
}
}
/*!
@brief Draw a vertical line. This is also invoked by the Adafruit_GFX
library in generating many higher-level graphics primitives.
@param x
Column of display -- 0 at left to (screen width -1) at right.
@param y
Topmost row -- 0 at top to (screen height - 1) at bottom.
@param h
Height of line, in pixels.
@param color
Line color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERT.
@return None (void).
@note Changes buffer contents only, no immediate effect on display.
Follow up with a call to SSD1306_OutBuffer(), or with other
graphics commands as needed by one's own application.
*/
void SSD1306_DrawFastVLine(
int16_t x, int16_t y, int16_t h, uint16_t color) {
int bSwap = false;
switch(rotation) {
case 1:
// 90 degree rotation, swap x & y for rotation,
// then invert x and adjust x for h (now to become w)
bSwap = true;
ssd1306_swap(x, y);
x = WIDTH - x - 1;
x -= (h-1);
break;
case 2:
// 180 degree rotation, invert x and y, then shift y around for height.
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
y -= (h-1);
break;
case 3:
// 270 degree rotation, swap x & y for rotation, then invert y
bSwap = true;
ssd1306_swap(x, y);
y = HEIGHT - y - 1;
break;
}
if(bSwap) ssd1306drawFastHLineInternal(x, y, h, color);
else ssd1306drawFastVLineInternal(x, y, h, color);
}
static const uint8_t premask[8] =
{ 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
static const uint8_t postmask[8] =
{ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };
void ssd1306drawFastVLineInternal(
int16_t x, int16_t __y, int16_t __h, uint16_t color) {
if((x >= 0) && (x < WIDTH)) { // X coord in bounds?
if(__y < 0) { // Clip top
__h += __y;
__y = 0;
}
if((__y + __h) > HEIGHT) { // Clip bottom
__h = (HEIGHT - __y);
}
if(__h > 0) { // Proceed only if height is now positive
// this display doesn't need ints for coordinates,
// use local byte registers for faster juggling
uint8_t y = __y, h = __h;
uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x];
// do the first partial byte, if necessary - this requires some masking
uint8_t mod = (y & 7);
if(mod) {
// mask off the high n bits we want to set
mod = 8 - mod;
// note - lookup table results in a nearly 10% performance
// improvement in fill* functions
// uint8_t mask = ~(0xFF >> mod);
uint8_t mask = premask[mod];
// adjust the mask if we're not going to reach the end of this byte
if(h < mod) mask &= (0XFF >> (mod - h));
switch(color) {
case SSD1306_WHITE: *pBuf |= mask; break;
case SSD1306_BLACK: *pBuf &= ~mask; break;
case SSD1306_INVERSE: *pBuf ^= mask; break;
}
pBuf += WIDTH;
}
if(h >= mod) { // More to go?
h -= mod;
// Write solid bytes while we can - effectively 8 rows at a time
if(h >= 8) {
if(color == SSD1306_INVERSE) {
// separate copy of the code so we don't impact performance of
// black/white write version with an extra comparison per loop
do {
*pBuf ^= 0xFF; // Invert byte
pBuf += WIDTH; // Advance pointer 8 rows
h -= 8; // Subtract 8 rows from height
} while(h >= 8);
} else {
// store a local value to work with
uint8_t val = (color != SSD1306_BLACK) ? 255 : 0;
do {
*pBuf = val; // Set byte
pBuf += WIDTH; // Advance pointer 8 rows
h -= 8; // Subtract 8 rows from height
} while(h >= 8);
}
}
if(h) { // Do the final partial byte, if necessary
mod = h & 7;
// this time we want to mask the low bits of the byte,
// vs the high bits we did above
// uint8_t mask = (1 << mod) - 1;
// note - lookup table results in a nearly 10% performance
// improvement in fill* functions
uint8_t mask = postmask[mod];
switch(color) {
case SSD1306_WHITE: *pBuf |= mask; break;
case SSD1306_BLACK: *pBuf &= ~mask; break;
case SSD1306_INVERSE: *pBuf ^= mask; break;
}
}
}
} // endif positive height
} // endif x in bounds
}
/*!
@brief Return color of a single pixel in display buffer.
@param x
Column of display -- 0 at left to (screen width - 1) at right.
@param y
Row of display -- 0 at top to (screen height -1) at bottom.
@return true if pixel is set (usually WHITE, unless display invert mode
is enabled), false if clear (BLACK).
@note Reads from buffer contents; may not reflect current contents of
screen if SSD1306_OutBuffer() has not been called.
*/
int SSD1306_GetPixel(int16_t x, int16_t y) {
if((x >= 0) && (x < WIDTH) && (y >= 0) && (y < HEIGHT)) {
// Pixel is in-bounds. Rotate coordinates if needed.
switch(rotation) {
case 1:
ssd1306_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
ssd1306_swap(x, y);
y = HEIGHT - y - 1;
break;
}
return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7)));
}
return false; // Pixel out of bounds
}
/*!
@brief Get base address of display buffer for direct reading or writing.
@return Pointer to an unsigned 8-bit array, column-major, columns padded
to full byte boundary if needed.
*/
uint8_t *SSD1306_GetBuffer(void) {
return buffer;
}
// REFRESH DISPLAY ---------------------------------------------------------
/*!
@brief Push data currently in RAM to SSD1306 display.
@return None (void).
@note Drawing operations are not visible until this function is
called. Call after each graphics command, or after a whole set
of graphics commands, as best needed by one's own application.
*/
static const uint8_t dlist1[] = {
SSD1306_PAGEADDR,
0, // Page start address
0xFF, // Page end (not really, but works here)
SSD1306_COLUMNADDR,
0 };
void SSD1306_OutBuffer(void) {
// Column start address
ssd1306commandList(dlist1, sizeof(dlist1));
ssd1306command1(WIDTH - 1); // Column end address
// SPI
// uint16_t count = WIDTH*HEIGHT/8;
// while(count--) datawrite(*ptr++);//SPI version
// I2C
I2C_SendData(SSD1306ADDR,buffer,(WIDTH*HEIGHT/8)); // 1024 bytes
}
/*!
@brief Fill the whole screen by drawing a 128x64 bitmap image.
@param ptr Pointer to 1024-byte image with no header or format
data.
@return None (void).
@note Assumes: OLED is in horizontal addressing mode (command 0x20, 0x00)<br>
Image is sent to screen directly, RAM buffer is unchanged.
*/
void SSD1306_DrawFullImage(const uint8_t *ptr){
// int i;
// over-write the entire screen, even any columns on the right that did not contain text
// ssd1306command(SSD1306_PAGEADDR);
// ssd1306command(0); // page start address
// ssd1306command((HEIGHT/8) - 1); // page end (bottom of screen)
// ssd1306command(SSD1306_COLUMNADDR);
// ssd1306command(0); // column start address
// ssd1306command(WIDTH - 1); // column end address
ssd1306commandList(dlist1, sizeof(dlist1));
ssd1306command1(WIDTH - 1); // Column end address
I2C_SendData(SSD1306ADDR,(uint8_t *)ptr,(WIDTH*HEIGHT/8));
// for(i=0; i<WIDTH*HEIGHT/8; i++){
// datawrite(ptr[i]); //SPI version
// }
SSD1306_SetCursor(0, 0);
}
// SCROLLING FUNCTIONS -----------------------------------------------------
/*!
@brief Activate a right-handed scroll for all or part of the display.
@param start
First row.
@param stop
Last row.
@return None (void).
*/
// To scroll the whole display, run: display.startscrollright(0x00, 0x0F)
static const uint8_t scrollList1a[] = {
SSD1306_RIGHT_HORIZONTAL_SCROLL,
0X00 };
static const uint8_t scrollList1b[] = {
0X00,
0XFF,
SSD1306_ACTIVATE_SCROLL };
void SSD1306_startscrollright(uint8_t start, uint8_t stop) {
ssd1306commandList(scrollList1a, sizeof(scrollList1a));
ssd1306command1(start);
ssd1306command1(0X00);
ssd1306command1(stop);
ssd1306commandList(scrollList1b, sizeof(scrollList1b));
}
/*!
@brief Activate a left-handed scroll for all or part of the display.
@param start
First row.
@param stop
Last row.
@return None (void).
*/
// To scroll the whole display, run: display.startscrollleft(0x00, 0x0F)
static const uint8_t scrollList2a[] = {
SSD1306_LEFT_HORIZONTAL_SCROLL,
0X00 };
static const uint8_t scrollList2b[] = {
0X00,
0XFF,
SSD1306_ACTIVATE_SCROLL };
void SSD1306_startscrollleft(uint8_t start, uint8_t stop) {
ssd1306commandList(scrollList2a, sizeof(scrollList2a));
ssd1306command1(start);
ssd1306command1(0X00);
ssd1306command1(stop);
ssd1306commandList(scrollList2b, sizeof(scrollList2b));
}
/*!
@brief Activate a diagonal scroll for all or part of the display.
@param start
First row.
@param stop
Last row.
@return None (void).
*/
// display.startscrolldiagright(0x00, 0x0F)
static const uint8_t scrollList3a[] = {
SSD1306_SET_VERTICAL_SCROLL_AREA,
0X00 };
static const uint8_t scrollList3b[] = {
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL,
0X00 };
static const uint8_t scrollList3c[] = {
0X01,
SSD1306_ACTIVATE_SCROLL };
void SSD1306_startscrolldiagright(uint8_t start, uint8_t stop) {
ssd1306commandList(scrollList3a, sizeof(scrollList3a));
ssd1306command1(HEIGHT);
ssd1306commandList(scrollList3b, sizeof(scrollList3b));
ssd1306command1(start);
ssd1306command1(0X00);
ssd1306command1(stop);
ssd1306commandList(scrollList3c, sizeof(scrollList3c));
}
/*!
@brief Activate alternate diagonal scroll for all or part of the display.
@param start
First row.
@param stop
Last row.
@return None (void).
*/
// To scroll the whole display, run: display.startscrolldiagleft(0x00, 0x0F)
static const uint8_t scrollList4a[] = {
SSD1306_SET_VERTICAL_SCROLL_AREA,
0X00 };
static const uint8_t scrollList4b[] = {
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL,
0X00 };
static const uint8_t scrollList4c[] = {
0X01,
SSD1306_ACTIVATE_SCROLL };
void SSD1306_startscrolldiagleft(uint8_t start, uint8_t stop) {
ssd1306commandList(scrollList4a, sizeof(scrollList4a));
ssd1306command1(HEIGHT);
ssd1306commandList(scrollList4b, sizeof(scrollList4b));
ssd1306command1(start);
ssd1306command1(0x00);
ssd1306command1(stop);
ssd1306commandList(scrollList4c, sizeof(scrollList4c));
}
/*!
@brief Cease a previously-begun scrolling action.
@return None (void).
*/
void SSD1306_stopscroll(void) {
ssd1306command1(SSD1306_DEACTIVATE_SCROLL);
}
// OTHER HARDWARE SETTINGS -------------------------------------------------
/*!
@brief Enable or disable display invert mode (white-on-black vs
black-on-white).
@param i
If true, switch to invert mode (black-on-white), else normal
mode (white-on-black).
@return None (void).
@note This has an immediate effect on the display, no need to call the
display() function -- buffer contents are not changed, rather a
different pixel mode of the display hardware is used. When
enabled, drawing BLACK (value 0) pixels will actually draw white,
WHITE (value 1) will draw black.
*/
void SSD1306_InvertDisplay(int i) {
ssd1306command1(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
}
/*!
@brief Dim the display.
@param dim
true to enable lower brightness mode, false for full brightness.
@return None (void).
@note This has an immediate effect on the display, no need to call the
display() function -- buffer contents are not changed.
*/
void SSD1306_Dim(int dim) {
uint8_t contrast;
if(dim) {
contrast = 0; // Dimmed display
} else {
contrast = (vccstate == SSD1306_EXTERNALVCC) ? 0x9F : 0xCF;
}
// the range of contrast to too small to be really useful
// it is useful to dim the display
ssd1306command1(SSD1306_SETCONTRAST);
ssd1306command1(contrast);
}
/*
* This deprecated function maintains a cursor and moves it
* with this function. The new function synchronizes this
* cursor with the one in the OLED display.
*/
//********SSD1306_SetCursor*****************
// Move the cursor to the desired X- and Y-position. The
// next character will be printed here. X=0 is the leftmost
// column. Y=0 is the top row.
// Inputs: newX new X-position of the cursor (0<=newX<=20)
// newY new Y-position of the cursor (0<=newY<=7)
// Outputs: none
/*uint16_t X, Y;
void SSD1306_SetCursor(uint16_t newX, uint16_t newY){
if((newX > 20) || (newY > 7)){ // bad input
return; // do nothing
}
// multiply newX by 6 because each character is 6 columns wide
X = newX*6;
Y = newY*8;
}*/
//********SSD1306_SetCursor*****************
// Move the cursor to the desired X- and Y-position. The
// next character will be printed here. X=0 is the leftmost
// column. Y=0 is the top row.
// Inputs: newX new X-position of the cursor (0<=newX<=20)
// newY new Y-position of the cursor (0<=newY<=7)
// Outputs: none
uint16_t StartX, CurrentX = 0, StartY, CurrentY = 0;
void SSD1306_SetCursor(uint16_t newX, uint16_t newY){
if((newX > ((WIDTH/6) - 1)) || (newY > ((HEIGHT/8) - 1))){
return; // bad input; do nothing
}
// multiply newX by 6 because each character is 6 columns wide
CurrentX = newX*6;
StartX = CurrentX;
CurrentY = newY;
StartY = CurrentY;
ssd1306command(SSD1306_PAGEADDR);
ssd1306command(CurrentY); // page start address
ssd1306command((HEIGHT/8) - 1); // page end (bottom of screen)
ssd1306command(SSD1306_COLUMNADDR);
ssd1306command(CurrentX); // column start address
ssd1306command(WIDTH - (WIDTH%6) - 1);// column end address
}
/*
* This deprecated function maintains a cursor and draws a
* character to the RAM buffer. Use DrawChar() instead.
* Use OutChar() to print one character immediately, which
* does not use the RAM buffer.
*/
//********SSD1306_OutChar*****************
// Print a character to the SSD1306 128by64 OLED. The
// character will be printed at the current cursor position,
// the cursor will automatically be updated, and it will
// wrap to the next row or back to the top if necessary.
// One blank column of pixels will be printed to the right
// of the character for readability. Since characters are 8
// pixels tall and 5 pixels wide, 21 characters fit per row,
// and there are 8 rows.
// Inputs: data character to print
// Outputs: none
/*void SSD1306_OutChar(char data){
if((X>20*6)||(data == CR)){
Y = Y+8;
X = 0;
if(Y>7*8){
Y = 0;
}
}
if(data<0x20) return;
SSD1306_DrawChar(X, Y, data, SSD1306_WHITE);
X = X+6;
}*/
//********SSD1306_OutChar*****************
// Print a character to the SSD1306 128by64 OLED. The
// character will be printed at the current cursor position,
// the cursor will automatically be updated, and it will
// wrap to the next row or back to the top if necessary.
// One blank column of pixels will be printed to the right
// of the character for readability. Since characters are 8
// pixels tall and 6 pixels wide, 21 characters fit per row,
// and there are 8 rows.
// Inputs: data character to print
// Outputs: none
// Assumes: OLED is in horizontal addressing mode (command 0x20, 0x00)
void SSD1306_OutChar(char data){//int i;
uint32_t k = (uint32_t)(data & 0xFF); // convert to unsigned regardless of whether char is signed or unsigned
if((data == 0x0A) || (data == 0x0D)){ // line feed or carriage return
// go to the first column
CurrentX = 0;
StartX = CurrentX;
ssd1306command(SSD1306_COLUMNADDR);
ssd1306command(CurrentX); // column start address
ssd1306command(WIDTH - (WIDTH%6) - 1);// column end address
// go to the next row or wrap back to the first row
CurrentY = CurrentY + 1;
if(CurrentY > ((HEIGHT/8) - 1)){
CurrentY = 0;
}
StartY = CurrentY;
ssd1306command(SSD1306_PAGEADDR);
ssd1306command(CurrentY); // page start address
ssd1306command((HEIGHT/8) - 1); // page end (bottom of screen)
}else if(k >= 0x20){ // printable character
CurrentX = CurrentX + 6;
if(CurrentX > (WIDTH - (WIDTH%6))){
// go to the first column
if(StartX != 0){
// The SSD1306 OLED display will wrap to the column start
// address (the first parameter of the SSD1306_COLUMNADDR
// command). See Figure 10-5 on page 36 of the datasheet.
// To be sure that the mid- and high-level functions work
// like they did with the Nokia 5110 LCD, this start
// address might need to be re-sent as zero when attempting
// to automatically wrap.
CurrentX = 0;
StartX = CurrentX;
ssd1306command(SSD1306_COLUMNADDR);
ssd1306command(CurrentX); // column start address
ssd1306command(WIDTH - (WIDTH%6) - 1);// column end address
}
// go to the next row or wrap back to the first row
CurrentY = CurrentY + 1;
if(CurrentY > ((HEIGHT/8) - 1)){
CurrentY = 0;
if(StartY != 0){
// The SSD1306 OLED display will wrap to the row (sometimes
// called "page") start address (the first parameter of the
// SSD1306_PAGEADDR command). See Figure 10-5 on page 36
// of the datasheet. To be sure that the mid- and high-
// level functions work like they did with the Nokia 5110
// LCD, this start address might need to be re-sent as zero
// when attempting to automatically wrap.
StartY = CurrentY;
ssd1306command(SSD1306_PAGEADDR);
ssd1306command(CurrentY); // page start address
ssd1306command((HEIGHT/8) - 1);// page end (bottom of screen)
}
}
}
I2C_SendData(SSD1306ADDR,(uint8_t *)&ASCII[k - 0x20],6);
// for(i=0; i<5; i=i+1){
// datawrite(ASCII[data - 0x20][i]);//SPI version
// }
// datawrite(0x00); // blank vertical line padding
}
}
//********SSD1306_OutString*****************
// Print a string of characters to the SSD1306 128by64 OLED.
// The string will automatically wrap, so padding spaces may
// be needed to make the output look optimal.
// Inputs: ptr pointer to NULL-terminated ASCII string
// Outputs: none
// Assumes: OLED is in horizontal addressing mode (command 0x20, 0x00)
void SSD1306_OutString(char *ptr){
// you write this as part of Lab 11
// ******solution*********
while(*ptr){
SSD1306_OutChar(*ptr);
ptr = ptr + 1;
}
}
//********SSD1306_OutUDec*****************
// Output a 16-bit number in unsigned decimal format with a
// fixed size of five right-justified digits of output.
// Inputs: n 16-bit unsigned number
// Outputs: none
// Assumes: OLED is in horizontal addressing mode (command 0x20, 0x00)
void SSD1306_OutUDec(uint16_t n){
// you write this as part of Lab 11
// ******solution*********
if(n < 10){
SSD1306_OutString(" ");
SSD1306_OutChar(n+'0'); /* n is between 0 and 9 */
} else if(n<100){
SSD1306_OutString(" ");
SSD1306_OutChar(n/10+'0'); /* tens digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
} else if(n<1000){
SSD1306_OutString(" ");
SSD1306_OutChar(n/100+'0'); /* hundreds digit */
n = n%100;
SSD1306_OutChar(n/10+'0'); /* tens digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}
else if(n<10000){
SSD1306_OutChar(' ');
SSD1306_OutChar(n/1000+'0'); /* thousands digit */
n = n%1000;
SSD1306_OutChar(n/100+'0'); /* hundreds digit */
n = n%100;
SSD1306_OutChar(n/10+'0'); /* tens digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}
else {
SSD1306_OutChar(n/10000+'0'); /* ten-thousands digit */
n = n%10000;
SSD1306_OutChar(n/1000+'0'); /* thousands digit */
n = n%1000;
SSD1306_OutChar(n/100+'0'); /* hundreds digit */
n = n%100;
SSD1306_OutChar(n/10+'0'); /* tens digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}
}
//********SSD1306_OutSDec*****************
// Output a 16-bit number in signed decimal format with a
// fixed size of six right-justified digits of output.
// Inputs: n 16-bit signed number
// Outputs: none
// Assumes: OLED is in horizontal addressing mode (command 0x20, 0x00)
void SSD1306_OutSDec(int16_t n){char sign=' ';
// you write this as part of Lab 11
// ******solution*********
if(n<0){
sign = '-';
n = -n; // positive now
}
if(n < 10){
SSD1306_OutString(" ");
SSD1306_OutChar(sign);
SSD1306_OutChar(n+'0'); /* n is between 0 and 9 */
} else if(n<100){
SSD1306_OutString(" ");
SSD1306_OutChar(sign);
SSD1306_OutChar(n/10+'0'); /* tens digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
} else if(n<1000){
SSD1306_OutString(" ");
SSD1306_OutChar(sign);
SSD1306_OutChar(n/100+'0'); /* hundreds digit */
n = n%100;
SSD1306_OutChar(n/10+'0'); /* tens digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}
else if(n<10000){
SSD1306_OutChar(' ');
SSD1306_OutChar(sign);
SSD1306_OutChar(n/1000+'0'); /* thousands digit */
n = n%1000;
SSD1306_OutChar(n/100+'0'); /* hundreds digit */
n = n%100;
SSD1306_OutChar(n/10+'0'); /* tens digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}
else {
SSD1306_OutChar(sign);
SSD1306_OutChar(n/10000+'0'); /* ten-thousands digit */
n = n%10000;
SSD1306_OutChar(n/1000+'0'); /* thousands digit */
n = n%1000;
SSD1306_OutChar(n/100+'0'); /* hundreds digit */
n = n%100;
SSD1306_OutChar(n/10+'0'); /* tens digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}
}
//********SSD1306_OutUFix1*****************
// Output a 16-bit number in unsigned 3-digit fixed point, 0.1 resolution
// numbers 0 to 999 printed as " 0.0" to "99.9"
// Inputs: n 16-bit unsigned number
// Outputs: none
void SSD1306_OutUFix1(uint16_t n){
char message[5];
if(n>999)n=999;
if(n>=100){ // 100 to 999
message[0] = (n/100+'0'); /* tens digit */
n = n%100; //the rest
}else { // 0 to 99
message[0] = ' '; /* n is between 0.0 and 9.9 */
}
message[1] = (n/10+'0'); /* ones digit */
n = n%10; //the rest
message[2] = '.';
message[3] = (n+'0'); /* tenths digit */
message[4] = 0;
SSD1306_OutString(message);
}
//********SSD1306_DrawUDec*****************
// Output a 16-bit number in unsigned 3-digit integer
// numbers 0 to 999 printed as " 0" to "999"
// Inputs: x is horizontal position in pixels
// y is vertical position in pixels
// n 16-bit unsigned number 0 to 999
// color is black or white
// Outputs: none
void SSD1306_DrawUDec(int16_t x, int16_t y, uint16_t n, uint16_t color){
char message[4];
if(n>999)n=999;
if(n>=100){ // 100 to 999
message[0] = (n/100+'0'); /* hundreds digit */
n = n%100; //the rest
message[1] = (n/10+'0'); /* tens digit */
n = n%10; //the rest
}else { // 0 to 99
message[0] = ' '; /* n is between 0.0 and 9.9 */
if(n>=10){ // 10 to 99
message[1] = (n/10+'0'); /* tens digit */
}else{
message[1] = ' '; /* n is between 0 and 9 */
}
}
n = n%10; //the rest
message[2] = (n+'0'); /* tenths digit */
message[3] = 0;
SSD1306_DrawString(x,y,message,color);
}
//********SSD1306_OutSFix1*****************
// Output a 16-bit number in signed 4-digit fixed point, 0.1 resolution
// numbers -9999 to 9999 printed as "-999.0" to " 999.9"
// Inputs: n 32-bit signed number
// Outputs: none
void SSD1306_OutSFix1(int32_t n){
char message[8];
if(n<-9999) n=-9999;
if(n>9999) n=9999;
if(n<0){
message[0] = '-';
n = -n; // now positive
}else{
message[0] = ' ';
}
if(n>=1000){ // 1000 to 9999
message[1] = (n/1000+'0'); /* hundreds digit */
n = n%1000; // the rest
message[2] = (n/100+'0'); /* tens digit */
n = n%100; // the rest
}else{
if(n>=100){ // 100 to 999
message[1] = ' '; /* n is between 0.0 and 9.9 */
message[2] = (n/100+'0'); /* tens digit */
n = n%100; //the rest
}else { // 0 to 99
message[1] = ' '; /* n is between 0.0 and 9.9 */
message[2] = ' ';
}
}
message[3] = (n/10+'0'); /* ones digit */
n = n%10; //the rest
message[4] = '.';
message[5] = (n+'0'); /* tenths digit */
message[6] = 0;
SSD1306_OutString(message);
}
void SSD1306_OutHex7(uint8_t n){
n = n&0x0F;
if(n>9){
SSD1306_OutChar('A'+n-10);
}else{
SSD1306_OutChar(n+'0'); /* ones digit */
}
}
void SSD1306_OutUHex7(uint8_t n){
SSD1306_OutString(" 0x");
SSD1306_OutHex7(n/16); /* 16s digit */
SSD1306_OutHex7(n); /* ones digit */
}
void SSD1306_OutUHex32(uint32_t n){
SSD1306_OutString("0x");
SSD1306_OutHex7(n>>28); /* bits 31-28 */
SSD1306_OutHex7(n>>24); /* bits 27-24 */
SSD1306_OutHex7(n>>20); /* bits 23-20 */
SSD1306_OutHex7(n>>16); /* bits 19-16 */
SSD1306_OutHex7(n>>12); /* bits 15-12 */
SSD1306_OutHex7(n>>8); /* bits 11-8 */
SSD1306_OutHex7(n>>4); /* bits 7-4 */
SSD1306_OutHex7(n); /* bits 3-0 */
}
void SSD1306_OutUDec16(uint32_t n){
SSD1306_OutChar(' ');
if(n>=100){
SSD1306_OutChar(n/100+'0'); /* 100 digit */
n = n%100;
SSD1306_OutChar(n/10+'0'); /* 10 digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}else{
SSD1306_OutChar(' ');
if(n>=10){
SSD1306_OutChar(n/10+'0'); /* 10 digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}else{
SSD1306_OutChar(' ');
SSD1306_OutChar(n+'0'); /* ones digit */
}
}
}
void SSD1306_OutUDec2(uint32_t n){
if(n>=100){
SSD1306_OutChar(' '); /* 100 digit */
SSD1306_OutChar('*'); /* illegal */
}else{
if(n>=10){
SSD1306_OutChar(n/10+'0'); /* 10 digit */
SSD1306_OutChar(n%10+'0'); /* ones digit */
}else{
SSD1306_OutChar(' ');
SSD1306_OutChar(n+'0'); /* ones digit */
}
}
}
// Print a character to SSD1306 LCD.
int fputc(int ch, FILE *f){
SSD1306_OutChar(ch);
return 1;
}
// No input from SSD1306, always return data.
int fgetc (FILE *f){
return 0;
}
// Function called when file error occurs.
int ferror(FILE *f){
/* Your implementation of ferror */
return EOF;
}
int SSD1306_open(const char *path, unsigned flags, int llv_fd){
SSD1306_Init(SSD1306_SWITCHCAPVCC);
return 0;
}
int SSD1306_close( int dev_fd){
return 0;
}
int SSD1306_read(int dev_fd, char *buf, unsigned count){char ch;
// not implemented
return 1;
}
int SSD1306_write(int dev_fd, const char *buf, unsigned count){ unsigned int num=count;
while(num){
SSD1306_OutChar(*buf);
buf++;
num--;
}
return count;
}
off_t SSD1306_lseek(int dev_fd, off_t ioffset, int origin){
return 0;
}
int SSD1306_unlink(const char * path){
return 0;
}
int SSD1306_rename(const char *old_name, const char *new_name){
return 0;
}
//------------SSD1306_InitPrintf------------
// Initialize the ST7735 for printf
// Input: none
// Output: none
void SSD1306_InitPrintf(void){int ret_val; FILE *fptr;
SSD1306_Init(SSD1306_SWITCHCAPVCC);
ret_val = add_device("ssd1306", _SSA, SSD1306_open, SSD1306_close, SSD1306_read, SSD1306_write, SSD1306_lseek, SSD1306_unlink, SSD1306_rename);
if(ret_val) return; // error
fptr = fopen("ssd1306","w");
if(fptr == 0) return; // error
freopen("ssd1306:", "w", stdout); // redirect stdout to SSD1306
setvbuf(stdout, NULL, _IONBF, 0); // turn off buffering for stdout
}