Compare commits
No commits in common. "b3bf3d1f04a58f2fb04350dc76c8c9c75ec5d9a7" and "842e23ae62fd74419ec7b6334c280497daf9fde2" have entirely different histories.
b3bf3d1f04
...
842e23ae62
4 changed files with 553 additions and 3338 deletions
122
oled_gol.c
122
oled_gol.c
|
@ -1,133 +1,23 @@
|
||||||
|
/* Template app on which you can build your own. */
|
||||||
|
|
||||||
#define SSD1306_128X32
|
|
||||||
#include "ch32v003fun.h"
|
#include "ch32v003fun.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "ssd1306_i2c.h"
|
|
||||||
#include "ssd1306.h"
|
|
||||||
|
|
||||||
#define PIN_LED PC4
|
#define PIN_LED PC4
|
||||||
#define PIN_BTN PC7
|
|
||||||
|
|
||||||
#define u8 uint8_t
|
|
||||||
#define u16 uint16_t
|
|
||||||
|
|
||||||
uint8_t gol_tmp[sizeof(ssd1306_buffer)];
|
|
||||||
|
|
||||||
uint32_t lfsr = 1; // PRNG state
|
|
||||||
|
|
||||||
u8 rand8();
|
|
||||||
u8 get_pixel(u8 x, u8 y);
|
|
||||||
void gol_step();
|
|
||||||
void r_pentomino();
|
|
||||||
void randomise();
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
SystemInit();
|
SystemInit();
|
||||||
funGpioInitAll();
|
|
||||||
funPinMode(PIN_LED, GPIO_CFGLR_OUT_10Mhz_PP);
|
|
||||||
funPinMode(PIN_BTN, GPIO_CFGLR_IN_PUPD);
|
|
||||||
|
|
||||||
ssd1306_i2c_init();
|
funGpioInitAll();
|
||||||
ssd1306_init();
|
|
||||||
randomise();
|
funPinMode(PIN_LED, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
ssd1306_refresh();
|
|
||||||
gol_step();
|
|
||||||
memcpy(ssd1306_buffer, gol_tmp, sizeof(ssd1306_buffer));
|
|
||||||
|
|
||||||
if (funDigitalRead(PIN_BTN))
|
|
||||||
{
|
|
||||||
randomise();
|
|
||||||
ssd1306_refresh();
|
|
||||||
funDigitalWrite(PIN_LED, FUN_HIGH);
|
funDigitalWrite(PIN_LED, FUN_HIGH);
|
||||||
Delay_Ms(100);
|
Delay_Ms(150);
|
||||||
funDigitalWrite(PIN_LED, FUN_LOW);
|
funDigitalWrite(PIN_LED, FUN_LOW);
|
||||||
}
|
Delay_Ms(150);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void r_pentomino()
|
|
||||||
{
|
|
||||||
ssd1306_setbuf(0);
|
|
||||||
ssd1306_drawPixel(20, 20, 1);
|
|
||||||
ssd1306_drawPixel(21, 20, 1);
|
|
||||||
ssd1306_drawPixel(21, 21, 1);
|
|
||||||
ssd1306_drawPixel(22, 21, 1);
|
|
||||||
ssd1306_drawPixel(21, 22, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void randomise()
|
|
||||||
{
|
|
||||||
lfsr = SysTick->CNT;
|
|
||||||
for (u16 b = 0; b < sizeof(ssd1306_buffer); b++)
|
|
||||||
{
|
|
||||||
ssd1306_buffer[b] = rand8();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 get_pixel(u8 x, u8 y)
|
|
||||||
{
|
|
||||||
x = x & 127;
|
|
||||||
y = y & 31;
|
|
||||||
u8 slice = ssd1306_buffer[(y / 8) * 128 + x];
|
|
||||||
return (slice & (1 << (y & 7))) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gol_step()
|
|
||||||
{
|
|
||||||
// slow pixel-by-pixel implementation
|
|
||||||
for (u8 x = 0; x < 128; x++)
|
|
||||||
{
|
|
||||||
for (u8 y = 0; y < 32; y++)
|
|
||||||
{
|
|
||||||
u8 this = get_pixel(x, y);
|
|
||||||
u8 sum = 0;
|
|
||||||
sum += get_pixel(x + 127, y + 31);
|
|
||||||
sum += get_pixel(x + 127, y);
|
|
||||||
sum += get_pixel(x + 127, y + 1);
|
|
||||||
sum += get_pixel(x, y + 31);
|
|
||||||
sum += get_pixel(x, y + 1);
|
|
||||||
sum += get_pixel(x + 1, y + 31);
|
|
||||||
sum += get_pixel(x + 1, y);
|
|
||||||
sum += get_pixel(x + 1, y + 1);
|
|
||||||
|
|
||||||
u8 new_state = (sum == 3) | ((sum == 2) & this);
|
|
||||||
u16 addr = x + (y / 8) * 128;
|
|
||||||
|
|
||||||
if (new_state)
|
|
||||||
gol_tmp[addr] |= (1 << (y & 7));
|
|
||||||
else
|
|
||||||
gol_tmp[addr] &= ~(1 << (y & 7));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* White Noise Generator State */
|
|
||||||
#define NOISE_BITS 8
|
|
||||||
#define NOISE_MASK ((1 << NOISE_BITS) - 1)
|
|
||||||
#define NOISE_POLY_TAP0 31
|
|
||||||
#define NOISE_POLY_TAP1 21
|
|
||||||
#define NOISE_POLY_TAP2 1
|
|
||||||
#define NOISE_POLY_TAP3 0
|
|
||||||
/*
|
|
||||||
* random byte generator, taken from ch32v003fun examples
|
|
||||||
*/
|
|
||||||
uint8_t rand8(void)
|
|
||||||
{
|
|
||||||
uint8_t bit;
|
|
||||||
uint32_t new_data;
|
|
||||||
|
|
||||||
for (bit = 0; bit < NOISE_BITS; bit++)
|
|
||||||
{
|
|
||||||
new_data = ((lfsr >> NOISE_POLY_TAP0) ^
|
|
||||||
(lfsr >> NOISE_POLY_TAP1) ^
|
|
||||||
(lfsr >> NOISE_POLY_TAP2) ^
|
|
||||||
(lfsr >> NOISE_POLY_TAP3));
|
|
||||||
lfsr = (lfsr << 1) | (new_data & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lfsr & NOISE_MASK;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
489
ssd1306.h
489
ssd1306.h
|
@ -1,489 +0,0 @@
|
||||||
/*
|
|
||||||
* Single-File-Header for using SPI OLED
|
|
||||||
* 05-05-2023 E. Brombaugh
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SSD1306_H
|
|
||||||
#define _SSD1306_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// comfortable packet size for this OLED
|
|
||||||
#define SSD1306_PSZ 32
|
|
||||||
|
|
||||||
// characteristics of each type
|
|
||||||
#if !defined(SSD1306_64X32) && !defined(SSD1306_128X32) && !defined(SSD1306_128X64)
|
|
||||||
#error "Please define the SSD1306_WXH resolution used in your application"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SSD1306_64X32
|
|
||||||
#define SSD1306_W 64
|
|
||||||
#define SSD1306_H 32
|
|
||||||
#define SSD1306_FULLUSE
|
|
||||||
#define SSD1306_OFFSET 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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 ssd1306_cmd(uint8_t cmd)
|
|
||||||
{
|
|
||||||
ssd1306_pkt_send(&cmd, 1, 1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* send OLED data packet (up to 32 bytes)
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_data(uint8_t *data, uint8_t sz)
|
|
||||||
{
|
|
||||||
ssd1306_pkt_send(data, sz, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SSD1306_SETCONTRAST 0x81
|
|
||||||
#define SSD1306_SEGREMAP 0xA0
|
|
||||||
#define SSD1306_DISPLAYALLON_RESUME 0xA4
|
|
||||||
#define SSD1306_DISPLAYALLON 0xA5
|
|
||||||
#define SSD1306_NORMALDISPLAY 0xA6
|
|
||||||
#define SSD1306_INVERTDISPLAY 0xA7
|
|
||||||
#define SSD1306_DISPLAYOFF 0xAE
|
|
||||||
#define SSD1306_DISPLAYON 0xAF
|
|
||||||
#define SSD1306_SETDISPLAYOFFSET 0xD3
|
|
||||||
#define SSD1306_SETCOMPINS 0xDA
|
|
||||||
#define SSD1306_SETVCOMDETECT 0xDB
|
|
||||||
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
|
|
||||||
#define SSD1306_SETPRECHARGE 0xD9
|
|
||||||
#define SSD1306_SETMULTIPLEX 0xA8
|
|
||||||
#define SSD1306_SETLOWCOLUMN 0x00
|
|
||||||
#define SSD1306_SETHIGHCOLUMN 0x10
|
|
||||||
#define SSD1306_SETSTARTLINE 0x40
|
|
||||||
#define SSD1306_MEMORYMODE 0x20
|
|
||||||
#define SSD1306_COLUMNADDR 0x21
|
|
||||||
#define SSD1306_PAGEADDR 0x22
|
|
||||||
#define SSD1306_COMSCANINC 0xC0
|
|
||||||
#define SSD1306_COMSCANDEC 0xC8
|
|
||||||
#define SSD1306_CHARGEPUMP 0x8D
|
|
||||||
#define SSD1306_EXTERNALVCC 0x1
|
|
||||||
#define SSD1306_SWITCHCAPVCC 0x2
|
|
||||||
#define SSD1306_TERMINATE_CMDS 0xFF
|
|
||||||
|
|
||||||
/* choose VCC mode */
|
|
||||||
#define SSD1306_EXTERNALVCC 0x1
|
|
||||||
#define SSD1306_SWITCHCAPVCC 0x2
|
|
||||||
// #define vccstate SSD1306_EXTERNALVCC
|
|
||||||
#define vccstate SSD1306_SWITCHCAPVCC
|
|
||||||
|
|
||||||
// OLED initialization commands for 128x32
|
|
||||||
const uint8_t ssd1306_init_array[] =
|
|
||||||
{
|
|
||||||
SSD1306_DISPLAYOFF, // 0xAE
|
|
||||||
SSD1306_SETDISPLAYCLOCKDIV, // 0xD5
|
|
||||||
0x80, // the suggested ratio 0x80
|
|
||||||
SSD1306_SETMULTIPLEX, // 0xA8
|
|
||||||
#ifdef SSD1306_64X32
|
|
||||||
0x1F, // for 64-wide displays
|
|
||||||
#else
|
|
||||||
0x3F, // for 128-wide displays
|
|
||||||
#endif
|
|
||||||
SSD1306_SETDISPLAYOFFSET, // 0xD3
|
|
||||||
0x00, // no offset
|
|
||||||
SSD1306_SETSTARTLINE | 0x0, // 0x40 | line
|
|
||||||
SSD1306_CHARGEPUMP, // 0x8D
|
|
||||||
0x14, // enable?
|
|
||||||
SSD1306_MEMORYMODE, // 0x20
|
|
||||||
0x00, // 0x0 act like ks0108
|
|
||||||
SSD1306_SEGREMAP | 0x1, // 0xA0 | bit
|
|
||||||
SSD1306_COMSCANDEC,
|
|
||||||
SSD1306_SETCOMPINS, // 0xDA
|
|
||||||
0x12, //
|
|
||||||
SSD1306_SETCONTRAST, // 0x81
|
|
||||||
0x8F,
|
|
||||||
SSD1306_SETPRECHARGE, // 0xd9
|
|
||||||
0xF1,
|
|
||||||
SSD1306_SETVCOMDETECT, // 0xDB
|
|
||||||
0x40,
|
|
||||||
SSD1306_DISPLAYALLON_RESUME, // 0xA4
|
|
||||||
SSD1306_NORMALDISPLAY, // 0xA6
|
|
||||||
SSD1306_DISPLAYON, // 0xAF --turn on oled panel
|
|
||||||
SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end
|
|
||||||
};
|
|
||||||
|
|
||||||
// the display buffer
|
|
||||||
uint8_t ssd1306_buffer[SSD1306_W * SSD1306_H / 8];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* set the buffer to a color
|
|
||||||
*/
|
|
||||||
void ssd1306_setbuf(uint8_t color)
|
|
||||||
{
|
|
||||||
memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef SSD1306_FULLUSE
|
|
||||||
/*
|
|
||||||
* expansion array for OLED with every other row unused
|
|
||||||
*/
|
|
||||||
const uint8_t expand[16] =
|
|
||||||
{
|
|
||||||
0x00,
|
|
||||||
0x02,
|
|
||||||
0x08,
|
|
||||||
0x0a,
|
|
||||||
0x20,
|
|
||||||
0x22,
|
|
||||||
0x28,
|
|
||||||
0x2a,
|
|
||||||
0x80,
|
|
||||||
0x82,
|
|
||||||
0x88,
|
|
||||||
0x8a,
|
|
||||||
0xa0,
|
|
||||||
0xa2,
|
|
||||||
0xa8,
|
|
||||||
0xaa,
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Send the frame buffer
|
|
||||||
*/
|
|
||||||
void ssd1306_refresh(void)
|
|
||||||
{
|
|
||||||
uint16_t i;
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
ssd1306_cmd(SSD1306_PAGEADDR);
|
|
||||||
ssd1306_cmd(0); // Page start address (0 = reset)
|
|
||||||
ssd1306_cmd(7); // Page end address
|
|
||||||
|
|
||||||
#ifdef SSD1306_FULLUSE
|
|
||||||
/* for fully used rows just plow thru everything */
|
|
||||||
for (i = 0; i < sizeof(ssd1306_buffer); i += SSD1306_PSZ)
|
|
||||||
{
|
|
||||||
/* send PSZ block of data */
|
|
||||||
ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* for displays with odd rows unused expand bytes */
|
|
||||||
uint8_t tbuf[SSD1306_PSZ], j, k;
|
|
||||||
for (i = 0; i < sizeof(ssd1306_buffer); i += 128) // for each page
|
|
||||||
{
|
|
||||||
/* low nybble */
|
|
||||||
for (j = 0; j < 128; j += SSD1306_PSZ)
|
|
||||||
{
|
|
||||||
for (k = 0; k < SSD1306_PSZ; k++)
|
|
||||||
tbuf[k] = expand[ssd1306_buffer[i + j + k] & 0xf];
|
|
||||||
|
|
||||||
/* send PSZ block of data */
|
|
||||||
ssd1306_data(tbuf, SSD1306_PSZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* high nybble */
|
|
||||||
for (j = 0; j < 128; j += SSD1306_PSZ)
|
|
||||||
{
|
|
||||||
for (k = 0; k < SSD1306_PSZ; k++)
|
|
||||||
tbuf[k] = expand[(ssd1306_buffer[i + j + k] >> 4) & 0xf];
|
|
||||||
|
|
||||||
/* send PSZ block of data */
|
|
||||||
ssd1306_data(tbuf, SSD1306_PSZ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* plot a pixel in the buffer
|
|
||||||
*/
|
|
||||||
void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color)
|
|
||||||
{
|
|
||||||
uint16_t addr;
|
|
||||||
|
|
||||||
/* clip */
|
|
||||||
if (x >= SSD1306_W)
|
|
||||||
return;
|
|
||||||
if (y >= SSD1306_H)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* compute buffer address */
|
|
||||||
addr = x + SSD1306_W * (y / 8);
|
|
||||||
|
|
||||||
/* set/clear bit in buffer */
|
|
||||||
if (color)
|
|
||||||
ssd1306_buffer[addr] |= (1 << (y & 7));
|
|
||||||
else
|
|
||||||
ssd1306_buffer[addr] &= ~(1 << (y & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* fast vert line
|
|
||||||
*/
|
|
||||||
void ssd1306_drawFastVLine(uint8_t x, uint8_t y, uint8_t h, uint8_t color)
|
|
||||||
{
|
|
||||||
// clipping
|
|
||||||
if ((x >= SSD1306_W) || (y >= SSD1306_H))
|
|
||||||
return;
|
|
||||||
if ((y + h - 1) >= SSD1306_H)
|
|
||||||
h = SSD1306_H - y;
|
|
||||||
while (h--)
|
|
||||||
{
|
|
||||||
ssd1306_drawPixel(x, y++, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* fast horiz line
|
|
||||||
*/
|
|
||||||
void ssd1306_drawFastHLine(uint8_t x, uint8_t y, uint8_t w, uint8_t color)
|
|
||||||
{
|
|
||||||
// clipping
|
|
||||||
if ((x >= SSD1306_W) || (y >= SSD1306_H))
|
|
||||||
return;
|
|
||||||
if ((x + w - 1) >= SSD1306_W)
|
|
||||||
w = SSD1306_W - x;
|
|
||||||
|
|
||||||
while (w--)
|
|
||||||
{
|
|
||||||
ssd1306_drawPixel(x++, y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* abs() helper function for line drawing
|
|
||||||
*/
|
|
||||||
int16_t gfx_abs(int16_t x)
|
|
||||||
{
|
|
||||||
return (x < 0) ? -x : x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* swap() helper function for line drawing
|
|
||||||
*/
|
|
||||||
void gfx_swap(uint16_t *z0, uint16_t *z1)
|
|
||||||
{
|
|
||||||
uint16_t temp = *z0;
|
|
||||||
*z0 = *z1;
|
|
||||||
*z1 = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bresenham line draw routine swiped from Wikipedia
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* flip sense 45deg to keep error calc in range */
|
|
||||||
steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0));
|
|
||||||
|
|
||||||
if (steep)
|
|
||||||
{
|
|
||||||
gfx_swap(&x0, &y0);
|
|
||||||
gfx_swap(&x1, &y1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* run low->high */
|
|
||||||
if (x0 > x1)
|
|
||||||
{
|
|
||||||
gfx_swap(&x0, &x1);
|
|
||||||
gfx_swap(&y0, &y1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set up loop initial conditions */
|
|
||||||
deltax = x1 - x0;
|
|
||||||
deltay = gfx_abs(y1 - y0);
|
|
||||||
error = deltax / 2;
|
|
||||||
y = y0;
|
|
||||||
if (y0 < y1)
|
|
||||||
ystep = 1;
|
|
||||||
else
|
|
||||||
ystep = -1;
|
|
||||||
|
|
||||||
/* loop x */
|
|
||||||
for (x = x0; x <= x1; x++)
|
|
||||||
{
|
|
||||||
/* plot point */
|
|
||||||
if (steep)
|
|
||||||
/* flip point & plot */
|
|
||||||
ssd1306_drawPixel(y, x, color);
|
|
||||||
else
|
|
||||||
/* just plot */
|
|
||||||
ssd1306_drawPixel(x, y, color);
|
|
||||||
|
|
||||||
/* update error */
|
|
||||||
error = error - deltay;
|
|
||||||
|
|
||||||
/* update y */
|
|
||||||
if (error < 0)
|
|
||||||
{
|
|
||||||
y = y + ystep;
|
|
||||||
error = error + deltax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* draws a circle
|
|
||||||
*/
|
|
||||||
void ssd1306_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
|
|
||||||
{
|
|
||||||
/* Bresenham algorithm */
|
|
||||||
int16_t x_pos = -radius;
|
|
||||||
int16_t y_pos = 0;
|
|
||||||
int16_t err = 2 - 2 * radius;
|
|
||||||
int16_t e2;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
if (-x_pos == y_pos && e2 <= x_pos)
|
|
||||||
{
|
|
||||||
e2 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e2 > x_pos)
|
|
||||||
{
|
|
||||||
err += ++x_pos * 2 + 1;
|
|
||||||
}
|
|
||||||
} while (x_pos <= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* draws a filled circle
|
|
||||||
*/
|
|
||||||
void ssd1306_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
|
|
||||||
{
|
|
||||||
/* Bresenham algorithm */
|
|
||||||
int16_t x_pos = -radius;
|
|
||||||
int16_t y_pos = 0;
|
|
||||||
int16_t err = 2 - 2 * radius;
|
|
||||||
int16_t e2;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
if (-x_pos == y_pos && e2 <= x_pos)
|
|
||||||
{
|
|
||||||
e2 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e2 > x_pos)
|
|
||||||
{
|
|
||||||
err += ++x_pos * 2 + 1;
|
|
||||||
}
|
|
||||||
} while (x_pos <= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* draw a rectangle
|
|
||||||
*/
|
|
||||||
void ssd1306_drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t 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 ssd1306_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
|
|
||||||
{
|
|
||||||
uint8_t m, n = y, iw = w;
|
|
||||||
|
|
||||||
/* scan vertical */
|
|
||||||
while (h--)
|
|
||||||
{
|
|
||||||
m = x;
|
|
||||||
w = iw;
|
|
||||||
/* scan horizontal */
|
|
||||||
while (w--)
|
|
||||||
{
|
|
||||||
/* invert pixels */
|
|
||||||
ssd1306_drawPixel(m++, n, color);
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* invert a rectangle in the buffer
|
|
||||||
*/
|
|
||||||
void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
|
|
||||||
{
|
|
||||||
uint8_t m, n = y, iw = w;
|
|
||||||
|
|
||||||
/* scan vertical */
|
|
||||||
while (h--)
|
|
||||||
{
|
|
||||||
m = x;
|
|
||||||
w = iw;
|
|
||||||
/* scan horizontal */
|
|
||||||
while (w--)
|
|
||||||
{
|
|
||||||
/* invert pixels */
|
|
||||||
ssd1306_xorPixel(m++, n);
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* initialize I2C and OLED
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_init(void)
|
|
||||||
{
|
|
||||||
// pulse reset
|
|
||||||
ssd1306_rst();
|
|
||||||
|
|
||||||
// initialize OLED
|
|
||||||
uint8_t *cmd_list = (uint8_t *)ssd1306_init_array;
|
|
||||||
while (*cmd_list != SSD1306_TERMINATE_CMDS)
|
|
||||||
{
|
|
||||||
if (ssd1306_cmd(*cmd_list++))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear display
|
|
||||||
// ssd1306_setbuf(0);
|
|
||||||
ssd1306_refresh();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
363
ssd1306_i2c.h
363
ssd1306_i2c.h
|
@ -1,363 +0,0 @@
|
||||||
/*
|
|
||||||
* Single-File-Header for SSD1306 I2C interface
|
|
||||||
* 05-07-2023 E. Brombaugh
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 SSD1306_I2C_CLKRATE 1000000
|
|
||||||
|
|
||||||
// I2C Logic clock rate - must be higher than Bus clock rate
|
|
||||||
#define SSD1306_I2C_PRERATE 2000000
|
|
||||||
|
|
||||||
// uncomment this for high-speed 36% duty cycle, otherwise 33%
|
|
||||||
#define SSD1306_I2C_DUTY
|
|
||||||
|
|
||||||
// I2C Timeout count
|
|
||||||
#define TIMEOUT_MAX 100000
|
|
||||||
|
|
||||||
// uncomment this to enable IRQ-driven operation
|
|
||||||
//#define SSD1306_I2C_IRQ
|
|
||||||
|
|
||||||
#ifdef SSD1306_I2C_IRQ
|
|
||||||
// some stuff that IRQ mode needs
|
|
||||||
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
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* init just I2C
|
|
||||||
*/
|
|
||||||
void ssd1306_i2c_setup(void)
|
|
||||||
{
|
|
||||||
uint16_t tempreg;
|
|
||||||
|
|
||||||
// Reset I2C1 to init all regs
|
|
||||||
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
|
|
||||||
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
|
|
||||||
|
|
||||||
// set freq
|
|
||||||
tempreg = I2C1->CTLR2;
|
|
||||||
tempreg &= ~I2C_CTLR2_FREQ;
|
|
||||||
tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK/SSD1306_I2C_PRERATE)&I2C_CTLR2_FREQ;
|
|
||||||
I2C1->CTLR2 = tempreg;
|
|
||||||
|
|
||||||
// Set clock config
|
|
||||||
tempreg = 0;
|
|
||||||
#if (SSD1306_I2C_CLKRATE <= 100000)
|
|
||||||
// standard mode good to 100kHz
|
|
||||||
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(2*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR;
|
|
||||||
#else
|
|
||||||
// fast mode over 100kHz
|
|
||||||
#ifndef SSD1306_I2C_DUTY
|
|
||||||
// 33% duty cycle
|
|
||||||
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(3*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR;
|
|
||||||
#else
|
|
||||||
// 36% duty cycle
|
|
||||||
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(25*SSD1306_I2C_CLKRATE))&I2C_CKCFGR_CCR;
|
|
||||||
tempreg |= I2C_CKCFGR_DUTY;
|
|
||||||
#endif
|
|
||||||
tempreg |= I2C_CKCFGR_FS;
|
|
||||||
#endif
|
|
||||||
I2C1->CKCFGR = tempreg;
|
|
||||||
|
|
||||||
#ifdef SSD1306_I2C_IRQ
|
|
||||||
// enable IRQ driven operation
|
|
||||||
NVIC_EnableIRQ(I2C1_EV_IRQn);
|
|
||||||
|
|
||||||
// initialize the state
|
|
||||||
ssd1306_i2c_irq_state = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Enable I2C
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_PE;
|
|
||||||
|
|
||||||
// set ACK mode
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_ACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* error descriptions
|
|
||||||
*/
|
|
||||||
char *errstr[] =
|
|
||||||
{
|
|
||||||
"not busy",
|
|
||||||
"master mode",
|
|
||||||
"transmit mode",
|
|
||||||
"tx empty",
|
|
||||||
"transmit complete",
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* error handler
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_i2c_error(uint8_t err)
|
|
||||||
{
|
|
||||||
// report error
|
|
||||||
printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]);
|
|
||||||
|
|
||||||
// reset & initialize I2C
|
|
||||||
ssd1306_i2c_setup();
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// event codes we use
|
|
||||||
#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 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 SSD1306_I2C_IRQ
|
|
||||||
/*
|
|
||||||
* packet send for IRQ-driven operation
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
|
|
||||||
{
|
|
||||||
int32_t timeout;
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(3));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// error out if buffer under/overflow
|
|
||||||
if((sz > sizeof(ssd1306_i2c_send_buffer)) || !sz)
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
// wait for previous packet to finish
|
|
||||||
while(ssd1306_i2c_irq_state);
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(16+3));
|
|
||||||
GPIOC->BSHR = (1<<(4));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// init buffer for sending
|
|
||||||
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 ssd1306_i2c_error(0);
|
|
||||||
|
|
||||||
// Set START condition
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_START;
|
|
||||||
|
|
||||||
// wait for master mode select
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(1);
|
|
||||||
|
|
||||||
// send 7-bit address + write flag
|
|
||||||
I2C1->DATAR = addr<<1;
|
|
||||||
|
|
||||||
// wait for transmit condition
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(2);
|
|
||||||
|
|
||||||
// Enable TXE interrupt
|
|
||||||
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN;
|
|
||||||
ssd1306_i2c_irq_state = 1;
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(16+4));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// exit
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IRQ handler for I2C events
|
|
||||||
*/
|
|
||||||
void I2C1_EV_IRQHandler(void) __attribute__((interrupt));
|
|
||||||
void I2C1_EV_IRQHandler(void)
|
|
||||||
{
|
|
||||||
uint16_t STAR1, STAR2 __attribute__((unused));
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(4));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// read status, clear any events
|
|
||||||
STAR1 = I2C1->STAR1;
|
|
||||||
STAR2 = I2C1->STAR2;
|
|
||||||
|
|
||||||
/* check for TXE */
|
|
||||||
if(STAR1 & I2C_STAR1_TXE)
|
|
||||||
{
|
|
||||||
/* check for remaining data */
|
|
||||||
if(ssd1306_i2c_send_sz--)
|
|
||||||
I2C1->DATAR = *ssd1306_i2c_send_ptr++;
|
|
||||||
|
|
||||||
/* was that the last byte? */
|
|
||||||
if(!ssd1306_i2c_send_sz)
|
|
||||||
{
|
|
||||||
// disable TXE interrupt
|
|
||||||
I2C1->CTLR2 &= ~(I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN);
|
|
||||||
|
|
||||||
// reset IRQ state
|
|
||||||
ssd1306_i2c_irq_state = 0;
|
|
||||||
|
|
||||||
// wait for tx complete
|
|
||||||
while(!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED));
|
|
||||||
|
|
||||||
// set STOP condition
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(16+4));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* low-level packet send for blocking polled operation via i2c
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
|
|
||||||
{
|
|
||||||
int32_t timeout;
|
|
||||||
|
|
||||||
// wait for not busy
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(0);
|
|
||||||
|
|
||||||
// Set START condition
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_START;
|
|
||||||
|
|
||||||
// wait for master mode select
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(1);
|
|
||||||
|
|
||||||
// send 7-bit address + write flag
|
|
||||||
I2C1->DATAR = addr<<1;
|
|
||||||
|
|
||||||
// wait for transmit condition
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(2);
|
|
||||||
|
|
||||||
// send data one byte at a time
|
|
||||||
while(sz--)
|
|
||||||
{
|
|
||||||
// wait for TX Empty
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(3);
|
|
||||||
|
|
||||||
// send command
|
|
||||||
I2C1->DATAR = *data++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for tx complete
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(4);
|
|
||||||
|
|
||||||
// set STOP condition
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
|
||||||
|
|
||||||
// we're happy
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#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] = *data;
|
|
||||||
}
|
|
||||||
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();
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// test if SSD1306 is on the bus by sending display off command
|
|
||||||
uint8_t command = 0xAF;
|
|
||||||
return ssd1306_pkt_send(&command, 1, 1);
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* reset is not used for SSD1306 I2C interface
|
|
||||||
*/
|
|
||||||
void ssd1306_rst(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
Loading…
Reference in a new issue