From a992ae126c5dc2c7218aacbd9524a557d484dbbc Mon Sep 17 00:00:00 2001
From: Eric Brombaugh <ebrombaugh1@cox.net>
Date: Sun, 7 May 2023 12:41:04 -0700
Subject: [PATCH] Abstracted low-level driver

---
 examples/i2c_oled/Makefile                 |   2 +-
 examples/i2c_oled/i2c_oled.c               |  73 ++++---
 examples/i2c_oled/{oled.h => ssd1306.h}    | 242 ++++++++++-----------
 examples/i2c_oled/{i2c.h => ssd1306_i2c.h} | 206 ++++++++++--------
 4 files changed, 278 insertions(+), 245 deletions(-)
 rename examples/i2c_oled/{oled.h => ssd1306.h} (62%)
 rename examples/i2c_oled/{i2c.h => ssd1306_i2c.h} (58%)

diff --git a/examples/i2c_oled/Makefile b/examples/i2c_oled/Makefile
index b9b2d1a..d525389 100644
--- a/examples/i2c_oled/Makefile
+++ b/examples/i2c_oled/Makefile
@@ -2,7 +2,7 @@ all : flash
 
 TARGET:=i2c_oled
 
-CFLAGS+=-DSTDOUT_UART
+#CFLAGS+=-DSTDOUT_UART
 
 include ../../ch32v003fun/ch32v003fun.mk
 
diff --git a/examples/i2c_oled/i2c_oled.c b/examples/i2c_oled/i2c_oled.c
index f2f43ff..94e6ce6 100644
--- a/examples/i2c_oled/i2c_oled.c
+++ b/examples/i2c_oled/i2c_oled.c
@@ -7,9 +7,15 @@
 #define SYSTEM_CORE_CLOCK 48000000
 #define APB_CLOCK SYSTEM_CORE_CLOCK
 
+// what type of OLED - uncomment just one
+//#define SSD1306_64X32
+#define SSD1306_128X32
+//#define SSD1306_128X64
+
 #include "ch32v003fun.h"
 #include <stdio.h>
