diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bootloader.c | 310 | ||||
-rw-r--r-- | src/crc16.c | 48 | ||||
-rw-r--r-- | src/fifo.c | 0 | ||||
-rw-r--r-- | src/font.c | 101 | ||||
-rw-r--r-- | src/i2c.c | 103 | ||||
-rw-r--r-- | src/input.c | 59 | ||||
-rw-r--r-- | src/main.c | 185 | ||||
-rw-r--r-- | src/menu.c | 269 | ||||
-rw-r--r-- | src/tft.c | 138 | ||||
-rw-r--r-- | src/tty.c | 164 |
10 files changed, 1377 insertions, 0 deletions
diff --git a/src/bootloader.c b/src/bootloader.c new file mode 100644 index 0000000..0f67269 --- /dev/null +++ b/src/bootloader.c @@ -0,0 +1,310 @@ +#include <zeta.h> +#include <tty.h> +#include <hardware.h> +#include <fifo.h> + +#include <assert.h> +#include <stddef.h> +#include <string.h> + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#else +#define PACKED +#endif + +enum header_type { + CMD_PING, + CMD_INFO, + CMD_BOOT, + CMD_READ, + CMD_WRITE, + CMD_IO_READ, + CMD_IO_WRITE, + CMD_ECHO +}; + +struct header { + uint8_t type; + uint8_t bank; + uint16_t address; + uint16_t length; + uint16_t checksum; +} PACKED; + +// static_assert(sizeof(struct header) == 8, "struct header is not PACKED"); + +enum error { + ERR_TIMEOUT = -1 +}; + +#define MAX_PACKET_SIZE 256 +#define TIMEOUT_MS 500 +#define MAX_ATTEMPTS 3 +#define MAX_TRANS_ATTEMPTS 5 + +uint16_t +crc16(const void *buf, size_t len); + +volatile struct fifo rx_fifo = {0, 0, {0}}; + +enum ack { + ACK = 0x00, + NACK = 0xFF +}; + +void +rx_isr(void) __critical __interrupt(0) +{ + fifo_push(&rx_fifo, sio_a_data); +} + +static volatile uint32_t millis = 0; + +void +ctc1_isr(void) __critical __interrupt(1) +{ + millis += 5; +} + +uint32_t +clock(void) +{ + volatile uint32_t ret; + DI; + ret = millis; + EI; + return ret; +} + +void +putbyte(unsigned char b) +{ + unsigned char ctrl = 0; + + sio_a_data = b; + + while (!(ctrl & 0x04)) { + sio_a_ctrl = 0; + ctrl = sio_a_ctrl; + } +} + +static volatile int32_t errno = 0; + +uint8_t +getbyte(void) +{ + uint8_t b; + uint32_t ms = clock(); + errno = 0; + while (fifo_empty(&rx_fifo)) { + if (clock() - ms > TIMEOUT_MS) { + errno = ERR_TIMEOUT; + return 0; + } + } + DI; + b = fifo_pop(&rx_fifo); + EI; + return b; +} + +void +flush(void) +{ + DI; + fifo_clear(&rx_fifo); + EI; +} + + +// Hamming(7,4) encoding +uint8_t +encode(uint8_t x) +{ + uint8_t y = 0; + const uint8_t c[4] = {0x61, 0x52, 0x34, 0x78}; + + for (uint8_t i = 0; i < 4; ++i) + y ^= ((x >> i) & 1) ? c[i] : 0; + + return y; +} + +// Hamming(7,4) decoding +uint8_t +decode(uint8_t x) +{ + uint8_t p = 0; + const uint8_t r[7] = {6, 5, 3, 7, 1, 2, 4}; + + for (int i = 0; i < 7; ++i) + p ^= ((x >> i) & 1) ? r[i] : 0; + + // Assume simple error, attempt correction + if (p) { + size_t i = 0; + + for (i = 0; i < LENGTH(r); ++i) { + if (r[i] == x) + break; + } + + x ^= (1 << i); + } + + return x & 0x0F; +} + + +int +read(void *buf, size_t count) +{ + uint8_t b; + uint8_t *p = buf; + + for (int n = 0; n < count; ++n) { + b = decode(getbyte()); + if (errno) + return errno; + + b |= (decode(getbyte()) << 4); + if (errno) + return errno; + + p[n] = b; + } + + return 0; +} + +int +write(const void *buf, size_t count) +{ + const uint8_t *p = buf; + + for (size_t i = 0; i < count; ++i) { + putbyte(encode(p[i] & 0x0F)); + putbyte(encode((p[i] >> 4) & 0x0F)); + } + + return 0; +} + +int +read_header(struct header *header) +{ + int err; + uint8_t ack; + uint16_t checksum; + + while (1) { + if ((err = read(header, sizeof(*header)))) + return err; + + checksum = header->checksum; + header->checksum = 0; + + if (checksum == crc16(header, sizeof(*header))) { + header->checksum = checksum; + ack = ACK; + write(&ack, sizeof(ack)); + return 0; + } else { + ack = NACK; + write(&ack, sizeof(ack)); + } + } +} + +int +read_buf(size_t len, void *buf) +{ + int err; + uint8_t ack; + uint16_t checksum; + + for (int i = 0; i < MAX_TRANS_ATTEMPTS; ++i) { + // TODO: reduce code? + if ((err = read(&checksum, sizeof(checksum))) + || (err = read(buf, len))) + break; + + if (checksum == crc16(buf, len)) { + ack = ACK; + write(&ack, sizeof(ack)); + return 0; + } else { + ack = NACK; + write(&ack, sizeof(ack)); + } + } + + return -1; +} + +int +write_buf(size_t len, const void *buf) +{ + int err; + uint8_t ack = NACK; + uint16_t checksum = crc16(buf, len); + + for (int i = 0; i < MAX_TRANS_ATTEMPTS; ++i){ + write(&checksum, sizeof(checksum)); + write(buf, len); + + // If TIMEOUT sending just give up + if ((err = read(&ack, sizeof(ack)))) + return err; + + if (ack == ACK) + return 0; + } + + return -1; +} + +// TODO: Restart after timeouts +void +bootloader(void) +{ + struct header header; + uint8_t buf[MAX_PACKET_SIZE]; + + const char *msg = "Bootloader ...\r\n"; + addstr(msg); + + while (1) { + if (read_header(&header)) { + flush(); + continue; + } + + switch (header.type) { + case CMD_BOOT: + ((void (*)(void))header.address)(); + break; + + case CMD_READ: + write_buf(header.length, (const void *)header.address); + break; + + case CMD_WRITE: + if (!read_buf(header.length, buf)) + memcpy((void *)header.address, buf, header.length); + break; + + case CMD_ECHO: + if (!read_buf(header.length, buf)) + write_buf(header.length, buf); + break; + + default: + break; + } + + flush(); + } +} diff --git a/src/crc16.c b/src/crc16.c new file mode 100644 index 0000000..6c80738 --- /dev/null +++ b/src/crc16.c @@ -0,0 +1,48 @@ +#include <zeta.h> + +static const u16 crc_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +u16 +crc16(const void *buf, size_t len) +{ + const u8 *p = buf; + + u16 crc = 0; + while (len--) + crc = crc_table[(crc >> 8) ^ (*p++)] ^ (crc << 8); + + return crc; +} diff --git a/src/fifo.c b/src/fifo.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/fifo.c diff --git a/src/font.c b/src/font.c new file mode 100644 index 0000000..c6eb038 --- /dev/null +++ b/src/font.c @@ -0,0 +1,101 @@ +#include <stdint.h> + +const uint8_t font[97][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* SPACE */ + {0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00}, /* ! */ + {0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */ + {0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, 0x00}, /* # */ + {0x18, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x18, 0x00}, /* $ */ + {0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00}, /* % */ + {0x3C, 0x66, 0x3C, 0x1C, 0xE6, 0x66, 0xFC, 0x00}, /* & */ + {0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */ + {0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00}, /* ( */ + {0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00}, /* ) */ + {0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, /* * */ + {0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00}, /* + */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C}, /* , */ + {0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00}, /* - */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00}, /* . */ + {0x00, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00}, /* / */ + {0x3C, 0x66, 0x76, 0x6E, 0x66, 0x66, 0x3C, 0x00}, /* 0 */ + {0x18, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00}, /* 1 */ + {0x3C, 0x66, 0x60, 0x30, 0x0C, 0x06, 0x7E, 0x00}, /* 2 */ + {0x3C, 0x66, 0x60, 0x38, 0x60, 0x66, 0x3C, 0x00}, /* 3 */ + {0x60, 0x70, 0x78, 0x66, 0xFE, 0x60, 0x60, 0x00}, /* 4 */ + {0x7E, 0x06, 0x3E, 0x60, 0x60, 0x66, 0x3C, 0x00}, /* 5 */ + {0x3C, 0x66, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00}, /* 6 */ + {0x7E, 0x66, 0x30, 0x18, 0x18, 0x18, 0x18, 0x00}, /* 7 */ + {0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00}, /* 8 */ + {0x3C, 0x66, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00}, /* 9 */ + {0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00}, /* : */ + {0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0C}, /* / */ + {0x70, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x70, 0x00}, /* < */ + {0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00}, /* = */ + {0x0E, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0E, 0x00}, /* > */ + {0x3C, 0x66, 0x60, 0x30, 0x18, 0x00, 0x18, 0x00}, /* ? */ + {0x3C, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3C, 0x00}, /* @ */ + {0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00}, /* A */ + {0x3E, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3E, 0x00}, /* B */ + {0x3C, 0x66, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00}, /* C */ + {0x1E, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1E, 0x00}, /* D */ + {0x7E, 0x06, 0x06, 0x1E, 0x06, 0x06, 0x7E, 0x00}, /* E */ + {0x7E, 0x06, 0x06, 0x1E, 0x06, 0x06, 0x06, 0x00}, /* F */ + {0x3C, 0x66, 0x06, 0x76, 0x66, 0x66, 0x3C, 0x00}, /* G */ + {0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00}, /* H */ + {0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00}, /* I */ + {0x78, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00}, /* J */ + {0x66, 0x36, 0x1E, 0x0E, 0x1E, 0x36, 0x66, 0x00}, /* K */ + {0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E, 0x00}, /* L */ + {0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0x00}, /* M */ + {0x66, 0x6E, 0x7E, 0x7E, 0x76, 0x66, 0x66, 0x00}, /* N */ + {0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* O */ + {0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00}, /* P */ + {0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x70, 0x00}, /* Q */ + {0x3E, 0x66, 0x66, 0x3E, 0x1E, 0x36, 0x66, 0x00}, /* R */ + {0x3C, 0x66, 0x06, 0x3C, 0x60, 0x66, 0x3C, 0x00}, /* S */ + {0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* T */ + {0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* U */ + {0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00}, /* V */ + {0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00}, /* W */ + {0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00}, /* X */ + {0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00}, /* Y */ + {0x7E, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00}, /* Z */ + {0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00}, /* [ */ + {0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00}, /* \ */ + {0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00}, /* ] */ + {0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00}, /* ^ */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, /* _ */ + {0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */ + {0x00, 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x7C, 0x00}, /* a */ + {0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00}, /* b */ + {0x00, 0x00, 0x3C, 0x06, 0x06, 0x06, 0x3C, 0x00}, /* c */ + {0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00}, /* d */ + {0x00, 0x00, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00}, /* e */ + {0x00, 0x70, 0x18, 0x7C, 0x18, 0x18, 0x18, 0x00}, /* f */ + {0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x3E}, /* g */ + {0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x00}, /* h */ + {0x00, 0x18, 0x00, 0x1C, 0x18, 0x18, 0x3C, 0x00}, /* i */ + {0x00, 0x60, 0x00, 0x60, 0x60, 0x60, 0x60, 0x3C}, /* j */ + {0x00, 0x06, 0x06, 0x36, 0x1E, 0x36, 0x66, 0x00}, /* k */ + {0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00}, /* l */ + {0x00, 0x00, 0x66, 0xFE, 0xFE, 0xD6, 0xC6, 0x00}, /* m */ + {0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00}, /* n */ + {0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* o */ + {0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06}, /* p */ + {0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60}, /* q */ + {0x00, 0x00, 0x3E, 0x66, 0x06, 0x06, 0x06, 0x00}, /* r */ + {0x00, 0x00, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x00}, /* s */ + {0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x70, 0x00}, /* t */ + {0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00}, /* u */ + {0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00}, /* v */ + {0x00, 0x00, 0xC6, 0xD6, 0xFE, 0x7C, 0x6C, 0x00}, /* w */ + {0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00}, /* x */ + {0x00, 0x00, 0x66, 0x66, 0x66, 0x7C, 0x30, 0x1E}, /* y */ + {0x00, 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x7E, 0x00}, /* z */ + {0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00}, /* { */ + {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, /* | */ + {0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00}, /* } */ + {0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ~ */ + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* DEL */ + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} /* CURSOR */ +}; diff --git a/src/i2c.c b/src/i2c.c new file mode 100644 index 0000000..cca68c6 --- /dev/null +++ b/src/i2c.c @@ -0,0 +1,103 @@ +#include <i2c.h> +#include <hardware.h> + +#define SDA (1 << 0) +#define SCL (1 << 1) + +void +_delay_ms(uint8_t ms); + +void +i2c_start_condition(void) +{ + port_a_data &= ~SDA; + _delay_ms(1); + port_a_data &= ~SCL; + _delay_ms(1); +} + +void +i2c_restart_condition(void) +{ + port_a_data |= SDA; + _delay_ms(1); + port_a_data |= SCL; + _delay_ms(1); + i2c_start_condition(); +} + +void +i2c_stop_condition(void) +{ + port_a_data |= SCL; + _delay_ms(1); + port_a_data |= SDA; + _delay_ms(1); +} + +void +i2c_send(uint8_t b) +{ + uint8_t i; + + for (i = 0; i < 8; ++i) { + if (b & 1) + port_a_data |= SDA; + else + port_a_data &= ~SDA; + + port_a_data |= SCL; + _delay_ms(1); + port_a_data &= ~SCL; + _delay_ms(1); + + b >>= 1; + } + + port_a_ctrl = PIO_MODE_3; + port_a_ctrl = 0x7D; + + // Read ack + port_a_data |= SCL; + _delay_ms(1); + port_a_data &= ~SCL; + _delay_ms(1); + + port_a_ctrl = PIO_MODE_3; + port_a_ctrl = 0x7C; +} + +uint8_t +i2c_recv(uint8_t ack) +{ + uint8_t i; + uint8_t b = 0; + + port_a_ctrl = PIO_MODE_3; + port_a_ctrl = 0x7D; + + for (i = 0; i < 8; ++i) { + port_a_data |= SCL; + _delay_ms(1); + if (port_a_data & 1) + b |= 1; + + port_a_data &= ~SCL; + _delay_ms(1); + b <<= 1; + } + + port_a_ctrl = PIO_MODE_3; + port_a_ctrl = 0x7C; + + if (ack) + port_a_data |= SDA; + else + port_a_data &= ~SDA; + + port_a_data |= SCL; + _delay_ms(1); + port_a_data &= ~SCL; + _delay_ms(1); + return b; +} diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..db306ea --- /dev/null +++ b/src/input.c @@ -0,0 +1,59 @@ +#include <hardware.h> +#include <tty.h> +#include <stdint.h> +#include <input.h> + +enum encoder_state { + START, + CW_START, + CW_NEXT, + CW_FINAL, + CCW_START, + CCW_NEXT, + CCW_FINAL, + CW = 0x10, + CCW = 0x20 +}; + +/* State machine to keep track of encoder */ +static const uint8_t encoder_table[7][4] = { + /* 00 01 10 11*/ + [START] = { START, CCW_START, CW_START, START }, + [CCW_START] = { CCW_NEXT, CCW_START, START, START }, + [CCW_NEXT] = { CCW_NEXT, CCW_START, CCW_FINAL, START }, + [CCW_FINAL] = { CCW_NEXT, START, CCW_FINAL, START | CCW }, + [CW_START] = { CW_NEXT, START, CW_START, START }, + [CW_NEXT] = { CW_NEXT, CW_FINAL, CW_START, START }, + [CW_FINAL] = { CW_NEXT, CW_FINAL, START, START | CW } +}; + +struct fifo input_fifo = {0, 0, {0}}; + +void +input_event(void) __critical __interrupt(4) +{ + if ((port_b_data & 0x03) != 0x03) { + // Encoder + uint8_t state = START; + uint8_t encoder; + uint8_t next_state; + + do { + encoder = port_b_data & 0x03; + next_state = encoder_table[state][encoder]; + state = next_state & 0x0F; + } while (state != START); + + if (next_state == CW || next_state == CCW) + fifo_push(&input_fifo, next_state); + } else if (port_b_data & 0x7C) { + // Key pressed + for (int i = 0; i < 5; i++) { + uint8_t bit = 1 << (i + 2); + uint8_t state = !(port_b_data & bit); + + if (state) + fifo_push(&input_fifo, i); + } + } +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..fe3da0e --- /dev/null +++ b/src/main.c @@ -0,0 +1,185 @@ +#include <tft.h> +#include <tty.h> +#include <i2c.h> +#include <zeta.h> +#include <input.h> + +void +uart_putchar(char c); + +static inline void +enable_input_fifo(void) +{ +} + +static inline void +disable_input_fifo(void) +{ +} + +static inline void +init_pio(void) +{ + // Control mode - i.e. no parallel port communication + port_b_ctrl = PIO_MODE_3; + // A, B and five keys as inputs + port_b_ctrl = 0x7F; + // Load interrupt vector + // port_b_ctrl = ISR_ADDRESS(port_b_isr_ptr); + // Interrupt word - interrupt if any pin goes low, mask follows + // port_b_ctrl = PIO_INT_CTRL(PIO_INT_EN | PIO_OR | PIO_LOW | PIO_MASK); + // Mask - 0 means to check line + // port_b_ctrl = 0x80; + + /* port_a_ctrl = PIO_MODE_3; */ + + /* port_a_ctrl = 0x7C; */ + + /* // Load interrupt vector */ + /* port_a_ctrl = ISR_ADDRESS(port_a_isr_ptr); */ + + /* port_a_ctrl = PIO_INT_CTRL(PIO_INT_EN | PIO_OR | PIO_HIGH | PIO_MASK); */ + /* port_a_ctrl = 0x83; */ + + /* // SDA and SCL high */ + /* port_a_data = 0x03; */ +} + +static inline void +init_ctc(void) +{ + /* 200Hz clock */ + ctc_channel_1 = CTC_CTRL(CTC_INT_BIT | CTC_PRESCALER_BIT + | CTC_TIME_CONST_BIT | CTC_RST_BIT); + ctc_channel_1 = 0; + + /* 200Hz clock */ + ctc_channel_2 = CTC_CTRL(CTC_INT_BIT | CTC_PRESCALER_BIT + | CTC_TIME_CONST_BIT | CTC_RST_BIT); + ctc_channel_2 = 0; + + /* ctc_channel_3 = (CPU_FREQ / 256 / 144); */ + + ctc_channel_3 = CTC_CTRL(CTC_INT_BIT | CTC_PRESCALER_BIT + | CTC_TIME_CONST_BIT | CTC_RST_BIT); + ctc_channel_3 = 0; // 256 + + // Interrupt table for CTC + // Final address is (Ireg << 8) | ctc_isr_ptr | {00/01/10/11} | 0 + ctc_channel_0 = ISR_ADDRESS(ctc0_isr_ptr); +} + +static inline void +init_sio(void) +{ + static const u8 sio_a_cfg[] = { + 0b00011000, // Reset channel + 4 , // wr4 + 0b01000100, // X16 clock (115200), one stop bit, no parity + 1 , // wr1 + SIO_RX_INT_MD0 | SIO_RX_INT_MD1, // interrupt on every Rx, no wait function + 3 , // wr3 + 0b11000001, // enable Rx - 8 bit char + 5 , // wr5 + 0b01101000 // enable Tx - 8 bit char + }; + + static const u8 sio_b_cfg[] = { + 0b00011000, // Reset channel + 2 , // load interrupt vector + ISR_ADDRESS(rx_isr_ptr) // int_table_rx + }; + + for (u8 i = 0; i < LENGTH(sio_a_cfg); ++i) + sio_a_ctrl = sio_a_cfg[i]; + + for (u8 i = 0; i < LENGTH(sio_b_cfg); ++i) + sio_b_ctrl = sio_b_cfg[i]; +} + +// copy code to ram, so function pointer is modifyable + +static uint8_t tick = 0; + +void +timer(void) __critical __interrupt(5) +{ + static int prescale = 0; + + if (++prescale == 150) { + prescale = 0; + tick = 1; + } +} + +/* i2c_start_condition(); */ +/* i2c_send(0xA0); */ +/* i2c_send(0x00); */ +/* i2c_send(0x00); */ +/* i2c_restart_condition(); */ +/* i2c_send(0xA1); */ +/* b = i2c_recv(NACK); */ +/* i2c_stop_condition(); */ + +void +main(void) +{ + tft_init(); + + init_ctc(); + init_pio(); + init_sio(); + + // Interrupt mode 2 + IM(2); + // Enable interrupts + EI; + + // Boot menu + addstr("1) Programming mode\r\n"); + addstr("2) Normal boot (default)\r\n"); + addstr("3) Boot address 0xC000\r\n"); + + for (int i = 5; i > 0; --i) { + addch('\r'); + addch(i + '0'); + + while (!tick) { + u8 keys = poll_keys(); + + if (!(keys & KEY2)) { + addch('\r'); + bootloader(); + } + + if (!(keys & KEY3)) { + goto boot; + } + + if (!(keys & KEY4)) { + // Definitely safe looking C code ... + void (*ptr)(void) = (void (*)(void))0xC000; + ptr(); + } + } + + DI; + tick = 0; + EI; + } + + addch('\r'); + +boot: + addstr("Starting system ...\r\n"); + // Load interrupt vector + port_b_ctrl = ISR_ADDRESS(port_b_isr_ptr); + // Interrupt word - interrupt if any pin goes low, mask follows + port_b_ctrl = PIO_INT_CTRL(PIO_INT_EN | PIO_OR | PIO_LOW | PIO_MASK); + // Mask - 0 means to check line + port_b_ctrl = 0x80; + _menu(); + + while (1) + ; +} diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..51f7c31 --- /dev/null +++ b/src/menu.c @@ -0,0 +1,269 @@ +#include <zeta.h> +#include <hardware.h> +#include <tty.h> +#include <input.h> +#include <stdio.h> +#include <string.h> + +struct time { + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t wkday; + uint8_t date; + uint8_t month; + uint8_t year; +}; + +struct value { + uint8_t roll; + int val; + int min, max; +}; + +void +value_inc(struct value *v, int d) +{ + v->val += d; + + if (v->roll) { + if (v->val > v->max) + v->val = v->min; + else if(v->val < v->min) + v->val = v->max - 1; + } else { + v->val = clamp(v->val, v->min, v->max); + } +} + +enum item_type { + VOID, + BUTTON, + VALUE, + LABEL +}; + +void +draw_time(void) +{ + char buf[32]; + struct time time; + memset(&time, 0, sizeof(time)); + // rtc_get_bcd_time(&time); + sprintf(buf, "20%hhd%hhd-%hhd%hhd-%hhd%hhd %hhd%hhd:%hhd%hhd:%hhd%hhd", + time.year >> 4, time.year & 0xF, + time.month >> 4, time.month & 0xF, + time.date >> 4, time.date & 0xF, + time.hour >> 4, time.hour & 0xF, + time.minute >> 4, time.minute & 0xF, + time.second >> 4, time.second & 0xF); + mvaddstr(5, 1, buf); +} + +void +draw_battery(void) +{ + uint16_t soc = 100; // bat_state_of_charge(); + char buf[16]; + sprintf(buf, "%u%%", soc); + mvaddstr(7, 1, buf); +} + +void +set_time(void); + +struct item { + uint8_t type; + const char *text; + union { + void (*action)(void); + struct value value; + } v; +}; + +struct menu_entry { + const char *text; + struct item items[8]; +}; + +struct menu_entry menu[] = { + { + .text = "TIME ", + .items = { + {.type = LABEL, .v = {.action = draw_time}}, + {.type = LABEL, .v = {.action = draw_battery}}, + {.type = VOID}, + {.type = VOID}, + {.type = VOID}, + {.type = VOID}, + {.type = VOID}, + {.type = VOID} + } + }, + { + .text = "CONF ", + .items = { + {.type = VALUE, .text = "SEC", .v = {.value = {1, 0, 0, 59}}}, + {.type = VALUE, .text = "MIN", .v = {.value = {1, 0, 0, 59}}}, + {.type = VALUE, .text = "HOUR", .v = {.value = {1, 0, 0, 23}}}, + {.type = VALUE, .text = "WKDAY", .v = {.value = {1, 0, 1, 7}}}, + {.type = VALUE, .text = "DATE", .v = {.value = {1, 1, 1, 31}}}, + {.type = VALUE, .text = "MONTH", .v = {.value = {1, 1, 1, 12}}}, + {.type = VALUE, .text = "YEAR", .v = {.value = {1, 0, 0, 3000}}}, + {.type = BUTTON, .text = "LOAD", .v = {.action = set_time}} + } + } +}; + +void +set_time(void) +{ + struct time time; + struct item *items = menu[1].items; + + time.second = items[0].v.value.val; + time.minute = items[1].v.value.val; + time.hour = items[2].v.value.val; + time.wkday = items[3].v.value.val; + time.date = items[4].v.value.val; + time.month = items[5].v.value.val; + time.year = items[6].v.value.val; + + // rtc_set_time(&time); +} + +enum direction { + VERTICAL, + HORIZONTAL +}; + +static uint8_t dir = HORIZONTAL; +static int menu_index = 0; +static int sub_menu_index = -1; +static int edit = 0; + +#define BUILD_VERSION "20241117" + +void +draw_menu(void) +{ + const char *build_version = BUILD_VERSION; + const int padding = (TTY_WIDTH - 8 * LENGTH(menu)) / 2; + + mvaddstr(1, 0, " ************* THE SOUTHERN STAR MK II ************* "); + mvaddch(3, 1, dir == HORIZONTAL ? '>' : '^'); + + for (int i = 0; i < LENGTH(menu); ++i) { + if (i == menu_index) { + swap_colors(); + mvaddstr(3, padding + i * 8, menu[i].text); + swap_colors(); + } else { + mvaddstr(3, padding + i * 8, menu[i].text); + } + } + + const struct menu_entry *entry = &menu[menu_index]; + + for (int i = 0; i < LENGTH(entry->items); ++i) { + mvaddstr(5 + 2 * i, 1, " "); + + if (entry->items[i].type == VOID) + continue; + + if (i == sub_menu_index && menu_index != 0) { + swap_colors(); + mvaddstr(5 + 2 * i, 1, entry->items[i].text); + swap_colors(); + } else { + mvaddstr(5 + 2 * i, 1, entry->items[i].text); + } + + if (entry->items[i].type == VALUE) { + char buf[16]; + sprintf(buf, "%8d", entry->items[i].v.value.val); + mvaddstr(5 + 2 * i, 6, buf); + } + + if (entry->items[i].type == LABEL) { + entry->items[i].v.action(); + } + } + + mvaddstr(TTY_HEIGHT - 2, TTY_WIDTH - strlen(build_version) - 1, build_version); +} + +void +dir_callback(void) +{ + if (sub_menu_index >= 0) { + struct item *item = &menu[menu_index].items[sub_menu_index]; + + switch (item->type) { + case VALUE: + edit = !edit; + break; + + case BUTTON: + if (item->v.action) + item->v.action(); + break; + } + } else { + dir = (dir + 1) & 1; + draw_menu(); + } +} + +#define CW 0x10 +#define CCW 0x20 + +typedef void (*callback)(void); +volatile callback callbacks[5] = {NULL, NULL, NULL, NULL, NULL}; + +void +_menu(void) +{ + callbacks[0] = dir_callback; + clear_screen(); + setcur(0, 0); + draw_menu(); + + while (1) { + DI; + while (!fifo_empty(&input_fifo)) { + u8 event = fifo_pop(&input_fifo); + if (event == CW) { + if (dir == HORIZONTAL) { + if (++menu_index >= LENGTH(menu)) + menu_index = 0; + } else { + if (edit) { + value_inc(&menu[menu_index].items[sub_menu_index].v.value, 1); + } else { + if (++sub_menu_index >= LENGTH(menu[0].items)) + sub_menu_index = 0; + } + } + draw_menu(); + } else if (event == CCW) { + if (dir == HORIZONTAL) { + if (--menu_index < 0) + menu_index = LENGTH(menu) - 1; + } else { + if (edit) { + value_inc(&menu[menu_index].items[sub_menu_index].v.value, -1); + } else { + if (--sub_menu_index < -1) + sub_menu_index = LENGTH(menu[0].items) - 1; + } + } + draw_menu(); + } else { + if (callbacks[event]) + callbacks[event](); + } + } + EI; + } +} diff --git a/src/tft.c b/src/tft.c new file mode 100644 index 0000000..d0c7d8f --- /dev/null +++ b/src/tft.c @@ -0,0 +1,138 @@ +#include <tft.h> +#include <hardware.h> +#include <zeta.h> + +void +_delay_ms(uint8_t ms); + +/* + * Driver for the ILI9341 TFT Chip + */ + +static inline void +tft_soft_reset(void) +{ + tft_ctrl = SWRESET; +} + +static inline void +tft_enable_ext_cmd(void) +{ + tft_ctrl = SETEXTC; + tft_data = 0xFF; + tft_data = 0x83; + tft_data = 0x57; +} + +static inline void +tft_memory_access_ctrl(uint8_t bits) +{ + tft_ctrl = MADCTL; + tft_data = bits; +} + +static inline void +tft_sleep_out(void) { + tft_ctrl = SLPOUT; +} + +static inline void +tft_display_on(void) +{ + tft_ctrl = DISPON; +} + +static inline void +tft_pixel_format(uint8_t dbi, uint8_t dpi) +{ + tft_ctrl = COLMOD; + tft_data = (dpi << 4) | dbi; +} + +static inline void +tft_set_rgb_interface(void) +{ + tft_ctrl = SETRGB; + tft_data = 0x00; + tft_data = 0x00; + tft_data = 0x06; + tft_data = 0x06; +} + +static inline void +tft_set_col_addr(uint16_t start, uint16_t end) +{ + tft_ctrl = CASET; + tft_data = start >> 8; + tft_data = start & 0xFF; + tft_data = end >> 8; + tft_data = end & 0xFF; +} + +static inline void +tft_set_page_addr(uint16_t start, uint16_t end) +{ + tft_ctrl = PASET; + tft_data = start >> 8; + tft_data = start & 0xFF; + tft_data = end >> 8; + tft_data = end & 0xFF; +} + +void +tft_set_area(unsigned int x, unsigned int y, unsigned int w, unsigned int h) +{ + tft_set_col_addr(x, x + w - 1); + tft_set_page_addr(y, y + h - 1); +} + +enum memory_access { + // MY MX MV ML | BGR SS X X + MV = 0x20, // 1: landscape 0 <= x < 480; 0 <= y < 320 + MX = 0x40, // MV = 0: Invert X; MV = 1: Invert Y + MY = 0x80, // MV = 0: Invert Y; MV = 1: Invert X + ML = 0x10 +}; + +void +clear_screen(void) +{ + DI; + tft_set_col_addr(0, TFT_WIDTH - 1); + tft_set_page_addr(0, TFT_HEIGHT - 1); + + tft_ctrl = RAMWR; + for (u16 i = 0; i < TFT_HEIGHT; ++i) { + for (u16 j = 0; j < TFT_WIDTH; ++j) { + tft_data = 0x00; + tft_data = 0x00; + tft_data = 0x00; + } + } + EI; +} + +void +tft_init(void) +{ + _delay_ms(200); + + tft_soft_reset(); + tft_enable_ext_cmd(); + + _delay_ms(250); + + tft_set_rgb_interface(); + + tft_pixel_format(BIT18, BIT18); + + tft_memory_access_ctrl(MV | 0x0C); + tft_sleep_out(); + + _delay_ms(150); + + tft_display_on(); + + _delay_ms(50); + clear_screen(); +} diff --git a/src/tty.c b/src/tty.c new file mode 100644 index 0000000..f9413b4 --- /dev/null +++ b/src/tty.c @@ -0,0 +1,164 @@ +#include <tft.h> +#include <zeta.h> +#include <tty.h> + +struct color { + uint8_t r, g, b; +}; + +static unsigned int row = 0; +static unsigned int col = 0; + +// Index of first row inside buf +static volatile uint8_t head = 0; + +// TTY's current contents +static volatile uint8_t buf[TTY_HEIGHT][TTY_WIDTH]; + +static volatile struct color bg_color = {0x00, 0x00, 0x00}; +static volatile struct color fg_color = {0xFF, 0xFB, 0x00}; + +extern const uint8_t font[97][8]; + + +static uint8_t cursor = 0; +static uint8_t timer = 0; + +void +blink_cursor(void) __critical __interrupt(3) +{ + if (++timer >= 75) { + uint8_t i; + + tft_set_area(8 * col, 8 * row, 8, 8); + tft_ram_wr(); + + if ((cursor ^= 1)) { + for (i = 0; i < 64; ++i) + tft_pixel(fg_color.r, fg_color.g, fg_color.b); + } else { + for (i = 0; i < 64; ++i) + tft_pixel(bg_color.r, bg_color.g, bg_color.b); + } + timer = 0; + } +} + +static void +draw(u8 c) +{ + u8 i, j; + const u8 *chr; + + if (c < ' ') + return; + + chr = font[c - ' ']; + + tft_ram_wr(); + + for (i = 0; i < 8; ++i) { + u8 row = chr[i]; + for (j = 0; j < 8; ++j) { + if (row & 1) + tft_pixel(fg_color.r, fg_color.g, fg_color.b); + else + tft_pixel(bg_color.r, bg_color.g, bg_color.b); + row >>= 1; + } + } +} + +static void +scroll(void) +{ + uint16_t i, j; + head = (head + 1) % TTY_HEIGHT; + + for (i = head; i < TTY_HEIGHT; ++i) { + for (j = 0; j < TTY_WIDTH; ++j) { + tft_set_area(8 * j, 8 * (i - head), 8, 8); + draw(buf[i][j]); + } + } + + for (i = 0; i < head - 1; ++i) { + for (j = 0; j < TTY_WIDTH; ++j) { + tft_set_area(8 * j, 8 * (TTY_HEIGHT - head + i), 8, 8); + draw(buf[i][j]); + } + } + + for (j = 0; j < TTY_WIDTH; ++j) { + tft_set_area(8 * j, 8 * (TTY_HEIGHT - 1), 8, 8); + draw(' '); + } +} + +static void +newline(void) +{ + if (++row >= TTY_HEIGHT) { + row = TTY_HEIGHT - 1; + scroll(); + } +} + +static void +advance(void) +{ + if (++col >= TTY_WIDTH) { + newline(); + col = 0; + } +} + +void +addch(char c) +{ + switch (c) { + case '\b': + if (col != 0) + --col; + return; + + case '\r': + col = 0; + return; + + case '\n': + newline(); + return; + } + + tft_set_area(8 * col, 8 * row, 8, 8); + + draw(c); + + buf[(head + row) % TTY_HEIGHT][col] = c; + advance(); +} + +void +setcur(unsigned int ncol, unsigned int nrow) +{ + col = ncol; + row = nrow; +} + +void +swap_colors(void) +{ + struct color tmp = {fg_color.r, fg_color.g, fg_color.b}; + fg_color = bg_color; + bg_color = tmp; +} + +void +addstr(const char *str) +{ + while (*str) { + addch(*str); + ++str; + } +} |