2017-07-05 14:06:58 +02:00
|
|
|
#include "pxt.h"
|
2017-07-11 16:18:59 +02:00
|
|
|
#include "ev3const.h"
|
2017-07-07 11:38:20 +02:00
|
|
|
#include <pthread.h>
|
2017-07-11 16:18:59 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
2017-07-05 14:06:58 +02:00
|
|
|
/**
|
|
|
|
* Drawing modes
|
|
|
|
*/
|
|
|
|
enum class Draw {
|
2017-07-11 14:43:12 +02:00
|
|
|
Normal = 0x00, // set pixels to black, no fill
|
|
|
|
Clear = 0x01,
|
|
|
|
Xor = 0x02,
|
|
|
|
Fill = 0x04,
|
|
|
|
Transparent = 0x08,
|
|
|
|
Double = 0x10,
|
2017-07-11 15:52:37 +02:00
|
|
|
Quad = 0x20,
|
2017-07-05 14:06:58 +02:00
|
|
|
};
|
|
|
|
|
2017-07-11 14:43:12 +02:00
|
|
|
inline bool operator&(Draw a, Draw b) {
|
|
|
|
return ((int)a & (int)b) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Draw operator|(Draw a, Draw b) {
|
|
|
|
return (Draw)((int)a | (int)b);
|
|
|
|
}
|
|
|
|
|
2017-07-05 14:06:58 +02:00
|
|
|
#define XX(v) ((uint32_t)(v)&0xffff)
|
|
|
|
#define YY(v) ((uint32_t)(v) >> 16)
|
|
|
|
|
|
|
|
// We only support up to 4 arguments for C++ functions - need to pack them on the TS side
|
|
|
|
namespace screen {
|
2017-07-05 20:17:26 +02:00
|
|
|
|
2017-07-10 22:37:29 +02:00
|
|
|
#define ROW_SIZE 32
|
|
|
|
#define FB_SIZE (60 * LCD_HEIGHT)
|
|
|
|
|
|
|
|
static const uint8_t pixmap[] = {0x00, 0xE0, 0x1C, 0xFC, 0x03, 0xE3, 0x1F, 0xFF};
|
|
|
|
static uint8_t bitBuffer[ROW_SIZE * LCD_HEIGHT];
|
|
|
|
static bool dirty;
|
|
|
|
|
|
|
|
static void bitBufferToFrameBuffer(uint8_t *bitBuffer, uint8_t *fb) {
|
|
|
|
uint32_t pixels;
|
|
|
|
|
|
|
|
for (int line = 0; line < LCD_HEIGHT; line++) {
|
|
|
|
int n = 7;
|
|
|
|
while (n--) {
|
|
|
|
pixels = *bitBuffer++ << 0;
|
|
|
|
pixels |= *bitBuffer++ << 8;
|
|
|
|
pixels |= *bitBuffer++ << 16;
|
|
|
|
|
|
|
|
int m = 8;
|
|
|
|
while (m--) {
|
|
|
|
*fb++ = pixmap[pixels & 0x07];
|
|
|
|
pixels >>= 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pixels = *bitBuffer++ << 0;
|
|
|
|
pixels |= *bitBuffer++ << 8;
|
|
|
|
|
2017-07-11 14:43:12 +02:00
|
|
|
bitBuffer += ROW_SIZE - 23;
|
2017-07-10 22:37:29 +02:00
|
|
|
|
|
|
|
int m = 4;
|
|
|
|
while (m--) {
|
|
|
|
*fb++ = pixmap[pixels & 0x07];
|
|
|
|
pixels >>= 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define OFF(x, y) (((y) << 5) + ((x) >> 3))
|
|
|
|
#define MASK(x, y) (1 << ((x)&7))
|
2017-07-11 14:43:12 +02:00
|
|
|
#define PIX2BYTES(x) (((x) + 7) >> 3)
|
2017-07-10 22:37:29 +02:00
|
|
|
|
|
|
|
static inline void applyMask(int off, int mask, Draw mode) {
|
|
|
|
if (mode & Draw::Clear)
|
|
|
|
bitBuffer[off] &= ~mask;
|
|
|
|
else if (mode & Draw::Xor)
|
|
|
|
bitBuffer[off] ^= mask;
|
|
|
|
else
|
|
|
|
bitBuffer[off] |= mask;
|
|
|
|
}
|
|
|
|
|
2017-07-11 10:35:00 +02:00
|
|
|
//%
|
|
|
|
void _setPixel(int x, int y, Draw mode) {
|
2017-07-10 22:37:29 +02:00
|
|
|
applyMask(OFF(x, y), MASK(x, y), mode);
|
|
|
|
}
|
|
|
|
|
2017-07-11 10:35:00 +02:00
|
|
|
void blitLineCore(int x, int y, int w, uint8_t *data, Draw mode) {
|
2017-07-11 14:43:12 +02:00
|
|
|
if (y < 0 || y >= LCD_HEIGHT)
|
2017-07-10 22:37:29 +02:00
|
|
|
return;
|
2017-07-11 10:35:00 +02:00
|
|
|
if (x + w <= 0)
|
2017-07-10 22:37:29 +02:00
|
|
|
return;
|
2017-07-11 14:43:12 +02:00
|
|
|
if (x >= LCD_WIDTH)
|
2017-07-10 22:37:29 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
int shift = x & 7;
|
|
|
|
int off = OFF(x, y);
|
2017-07-11 10:35:00 +02:00
|
|
|
int off0 = OFF(0, y);
|
2017-07-11 14:43:12 +02:00
|
|
|
int off1 = OFF(LCD_WIDTH - 1, y);
|
2017-07-11 15:25:14 +02:00
|
|
|
int x1 = x + w + shift;
|
2017-07-10 22:37:29 +02:00
|
|
|
int prev = 0;
|
|
|
|
|
|
|
|
while (x < x1 - 8) {
|
|
|
|
int curr = *data++ << shift;
|
2017-07-11 10:35:00 +02:00
|
|
|
if (off0 <= off && off <= off1)
|
2017-07-11 14:43:12 +02:00
|
|
|
applyMask(off, curr | prev, mode);
|
2017-07-11 10:35:00 +02:00
|
|
|
off++;
|
2017-07-10 22:37:29 +02:00
|
|
|
prev = curr >> 8;
|
|
|
|
x += 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
int left = x1 - x;
|
|
|
|
if (left > 0) {
|
|
|
|
int curr = *data << shift;
|
2017-07-11 10:35:00 +02:00
|
|
|
if (off0 <= off && off <= off1)
|
2017-07-11 14:43:12 +02:00
|
|
|
applyMask(off, (curr | prev) & ((1 << left) - 1), mode);
|
2017-07-10 22:37:29 +02:00
|
|
|
}
|
2017-07-11 14:43:12 +02:00
|
|
|
|
|
|
|
dirty = true;
|
2017-07-10 22:37:29 +02:00
|
|
|
}
|
|
|
|
|
2017-07-05 14:06:58 +02:00
|
|
|
//%
|
2017-07-11 10:35:00 +02:00
|
|
|
void _blitLine(int xw, int y, Buffer buf, Draw mode) {
|
|
|
|
blitLineCore(XX(xw), y, YY(xw), buf->data, mode);
|
2017-07-05 14:06:58 +02:00
|
|
|
}
|
|
|
|
|
2017-07-05 14:56:13 +02:00
|
|
|
/** Clear screen and reset font to normal. */
|
|
|
|
//%
|
|
|
|
void clear() {
|
2017-07-11 10:35:00 +02:00
|
|
|
memset(bitBuffer, 0, sizeof(bitBuffer));
|
|
|
|
dirty = true;
|
2017-07-05 14:56:13 +02:00
|
|
|
}
|
2017-07-11 14:43:12 +02:00
|
|
|
|
|
|
|
//%
|
|
|
|
void dump() {
|
|
|
|
char buf[LCD_WIDTH + 1];
|
|
|
|
FILE *f = fopen("/tmp/screen.txt", "w");
|
|
|
|
for (int i = 0; i < LCD_HEIGHT; ++i) {
|
|
|
|
for (int j = 0; j < LCD_WIDTH; ++j) {
|
|
|
|
if (bitBuffer[OFF(j, i)] & MASK(j, i))
|
|
|
|
buf[j] = '#';
|
|
|
|
else
|
|
|
|
buf[j] = '.';
|
|
|
|
}
|
|
|
|
buf[LCD_WIDTH] = 0;
|
|
|
|
fprintf(f, "%s\n", buf);
|
|
|
|
}
|
|
|
|
fclose(f);
|
2017-07-05 14:06:58 +02:00
|
|
|
}
|
2017-07-07 11:38:20 +02:00
|
|
|
|
2017-07-11 14:43:12 +02:00
|
|
|
static uint8_t *mappedFrameBuffer;
|
|
|
|
|
|
|
|
//%
|
|
|
|
void updateLCD() {
|
|
|
|
if (dirty && mappedFrameBuffer != MAP_FAILED) {
|
|
|
|
dirty = false;
|
|
|
|
bitBufferToFrameBuffer(bitBuffer, mappedFrameBuffer);
|
|
|
|
}
|
|
|
|
}
|
2017-07-07 11:38:20 +02:00
|
|
|
|
|
|
|
void *screenRefresh(void *dummy) {
|
|
|
|
while (true) {
|
|
|
|
sleep_core_us(30000);
|
2017-07-11 14:43:12 +02:00
|
|
|
updateLCD();
|
2017-07-07 11:38:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-11 14:43:12 +02:00
|
|
|
void init() {
|
|
|
|
DMESG("init screen");
|
|
|
|
if (mappedFrameBuffer)
|
|
|
|
return;
|
|
|
|
int fd = open("/dev/fb0", O_RDWR);
|
|
|
|
DMESG("init screen %d", fd);
|
|
|
|
mappedFrameBuffer = (uint8_t *)mmap(NULL, FB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
|
|
DMESG("map %p", mappedFrameBuffer);
|
|
|
|
if (mappedFrameBuffer == MAP_FAILED) {
|
|
|
|
target_panic(111);
|
|
|
|
}
|
|
|
|
clear();
|
2017-07-07 11:38:20 +02:00
|
|
|
|
|
|
|
pthread_t pid;
|
|
|
|
pthread_create(&pid, NULL, screenRefresh, NULL);
|
|
|
|
pthread_detach(pid);
|
|
|
|
}
|
2017-07-15 22:32:10 +02:00
|
|
|
|
|
|
|
static const uint8_t numbers[] = {
|
|
|
|
0x06, 0x09, 0x09, 0x09, 0x06, 0x04, 0x06, 0x04, 0x04, 0x0e, 0x07, 0x08, 0x06, 0x01, 0x0f, 0x0f,
|
|
|
|
0x08, 0x04, 0x09, 0x06, 0x0c, 0x0a, 0x09, 0x1f, 0x08, 0x1f, 0x01, 0x0f, 0x10, 0x0f, 0x08, 0x04,
|
|
|
|
0x0e, 0x11, 0x0e, 0x1f, 0x08, 0x04, 0x02, 0x01, 0x0e, 0x11, 0x0e, 0x11, 0x0e, 0x0e, 0x11, 0x0e,
|
|
|
|
0x04, 0x02,
|
|
|
|
// face
|
|
|
|
0b11011, 0b11011, 0b00000, 0b11111, 0b11011,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void drawNumber(int off, int idx) {
|
|
|
|
const uint8_t *src = &numbers[idx * 5];
|
|
|
|
uint8_t *dst = &bitBuffer[off];
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
uint8_t ch = *src++;
|
|
|
|
for (int jj = 0; jj < 8; ++jj) {
|
|
|
|
for (int j = 0; j < 5; j++) {
|
|
|
|
if (ch & (1 << j))
|
|
|
|
*dst = 0xff;
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
dst += ROW_SIZE - 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" void drawPanic(int code) {
|
|
|
|
clear();
|
|
|
|
|
|
|
|
int ptr = ROW_SIZE * 16 + 3 + 6;
|
|
|
|
drawNumber(ptr, 10);
|
|
|
|
ptr += 6;
|
|
|
|
|
|
|
|
ptr = ROW_SIZE * 70 + 3;
|
|
|
|
|
|
|
|
drawNumber(ptr, (code / 100) % 10);
|
|
|
|
ptr += 6;
|
|
|
|
drawNumber(ptr, (code / 10) % 10);
|
|
|
|
ptr += 6;
|
|
|
|
drawNumber(ptr, (code / 1) % 10);
|
|
|
|
ptr += 6;
|
|
|
|
|
|
|
|
updateLCD();
|
|
|
|
|
|
|
|
int fd = open("/dev/lms_ui", O_RDWR);
|
2017-10-30 14:04:12 +01:00
|
|
|
uint8_t cmd[] = {48 + 5, 0};
|
2017-07-15 22:32:10 +02:00
|
|
|
write(fd, cmd, 2);
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2017-10-30 14:28:01 +01:00
|
|
|
bool isValidImage(Buffer buf) {
|
2017-10-30 14:04:12 +01:00
|
|
|
return buf->length >= 3 && buf->data[0] == 0xf0;
|
|
|
|
}
|
|
|
|
|
2017-10-30 14:28:01 +01:00
|
|
|
/** Makes an image bound to a buffer. */
|
2017-10-30 14:04:12 +01:00
|
|
|
//%
|
2017-10-30 14:28:01 +01:00
|
|
|
Image imageOf(Buffer buf) {
|
|
|
|
if (!isValidImage(buf))
|
2017-10-30 14:04:12 +01:00
|
|
|
return NULL;
|
|
|
|
incrRC(buf);
|
|
|
|
return buf;
|
|
|
|
}
|
2017-07-07 11:38:20 +02:00
|
|
|
}
|
2017-07-11 14:43:12 +02:00
|
|
|
|
|
|
|
namespace pxt {
|
|
|
|
void screen_init() {
|
|
|
|
screen::init();
|
|
|
|
}
|
|
|
|
}
|
2017-10-30 14:04:12 +01:00
|
|
|
|
|
|
|
//% fixedInstances
|
2017-10-30 14:28:01 +01:00
|
|
|
namespace ImageMethods {
|
2017-10-30 14:04:12 +01:00
|
|
|
|
|
|
|
using namespace screen;
|
|
|
|
|
|
|
|
static const uint8_t bitdouble[] = {
|
|
|
|
0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff,
|
|
|
|
};
|
|
|
|
|
|
|
|
static uint8_t ones[] = {
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Returns the underlaying Buffer object. */
|
|
|
|
//% property
|
2017-10-30 14:28:01 +01:00
|
|
|
Buffer buffer(Image ic) {
|
2017-10-30 14:04:12 +01:00
|
|
|
incrRC(ic);
|
|
|
|
return ic;
|
|
|
|
}
|
|
|
|
|
2017-10-30 14:28:01 +01:00
|
|
|
/** Returns the width of an image. */
|
2017-10-30 14:04:12 +01:00
|
|
|
//% property
|
2017-10-30 14:28:01 +01:00
|
|
|
int width(Image ic) {
|
|
|
|
if (!isValidImage(ic))
|
2017-10-30 14:04:12 +01:00
|
|
|
return 0;
|
|
|
|
return ic->data[1];
|
|
|
|
}
|
|
|
|
|
2017-10-30 14:28:01 +01:00
|
|
|
/** Returns the height of an image. */
|
2017-10-30 14:04:12 +01:00
|
|
|
//% property
|
2017-10-30 14:28:01 +01:00
|
|
|
int height(Image ic) {
|
|
|
|
if (!isValidImage(ic))
|
2017-10-30 14:04:12 +01:00
|
|
|
return 0;
|
|
|
|
int bw = PIX2BYTES(ic->data[1]);
|
|
|
|
return (ic->length - 2) / bw;
|
|
|
|
}
|
|
|
|
|
2017-10-30 14:28:01 +01:00
|
|
|
/** Double size of an image. */
|
2017-10-30 14:04:12 +01:00
|
|
|
//%
|
2017-10-30 14:28:01 +01:00
|
|
|
Image doubled(Image buf) {
|
|
|
|
if (!isValidImage(buf))
|
2017-10-30 14:04:12 +01:00
|
|
|
return NULL;
|
|
|
|
int w = buf->data[1];
|
|
|
|
if (w > 126)
|
|
|
|
return NULL;
|
|
|
|
int bw = PIX2BYTES(w);
|
|
|
|
int h = (buf->length - 2) / bw;
|
|
|
|
int bw2 = PIX2BYTES(w * 2);
|
|
|
|
Buffer out = mkBuffer(NULL, 2 + bw2 * h * 2);
|
|
|
|
out->data[0] = 0xf0;
|
|
|
|
out->data[1] = w * 2;
|
|
|
|
uint8_t *src = buf->data + 2;
|
|
|
|
uint8_t *dst = out->data + 2;
|
|
|
|
for (int i = 0; i < h; ++i) {
|
|
|
|
for (int jj = 0; jj < 2; ++jj) {
|
|
|
|
auto p = src;
|
|
|
|
for (int j = 0; j < bw; ++j) {
|
|
|
|
*dst++ = bitdouble[*p & 0xf];
|
|
|
|
*dst++ = bitdouble[*p >> 4];
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src += bw;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2017-10-30 14:28:01 +01:00
|
|
|
/** Draw an image on the screen. */
|
2017-10-30 14:04:12 +01:00
|
|
|
//%
|
2017-10-30 14:28:01 +01:00
|
|
|
void draw(Image buf, int x, int y, Draw mode) {
|
|
|
|
if (!isValidImage(buf))
|
2017-10-30 14:04:12 +01:00
|
|
|
return;
|
|
|
|
if (mode & (Draw::Double | Draw::Quad)) {
|
|
|
|
buf = doubled(buf);
|
|
|
|
if (mode & Draw::Quad) {
|
|
|
|
auto pbuf = buf;
|
|
|
|
buf = doubled(buf);
|
|
|
|
decrRC(pbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int pixwidth = buf->data[1];
|
|
|
|
int ptr = 2;
|
|
|
|
int bytewidth = PIX2BYTES(pixwidth);
|
|
|
|
pixwidth = min(pixwidth, LCD_WIDTH);
|
|
|
|
while (ptr + bytewidth <= buf->length) {
|
|
|
|
if (mode & (Draw::Clear | Draw::Xor | Draw::Transparent)) {
|
|
|
|
// no erase of background
|
|
|
|
} else {
|
|
|
|
blitLineCore(x, y, pixwidth, ones, Draw::Clear);
|
|
|
|
}
|
|
|
|
blitLineCore(x, y, pixwidth, &buf->data[ptr], mode);
|
|
|
|
y++;
|
|
|
|
ptr += bytewidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode & (Draw::Double | Draw::Quad))
|
|
|
|
decrRC(buf);
|
|
|
|
}
|
|
|
|
}
|