-#include "oled.h"
+#include "ssd1306_i2c.h"
+#include "ssd1306.h"
 
 int main()
 {
@@ -17,39 +23,44 @@ int main()
 	SystemInit48HSI();
 
 	// start serial @ default 115200bps
+#ifdef STDOUT_UART
 	SetupUART( UART_BRR );
 	Delay_Ms( 100 );
+#else
+	SetupDebugPrintf();
+#endif
 	printf("\r\r\n\ni2c_oled example\n\r");
 
 	// init i2c and oled
 	Delay_Ms( 100 );	// give OLED some more time
 	printf("initializing i2c oled...");
-	if(!oled_init())
+	if(!ssd1306_i2c_init())
 	{
+		ssd1306_init();
 		printf("done.\n\r");
 		
 		printf("Looping on test modes...");
 		while(1)
 		{
-			for(uint8_t mode=0;mode<(OLED_H>32?8:7);mode++)
+			for(uint8_t mode=0;mode<(SSD1306_H>32?8:7);mode++)
 			{
 				// clear buffer for next mode
-				oled_setbuf(0);
+				ssd1306_setbuf(0);
 
 				switch(mode)
 				{
 					case 0:
 						printf("buffer fill with binary\n\r");
-						for(int i=0;i<sizeof(oled_buffer);i++)
-							oled_buffer[i] = i;
+						for(int i=0;i<sizeof(ssd1306_buffer);i++)
+							ssd1306_buffer[i] = i;
 						break;
 					
 					case 1:
 						printf("pixel plots\n\r");
-						for(int i=0;i<OLED_W;i++)
+						for(int i=0;i<SSD1306_W;i++)
 						{
-							oled_drawPixel(i, i/(OLED_W/OLED_H), 1);
-							oled_drawPixel(i, OLED_H-1-(i/(OLED_W/OLED_H)), 1);
+							ssd1306_drawPixel(i, i/(SSD1306_W/SSD1306_H), 1);
+							ssd1306_drawPixel(i, SSD1306_H-1-(i/(SSD1306_W/SSD1306_H)), 1);
 						}
 						break;
 					
@@ -57,61 +68,61 @@ int main()
 						{
 							printf("Line plots\n\r");
 							uint8_t y= 0;
-							for(uint8_t x=0;x<OLED_W;x+=16)
+							for(uint8_t x=0;x<SSD1306_W;x+=16)
 							{
-								oled_drawLine(x, 0, OLED_W, y, 1);
-								oled_drawLine(OLED_W-x, OLED_H, 0, OLED_H-y, 1);
-								y+= OLED_H/8;
+								ssd1306_drawLine(x, 0, SSD1306_W, y, 1);
+								ssd1306_drawLine(SSD1306_W-x, SSD1306_H, 0, SSD1306_H-y, 1);
+								y+= SSD1306_H/8;
 							}
 						}
 						break;
 						
 					case 3:
 						printf("Circles empty and filled\n\r");
-						for(uint8_t x=0;x<OLED_W;x+=16)
+						for(uint8_t x=0;x<SSD1306_W;x+=16)
 							if(x<64)
-								oled_drawCircle(x, OLED_H/2, 15, 1);
+								ssd1306_drawCircle(x, SSD1306_H/2, 15, 1);
 							else
-								oled_fillCircle(x, OLED_H/2, 15, 1);
+								ssd1306_fillCircle(x, SSD1306_H/2, 15, 1);
 						break;
 					
 					case 4:
 						printf("Unscaled Text\n\r");
-						oled_drawstr(0,0, "This is a test", 1);
-						oled_drawstr(0,8, "of the emergency", 1);
-						oled_drawstr(0,16,"broadcasting", 1);
-						oled_drawstr(0,24,"system.",1);
-						if(OLED_H>32)
+						ssd1306_drawstr(0,0, "This is a test", 1);
+						ssd1306_drawstr(0,8, "of the emergency", 1);
+						ssd1306_drawstr(0,16,"broadcasting", 1);
+						ssd1306_drawstr(0,24,"system.",1);
+						if(SSD1306_H>32)
 						{
-							oled_drawstr(0,32, "Lorem ipsum", 1);
-							oled_drawstr(0,40, "dolor sit amet,", 1);
-							oled_drawstr(0,48,"consectetur", 1);
-							oled_drawstr(0,56,"adipiscing",1);
+							ssd1306_drawstr(0,32, "Lorem ipsum", 1);
+							ssd1306_drawstr(0,40, "dolor sit amet,", 1);
+							ssd1306_drawstr(0,48,"consectetur", 1);
+							ssd1306_drawstr(0,56,"adipiscing",1);
 						}
-						oled_xorrect(OLED_W/2, 0, OLED_W/2, OLED_W);
+						ssd1306_xorrect(SSD1306_W/2, 0, SSD1306_W/2, SSD1306_W);
 						break;
 						
 					case 5:
 						printf("Scaled Text 1, 2\n\r");
-						oled_drawstr_sz(0,0, "sz 8x8", 1, fontsize_8x8);
-						oled_drawstr_sz(0,16, "16x16", 1, fontsize_16x16);
+						ssd1306_drawstr_sz(0,0, "sz 8x8", 1, fontsize_8x8);
+						ssd1306_drawstr_sz(0,16, "16x16", 1, fontsize_16x16);
 						break;
 					
 					case 6:
 						printf("Scaled Text 4\n\r");
-						oled_drawstr_sz(0,0, "32x32", 1, fontsize_32x32);
+						ssd1306_drawstr_sz(0,0, "32x32", 1, fontsize_32x32);
 						break;
 					
 					
 					case 7:
 						printf("Scaled Text 8\n\r");
-						oled_drawstr_sz(0,0, "64", 1, fontsize_64x64);
+						ssd1306_drawstr_sz(0,0, "64", 1, fontsize_64x64);
 						break;
 
 					default:
 						break;
 				}
-				oled_refresh();
+				ssd1306_refresh();
 			
 				Delay_Ms(2000);
 			}
diff --git a/examples/i2c_oled/oled.h b/examples/i2c_oled/ssd1306.h
similarity index 62%
rename from examples/i2c_oled/oled.h
rename to examples/i2c_oled/ssd1306.h
index 378a27d..19ea88d 100644
--- a/examples/i2c_oled/oled.h
+++ b/examples/i2c_oled/ssd1306.h
@@ -1,73 +1,59 @@
 /*
- * Single-File-Header for using I2C OLED
- * 03-29-2023 E. Brombaugh
+ * Single-File-Header for using SPI OLED
+ * 05-05-2023 E. Brombaugh
  */
 
-#ifndef _OLED_H
-#define _OLED_H
+#ifndef _SSD1306_H
+#define _SSD1306_H
 
 #include <stdint.h>
 #include <string.h>
-#include "i2c.h"
 #include "font_8x8.h"
 
-// OLED I2C address
-#define OLED_ADDR 0x3c
-
-// comfortable I2C packet size for this OLED
-#define OLED_PSZ 32
-
-// what type of OLED - uncomment just one
-//#define OLED_64X32
-#define OLED_128X32
-//#define OLED_128X64
+// comfortable packet size for this OLED
+#define SSD1306_PSZ 32
 
 // characteristics of each type
-#ifdef OLED_64X32
-#define OLED_W 64
-#define OLED_H 32
-#define OLED_FULLUSE
-#define OLED_OFFSET 32
+#if !defined (SSD1306_64X32) && !defined (SSD1306_128X32) && !defined (SSD1306_128X64)
+	#error "Please define the SSD1306_WXH resolution used in your application"
 #endif
 
-#ifdef OLED_128X32
-#define OLED_W 128
-#define OLED_H 32
-#define OLED_OFFSET 0
+#ifdef SSD1306_64X32
+#define SSD1306_W 64
+#define SSD1306_H 32
+#define SSD1306_FULLUSE
+#define SSD1306_OFFSET 32
 #endif
 
-#ifdef OLED_128X64
-#define OLED_W 128
-#define OLED_H 64
-#define OLED_FULLUSE
-#define OLED_OFFSET 0
+#ifdef SSD1306_128X32
+#define SSD1306_W 128
+#define SSD1306_H 32
+#define SSD1306_OFFSET 0
+#endif
+
+#ifdef SSD1306_128X64
+#define SSD1306_W 128
+#define SSD1306_H 64
+#define SSD1306_FULLUSE
+#define SSD1306_OFFSET 0
 #endif
 
 /*
  * send OLED command byte
  */
-uint8_t oled_cmd(uint8_t cmd)
+uint8_t ssd1306_cmd(uint8_t cmd)
 {
-	uint8_t pkt[2];
-	
-	pkt[0] = 0;
-	pkt[1] = cmd;
-	return i2c_send(OLED_ADDR, pkt, 2);
+	ssd1306_pkt_send(&cmd, 1, 1);
+	return 0;
 }
 
 /*
  * send OLED data packet (up to 32 bytes)
  */
-uint8_t oled_data(uint8_t *data, uint8_t sz)
+uint8_t ssd1306_data(uint8_t *data, uint8_t sz)
 {
-	uint8_t pkt[33];
-	
-	// max 32 bytes
-	sz = sz > 32 ? 32 : sz;
-	
-	pkt[0] = 0x40;
-	memcpy(&pkt[1], data, sz);
-	return i2c_send(OLED_ADDR, pkt, sz+1);
+	ssd1306_pkt_send(data, sz, 0);
+	return 0;
 }
 
 #define SSD1306_SETCONTRAST 0x81
@@ -104,13 +90,13 @@ uint8_t oled_data(uint8_t *data, uint8_t sz)
 #define vccstate SSD1306_SWITCHCAPVCC
 
 // OLED initialization commands for 128x32
-const uint8_t oled_init_array[] =
+const uint8_t ssd1306_init_array[] =
 {
     SSD1306_DISPLAYOFF,                    // 0xAE
     SSD1306_SETDISPLAYCLOCKDIV,            // 0xD5
     0x80,                                  // the suggested ratio 0x80
     SSD1306_SETMULTIPLEX,                  // 0xA8
-#ifdef OLED_64X32
+#ifdef SSD1306_64X32
 	0x1F,                                  // for 64-wide displays
 #else
 	0x3F,                                  // for 128-wide displays
@@ -139,17 +125,17 @@ const uint8_t oled_init_array[] =
 };
 
 // the display buffer
-uint8_t oled_buffer[OLED_W*OLED_H/8];
+uint8_t ssd1306_buffer[SSD1306_W*SSD1306_H/8];
 
 /*
  * set the buffer to a color
  */
-void oled_setbuf(uint8_t color)
+void ssd1306_setbuf(uint8_t color)
 {
-	memset(oled_buffer, color ? 0xFF : 0x00, sizeof(oled_buffer));
+	memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer));
 }
 
-#ifndef OLED_FULLUSE
+#ifndef SSD1306_FULLUSE
 /*
  * expansion array for OLED with every other row unused
  */
@@ -165,48 +151,48 @@ const uint8_t expand[16] =
 /*
  * Send the frame buffer
  */
-void oled_refresh(void)
+void ssd1306_refresh(void)
 {
 	uint16_t i;
 	
-	oled_cmd(SSD1306_COLUMNADDR);
-	oled_cmd(OLED_OFFSET);   // Column start address (0 = reset)
-	oled_cmd(OLED_OFFSET+OLED_W-1); // Column end address (127 = reset)
+	ssd1306_cmd(SSD1306_COLUMNADDR);
+	ssd1306_cmd(SSD1306_OFFSET);   // Column start address (0 = reset)
+	ssd1306_cmd(SSD1306_OFFSET+SSD1306_W-1); // Column end address (127 = reset)
 	
-	oled_cmd(SSD1306_PAGEADDR);
-	oled_cmd(0); // Page start address (0 = reset)
-	oled_cmd(7); // Page end address
+	ssd1306_cmd(SSD1306_PAGEADDR);
+	ssd1306_cmd(0); // Page start address (0 = reset)
+	ssd1306_cmd(7); // Page end address
 
-#ifdef OLED_FULLUSE
+#ifdef SSD1306_FULLUSE
 	/* for fully used rows just plow thru everything */
-    for(i=0;i<sizeof(oled_buffer);i+=OLED_PSZ)
+    for(i=0;i<sizeof(ssd1306_buffer);i+=SSD1306_PSZ)
 	{
 		/* send PSZ block of data */
-		oled_data(&oled_buffer[i], OLED_PSZ);
+		ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ);
 	}
 #else
 	/* for displays with odd rows unused expand bytes */
-	uint8_t tbuf[OLED_PSZ], j, k;
-    for(i=0;i<sizeof(oled_buffer);i+=128)
+	uint8_t tbuf[SSD1306_PSZ], j, k;
+    for(i=0;i<sizeof(ssd1306_buffer);i+=128)
 	{
 		/* low nybble */
-		for(j=0;j<128;j+=OLED_PSZ)
+		for(j=0;j<128;j+=SSD1306_PSZ)
 		{
-			for(k=0;k<OLED_PSZ;k++)
-				tbuf[k] = expand[oled_buffer[i+j+k]&0xf];
+			for(k=0;k<SSD1306_PSZ;k++)
+				tbuf[k] = expand[ssd1306_buffer[i+j+k]&0xf];
 			
 			/* send PSZ block of data */
-			oled_data(tbuf, OLED_PSZ);
+			ssd1306_data(tbuf, SSD1306_PSZ);
 		}
 		
 		/* high nybble */
-		for(j=0;j<128;j+=OLED_PSZ)
+		for(j=0;j<128;j+=SSD1306_PSZ)
 		{
-			for(k=0;k<OLED_PSZ;k++)
-				tbuf[k] = expand[(oled_buffer[i+j+k]>>4)&0xf];
+			for(k=0;k<SSD1306_PSZ;k++)
+				tbuf[k] = expand[(ssd1306_buffer[i+j+k]>>4)&0xf];
 			
 			/* send PSZ block of data */
-			oled_data(tbuf, OLED_PSZ);
+			ssd1306_data(tbuf, SSD1306_PSZ);
 		}
 	}
 #endif
@@ -215,71 +201,71 @@ void oled_refresh(void)
 /*
  * plot a pixel in the buffer
  */
-void oled_drawPixel(uint8_t x, uint8_t y, uint8_t color)
+void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color)
 {
 	uint16_t addr;
 	
 	/* clip */
-	if(x >= OLED_W)
+	if(x >= SSD1306_W)
 		return;
-	if(y >= OLED_H)
+	if(y >= SSD1306_H)
 		return;
 	
 	/* compute buffer address */
-	addr = x + OLED_W*(y/8);
+	addr = x + SSD1306_W*(y/8);
 	
 	/* set/clear bit in buffer */
 	if(color)
-		oled_buffer[addr] |= (1<<(y&7));
+		ssd1306_buffer[addr] |= (1<<(y&7));
 	else
-		oled_buffer[addr] &= ~(1<<(y&7));
+		ssd1306_buffer[addr] &= ~(1<<(y&7));
 }
 
 /*
  * plot a pixel in the buffer
  */
