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