demo/src/ssd1306.c

250 lines
6.3 KiB
C

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "ssd1306.h"
static void ssd1306_display(const struct driver *drv);
static void ssd1306_cls(const struct driver *drv);
static void ssd1306_dot(const struct driver *drv, uint32_t x, uint32_t y);
static void ssd1306_line(const struct driver *drv, int16_t x0, int16_t y0, int16_t x1, int16_t y1);
static void ssd1306_circle(const struct driver *drv, int16_t x0, int16_t y0, int16_t radius);
static void ssd1306_on(const struct driver *drv);
static void ssd1306_off(const struct driver *drv);
static void ssd1306_cmd(const struct driver *drv, uint8_t c);
static void ssd1306_reset_cursor(const struct driver *drv);
int ssd1306_open(const struct driver *drv)
{
assert(drv != NULL);
struct ssd1306 *this = (struct ssd1306 *)(drv->dev);
drv_open(this->output_dev);
ssd1306_off(drv);
ssd1306_cmd(drv, 0xa8); // Set Multiplex Ratio
ssd1306_cmd(drv, 0x3f); // Value
ssd1306_cmd(drv, 0xd3); // Set Display offset
ssd1306_cmd(drv, 0x00); // no vertical shift
ssd1306_cmd(drv, 0x40); // set display start line
ssd1306_cmd(drv, 0xa1); // set segment re-map
ssd1306_cmd(drv, 0xc8);
ssd1306_cmd(drv, 0xda);
ssd1306_cmd(drv, 0x12);
ssd1306_cmd(drv, 0xa4);
ssd1306_cmd(drv, 0xa6);
ssd1306_cmd(drv, 0xd5);
ssd1306_cmd(drv, 0x80);
ssd1306_cmd(drv, 0x8d);
ssd1306_cmd(drv, 0x14);
ssd1306_cmd(drv, 0x20);
ssd1306_cmd(drv, 0x20);
ssd1306_on(drv);
ssd1306_reset_cursor(drv);
return 0;
}
int ssd1306_close(const struct driver *drv)
{
assert(drv != 0);
struct ssd1306 *this = (struct ssd1306 *)(drv->dev);
ssd1306_off(drv);
drv_close(this->output_dev);
return 0;
}
int ssd1306_ioctl(const struct driver *drv, unsigned int cmd, unsigned int argc, va_list args)
{
assert(drv != 0);
int res = 0;
unsigned int xy[4];
switch(cmd) {
case IOCTL_CMD_CLS:
ssd1306_cls(drv);
break;
case IOCTL_CMD_REFRESH:
ssd1306_display(drv);
break;
case IOCTL_CMD_DRAW_PIXEL:
if(argc != 2) {
res = -1;
break;
}
for(unsigned int i = 0; i < argc; i++) {
xy[i] = va_arg(args, unsigned int);
}
ssd1306_dot(drv, xy[0], xy[1]);
break;
case IOCTL_CMD_DRAW_LINE:
if(argc != 4) {
res = -1;
break;
}
for(unsigned int i = 0; i < argc; i++) {
xy[i] = va_arg(args, unsigned int);
}
ssd1306_line(drv, xy[0], xy[1], xy[2], xy[3]);
break;
case IOCTL_CMD_DRAW_CIRCLE:
if(argc != 3) {
res = -1;
break;
}
for(unsigned int i = 0; i < argc; i++) {
xy[i] = va_arg(args, unsigned int);
}
ssd1306_circle(drv, xy[0], xy[1], xy[2]);
break;
}
return res;
}
static void ssd1306_cmd(const struct driver *drv, uint8_t c)
{
assert(drv != NULL);
struct ssd1306 *this = (struct ssd1306 *)(drv->dev);
char control[] = {0x00, c}; // Co = 0, D/C = 0
drv_write(this->output_dev, control, sizeof(control));
}
static void ssd1306_off(const struct driver *drv)
{
assert(drv != NULL);
ssd1306_cmd(drv, 0xae); // display off
}
static void ssd1306_on(const struct driver *drv)
{
assert(drv != NULL);
ssd1306_cmd(drv, 0xaf); // display on
}
static void ssd1306_reset_cursor(const struct driver *drv)
{
assert(drv != NULL);
ssd1306_cmd(drv, 0x21); // # set column address
ssd1306_cmd(drv, 0x00); // # set start address
ssd1306_cmd(drv, 0x7F); // # set end address (127 max)
ssd1306_cmd(drv, 0x22); // # set page address
ssd1306_cmd(drv, 0x00); // # set start address
ssd1306_cmd(drv, 0x07); // # set end address (7 max)
}
static void ssd1306_cls(const struct driver *drv)
{
assert(drv != NULL);
struct ssd1306 *this = (struct ssd1306 *)(drv->dev);
memset(this->bitmap, 0x00, (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8));
}
static void ssd1306_display(const struct driver *drv)
{
assert(drv != NULL);
struct ssd1306 *this = (struct ssd1306 *)(drv->dev);
drv_write(this->output_dev, this->output_buffer, (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8) + 1);
}
static void ssd1306_dot(const struct driver *drv, uint32_t x, uint32_t y)
{
assert(drv != NULL);
struct ssd1306 *this = (struct ssd1306 *)(drv->dev);
this->bitmap[x + (y / 8) * SSD1306_LCDWIDTH] |= (1 << (y & 7));
}
#ifndef _swap_int16_t
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
#endif
static void ssd1306_line(const struct driver *drv, int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
assert(drv != NULL);
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
if(steep) {
_swap_int16_t(x0, y0);
_swap_int16_t(x1, y1);
}
if(x0 > x1) {
_swap_int16_t(x0, x1);
_swap_int16_t(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t err = dx / 2;
int16_t ystep;
if(y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
for(; x0 <= x1; x0++) {
if(steep) {
ssd1306_dot(drv, y0, x0);
} else {
ssd1306_dot(drv, x0, y0);
}
err -= dy;
if (err < 0) {
y0 += ystep;
err += dx;
}
}
}
static void ssd1306_circle(const struct driver *drv, int16_t x0, int16_t y0, int16_t radius)
{
assert(drv != NULL);
int16_t x = 0, y = radius;
int16_t dp = 1 - radius;
do {
if (dp < 0) {
dp = dp + (x++) * 2 + 3;
} else {
dp = dp + (x++) * 2 - (y--) * 2 + 5;
}
ssd1306_dot(drv, x0 + x, y0 + y); //For the 8 octants
ssd1306_dot(drv, x0 - x, y0 + y);
ssd1306_dot(drv, x0 + x, y0 - y);
ssd1306_dot(drv, x0 - x, y0 - y);
ssd1306_dot(drv, x0 + y, y0 + x);
ssd1306_dot(drv, x0 - y, y0 + x);
ssd1306_dot(drv, x0 + y, y0 - x);
ssd1306_dot(drv, x0 - y, y0 - x);
} while (x < y);
ssd1306_dot(drv, x0 + radius, y0);
ssd1306_dot(drv, x0, y0 + radius);
ssd1306_dot(drv, x0 - radius, y0);
ssd1306_dot(drv, x0, y0 - radius);
}