-void oled_xorPixel(uint8_t x, uint8_t y)
+void ssd1306_xorPixel(uint8_t x, uint8_t y)
 {
 	uint16_t addr;
 	
 	/* clip */
-	if(x >= OLED_W)
+	if(x >= SSD1306_W)
 		return;
-	if(y >= OLED_H)
+	if(y >= SSD1306_H)
 		return;
 	
 	/* compute buffer address */
-	addr = x + OLED_W*(y/8);
+	addr = x + SSD1306_W*(y/8);
 	
-	oled_buffer[addr] ^= (1<<(y&7));
+	ssd1306_buffer[addr] ^= (1<<(y&7));
 }
 
 /*
  *  fast vert line
  */
-void oled_drawFastVLine(uint8_t x, uint8_t y, uint8_t h, uint8_t color)
+void ssd1306_drawFastVLine(uint8_t x, uint8_t y, uint8_t h, uint8_t color)
 {
 	// clipping
-	if((x >= OLED_W) || (y >= OLED_H)) return;
-	if((y+h-1) >= OLED_H) h = OLED_H-y;
+	if((x >= SSD1306_W) || (y >= SSD1306_H)) return;
+	if((y+h-1) >= SSD1306_H) h = SSD1306_H-y;
 	while(h--)
 	{
-        oled_drawPixel(x, y++, color);
+        ssd1306_drawPixel(x, y++, color);
 	}
 }
 
 /*
  *  fast horiz line
  */
