implement ch32v003 decoder
This commit is contained in:
parent
dc26664372
commit
0e95a0a1bb
9 changed files with 2082 additions and 4 deletions
586
ch32_decoder/ssd1306.h
Normal file
586
ch32_decoder/ssd1306.h
Normal file
|
@ -0,0 +1,586 @@
|
|||
/*
|
||||
* 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, // 0000 0000
|
||||
0x02, // 0000 0010
|
||||
0x08, // 0000 1000
|
||||
0x0a, // 0000 1010
|
||||
0x20, // 0010 0000
|
||||
0x22, // 0010 0010
|
||||
0x28, // 0010 1000
|
||||
0x2a, // 0010 1010
|
||||
0x80, // 1000 0000
|
||||
0x82, // 1000 0010
|
||||
0x88, // 1000 1000
|
||||
0x8a, // 1000 1010
|
||||
0xa0, // 1010 0000
|
||||
0xa2, // 1010 0010
|
||||
0xa8, // 1010 1000
|
||||
0xaa, // 1010 1010
|
||||
};
|
||||
#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));
|
||||
}
|
||||
|
||||
/*
|
||||
* plot a pixel in the buffer
|
||||
*/
|
||||
void ssd1306_xorPixel(uint8_t x, uint8_t y)
|
||||
{
|
||||
uint16_t addr;
|
||||
|
||||
/* clip */
|
||||
if (x >= SSD1306_W)
|
||||
return;
|
||||
if (y >= SSD1306_H)
|
||||
return;
|
||||
|
||||
/* compute buffer address */
|
||||
addr = x + SSD1306_W * (y / 8);
|
||||
|
||||
ssd1306_buffer[addr] ^= (1 << (y & 7));
|
||||
}
|
||||
|
||||
/*
|
||||
* draw a an image from an array, directly into to the display buffer
|
||||
* the color modes allow for overwriting and even layering (sprites!)
|
||||
*/
|
||||
void ssd1306_drawImage(uint8_t x, uint8_t y, const unsigned char *input, uint8_t width, uint8_t height, uint8_t color_mode)
|
||||
{
|
||||
uint8_t x_absolute;
|
||||
uint8_t y_absolute;
|
||||
uint8_t pixel;
|
||||
uint8_t bytes_to_draw = width / 8;
|
||||
uint16_t buffer_addr;
|
||||
|
||||
for (uint8_t line = 0; line < height; line++)
|
||||
{
|
||||
y_absolute = y + line;
|
||||
if (y_absolute >= SSD1306_H)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// SSD1306 is in vertical mode, yet we want to draw horizontally, which necessitates assembling the output bytes from the input data
|
||||
// bitmask for current pixel in vertical (output) byte
|
||||
uint8_t v_mask = 1 << (y_absolute & 7);
|
||||
|
||||
for (uint8_t byte = 0; byte < bytes_to_draw; byte++)
|
||||
{
|
||||
uint8_t input_byte = input[byte + line * bytes_to_draw];
|
||||
|
||||
for (pixel = 0; pixel < 8; pixel++)
|
||||
{
|
||||
x_absolute = x + 8 * (bytes_to_draw - byte) + pixel;
|
||||
if (x_absolute >= SSD1306_W)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8
|
||||
buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8);
|
||||
// state of current pixel
|
||||
uint8_t input_pixel = input_byte & (1 << pixel);
|
||||
|
||||
switch (color_mode)
|
||||
{
|
||||
case 0:
|
||||
// write pixels as they are
|
||||
ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0);
|
||||
break;
|
||||
case 1:
|
||||
// write pixels after inversion
|
||||
ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0);
|
||||
break;
|
||||
case 2:
|
||||
// 0 clears pixel
|
||||
ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask;
|
||||
break;
|
||||
case 3:
|
||||
// 1 sets pixel
|
||||
ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0;
|
||||
break;
|
||||
case 4:
|
||||
// 0 sets pixel
|
||||
ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0;
|
||||
break;
|
||||
case 5:
|
||||
// 1 clears pixel
|
||||
ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if SSD1306_LOG_IMAGE == 1
|
||||
printf("%02x ", input_byte);
|
||||
#endif
|
||||
}
|
||||
#if SSD1306_LOG_IMAGE == 1
|
||||
printf("\n\r");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
Loading…
Add table
Add a link
Reference in a new issue