uWFG-Pico/lcd.c

276 wiersze
10 KiB
C

/*
* lcd.c
*
* Created: Jan 2022
* Author: Arjan te Marvelde
*
* Driver for SSD1327 based 128x128 display.
*
* It is an output-only MMI, so only write is supported
*
* The interface functions are:
* lcd_putc(int x, int y, uint8_t c, uint8_t *font);
* lcd_puts(int x, int y, char *buf, uint8_t *font);
* lcd_putg(int x, int y, uint8_t *bitmap);
* lcd_clrscr(void);
* lcd_init(void);
* The parameters (x, y) determine upper left corner of object item, should be even numbers.
* The font or bitmap object itself contains other parameters like (w, h) and the item data content.
* (w, h) also should be even numbers.
*
* A font or bitmap object definition starts with:
* - uint8_t item field width
* - uint8_t item field height
* - uint8_t first item number (e.g. character code)
* - uint8_t last item number
* followed by the item data, a nibble per pixel.
*
* Use of graphics functions requires a bitmap, where the same format as for fonts is used but there is only one character.
* A predefined bitmap is for example the Udjat logo, but user defined bitmap can also be dumped on display.
* For filling the bitmap dynamically, several graphics functions are provided:
*
* I2C write sequence:
* control byte + following bytes
* control byte = 0bxy000000, where
* x: single (1) or multiple (0)
* y: data(1) or control(0)
*
* Display is 128 columns wide by 128 rows high. Each (col, row) is a grey value nibble [1-15].
* The nibbles are organized in display RAM as follows:
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
* |L 0 H 0|L 1 H 1|L 2 H 2| |L 63 H 63|
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
* |L 64 H 64|L 65 H 65|L 66 H 66| |L 127 H 127|
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
* / / / / / / / / / /
* \ \ \ \ \ \ \ \ \ \
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
* |L8128 H8128|L8129 H8129|L8130 H8130| |L8191 H8191|
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
*
*
* For writing Display RAM, first the conditions and start location are set (commands) then followed by a data burst.
*
*/
#include <string.h>
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "lcd.h"
// Command definition D Function (reset values)
// SSD1327 #
// ----------------------------------------------------------------------------------------------------------------------------
#define LCD_WINCOLADDR 0x15 // 2 Sets first and last column for window (0x00, 0x3F).
#define LCD_WINROWADDR 0x75 // 2 Sets first and last row for window (0x00, 0x7F).
#define LCD_SCR_RIGHT 0x26 // 7 Setup right-scrolling part of display (see below)
#define LCD_SCR_LEFT 0x27 // 7 Setup left-scrolling part of display
#define LCD_SCR_STOP 0x2e // 0 Stop scrolling window
#define LCD_SCR_START 0x2f // 0 Start scrolling window
#define LCD_CONTRAST 0x81 // 1 Sets Contrast of the display (0x7F).
#define LCD_REMAP 0xa0 // 1 Enables/Disables address remapping (0x00).
#define LCD_DSTARTLINE 0xa1 // 1 Sets display start line (0x00).
#define LCD_DOFFSET 0xa2 // 1 Sets display vertical offset (0x00).
#define LCD_MODENORM 0xa4 // 0 Display in normal mode
#define LCD_MODEWHITE 0xa5 // 0 Display all pixels white, greyscale=15
#define LCD_MODEBLACK 0xa6 // 0 Display all pixels black, greyscale=0
#define LCD_MODEINVERS 0xa7 // 0 Display all pixels inverted, greyscale=15-val
#define LCD_MUXRATIO 0xa8 // 1 Set ratio to X+1, X>14, (0x7f)
#define LCD_FASELECT 0xab // 0 Select internal Vdd regulator when 1, external when 0 (0x01)
#define LCD_INACTIVE 0xae // 0 Switches display to sleep mode
#define LCD_ACTIVE 0xaf // 0 Switches display on, normal mode
#define LCD_PHASELEN 0xb1 // 1 High nibble phase2, low nibbel phase1 (0x74)
#define LCD_NOP1 0xb2 // 0 No operation
#define LCD_OSC_D_F 0xb3 // 1 Set oscillator divider (0x00)
#define LCD_GPIO 0xb5 // 1 (0x02)
#define LCD_PCPER2 0xb6 // 1 (0x04)
#define LCD_GS_TABLE 0xb8 // 15 Pulse width for GS levels 1..15, all unequal and value rising
#define LCD_GS_LINEAR 0xb9 // 0 Sets linear GS table (default)
#define LCD_NOP2 0xbb // 0 No operation
#define LCD_PCLEVEL 0xbc // 1 (0x05)
#define LCD_CDLEVEL 0xbe // 1 (0x05)
#define LCD_FBSELECT 0xd5 // 1 (0x00)
#define LCD_CMDLOCK 0xfd // 1 Lock OLED command interface (0x16) or unlock (0x12)
#define I2C_SSD1327 0x3c // I2C address (0x3C)
#define LCD_CTRLCMD 0x00 // Control byte for burst commands
#define LCD_CTRLDATA 0x40 // Control byte for burst data
#define LCD_CTRLSINGLE 0x80 // OR in case of single command/data
#define LCD_CTRLMULTI 0x00 // OR in case of multibyte command/data
#define LCD_DELAY 1000 // Screen refresh time
#define LCD_WIDTH 0x80 // Pixels
#define LCD_HEIGHT 0x80 // Pixels
uint8_t txdata[1+LCD_WIDTH]; // Maximum transfer size, one line of data
/*
* Set display active area (cursor) to (x,y) left upper corner and (width x height) size
*/
void lcd_cursor(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
txdata[0] = LCD_CTRLCMD | LCD_CTRLMULTI; // Multiple command byte
txdata[1] = LCD_WINCOLADDR; // Set window columns
txdata[2] = (x/2)&0x3f; // left
txdata[3] = (((x+w)/2)-1)&0x3f; // right
txdata[4] = LCD_WINROWADDR; // Set window rows
txdata[5] = (y)&0x7f; // top
txdata[6] = (y+h-1)&0x7f; // bottom
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 7, false); // Send commands
}
/*
* Clear the display
*/
void lcd_clrscr(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
int i;
lcd_cursor(x,y,w,h); // Set window
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI;
memset(&txdata[1], 0x00, w/2); // Black line
for (i=0; i<h; i++)
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+w/2, false); // Clear one line
}
/*
* Output one character to location (x,y)
* Inverse graphics if invert is true
*/
void lcd_putc(uint8_t x, uint8_t y, uint8_t c, uint8_t *font, bool invert)
{
int i, w, h;
uint8_t xorbyte; // XOR value for memcpy
uint8_t *srce, *dest; // Pointers for memcpy
if (!((c>=font[2]) && (c<=font[3]))) // Range check character code
c=0; // Not good: default on first
else
c-=font[2]; // Good: shift down value
w=font[0]; h=font[1]; // Retrieve character width and height
if (((x+w)>LCD_WIDTH)||((y+h)>LCD_HEIGHT)) return; // Out of range!
lcd_cursor(x,y,w,h); // Define window
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI ; // Multiple data byte
xorbyte = (invert?0xff:0x00); // Optionally set inversion
srce = &font[4 + (int)c*w*h/2]; // Initialize pointers
dest = &txdata[1];
for (i=0; i<w*h/2; i++) // Copy data
*dest++ = *srce++ ^ xorbyte;
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+w*h/2, false); // Send character
}
/*
* Output multiple characters, starting on (x,y)
*/
void lcd_puts(uint8_t x, uint8_t y, char *buf, uint8_t *font, bool invert)
{
int len, i;
len = strlen(buf);
for (i=0; i<len; i++)
lcd_putc(x+font[0]*i, y, (char)(buf[i]), font, invert);
}
/*
* Dump bitmap graphic on screen
* This is essentially lcd_putc(x, y, 0, bitmap): bitmap being a single character font...
*/
void lcd_putg(uint8_t x, uint8_t y, uint8_t *bitmap, bool invert)
{
int w, h, i, j;
uint8_t xorbyte = 0x00; // XOR value for memcpy
uint8_t *srce, *dest; // Pointers for memcpy
w=bitmap[0]; h=bitmap[1]; // Retrieve bitmap width and height
if (((x+w)>LCD_WIDTH)||((y+h)>LCD_HEIGHT)) return; // Out of range!
lcd_cursor(x,y,w,h); // Define window for writing
if (invert) xorbyte = 0xff; // Optionally set inversion
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI ; // Multiple data byte
for (i=0; i<h; i++) // Split up graphic into lines
{
srce = &bitmap[4 + i*w/2]; // Initialize pointers
dest = &txdata[1];
for (j=0; j<w/2; j++) // Copy data
*dest++ = *srce++ ^ xorbyte;
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+w/2, false); // Send graphic line
}
}
/*
* Write a horizontal line on row y
*/
void lcd_hruler(uint8_t x, uint8_t y, uint8_t w)
{
lcd_cursor(x,y,w,2); // Just the line
// range x, y and truncate w
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI;
memset(&txdata[1], 0x88, w); // White line
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+w, false); // Dump two lines
}
/*
* Write a vertical line on col x and x+1
*/
void lcd_vruler(uint8_t x, uint8_t y, uint8_t h)
{
lcd_cursor(x,y,2,h); // Just the line
// range x, y and truncate w
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI;
memset(&txdata[1], 0x88, h); // White line
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+h, false); // Dump two lines
}
/*
* Initialize and clear display
*/
void lcd_init()
{
int page;
sleep_ms(1);
txdata[0] = LCD_CTRLCMD | LCD_CTRLMULTI ; // Multiple command byte
txdata[1] = LCD_CMDLOCK; // Unlock command interface
txdata[2] = 0x12;
txdata[3] = LCD_CTRLCMD | LCD_CTRLMULTI ; // Multiple command byte
txdata[4] = LCD_REMAP; // Display upside-down
txdata[5] = 0x53; // So change GDRAM mapping
txdata[6] = LCD_CTRLCMD | LCD_CTRLSINGLE ; // Multiple command byte
txdata[7] = LCD_ACTIVE; // DISPLAY ON
txdata[8] = LCD_CTRLCMD | LCD_CTRLSINGLE ; // Multiple command byte
txdata[9] = LCD_MODENORM; // NORMAL MODE
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 10, false);
// Output init flush screen
txdata[0] = LCD_CTRLCMD | LCD_CTRLSINGLE;
txdata[1] = LCD_INACTIVE;
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 2, false); // Send commands
lcd_clrscr(0,0,128,128);
lcd_putg(0, 0, LCD_UDJAT128, true);
txdata[0] = LCD_CTRLCMD | LCD_CTRLSINGLE;
txdata[1] = LCD_ACTIVE;
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 2, false); // Send commands
}