-void oled_drawFastHLine(uint8_t x, uint8_t y, uint8_t w, uint8_t color)
+void ssd1306_drawFastHLine(uint8_t x, uint8_t y, uint8_t w, uint8_t color)
 {
 	// clipping
-	if((x >= OLED_W) || (y >= OLED_H)) return;
-	if((x+w-1) >= OLED_W)  w = OLED_W-x;
+	if((x >= SSD1306_W) || (y >= SSD1306_H)) return;
+	if((x+w-1) >= SSD1306_W)  w = SSD1306_W-x;
 
 	while (w--)
 	{
-        oled_drawPixel(x++, y, color);
+        ssd1306_drawPixel(x++, y, color);
 	}
 }
 
@@ -304,7 +290,7 @@ void gfx_swap(uint16_t *z0, uint16_t *z1)
 /*
  * Bresenham line draw routine swiped from Wikipedia
  */
-void oled_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t color)
+void ssd1306_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t color)
 {
 	int16_t steep;
 	int16_t deltax, deltay, error, ystep, x, y;
@@ -341,10 +327,10 @@ void oled_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t c
 		/* plot point */
 		if(steep)
 			/* flip point & plot */
-			oled_drawPixel(y, x, color);
+			ssd1306_drawPixel(y, x, color);
 		else
 			/* just plot */
-			oled_drawPixel(x, y, color);
+			ssd1306_drawPixel(x, y, color);
 
 		/* update error */
 		error = error - deltay;
@@ -361,7 +347,7 @@ void oled_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t c
 /*
  *  draws a circle
  */
-void oled_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
+void ssd1306_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
 {
     /* Bresenham algorithm */
     int16_t x_pos = -radius;
@@ -370,10 +356,10 @@ void oled_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
     int16_t e2;
 
     do {
-        oled_drawPixel(x - x_pos, y + y_pos, color);
-        oled_drawPixel(x + x_pos, y + y_pos, color);
-        oled_drawPixel(x + x_pos, y - y_pos, color);
-        oled_drawPixel(x - x_pos, y - y_pos, color);
+        ssd1306_drawPixel(x - x_pos, y + y_pos, color);
+        ssd1306_drawPixel(x + x_pos, y + y_pos, color);
+        ssd1306_drawPixel(x + x_pos, y - y_pos, color);
+        ssd1306_drawPixel(x - x_pos, y - y_pos, color);
         e2 = err;
         if (e2 <= y_pos) {
             err += ++y_pos * 2 + 1;
@@ -390,7 +376,7 @@ void oled_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
 /*
  *  draws a filled circle
  */
-void oled_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
+void ssd1306_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
 {
     /* Bresenham algorithm */
     int16_t x_pos = -radius;
@@ -399,12 +385,12 @@ void oled_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
     int16_t e2;
 
     do {
-        oled_drawPixel(x - x_pos, y + y_pos, color);
-        oled_drawPixel(x + x_pos, y + y_pos, color);
-        oled_drawPixel(x + x_pos, y - y_pos, color);
-        oled_drawPixel(x - x_pos, y - y_pos, color);
-        oled_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color);
-        oled_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color);
+        ssd1306_drawPixel(x - x_pos, y + y_pos, color);
+        ssd1306_drawPixel(x + x_pos, y + y_pos, color);
+        ssd1306_drawPixel(x + x_pos, y - y_pos, color);
+        ssd1306_drawPixel(x - x_pos, y - y_pos, color);
+        ssd1306_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color);
+        ssd1306_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color);
         e2 = err;
         if (e2 <= y_pos) {
             err += ++y_pos * 2 + 1;
@@ -421,18 +407,18 @@ void oled_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
 /*
  *  draw a rectangle
  */
-void oled_drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
+void ssd1306_drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
 {
-	oled_drawFastVLine(x, y, h, color);
-	oled_drawFastVLine(x+w-1, y, h, color);
-	oled_drawFastHLine(x, y, w, color);
-	oled_drawFastHLine(x, y+h-1, w, color);
+	ssd1306_drawFastVLine(x, y, h, color);
+	ssd1306_drawFastVLine(x+w-1, y, h, color);
+	ssd1306_drawFastHLine(x, y, w, color);
+	ssd1306_drawFastHLine(x, y+h-1, w, color);
 }
 
 /*
  * fill a rectangle
  */
-void oled_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
+void ssd1306_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
 {
 	uint8_t m, n=y, iw = w;
 	
@@ -445,7 +431,7 @@ void oled_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
 		while(w--)
 		{
 			/* invert pixels */
-			oled_drawPixel(m++, n, color);
+			ssd1306_drawPixel(m++, n, color);
 		}
 		n++;
 	}
@@ -454,7 +440,7 @@ void oled_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
 /*
  * invert a rectangle in the buffer
  */
-void oled_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
+void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
 {
 	uint8_t m, n=y, iw = w;
 	
@@ -467,7 +453,7 @@ void oled_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
 		while(w--)
 		{
 			/* invert pixels */
-			oled_xorPixel(m++, n);
+			ssd1306_xorPixel(m++, n);
 		}
 		n++;
 	}
@@ -476,7 +462,7 @@ void oled_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
 /*
  * Draw character to the display buffer
  */
-void oled_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color)
+void ssd1306_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color)
 {
 	uint16_t i, j, col;
 	uint8_t d;
@@ -491,7 +477,7 @@ void oled_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color)
 			else
 				col = (~color)&1;
 			
-			oled_drawPixel(x+j, y+i, col);
+			ssd1306_drawPixel(x+j, y+i, col);
 			
 			// next bit
 			d <<= 1;
@@ -502,13 +488,13 @@ void oled_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color)
 /*
  * draw a string to the display
  */
