Added custom chips to simulate creaturedex setup
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
#ifndef WOKWI_API_H
|
||||
#define WOKWI_API_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef int32_t pin_t;
|
||||
|
||||
#define NO_PIN ((pin_t)-1)
|
||||
|
||||
enum pin_mode {
|
||||
INPUT = 0,
|
||||
OUTPUT = 1,
|
||||
INPUT_PULLUP = 2,
|
||||
INPUT_PULLDOWN = 3,
|
||||
ANALOG = 4,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
uint32_t edge;
|
||||
void (*pin_change)(void *user_data, pin_t pin, uint32_t value);
|
||||
} pin_watch_config_t;
|
||||
|
||||
extern __attribute__((import_name("pinInit"))) pin_t pin_init(const char *name, uint32_t mode);
|
||||
extern __attribute__((import_name("pinWatch"))) bool pin_watch(pin_t pin, const pin_watch_config_t *config);
|
||||
extern __attribute__((import_name("pinWatchStop"))) void pin_watch_stop(pin_t pin);
|
||||
extern __attribute__((export_name("chipInit"))) void chip_init(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
SOURCES = src/main.c
|
||||
TARGET = dist/chip.wasm
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) dist/chip.json
|
||||
|
||||
clean:
|
||||
rm -rf dist
|
||||
|
||||
dist:
|
||||
mkdir -p dist
|
||||
|
||||
$(TARGET): dist $(SOURCES) wokwi-api.h
|
||||
podman run --rm -u root -v "$(CURDIR)":/src:Z -w /src docker.io/wokwi/builder-clang-wasm:latest \
|
||||
clang --target=wasm32-unknown-wasi --sysroot /opt/wasi-libc -nostartfiles -Wl,--import-memory -Wl,--export-table -Wl,--no-entry -Werror -o $(TARGET) $(SOURCES)
|
||||
|
||||
dist/chip.json: dist chip.json
|
||||
cp chip.json dist
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Main Display",
|
||||
"author": "GitHub Copilot",
|
||||
"display": { "width": 240, "height": 320 },
|
||||
"pins": ["VCC", "GND", "SCK", "MOSI", "CS", "DC", "RESET", "LED"]
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Main Display",
|
||||
"author": "GitHub Copilot",
|
||||
"display": { "width": 240, "height": 320 },
|
||||
"pins": ["VCC", "GND", "SCK", "MOSI", "CS", "DC", "RESET", "LED"]
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,243 @@
|
||||
#include "../wokwi-api.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
buffer_t framebuffer;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
spi_dev_t spi;
|
||||
pin_t cs_pin;
|
||||
pin_t dc_pin;
|
||||
pin_t rst_pin;
|
||||
pin_t led_pin;
|
||||
uint8_t spi_buffer[128];
|
||||
uint8_t current_cmd;
|
||||
uint8_t expected_params;
|
||||
uint8_t param_index;
|
||||
uint8_t params[4];
|
||||
bool memory_write;
|
||||
bool color_high_pending;
|
||||
uint8_t color_high;
|
||||
uint16_t col_start;
|
||||
uint16_t col_end;
|
||||
uint16_t row_start;
|
||||
uint16_t row_end;
|
||||
uint16_t cursor_col;
|
||||
uint16_t cursor_row;
|
||||
} main_display_state_t;
|
||||
|
||||
static main_display_state_t *chip;
|
||||
|
||||
static uint16_t clamp_u16(uint16_t value, uint16_t limit) {
|
||||
return value > limit ? limit : value;
|
||||
}
|
||||
|
||||
static void draw_pixel(uint16_t x, uint16_t y, uint16_t color565) {
|
||||
if (x >= chip->width || y >= chip->height) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t pixel[4];
|
||||
pixel[0] = (uint8_t)(((color565 >> 11) & 0x1F) << 3);
|
||||
pixel[1] = (uint8_t)(((color565 >> 5) & 0x3F) << 2);
|
||||
pixel[2] = (uint8_t)((color565 & 0x1F) << 3);
|
||||
pixel[3] = 255;
|
||||
buffer_write(chip->framebuffer, ((y * chip->width) + x) * 4, pixel, 4);
|
||||
}
|
||||
|
||||
static void fill_screen(uint16_t color565) {
|
||||
for (uint32_t y = 0; y < chip->height; ++y) {
|
||||
for (uint32_t x = 0; x < chip->width; ++x) {
|
||||
draw_pixel((uint16_t)x, (uint16_t)y, color565);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_state(void) {
|
||||
chip->current_cmd = 0;
|
||||
chip->expected_params = 0;
|
||||
chip->param_index = 0;
|
||||
chip->memory_write = false;
|
||||
chip->color_high_pending = false;
|
||||
chip->col_start = 0;
|
||||
chip->col_end = (uint16_t)(chip->width - 1);
|
||||
chip->row_start = 0;
|
||||
chip->row_end = (uint16_t)(chip->height - 1);
|
||||
chip->cursor_col = 0;
|
||||
chip->cursor_row = 0;
|
||||
fill_screen(0x0000);
|
||||
}
|
||||
|
||||
static void advance_pixel_cursor(void) {
|
||||
if (chip->cursor_col < chip->col_end) {
|
||||
++chip->cursor_col;
|
||||
return;
|
||||
}
|
||||
|
||||
chip->cursor_col = chip->col_start;
|
||||
if (chip->cursor_row < chip->row_end) {
|
||||
++chip->cursor_row;
|
||||
return;
|
||||
}
|
||||
|
||||
chip->cursor_row = chip->row_start;
|
||||
}
|
||||
|
||||
static void apply_params(void) {
|
||||
switch (chip->current_cmd) {
|
||||
case 0x2A:
|
||||
chip->col_start = clamp_u16((uint16_t)((chip->params[0] << 8) | chip->params[1]), (uint16_t)(chip->width - 1));
|
||||
chip->col_end = clamp_u16((uint16_t)((chip->params[2] << 8) | chip->params[3]), (uint16_t)(chip->width - 1));
|
||||
if (chip->col_end < chip->col_start) {
|
||||
chip->col_end = chip->col_start;
|
||||
}
|
||||
chip->cursor_col = chip->col_start;
|
||||
break;
|
||||
case 0x2B:
|
||||
chip->row_start = clamp_u16((uint16_t)((chip->params[0] << 8) | chip->params[1]), (uint16_t)(chip->height - 1));
|
||||
chip->row_end = clamp_u16((uint16_t)((chip->params[2] << 8) | chip->params[3]), (uint16_t)(chip->height - 1));
|
||||
if (chip->row_end < chip->row_start) {
|
||||
chip->row_end = chip->row_start;
|
||||
}
|
||||
chip->cursor_row = chip->row_start;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
chip->current_cmd = 0;
|
||||
chip->expected_params = 0;
|
||||
chip->param_index = 0;
|
||||
}
|
||||
|
||||
static void process_command(uint8_t command) {
|
||||
chip->current_cmd = command;
|
||||
chip->param_index = 0;
|
||||
chip->expected_params = 0;
|
||||
chip->memory_write = false;
|
||||
|
||||
switch (command) {
|
||||
case 0x01:
|
||||
reset_state();
|
||||
break;
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
chip->expected_params = 4;
|
||||
break;
|
||||
case 0x2C:
|
||||
chip->memory_write = true;
|
||||
chip->cursor_col = chip->col_start;
|
||||
chip->cursor_row = chip->row_start;
|
||||
chip->color_high_pending = false;
|
||||
break;
|
||||
case 0x36:
|
||||
case 0x3A:
|
||||
chip->expected_params = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_data_byte(uint8_t data) {
|
||||
if (chip->current_cmd != 0 && chip->expected_params > 0) {
|
||||
chip->params[chip->param_index++] = data;
|
||||
if (chip->param_index >= chip->expected_params) {
|
||||
apply_params();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chip->memory_write) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chip->color_high_pending) {
|
||||
chip->color_high = data;
|
||||
chip->color_high_pending = true;
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t color565 = (uint16_t)((chip->color_high << 8) | data);
|
||||
draw_pixel(chip->cursor_col, chip->cursor_row, color565);
|
||||
advance_pixel_cursor();
|
||||
chip->color_high_pending = false;
|
||||
}
|
||||
|
||||
static void chip_spi_done(void *user_data, uint8_t *buffer, uint32_t count) {
|
||||
(void)user_data;
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (pin_read(chip->dc_pin) == 0) {
|
||||
process_command(buffer[i]);
|
||||
} else {
|
||||
handle_data_byte(buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (pin_read(chip->cs_pin) == 0) {
|
||||
spi_start(chip->spi, chip->spi_buffer, sizeof(chip->spi_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
static void chip_cs_changed(void *user_data, pin_t pin, uint32_t value) {
|
||||
(void)user_data;
|
||||
(void)pin;
|
||||
|
||||
if (value == 0) {
|
||||
spi_start(chip->spi, chip->spi_buffer, sizeof(chip->spi_buffer));
|
||||
} else {
|
||||
spi_stop(chip->spi);
|
||||
chip->memory_write = false;
|
||||
chip->color_high_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void chip_reset_changed(void *user_data, pin_t pin, uint32_t value) {
|
||||
(void)user_data;
|
||||
(void)pin;
|
||||
|
||||
if (value == 0) {
|
||||
reset_state();
|
||||
}
|
||||
}
|
||||
|
||||
void chip_init(void) {
|
||||
chip = (main_display_state_t *)malloc(sizeof(main_display_state_t));
|
||||
chip->framebuffer = framebuffer_init(&chip->width, &chip->height);
|
||||
chip->cs_pin = pin_init("CS", INPUT);
|
||||
chip->dc_pin = pin_init("DC", INPUT);
|
||||
chip->rst_pin = pin_init("RESET", INPUT_PULLUP);
|
||||
chip->led_pin = pin_init("LED", INPUT);
|
||||
|
||||
const spi_config_t spi_config = {
|
||||
.user_data = chip,
|
||||
.sck = pin_init("SCK", INPUT),
|
||||
.mosi = pin_init("MOSI", INPUT),
|
||||
.miso = NO_PIN,
|
||||
.mode = 0,
|
||||
.done = chip_spi_done,
|
||||
};
|
||||
|
||||
chip->spi = spi_init(&spi_config);
|
||||
|
||||
const pin_watch_config_t cs_watch = {
|
||||
.user_data = chip,
|
||||
.edge = BOTH,
|
||||
.pin_change = chip_cs_changed,
|
||||
};
|
||||
pin_watch(chip->cs_pin, &cs_watch);
|
||||
|
||||
const pin_watch_config_t reset_watch = {
|
||||
.user_data = chip,
|
||||
.edge = BOTH,
|
||||
.pin_change = chip_reset_changed,
|
||||
};
|
||||
pin_watch(chip->rst_pin, &reset_watch);
|
||||
|
||||
pin_mode(chip->led_pin, OUTPUT_HIGH);
|
||||
reset_state();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef WOKWI_API_H
|
||||
#define WOKWI_API_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef int32_t pin_t;
|
||||
#define NO_PIN ((pin_t)-1)
|
||||
|
||||
enum pin_mode {
|
||||
INPUT = 0,
|
||||
OUTPUT = 1,
|
||||
INPUT_PULLUP = 2,
|
||||
INPUT_PULLDOWN = 3,
|
||||
ANALOG = 4,
|
||||
OUTPUT_LOW = 16,
|
||||
OUTPUT_HIGH = 17,
|
||||
};
|
||||
|
||||
enum edge {
|
||||
RISING = 1,
|
||||
FALLING = 2,
|
||||
BOTH = 3,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
uint32_t edge;
|
||||
void (*pin_change)(void *user_data, pin_t pin, uint32_t value);
|
||||
} pin_watch_config_t;
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
pin_t sck;
|
||||
pin_t mosi;
|
||||
pin_t miso;
|
||||
uint32_t mode;
|
||||
void (*done)(void *user_data, uint8_t *buffer, uint32_t count);
|
||||
uint32_t reserved[8];
|
||||
} spi_config_t;
|
||||
|
||||
typedef uint32_t spi_dev_t;
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
uint32_t address;
|
||||
pin_t scl;
|
||||
pin_t sda;
|
||||
bool (*connect)(void *user_data, uint32_t address, bool read);
|
||||
uint8_t (*read)(void *user_data);
|
||||
bool (*write)(void *user_data, uint8_t data);
|
||||
void (*disconnect)(void *user_data);
|
||||
uint32_t reserved[8];
|
||||
} i2c_config_t;
|
||||
|
||||
typedef uint32_t i2c_dev_t;
|
||||
|
||||
typedef uint32_t buffer_t;
|
||||
|
||||
extern __attribute__((export_name("chipInit"))) void chip_init(void);
|
||||
extern __attribute__((import_name("pinInit"))) pin_t pin_init(const char *name, uint32_t mode);
|
||||
extern __attribute__((import_name("pinWatch"))) bool pin_watch(pin_t pin, const pin_watch_config_t *config);
|
||||
extern __attribute__((import_name("pinWatchStop"))) void pin_watch_stop(pin_t pin);
|
||||
extern __attribute__((import_name("pinMode"))) void pin_mode(pin_t pin, uint32_t value);
|
||||
extern __attribute__((import_name("pinRead"))) uint32_t pin_read(pin_t pin);
|
||||
extern __attribute__((import_name("pinWrite"))) void pin_write(pin_t pin, uint32_t value);
|
||||
extern __attribute__((import_name("spiInit"))) spi_dev_t spi_init(const spi_config_t *config);
|
||||
extern __attribute__((import_name("spiStart"))) void spi_start(const spi_dev_t spi, uint8_t *buffer, uint32_t count);
|
||||
extern __attribute__((import_name("spiStop"))) void spi_stop(const spi_dev_t spi);
|
||||
extern __attribute__((import_name("i2cInit"))) i2c_dev_t i2c_init(const i2c_config_t *config);
|
||||
extern __attribute__((import_name("framebufferInit"))) buffer_t framebuffer_init(uint32_t *pixel_width, uint32_t *pixel_height);
|
||||
extern __attribute__((import_name("bufferWrite"))) void buffer_write(buffer_t buffer, uint32_t offset, void *data, uint32_t data_len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
SOURCES = src/main.c
|
||||
TARGET = dist/chip.wasm
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) dist/chip.json
|
||||
|
||||
clean:
|
||||
rm -rf dist
|
||||
|
||||
dist:
|
||||
mkdir -p dist
|
||||
|
||||
$(TARGET): dist $(SOURCES) wokwi-api.h
|
||||
podman run --rm -u root -v "$(CURDIR)":/src:Z -w /src docker.io/wokwi/builder-clang-wasm:latest \
|
||||
clang --target=wasm32-unknown-wasi --sysroot /opt/wasi-libc -nostartfiles -Wl,--import-memory -Wl,--export-table -Wl,--no-entry -Werror -o $(TARGET) $(SOURCES)
|
||||
|
||||
dist/chip.json: dist chip.json
|
||||
cp chip.json dist
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "PN532 NFC Reader",
|
||||
"author": "GitHub Copilot",
|
||||
"pins": ["VCC", "GND", "SCL", "SDA", "IRQ", "RSTPDN", "SS", "MOSI", "MISO", "SCK"]
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "PN532 NFC Reader",
|
||||
"author": "GitHub Copilot",
|
||||
"pins": ["VCC", "GND", "SCL", "SDA", "IRQ", "RSTPDN", "SS", "MOSI", "MISO", "SCK"]
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,127 @@
|
||||
#include "../wokwi-api.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
i2c_dev_t i2c;
|
||||
spi_dev_t spi;
|
||||
pin_t irq_pin;
|
||||
pin_t rst_pin;
|
||||
pin_t ss_pin;
|
||||
uint8_t spi_buffer[16];
|
||||
uint8_t ack_frame[6];
|
||||
bool ready;
|
||||
} nfc_reader_state_t;
|
||||
|
||||
static nfc_reader_state_t *chip;
|
||||
|
||||
static void update_irq(void) {
|
||||
pin_write(chip->irq_pin, chip->ready ? 0 : 1);
|
||||
}
|
||||
|
||||
static void load_ack_frame(void) {
|
||||
chip->ack_frame[0] = 0x00;
|
||||
chip->ack_frame[1] = 0x00;
|
||||
chip->ack_frame[2] = 0xFF;
|
||||
chip->ack_frame[3] = 0x00;
|
||||
chip->ack_frame[4] = 0xFF;
|
||||
chip->ack_frame[5] = 0x00;
|
||||
}
|
||||
|
||||
static void on_spi_done(void *user_data, uint8_t *buffer, uint32_t count) {
|
||||
(void)user_data;
|
||||
(void)buffer;
|
||||
(void)count;
|
||||
load_ack_frame();
|
||||
if (pin_read(chip->ss_pin) == 0) {
|
||||
spi_start(chip->spi, chip->spi_buffer, sizeof(chip->spi_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
static void on_ss_changed(void *user_data, pin_t pin, uint32_t value) {
|
||||
(void)user_data;
|
||||
(void)pin;
|
||||
if (value == 0) {
|
||||
spi_start(chip->spi, chip->spi_buffer, sizeof(chip->spi_buffer));
|
||||
} else {
|
||||
spi_stop(chip->spi);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_rst_changed(void *user_data, pin_t pin, uint32_t value) {
|
||||
(void)user_data;
|
||||
(void)pin;
|
||||
chip->ready = value != 0;
|
||||
update_irq();
|
||||
}
|
||||
|
||||
static bool on_i2c_connect(void *user_data, uint32_t address, bool read) {
|
||||
(void)user_data;
|
||||
(void)address;
|
||||
(void)read;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t on_i2c_read(void *user_data) {
|
||||
(void)user_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool on_i2c_write(void *user_data, uint8_t data) {
|
||||
(void)user_data;
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void on_i2c_disconnect(void *user_data) {
|
||||
(void)user_data;
|
||||
}
|
||||
|
||||
void chip_init(void) {
|
||||
chip = (nfc_reader_state_t *)malloc(sizeof(nfc_reader_state_t));
|
||||
chip->irq_pin = pin_init("IRQ", INPUT);
|
||||
chip->rst_pin = pin_init("RSTPDN", INPUT_PULLUP);
|
||||
chip->ss_pin = pin_init("SS", INPUT_PULLUP);
|
||||
|
||||
const spi_config_t spi_config = {
|
||||
.user_data = chip,
|
||||
.sck = pin_init("SCK", INPUT),
|
||||
.mosi = pin_init("MOSI", INPUT),
|
||||
.miso = pin_init("MISO", INPUT),
|
||||
.mode = 0,
|
||||
.done = on_spi_done,
|
||||
};
|
||||
chip->spi = spi_init(&spi_config);
|
||||
|
||||
const pin_watch_config_t ss_watch = {
|
||||
.user_data = chip,
|
||||
.edge = BOTH,
|
||||
.pin_change = on_ss_changed,
|
||||
};
|
||||
pin_watch(chip->ss_pin, &ss_watch);
|
||||
|
||||
const pin_watch_config_t rst_watch = {
|
||||
.user_data = chip,
|
||||
.edge = BOTH,
|
||||
.pin_change = on_rst_changed,
|
||||
};
|
||||
pin_watch(chip->rst_pin, &rst_watch);
|
||||
|
||||
const i2c_config_t i2c_config = {
|
||||
.user_data = chip,
|
||||
.address = 0x24,
|
||||
.scl = pin_init("SCL", INPUT_PULLUP),
|
||||
.sda = pin_init("SDA", INPUT_PULLUP),
|
||||
.connect = on_i2c_connect,
|
||||
.read = on_i2c_read,
|
||||
.write = on_i2c_write,
|
||||
.disconnect = on_i2c_disconnect,
|
||||
};
|
||||
chip->i2c = i2c_init(&i2c_config);
|
||||
|
||||
chip->ready = true;
|
||||
load_ack_frame();
|
||||
update_irq();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef WOKWI_API_H
|
||||
#define WOKWI_API_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef int32_t pin_t;
|
||||
#define NO_PIN ((pin_t)-1)
|
||||
|
||||
enum pin_mode {
|
||||
INPUT = 0,
|
||||
OUTPUT = 1,
|
||||
INPUT_PULLUP = 2,
|
||||
INPUT_PULLDOWN = 3,
|
||||
ANALOG = 4,
|
||||
OUTPUT_LOW = 16,
|
||||
OUTPUT_HIGH = 17,
|
||||
};
|
||||
|
||||
enum edge {
|
||||
RISING = 1,
|
||||
FALLING = 2,
|
||||
BOTH = 3,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
uint32_t edge;
|
||||
void (*pin_change)(void *user_data, pin_t pin, uint32_t value);
|
||||
} pin_watch_config_t;
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
pin_t sck;
|
||||
pin_t mosi;
|
||||
pin_t miso;
|
||||
uint32_t mode;
|
||||
void (*done)(void *user_data, uint8_t *buffer, uint32_t count);
|
||||
uint32_t reserved[8];
|
||||
} spi_config_t;
|
||||
|
||||
typedef uint32_t spi_dev_t;
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
uint32_t address;
|
||||
pin_t scl;
|
||||
pin_t sda;
|
||||
bool (*connect)(void *user_data, uint32_t address, bool read);
|
||||
uint8_t (*read)(void *user_data);
|
||||
bool (*write)(void *user_data, uint8_t data);
|
||||
void (*disconnect)(void *user_data);
|
||||
uint32_t reserved[8];
|
||||
} i2c_config_t;
|
||||
|
||||
typedef uint32_t i2c_dev_t;
|
||||
|
||||
typedef uint32_t buffer_t;
|
||||
|
||||
extern __attribute__((export_name("chipInit"))) void chip_init(void);
|
||||
extern __attribute__((import_name("pinInit"))) pin_t pin_init(const char *name, uint32_t mode);
|
||||
extern __attribute__((import_name("pinWatch"))) bool pin_watch(pin_t pin, const pin_watch_config_t *config);
|
||||
extern __attribute__((import_name("pinWatchStop"))) void pin_watch_stop(pin_t pin);
|
||||
extern __attribute__((import_name("pinMode"))) void pin_mode(pin_t pin, uint32_t value);
|
||||
extern __attribute__((import_name("pinRead"))) uint32_t pin_read(pin_t pin);
|
||||
extern __attribute__((import_name("pinWrite"))) void pin_write(pin_t pin, uint32_t value);
|
||||
extern __attribute__((import_name("spiInit"))) spi_dev_t spi_init(const spi_config_t *config);
|
||||
extern __attribute__((import_name("spiStart"))) void spi_start(const spi_dev_t spi, uint8_t *buffer, uint32_t count);
|
||||
extern __attribute__((import_name("spiStop"))) void spi_stop(const spi_dev_t spi);
|
||||
extern __attribute__((import_name("i2cInit"))) i2c_dev_t i2c_init(const i2c_config_t *config);
|
||||
extern __attribute__((import_name("framebufferInit"))) buffer_t framebuffer_init(uint32_t *pixel_width, uint32_t *pixel_height);
|
||||
extern __attribute__((import_name("bufferWrite"))) void buffer_write(buffer_t buffer, uint32_t offset, void *data, uint32_t data_len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
SOURCES = src/main.c
|
||||
TARGET = dist/chip.wasm
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) dist/chip.json
|
||||
|
||||
clean:
|
||||
rm -rf dist
|
||||
|
||||
dist:
|
||||
mkdir -p dist
|
||||
|
||||
$(TARGET): dist $(SOURCES) wokwi-api.h
|
||||
podman run --rm -u root -v "$(CURDIR)":/src:Z -w /src docker.io/wokwi/builder-clang-wasm:latest \
|
||||
clang --target=wasm32-unknown-wasi --sysroot /opt/wasi-libc -nostartfiles -Wl,--import-memory -Wl,--export-table -Wl,--no-entry -Werror -o $(TARGET) $(SOURCES)
|
||||
|
||||
dist/chip.json: dist chip.json
|
||||
cp chip.json dist
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Secondary Display",
|
||||
"author": "GitHub Copilot",
|
||||
"display": { "width": 84, "height": 48 },
|
||||
"pins": ["VCC", "LIGHT", "GND", "CLK", "DIN", "CE", "DC", "RST"]
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Secondary Display",
|
||||
"author": "GitHub Copilot",
|
||||
"display": { "width": 84, "height": 48 },
|
||||
"pins": ["VCC", "LIGHT", "GND", "CLK", "DIN", "CE", "DC", "RST"]
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,171 @@
|
||||
#include "../wokwi-api.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
buffer_t framebuffer;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
spi_dev_t spi;
|
||||
pin_t ce_pin;
|
||||
pin_t dc_pin;
|
||||
pin_t rst_pin;
|
||||
uint8_t spi_buffer[64];
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
bool extended_commands;
|
||||
} secondary_display_state_t;
|
||||
|
||||
static secondary_display_state_t *chip;
|
||||
|
||||
static void set_pixel(uint8_t x, uint8_t y, bool on) {
|
||||
if (x >= chip->width || y >= chip->height) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t pixel[4] = {0, 0, 0, 255};
|
||||
if (on) {
|
||||
pixel[0] = 0x20;
|
||||
pixel[1] = 0x90;
|
||||
pixel[2] = 0x20;
|
||||
}
|
||||
|
||||
buffer_write(chip->framebuffer, ((uint32_t)y * chip->width + x) * 4, pixel, 4);
|
||||
}
|
||||
|
||||
static void clear_display(void) {
|
||||
for (uint32_t y = 0; y < chip->height; ++y) {
|
||||
for (uint32_t x = 0; x < chip->width; ++x) {
|
||||
set_pixel((uint8_t)x, (uint8_t)y, false);
|
||||
}
|
||||
}
|
||||
chip->x = 0;
|
||||
chip->y = 0;
|
||||
}
|
||||
|
||||
static void write_data(uint8_t data) {
|
||||
for (uint8_t bit = 0; bit < 8; ++bit) {
|
||||
set_pixel(chip->x, (uint8_t)(chip->y * 8 + bit), (data >> bit) & 1);
|
||||
}
|
||||
|
||||
++chip->x;
|
||||
if (chip->x >= chip->width) {
|
||||
chip->x = 0;
|
||||
++chip->y;
|
||||
if (chip->y >= (chip->height / 8)) {
|
||||
chip->y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_command(uint8_t command) {
|
||||
if (command == 0x20) {
|
||||
chip->extended_commands = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == 0x21) {
|
||||
chip->extended_commands = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == 0x0C) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == 0x08) {
|
||||
clear_display();
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == 0x80 || (command & 0x80) == 0x80) {
|
||||
chip->x = (uint8_t)(command & 0x7F);
|
||||
if (chip->x >= chip->width) {
|
||||
chip->x = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == 0x40 || (command & 0x40) == 0x40) {
|
||||
chip->y = (uint8_t)(command & 0x07);
|
||||
if (chip->y >= (chip->height / 8)) {
|
||||
chip->y = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == 0x01) {
|
||||
clear_display();
|
||||
}
|
||||
}
|
||||
|
||||
static void chip_spi_done(void *user_data, uint8_t *buffer, uint32_t count) {
|
||||
(void)user_data;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (pin_read(chip->dc_pin) == 0) {
|
||||
write_command(buffer[i]);
|
||||
} else {
|
||||
write_data(buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (pin_read(chip->ce_pin) == 0) {
|
||||
spi_start(chip->spi, chip->spi_buffer, sizeof(chip->spi_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
static void chip_ce_changed(void *user_data, pin_t pin, uint32_t value) {
|
||||
(void)user_data;
|
||||
(void)pin;
|
||||
|
||||
if (value == 0) {
|
||||
spi_start(chip->spi, chip->spi_buffer, sizeof(chip->spi_buffer));
|
||||
} else {
|
||||
spi_stop(chip->spi);
|
||||
}
|
||||
}
|
||||
|
||||
static void chip_rst_changed(void *user_data, pin_t pin, uint32_t value) {
|
||||
(void)user_data;
|
||||
(void)pin;
|
||||
|
||||
if (value == 0) {
|
||||
clear_display();
|
||||
}
|
||||
}
|
||||
|
||||
void chip_init(void) {
|
||||
chip = (secondary_display_state_t *)malloc(sizeof(secondary_display_state_t));
|
||||
chip->framebuffer = framebuffer_init(&chip->width, &chip->height);
|
||||
chip->ce_pin = pin_init("CE", INPUT);
|
||||
chip->dc_pin = pin_init("DC", INPUT);
|
||||
chip->rst_pin = pin_init("RST", INPUT_PULLUP);
|
||||
|
||||
const spi_config_t spi_config = {
|
||||
.user_data = chip,
|
||||
.sck = pin_init("CLK", INPUT),
|
||||
.mosi = pin_init("DIN", INPUT),
|
||||
.miso = NO_PIN,
|
||||
.mode = 0,
|
||||
.done = chip_spi_done,
|
||||
};
|
||||
chip->spi = spi_init(&spi_config);
|
||||
|
||||
const pin_watch_config_t ce_watch = {
|
||||
.user_data = chip,
|
||||
.edge = BOTH,
|
||||
.pin_change = chip_ce_changed,
|
||||
};
|
||||
pin_watch(chip->ce_pin, &ce_watch);
|
||||
|
||||
const pin_watch_config_t rst_watch = {
|
||||
.user_data = chip,
|
||||
.edge = BOTH,
|
||||
.pin_change = chip_rst_changed,
|
||||
};
|
||||
pin_watch(chip->rst_pin, &rst_watch);
|
||||
|
||||
clear_display();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef WOKWI_API_H
|
||||
#define WOKWI_API_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef int32_t pin_t;
|
||||
#define NO_PIN ((pin_t)-1)
|
||||
|
||||
enum pin_mode {
|
||||
INPUT = 0,
|
||||
OUTPUT = 1,
|
||||
INPUT_PULLUP = 2,
|
||||
INPUT_PULLDOWN = 3,
|
||||
ANALOG = 4,
|
||||
OUTPUT_LOW = 16,
|
||||
OUTPUT_HIGH = 17,
|
||||
};
|
||||
|
||||
enum edge {
|
||||
RISING = 1,
|
||||
FALLING = 2,
|
||||
BOTH = 3,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
uint32_t edge;
|
||||
void (*pin_change)(void *user_data, pin_t pin, uint32_t value);
|
||||
} pin_watch_config_t;
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
pin_t sck;
|
||||
pin_t mosi;
|
||||
pin_t miso;
|
||||
uint32_t mode;
|
||||
void (*done)(void *user_data, uint8_t *buffer, uint32_t count);
|
||||
uint32_t reserved[8];
|
||||
} spi_config_t;
|
||||
|
||||
typedef uint32_t spi_dev_t;
|
||||
|
||||
typedef struct {
|
||||
void *user_data;
|
||||
uint32_t address;
|
||||
pin_t scl;
|
||||
pin_t sda;
|
||||
bool (*connect)(void *user_data, uint32_t address, bool read);
|
||||
uint8_t (*read)(void *user_data);
|
||||
bool (*write)(void *user_data, uint8_t data);
|
||||
void (*disconnect)(void *user_data);
|
||||
uint32_t reserved[8];
|
||||
} i2c_config_t;
|
||||
|
||||
typedef uint32_t i2c_dev_t;
|
||||
|
||||
typedef uint32_t buffer_t;
|
||||
|
||||
extern __attribute__((export_name("chipInit"))) void chip_init(void);
|
||||
extern __attribute__((import_name("pinInit"))) pin_t pin_init(const char *name, uint32_t mode);
|
||||
extern __attribute__((import_name("pinWatch"))) bool pin_watch(pin_t pin, const pin_watch_config_t *config);
|
||||
extern __attribute__((import_name("pinWatchStop"))) void pin_watch_stop(pin_t pin);
|
||||
extern __attribute__((import_name("pinMode"))) void pin_mode(pin_t pin, uint32_t value);
|
||||
extern __attribute__((import_name("pinRead"))) uint32_t pin_read(pin_t pin);
|
||||
extern __attribute__((import_name("pinWrite"))) void pin_write(pin_t pin, uint32_t value);
|
||||
extern __attribute__((import_name("spiInit"))) spi_dev_t spi_init(const spi_config_t *config);
|
||||
extern __attribute__((import_name("spiStart"))) void spi_start(const spi_dev_t spi, uint8_t *buffer, uint32_t count);
|
||||
extern __attribute__((import_name("spiStop"))) void spi_stop(const spi_dev_t spi);
|
||||
extern __attribute__((import_name("i2cInit"))) i2c_dev_t i2c_init(const i2c_config_t *config);
|
||||
extern __attribute__((import_name("framebufferInit"))) buffer_t framebuffer_init(uint32_t *pixel_width, uint32_t *pixel_height);
|
||||
extern __attribute__((import_name("bufferWrite"))) void buffer_write(buffer_t buffer, uint32_t offset, void *data, uint32_t data_len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"version": 1,
|
||||
"author": "CreatureDex Builder",
|
||||
"editor": "wokwi",
|
||||
"parts": [
|
||||
{
|
||||
"type": "board-esp32-s3-devkitc-1",
|
||||
"id": "esp",
|
||||
"top": -144.18,
|
||||
"left": -168.23,
|
||||
"attrs": {}
|
||||
},
|
||||
{
|
||||
"type": "wokwi-lcd1602",
|
||||
"id": "lcd1",
|
||||
"top": -310.4,
|
||||
"left": -253.6,
|
||||
"attrs": { "pins": "i2c" }
|
||||
},
|
||||
{
|
||||
"type": "chip-main-display",
|
||||
"id": "lcd2",
|
||||
"top": -162.4,
|
||||
"left": 105.3,
|
||||
"attrs": {}
|
||||
},
|
||||
{
|
||||
"type": "chip-secondary-display",
|
||||
"id": "oled_spi_1",
|
||||
"top": 210,
|
||||
"left": -291.78,
|
||||
"attrs": {}
|
||||
},
|
||||
{
|
||||
"type": "chip-secondary-display",
|
||||
"id": "oled_spi_2",
|
||||
"top": 210,
|
||||
"left": -109.38,
|
||||
"attrs": {}
|
||||
},
|
||||
{
|
||||
"type": "chip-secondary-display",
|
||||
"id": "oled_spi_3",
|
||||
"top": 210,
|
||||
"left": 73.02,
|
||||
"attrs": {}
|
||||
},
|
||||
{
|
||||
"type": "chip-nfc-reader",
|
||||
"id": "nfc1",
|
||||
"top": 390,
|
||||
"left": -24,
|
||||
"attrs": {},
|
||||
"hide": true
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
[ "esp:TX", "$serialMonitor:RX", "", [] ],
|
||||
[ "esp:RX", "$serialMonitor:TX", "", [] ],
|
||||
|
||||
[ "esp:3V3", "lcd1:VCC", "", [] ],
|
||||
[ "esp:GND", "lcd1:GND", "", [] ],
|
||||
[ "esp:8", "lcd1:SDA", "", [] ],
|
||||
[ "esp:9", "lcd1:SCL", "", [] ],
|
||||
|
||||
[ "esp:3V3", "lcd2:VCC", "", [] ],
|
||||
[ "esp:3V3", "lcd2:LED", "", [] ],
|
||||
[ "esp:GND", "lcd2:GND", "", [] ],
|
||||
[ "esp:12", "lcd2:SCK", "", [] ],
|
||||
[ "esp:11", "lcd2:MOSI", "", [] ],
|
||||
[ "esp:10", "lcd2:CS", "", [] ],
|
||||
[ "esp:7", "lcd2:D/C", "", [] ],
|
||||
[ "esp:5", "lcd2:RESET", "", [] ],
|
||||
|
||||
[ "esp:3V3", "oled_spi_1:VCC", "", [] ],
|
||||
[ "esp:3V3", "oled_spi_1:LIGHT", "", [] ],
|
||||
[ "esp:GND", "oled_spi_1:GND", "", [] ],
|
||||
[ "esp:12", "oled_spi_1:CLK", "", [] ],
|
||||
[ "esp:11", "oled_spi_1:DIN", "", [] ],
|
||||
[ "esp:14", "oled_spi_1:CE", "", [] ],
|
||||
[ "esp:6", "oled_spi_1:DC", "", [] ],
|
||||
[ "esp:5", "oled_spi_1:RST", "", [] ],
|
||||
|
||||
[ "esp:3V3", "oled_spi_2:VCC", "", [] ],
|
||||
[ "esp:3V3", "oled_spi_2:LIGHT", "", [] ],
|
||||
[ "esp:GND", "oled_spi_2:GND", "", [] ],
|
||||
[ "esp:12", "oled_spi_2:CLK", "", [] ],
|
||||
[ "esp:11", "oled_spi_2:DIN", "", [] ],
|
||||
[ "esp:15", "oled_spi_2:CE", "", [] ],
|
||||
[ "esp:6", "oled_spi_2:DC", "", [] ],
|
||||
[ "esp:5", "oled_spi_2:RST", "", [] ],
|
||||
|
||||
[ "esp:3V3", "oled_spi_3:VCC", "", [] ],
|
||||
[ "esp:3V3", "oled_spi_3:LIGHT", "", [] ],
|
||||
[ "esp:GND", "oled_spi_3:GND", "", [] ],
|
||||
[ "esp:12", "oled_spi_3:CLK", "", [] ],
|
||||
[ "esp:11", "oled_spi_3:DIN", "", [] ],
|
||||
[ "esp:16", "oled_spi_3:CE", "", [] ],
|
||||
[ "esp:6", "oled_spi_3:DC", "", [] ],
|
||||
[ "esp:5", "oled_spi_3:RST", "", [] ]
|
||||
],
|
||||
"dependencies": {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
[wokwi]
|
||||
version = 1
|
||||
firmware = "../build/firmware.bin"
|
||||
elf = "../build/firmware.elf"
|
||||
|
||||
[[chip]]
|
||||
name = "main-display"
|
||||
binary = "chips/main-display/dist/chip.wasm"
|
||||
|
||||
[[chip]]
|
||||
name = "secondary-display"
|
||||
binary = "chips/secondary-display/dist/chip.wasm"
|
||||
|
||||
[[chip]]
|
||||
name = "nfc-reader"
|
||||
binary = "chips/nfc-reader/dist/chip.wasm"
|
||||
Reference in New Issue
Block a user