-void oled_drawstr(uint8_t x, uint8_t y, char *str, uint8_t color)
+void ssd1306_drawstr(uint8_t x, uint8_t y, char *str, uint8_t color)
 {
 	uint8_t c;
 	
 	while((c=*str++))
 	{
-		oled_drawchar(x, y, c, color);
+		ssd1306_drawchar(x, y, c, color);
 		x += 8;
 		if(x>120)
 			break;
@@ -528,7 +514,7 @@ typedef enum {
 /*
  * Draw character to the display buffer, scaled to size
  */
-void oled_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_size_t font_size)
+void ssd1306_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_size_t font_size)
 {
     uint16_t i, j, col;
     uint8_t d;
@@ -554,7 +540,7 @@ void oled_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_siz
             // Draw the pixel at the original size and scaled size using nested for-loops
             for (uint8_t k = 0; k < font_scale; k++) {
                 for (uint8_t l = 0; l < font_scale; l++) {
-                    oled_drawPixel(x + (j * font_scale) + k, y + (i * font_scale) + l, col);
+                    ssd1306_drawPixel(x + (j * font_scale) + k, y + (i * font_scale) + l, col);
                 }
             }
 
@@ -567,13 +553,13 @@ void oled_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_siz
 /*
  * draw a string to the display buffer, scaled to size
  */
-void oled_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t font_size)
+void ssd1306_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t font_size)
 {
 	uint8_t c;
 	
 	while((c=*str++))
 	{
-		oled_drawchar_sz(x, y, c, color, font_size);
+		ssd1306_drawchar_sz(x, y, c, color, font_size);
 		x += 8 * font_size;
 		if(x>128 - 8 * font_size)
 			break;
@@ -583,22 +569,22 @@ void oled_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t
 /*
  * initialize I2C and OLED
  */
-uint8_t oled_init(void)
+uint8_t ssd1306_init(void)
 {
-	// initialize port
-	i2c_init();
+	// pulse reset
+	ssd1306_rst();
 	
 	// initialize OLED
-	uint8_t *cmd_list = (uint8_t *)oled_init_array;
+	uint8_t *cmd_list = (uint8_t *)ssd1306_init_array;
 	while(*cmd_list != SSD1306_TERMINATE_CMDS)
 	{
-		if(oled_cmd(*cmd_list++))
+		if(ssd1306_cmd(*cmd_list++))
 			return 1;
 	}
 	
 	// clear display
-	oled_setbuf(0);
-	oled_refresh();
+	ssd1306_setbuf(0);
+	ssd1306_refresh();
 	
 	return 0;
 }
diff --git a/examples/i2c_oled/i2c.h b/examples/i2c_oled/ssd1306_i2c.h
similarity index 58%
rename from examples/i2c_oled/i2c.h
rename to examples/i2c_oled/ssd1306_i2c.h
index 335aafa..45a5197 100644
--- a/examples/i2c_oled/i2c.h
+++ b/examples/i2c_oled/ssd1306_i2c.h
@@ -1,29 +1,34 @@
 /*
- * Single-File-Header for I2C
- * 04-07-2023 E. Brombaugh
+ * Single-File-Header for SSD1306 I2C interface
+ * 05-07-2023 E. Brombaugh
  */
 
-#ifndef _I2C_H
-#define _I2C_H
+#ifndef _SSD1306_I2C_H
+#define _SSD1306_I2C_H
+
+#include <string.h>
+
+// SSD1306 I2C address
+#define SSD1306_I2C_ADDR 0x3c
 
 // I2C Bus clock rate - must be lower the Logic clock rate
-#define I2C_CLKRATE 1000000
+#define SSD1306_I2C_CLKRATE 1000000
 
 // I2C Logic clock rate - must be higher than Bus clock rate
-#define I2C_PRERATE 2000000
+#define SSD1306_I2C_PRERATE 2000000
 
 // uncomment this for high-speed 36% duty cycle, otherwise 33%
-#define I2C_DUTY
+#define SSD1306_I2C_DUTY
 
 // I2C Timeout count
 #define TIMEOUT_MAX 100000
 
 // uncomment this to enable IRQ-driven operation
-#define I2C_IRQ
+//#define SSD1306_I2C_IRQ
 
-#ifdef I2C_IRQ
+#ifdef SSD1306_I2C_IRQ
 // some stuff that IRQ mode needs
-volatile uint8_t i2c_send_buffer[64], *i2c_send_ptr, i2c_send_sz, i2c_irq_state;
+volatile uint8_t ssd1306_i2c_send_buffer[64], *ssd1306_i2c_send_ptr, ssd1306_i2c_send_sz, ssd1306_i2c_irq_state;
 
 // uncomment this to enable time diags in IRQ
 //#define IRQ_DIAG
@@ -32,7 +37,7 @@ volatile uint8_t i2c_send_buffer[64], *i2c_send_ptr, i2c_send_sz, i2c_irq_state;
 /*
  * init just I2C
  */
-void i2c_setup(void)
+void ssd1306_i2c_setup(void)
 {
 	uint16_t tempreg;
 	
@@ -43,34 +48,34 @@ void i2c_setup(void)
 	// set freq
 	tempreg = I2C1->CTLR2;
 	tempreg &= ~I2C_CTLR2_FREQ;
-	tempreg |= (APB_CLOCK/I2C_PRERATE)&I2C_CTLR2_FREQ;
+	tempreg |= (APB_CLOCK/SSD1306_I2C_PRERATE)&I2C_CTLR2_FREQ;
 	I2C1->CTLR2 = tempreg;
 	
 	// Set clock config
 	tempreg = 0;
-#if (I2C_CLKRATE <= 100000)
+#if (SSD1306_I2C_CLKRATE <= 100000)
 	// standard mode good to 100kHz
-	tempreg = (APB_CLOCK/(2*I2C_CLKRATE))&I2C_CKCFGR_CCR;
+	tempreg = (APB_CLOCK/(2*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR;
 #else
 	// fast mode over 100kHz
-#ifndef I2C_DUTY
+#ifndef SSD1306_I2C_DUTY
 	// 33% duty cycle
-	tempreg = (APB_CLOCK/(3*I2C_CLKRATE))&I2C_CKCFGR_CCR;
+	tempreg = (APB_CLOCK/(3*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR;
 #else
 	// 36% duty cycle
-	tempreg = (APB_CLOCK/(25*I2C_CLKRATE))&I2C_CKCFGR_CCR;
+	tempreg = (APB_CLOCK/(25*SSD1306_I2C_CLKRATE))&I2C_CKCFGR_CCR;
 	tempreg |= I2C_CKCFGR_DUTY;
 #endif
 	tempreg |= I2C_CKCFGR_FS;
 #endif
 	I2C1->CKCFGR = tempreg;
 
-#ifdef I2C_IRQ
+#ifdef SSD1306_I2C_IRQ
 	// enable IRQ driven operation
 	NVIC_EnableIRQ(I2C1_EV_IRQn);
 	
 	// initialize the state
-	i2c_irq_state = 0;
+	ssd1306_i2c_irq_state = 0;
 #endif
 	
 	// Enable I2C
@@ -80,37 +85,6 @@ void i2c_setup(void)
 	I2C1->CTLR1 |= I2C_CTLR1_ACK;
 }
 
-/*
- * init I2C and GPIO
- */
-void i2c_init(void)
-{
-	// Enable GPIOC and I2C
-	RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;
-	RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
-	
-	// PC1 is SDA, 10MHz Output, alt func, open-drain
-	GPIOC->CFGLR &= ~(0xf<<(4*1));
-	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*1);
-	
-	// PC2 is SCL, 10MHz Output, alt func, open-drain
-	GPIOC->CFGLR &= ~(0xf<<(4*2));
-	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*2);
-	
-#ifdef IRQ_DIAG
-	// GPIO diags on PC3/PC4
-	GPIOC->CFGLR &= ~(0xf<<(4*3));
-	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*3);
-	GPIOC->BSHR = (1<<(16+3));
-	GPIOC->CFGLR &= ~(0xf<<(4*4));
-	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
-	GPIOC->BSHR = (1<<(16+4));
-#endif
-
-	// load I2C regs
-	i2c_setup();
-}
-
 /*
  * error descriptions
  */
@@ -126,37 +100,37 @@ char *errstr[] =
 /*
  * error handler
  */
-uint8_t i2c_error(uint8_t err)
+uint8_t ssd1306_i2c_error(uint8_t err)
 {
 	// report error
-	printf("i2c_error - timeout waiting for %s\n\r", errstr[err]);
+	printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]);
 	
 	// reset & initialize I2C
-	i2c_setup();
+	ssd1306_i2c_setup();
 
 	return 1;
 }
 
 // event codes we use
-#define  I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001)  /* BUSY, MSL and SB flag */
-#define  I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082)  /* BUSY, MSL, ADDR, TXE and TRA flags */
-#define  I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084)  /* TRA, BUSY, MSL, TXE and BTF flags */
+#define  SSD1306_I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001)  /* BUSY, MSL and SB flag */
+#define  SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082)  /* BUSY, MSL, ADDR, TXE and TRA flags */
+#define  SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084)  /* TRA, BUSY, MSL, TXE and BTF flags */
 
 /*
  * check for 32-bit event codes
  */
-uint8_t i2c_chk_evt(uint32_t event_mask)
+uint8_t ssd1306_i2c_chk_evt(uint32_t event_mask)
 {
 	/* read order matters here! STAR1 before STAR2!! */
 	uint32_t status = I2C1->STAR1 | (I2C1->STAR2<<16);
 	return (status & event_mask) == event_mask;
 }
 
-#ifdef I2C_IRQ
+#ifdef SSD1306_I2C_IRQ
 /*
  * packet send for IRQ-driven operation
  */
-uint8_t i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
+uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
 {
 	int32_t timeout;
 	
@@ -165,11 +139,11 @@ uint8_t i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
 #endif
 	
 	// error out if buffer under/overflow
-	if((sz > sizeof(i2c_send_buffer)) || !sz)
+	if((sz > sizeof(ssd1306_i2c_send_buffer)) || !sz)
 		return 2;
 	
 	// wait for previous packet to finish
-	while(i2c_irq_state);
+	while(ssd1306_i2c_irq_state);
 	
 #ifdef IRQ_DIAG
 	GPIOC->BSHR = (1<<(16+3));
@@ -177,37 +151,37 @@ uint8_t i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
 #endif
 	
 	// init buffer for sending
-	i2c_send_sz = sz;
-	i2c_send_ptr = i2c_send_buffer;
-	memcpy((uint8_t *)i2c_send_buffer, data, sz);
+	ssd1306_i2c_send_sz = sz;
+	ssd1306_i2c_send_ptr = ssd1306_i2c_send_buffer;
+	memcpy((uint8_t *)ssd1306_i2c_send_buffer, data, sz);
 	
 	// wait for not busy
 	timeout = TIMEOUT_MAX;
 	while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--));
 	if(timeout==-1)
-		return i2c_error(0);
+		return ssd1306_i2c_error(0);
 
 	// Set START condition
 	I2C1->CTLR1 |= I2C_CTLR1_START;
 
 	// wait for master mode select
 	timeout = TIMEOUT_MAX;
-	while((!i2c_chk_evt(I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
+	while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
 	if(timeout==-1)
-		return i2c_error(1);
+		return ssd1306_i2c_error(1);
 	
 	// send 7-bit address + write flag
 	I2C1->DATAR = addr<<1;
 
 	// wait for transmit condition
 	timeout = TIMEOUT_MAX;
-	while((!i2c_chk_evt(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
+	while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
 	if(timeout==-1)
-		return i2c_error(2);
+		return ssd1306_i2c_error(2);
 
 	// Enable TXE interrupt
 	I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN;
-	i2c_irq_state = 1;
+	ssd1306_i2c_irq_state = 1;
 
 #ifdef IRQ_DIAG
 	GPIOC->BSHR = (1<<(16+4));
@@ -237,20 +211,20 @@ void I2C1_EV_IRQHandler(void)
 	if(STAR1 & I2C_STAR1_TXE)
 	{
 		/* check for remaining data */
-		if(i2c_send_sz--)
-			I2C1->DATAR = *i2c_send_ptr++;
+		if(ssd1306_i2c_send_sz--)
+			I2C1->DATAR = *ssd1306_i2c_send_ptr++;
 
 		/* was that the last byte? */
-		if(!i2c_send_sz)
+		if(!ssd1306_i2c_send_sz)
 		{
 			// disable TXE interrupt
 			I2C1->CTLR2 &= ~(I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN);
 			
 			// reset IRQ state
-			i2c_irq_state = 0;
+			ssd1306_i2c_irq_state = 0;
 			
 			// wait for tx complete
-			while(!i2c_chk_evt(I2C_EVENT_MASTER_BYTE_TRANSMITTED));
+			while(!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED));
 
 			// set STOP condition
 			I2C1->CTLR1 |= I2C_CTLR1_STOP;
@@ -263,9 +237,9 @@ void I2C1_EV_IRQHandler(void)
 }
 #else
 /*
- * packet send for polled operation
+ * low-level packet send for blocking polled operation via i2c
  */
-uint8_t i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
+uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
 {
 	int32_t timeout;
 	
@@ -273,25 +247,25 @@ uint8_t i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
 	timeout = TIMEOUT_MAX;
 	while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--));
 	if(timeout==-1)
-		return i2c_error(0);
+		return ssd1306_i2c_error(0);
 
 	// Set START condition
 	I2C1->CTLR1 |= I2C_CTLR1_START;
 	
 	// wait for master mode select
 	timeout = TIMEOUT_MAX;
-	while((!i2c_chk_evt(I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
+	while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
 	if(timeout==-1)
-		return i2c_error(1);
+		return ssd1306_i2c_error(1);
 	
 	// send 7-bit address + write flag
 	I2C1->DATAR = addr<<1;
 
 	// wait for transmit condition
 	timeout = TIMEOUT_MAX;
-	while((!i2c_chk_evt(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
+	while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
 	if(timeout==-1)
-		return i2c_error(2);
+		return ssd1306_i2c_error(2);
 
 	// send data one byte at a time
 	while(sz--)
@@ -300,7 +274,7 @@ uint8_t i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
 		timeout = TIMEOUT_MAX;
 		while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--));
 		if(timeout==-1)
-			return i2c_error(3);
+			return ssd1306_i2c_error(3);
 		
 		// send command
 		I2C1->DATAR = *data++;
@@ -308,9 +282,9 @@ uint8_t i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
 
 	// wait for tx complete
 	timeout = TIMEOUT_MAX;
-	while((!i2c_chk_evt(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--));
+	while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--));
 	if(timeout==-1)
-		return i2c_error(4);
+		return ssd1306_i2c_error(4);
 
 	// set STOP condition
 	I2C1->CTLR1 |= I2C_CTLR1_STOP;
@@ -320,4 +294,66 @@ uint8_t i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
 }
 #endif
 
+/*
+ * high-level packet send for I2C
+ */
+uint8_t ssd1306_pkt_send(uint8_t *data, uint8_t sz, uint8_t cmd)
+{
+	uint8_t pkt[33];
+	
+	/* build command or data packets */
+	if(cmd)
+	{
+		pkt[0] = 0;
+		pkt[1] = cmd;
+	}
+	else
+	{
+		pkt[0] = 0x40;
+		memcpy(&pkt[1], data, sz);
+	}
+	return ssd1306_i2c_send(SSD1306_I2C_ADDR, pkt, sz+1);
+}
+
+/*
+ * init I2C and GPIO
+ */
+uint8_t ssd1306_i2c_init(void)
+{
+	// Enable GPIOC and I2C
+	RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;
+	RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
+	
+	// PC1 is SDA, 10MHz Output, alt func, open-drain
+	GPIOC->CFGLR &= ~(0xf<<(4*1));
+	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*1);
+	
+	// PC2 is SCL, 10MHz Output, alt func, open-drain
+	GPIOC->CFGLR &= ~(0xf<<(4*2));
+	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*2);
+	
+#ifdef IRQ_DIAG
+	// GPIO diags on PC3/PC4
+	GPIOC->CFGLR &= ~(0xf<<(4*3));
+	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*3);
+	GPIOC->BSHR = (1<<(16+3));
+	GPIOC->CFGLR &= ~(0xf<<(4*4));
+	GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
+	GPIOC->BSHR = (1<<(16+4));
+#endif
+
+	// load I2C regs
+	ssd1306_i2c_setup();
+	
+	// test if SSD1306 is on the bus by sending display off command
+	uint8_t command = 0xAF;
+	return ssd1306_pkt_send(&command, 1, 1);
+}
+
+/*
+ * reset is not used for SSD1306 I2C interface
+ */
+void ssd1306_rst(void)
+{
+}
 #endif
-- 
GitLab