From 2292aeaf7e9c7570b4b64b89f1a84e18174c26bd Mon Sep 17 00:00:00 2001 From: jjwbruijn Date: Sat, 12 Aug 2023 02:26:33 +0200 Subject: [PATCH] another beta of the 88mz100 7.5 fw --- .../12x20_horizontal_LSB_1.h | 98 ++ .../88MZ100_7.4_BETA-0.2.bin | Bin 0 -> 64160 bytes .../88MZ100_OpenEpaperLink_7.4/Makefile | 28 +- ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/comms.c | 192 +-- ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/comms.h | 8 - .../88MZ100_OpenEpaperLink_7.4/compression.c | 2 +- .../88MZ100_OpenEpaperLink_7.4/compression.h | 2 +- .../88MZ100_OpenEpaperLink_7.4/drawing.c | 373 +----- ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.c | 1110 +++++++++++------ ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.h | 28 +- ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/gpio.c | 4 +- ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.c | 898 +++++-------- ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.h | 2 +- .../88MZ100_OpenEpaperLink_7.4/mz100.ld | 26 +- .../mz100_aon_ram.c | 43 + .../mz100_aon_ram.h | 11 + .../88MZ100_OpenEpaperLink_7.4/mz100_sleep.c | 13 +- .../88MZ100_OpenEpaperLink_7.4/mz100_sleep.h | 2 +- ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.c | 4 +- ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.h | 2 +- .../88MZ100_OpenEpaperLink_7.4/powermgt.c | 143 ++- .../88MZ100_OpenEpaperLink_7.4/powermgt.h | 18 +- .../88MZ100_OpenEpaperLink_7.4/printf.c | 914 ++++++++++++++ .../88MZ100_OpenEpaperLink_7.4/printf.h | 116 ++ ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/proto.h | 241 ++-- .../88MZ100_OpenEpaperLink_7.4/settings.c | 155 +-- .../88MZ100_OpenEpaperLink_7.4/settings.h | 92 +- .../88MZ100_OpenEpaperLink_7.4/syncedproto.c | 620 ++++----- .../88MZ100_OpenEpaperLink_7.4/syncedproto.h | 13 +- .../userinterface.c | 181 +++ .../userinterface.h | 22 + ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/util.c | 320 +++-- .../88MZ100_OpenEpaperLink_7.4/zigbee.c | 26 +- .../88MZ100_OpenEpaperLink_7.4/zigbee.h | 10 +- 34 files changed, 3214 insertions(+), 2503 deletions(-) create mode 100644 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/12x20_horizontal_LSB_1.h create mode 100755 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/88MZ100_7.4_BETA-0.2.bin mode change 100644 => 100755 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.c mode change 100644 => 100755 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.c create mode 100644 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_aon_ram.c create mode 100644 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_aon_ram.h mode change 100644 => 100755 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.c create mode 100644 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/printf.c create mode 100644 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/printf.h create mode 100644 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/userinterface.c create mode 100644 ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/userinterface.h diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/12x20_horizontal_LSB_1.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/12x20_horizontal_LSB_1.h new file mode 100644 index 00000000..c8f9d8a2 --- /dev/null +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/12x20_horizontal_LSB_1.h @@ -0,0 +1,98 @@ +const char font[256][40]={ +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x20 +{0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x21 +{0x00,0x00,0xC0,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x22 +{0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x22,0x00,0x11,0x00,0x11,0x00,0x11,0xE0,0xFF,0x80,0x08,0x80,0x08,0x80,0x08,0xE0,0x7F,0x40,0x04,0x40,0x04,0x20,0x02,0x20,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x23 +{0x00,0x00,0x00,0x04,0x00,0x1F,0x80,0x3F,0xC0,0x24,0xC0,0x04,0xC0,0x04,0x80,0x07,0x00,0x07,0x00,0x1C,0x00,0x1C,0x00,0x34,0x00,0x34,0x40,0x34,0xC0,0x1F,0x80,0x0F,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x24 +{0x00,0x00,0x00,0x00,0xE0,0x81,0x30,0x43,0x30,0x23,0x30,0x13,0x30,0x0B,0x30,0x0B,0xE0,0x05,0x00,0x7A,0x00,0xCD,0x00,0xCD,0x80,0xCC,0x40,0xCC,0x20,0xCC,0x10,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x25 +{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0x80,0x19,0x80,0x19,0x80,0x0D,0x00,0x07,0xC0,0x03,0x60,0xC6,0x30,0xCE,0x30,0xCC,0x30,0x78,0x70,0x78,0xE0,0x7F,0xC0,0xEF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x26 +{0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x27 +{0x00,0x00,0x00,0x30,0x00,0x3C,0x00,0x0E,0x00,0x06,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x0E,0x00,0x3C,0x00,0x30,0x00,0x00}, // 0x28 +{0x00,0x00,0xC0,0x00,0xC0,0x03,0x00,0x07,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x07,0xC0,0x03,0xC0,0x00,0x00,0x00}, // 0x29 +{0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x60,0x33,0xE0,0x3C,0x00,0x00,0x80,0x0D,0xC0,0x19,0x80,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2A +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xE0,0x7F,0xE0,0x7F,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2B +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x00}, // 0x2C +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3F,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2D +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2E +{0x00,0x00,0x00,0x60,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x80,0x01,0xC0,0x00,0xC0,0x00,0x60,0x00,0x00,0x00}, // 0x2F +{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0xC0,0x30,0xC0,0x30,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xC0,0x30,0xC0,0x30,0x80,0x1F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x30 +{0x00,0x00,0x00,0x00,0x00,0x06,0xC0,0x07,0x60,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x31 +{0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0x40,0x38,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x32 +{0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x3F,0x40,0x30,0x00,0x30,0x00,0x18,0x80,0x0F,0x80,0x0F,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x30,0x40,0x38,0xC0,0x1F,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x33 +{0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x1C,0x00,0x1E,0x00,0x1A,0x00,0x19,0x80,0x19,0xC0,0x18,0x40,0x18,0xE0,0x7F,0xE0,0x7F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x34 +{0x00,0x00,0x00,0x00,0x80,0x3F,0x80,0x3F,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x0F,0x80,0x1F,0x00,0x38,0x00,0x30,0x00,0x30,0x00,0x38,0x80,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x35 +{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0xC0,0x11,0xC0,0x00,0x60,0x00,0x60,0x0E,0x60,0x1F,0xE0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0xC0,0x38,0xC0,0x1F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x36 +{0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x60,0x00,0x30,0x00,0x10,0x00,0x18,0x00,0x0C,0x00,0x04,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x37 +{0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x1F,0xC0,0x18,0xC0,0x18,0xC0,0x19,0x80,0x0F,0x00,0x07,0xC0,0x1E,0x60,0x38,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x38 +{0x00,0x00,0x00,0x00,0x80,0x07,0xC0,0x1F,0xE0,0x18,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x37,0x80,0x33,0x00,0x30,0x00,0x18,0x40,0x1C,0xC0,0x0F,0x80,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x39 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3A +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x00}, // 0x3B +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x70,0x00,0x3C,0x00,0x0E,0x80,0x03,0xE0,0x00,0x80,0x03,0x00,0x0E,0x00,0x3C,0x00,0x70,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3C +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3D +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0xE0,0x00,0xC0,0x03,0x00,0x07,0x00,0x1C,0x00,0x70,0x00,0x1C,0x00,0x07,0xC0,0x03,0xE0,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3E +{0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x3F,0x20,0x38,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3F +{0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x31,0xC0,0x60,0x60,0x7C,0x30,0x66,0x30,0x63,0x30,0x63,0x30,0x73,0x30,0x73,0x30,0x6F,0x60,0xE6,0x60,0x00,0xC0,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x40 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0F,0x00,0x0F,0x00,0x0D,0x80,0x19,0x80,0x19,0xC0,0x38,0xC0,0x30,0xC0,0x3F,0xE0,0x7F,0x60,0x60,0x60,0x60,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x41 +{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x1F,0x60,0x18,0x60,0x18,0x60,0x0C,0xE0,0x07,0xE0,0x0F,0x60,0x18,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x1F,0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x42 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x80,0x7F,0xC0,0x41,0xC0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xC0,0x00,0xC0,0x43,0x80,0x7F,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x43 +{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x1F,0x60,0x38,0x60,0x70,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x30,0x60,0x38,0xE0,0x1F,0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x44 +{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x45 +{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x46 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x80,0x7F,0xC0,0x41,0xC0,0x00,0x60,0x00,0x60,0x00,0x60,0x78,0x60,0x78,0x60,0x60,0xC0,0x60,0xC0,0x61,0x80,0x7F,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x47 +{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x3F,0xE0,0x3F,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x48 +{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x1F,0xE0,0x1F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0xE0,0x1F,0xE0,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x49 +{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0x80,0x1F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1C,0xC0,0x0F,0xC0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4A +{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x70,0x60,0x38,0x60,0x1C,0x60,0x0E,0x60,0x06,0x60,0x03,0xE0,0x03,0x60,0x07,0x60,0x0E,0x60,0x1C,0x60,0x38,0x60,0x70,0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4B +{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4C +{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x70,0x70,0x70,0xF0,0x78,0xB0,0x68,0xB0,0x68,0xB0,0x6D,0x30,0x65,0x30,0x65,0x30,0x67,0x30,0x62,0x30,0x60,0x30,0x60,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4D +{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x60,0xE0,0x60,0xE0,0x61,0xE0,0x61,0x60,0x63,0x60,0x67,0x60,0x66,0x60,0x6E,0x60,0x6C,0x60,0x78,0x60,0x78,0x60,0x70,0x60,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4E +{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0xE0,0x38,0x70,0x70,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x70,0x70,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4F +{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x1F,0xC0,0x3F,0xC0,0x70,0xC0,0x60,0xC0,0x60,0xC0,0x70,0xC0,0x3F,0xC0,0x0F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x50 +{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0xE0,0x38,0x70,0x70,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x38,0x00,0xF0,0x00,0x40,0x00,0x00}, // 0x51 +{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0xE0,0x1F,0x60,0x18,0x60,0x18,0x60,0x18,0x60,0x1C,0xE0,0x0F,0xE0,0x07,0x60,0x0E,0x60,0x1C,0x60,0x38,0x60,0x70,0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x52 +{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0x60,0x10,0x60,0x00,0xE0,0x00,0xC0,0x03,0x80,0x0F,0x00,0x3C,0x00,0x30,0x00,0x30,0x60,0x38,0xE0,0x1F,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x53 +{0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x54 +{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x55 +{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xC0,0x60,0x60,0x60,0x60,0xE0,0x60,0xC0,0x30,0xC0,0x30,0xC0,0x31,0x80,0x19,0x80,0x1B,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x56 +{0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xC0,0x30,0xC0,0x30,0xC0,0x20,0x46,0x20,0x46,0x20,0x6E,0x60,0x6F,0x60,0x69,0x60,0x69,0x60,0x39,0xC0,0x39,0xC0,0x39,0xC0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x57 +{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xE0,0xE0,0x60,0xC0,0x30,0x80,0x19,0x80,0x0F,0x00,0x0F,0x00,0x06,0x00,0x0F,0x80,0x1D,0x80,0x19,0xC0,0x30,0x60,0x70,0x30,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x58 +{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xC0,0x60,0x60,0xC0,0x30,0xC0,0x31,0x80,0x19,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x59 +{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x60,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0x60,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x5A +{0x00,0x00,0x00,0x3F,0x00,0x3F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x3F,0x00,0x3F,0x00,0x00}, // 0x5B +{0x00,0x00,0x60,0x00,0xC0,0x00,0xC0,0x00,0x80,0x01,0x80,0x01,0x80,0x01,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0x00}, // 0x5C +{0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0xC0,0x0F,0xC0,0x0F,0x00,0x00}, // 0x5D +{0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x0E,0x00,0x0A,0x00,0x0B,0x00,0x1B,0x80,0x11,0x80,0x31,0xC0,0x30,0xC0,0x20,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x5E +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00}, // 0x5F +{0x00,0x06,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x60 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0xC0,0x3F,0x40,0x30,0x00,0x30,0x00,0x30,0x80,0x3F,0xC0,0x30,0x60,0x30,0x60,0x38,0xE0,0xFF,0xC0,0xE7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x61 +{0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x0E,0x60,0x1F,0xE0,0x39,0xE0,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x18,0xE0,0x1F,0x60,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x62 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xC0,0x3F,0xC0,0x21,0xE0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xE0,0x00,0xC0,0x01,0xC0,0x3F,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x63 +{0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x3F,0x80,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x64 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x1F,0xC0,0x38,0x60,0x30,0xE0,0x3F,0xE0,0x3F,0x60,0x00,0x60,0x00,0xC0,0x20,0xC0,0x3F,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x65 +{0x00,0x00,0x00,0x7E,0x00,0x7F,0x00,0x03,0x00,0x03,0xE0,0x7F,0xE0,0x7F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x66 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x3F,0x80,0x37,0x00,0x30,0x40,0x38,0xC0,0x1F,0x80,0x0F}, // 0x67 +{0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x1E,0x60,0x3F,0xE0,0x31,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x68 +{0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x69 +{0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x80,0x1F,0x80,0x1F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1C,0xC0,0x0F,0xC0,0x07}, // 0x6A +{0x00,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x70,0xC0,0x38,0xC0,0x1C,0xC0,0x0E,0xC0,0x06,0xC0,0x07,0xC0,0x0E,0xC0,0x1C,0xC0,0x38,0xC0,0x70,0xC0,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6B +{0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6C +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xB0,0x39,0xF0,0x7F,0x70,0x67,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6D +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x1E,0x60,0x3F,0xE0,0x31,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6E +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x3F,0xC0,0x30,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xC0,0x30,0xC0,0x3F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6F +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x0F,0xE0,0x1F,0xE0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x18,0xE0,0x1F,0x60,0x0F,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00}, // 0x70 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x37,0x80,0x33,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30}, // 0x71 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3C,0xC0,0x3E,0xC0,0x23,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x72 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0xC0,0x1F,0xC0,0x00,0xC0,0x00,0xC0,0x03,0x00,0x1F,0x00,0x38,0x00,0x30,0x40,0x30,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x73 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0xE0,0x7F,0xE0,0x7F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x7F,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x74 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x3C,0xE0,0x37,0xC0,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x75 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x20,0xC0,0x30,0xC0,0x30,0x80,0x11,0x80,0x19,0x80,0x19,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x76 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xC0,0x30,0xC6,0x30,0xC6,0x20,0x4E,0x60,0x4F,0x60,0x49,0x60,0x69,0x60,0x79,0xC0,0x39,0xC0,0x30,0xC0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x77 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x70,0xC0,0x30,0x80,0x19,0x80,0x0B,0x00,0x0F,0x00,0x06,0x00,0x0F,0x80,0x1D,0x80,0x19,0xC0,0x30,0x60,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x78 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x20,0xC0,0x30,0xC0,0x31,0x80,0x19,0x80,0x19,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x06,0x00,0x02,0x00,0x03,0xC0,0x03,0xC0,0x01}, // 0x79 +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x3F,0xE0,0x3F,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0xE0,0x3F,0xE0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x7A +{0x00,0x00,0x00,0x3C,0x00,0x3E,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xC0,0x03,0xC0,0x03,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x3E,0x00,0x3C,0x00,0x00}, // 0x7B +{0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00}, // 0x7C +{0x00,0x00,0xC0,0x03,0xC0,0x07,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x3C,0x00,0x3C,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xC0,0x07,0xC0,0x03,0x00,0x00}, // 0x7D +{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x43,0xE0,0x7F,0x20,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x7E +{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3F,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} // 0x7F +}; \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/88MZ100_7.4_BETA-0.2.bin b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/88MZ100_7.4_BETA-0.2.bin new file mode 100755 index 0000000000000000000000000000000000000000..b170b0075608bea0a4dc859f7329bc30c9d789c1 GIT binary patch literal 64160 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 nJ3<0kg?$JK4ybSlA;Do4o -#include -#include #include "comms.h" -#include "proto.h" + +#include +//#include +#include + #include "ccm.h" +#include "proto.h" extern uint8_t Zigbee_tx_buffer(uint8_t tx_buffer[], int len); -static uint8_t packet[128]; -static uint8_t mSeq = 0; uint8_t mLastLqi = 0; int8_t mLastRSSI = 0; -uint8_t commsGetLastPacketLQI(void) -{ - return mLastLqi; +uint8_t commsGetLastPacketLQI(void) { + return mLastLqi; } -int8_t commsGetLastPacketRSSI(void) -{ - return mLastRSSI; +int8_t commsGetLastPacketRSSI(void) { + return mLastRSSI; } -static inline void __attribute__((always_inline)) macCopy(uint8_t *restrict dst, const uint8_t *restrict src) -{ - ((uint32_t *)dst)[0] = ((const uint32_t *)src)[0]; - ((uint32_t *)dst)[1] = ((const uint32_t *)src)[1]; +static inline void __attribute__((always_inline)) macCopy(uint8_t *restrict dst, const uint8_t *restrict src) { + ((uint32_t *)dst)[0] = ((const uint32_t *)src)[0]; + ((uint32_t *)dst)[1] = ((const uint32_t *)src)[1]; } -static inline bool __attribute__((always_inline)) macIsEq(const uint8_t *restrict dst, const uint8_t *restrict src) -{ - return ((uint32_t *)dst)[0] == ((const uint32_t *)src)[0] && ((uint32_t *)dst)[1] == ((const uint32_t *)src)[1]; -} - -bool commsTx(struct CommsInfo *info, bool bcast, const void *packet_in, uint32_t len) -{ - uint8_t nonce[AES_CCM_NONCE_SIZE] = {}; - struct MacFrameNormal *mfn; - struct MacFrameBcast *mfb; - uint32_t hdrSz; - char *payload; - static const struct MacFcs normalFcs = { - .frameType = FRAME_TYPE_DATA, - .panIdCompressed = 1, - .destAddrType = ADDR_MODE_LONG, - .srcAddrType = ADDR_MODE_LONG, - }; - static const struct MacFcs broadcastFcs = { - .frameType = FRAME_TYPE_DATA, - .destAddrType = ADDR_MODE_SHORT, - .srcAddrType = ADDR_MODE_LONG, - }; - - if (len > COMMS_MAX_PACKET_SZ) - return false; - - if (bcast) - { - mfb = (struct MacFrameBcast *)packet; - hdrSz = sizeof(struct MacFrameBcast); - payload = (char *)(mfb + 1); - mfb->fcs = broadcastFcs; - mfb->seq = mSeq++; - mfb->dstPan = 0xffff; - mfb->dstAddr = 0xffff; - mfb->srcPan = PROTO_PAN_ID; - macCopy(mfb->src, info->myMac); - } - else - { - mfn = (struct MacFrameNormal *)packet; - hdrSz = sizeof(struct MacFrameNormal); - payload = (char *)(mfn + 1); - mfn->fcs = normalFcs; - mfn->seq = mSeq++; - mfn->pan = PROTO_PAN_ID; - macCopy(mfn->dst, info->masterMac); - macCopy(mfn->src, info->myMac); - } - - *(uint32_t *)nonce = (*info->nextIV)++; - macCopy(nonce + sizeof(uint32_t), info->myMac); - memcpy(payload, packet_in, len); - - aesCcmEnc((void *)packet, (void *)packet, hdrSz, len, info->encrKey, nonce); - *(uint32_t *)(payload + len + AES_CCM_MIC_SIZE) = *(uint32_t *)nonce; // send nonce - - len += hdrSz; - len += AES_CCM_MIC_SIZE; - len += sizeof(uint32_t); - - return !Zigbee_tx_buffer((uint8_t *)&packet, len); +static inline bool __attribute__((always_inline)) macIsEq(const uint8_t *restrict dst, const uint8_t *restrict src) { + return ((uint32_t *)dst)[0] == ((const uint32_t *)src)[0] && ((uint32_t *)dst)[1] == ((const uint32_t *)src)[1]; } extern volatile uint8_t rx_buffer[0x400]; @@ -98,94 +34,16 @@ extern volatile uint8_t new_rx; extern volatile uint8_t new_rssi; extern volatile int rx_len; -int32_t __attribute__((noinline)) commsRx(struct CommsInfo *info, void *data, uint8_t *fromMacP) -{ - uint8_t *buf = packet, nonce[13] = {}, fromMac[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - uint32_t len, minNeedLen, hdrLen = 0; - struct MacFrameFromMaster *mfm; - struct MacFrameNormal *mfn; - - // sort out how many bytes minimum are a valid packet - minNeedLen = sizeof(struct MacFrameFromMaster); // mac header - minNeedLen += sizeof(uint8_t); // packet type - minNeedLen += AES_CCM_MIC_SIZE; // MIC - minNeedLen += sizeof(uint32_t); // nonce counter - minNeedLen += 2 * sizeof(uint8_t); // RSSI/LQI - - if (!new_rx) - return COMMS_RX_ERR_NO_PACKETS; - - // some basic checks - mfm = (struct MacFrameFromMaster *)rx_buffer; - if (rx_len >= sizeof(packet) || rx_len < minNeedLen || mfm->fcs.frameType != FRAME_TYPE_DATA || - mfm->fcs.secure || mfm->fcs.frameVer || mfm->fcs.destAddrType != ADDR_MODE_LONG || !mfm->fcs.panIdCompressed || - (mfm->fcs.srcAddrType != ADDR_MODE_LONG && mfm->fcs.srcAddrType != ADDR_MODE_SHORT) || - mfm->pan != PROTO_PAN_ID || !macIsEq(mfm->dst, info->myMac)) - { - new_rx = 0; - return COMMS_RX_ERR_INVALID_PACKET; - } - - // copy out and release buffer - memcpy(buf, &rx_buffer, len = rx_len - 2 * sizeof(uint8_t)); - mLastLqi = rx_buffer[len + 0]; - mLastRSSI = rx_buffer[len + 1]; - - mfm = (struct MacFrameFromMaster *)buf; - mfn = (struct MacFrameNormal *)buf; - new_rx = 0; - - // sort out header len, copy mac into nonce - if (mfm->fcs.srcAddrType == ADDR_MODE_LONG) - { - - macCopy(fromMac, mfn->src); - hdrLen = sizeof(struct MacFrameNormal); - - // re-verify needed length - minNeedLen -= sizeof(struct MacFrameFromMaster); - minNeedLen += sizeof(struct MacFrameNormal); - - if (rx_len < minNeedLen) - return COMMS_RX_ERR_INVALID_PACKET; - } - else if (mfm->fcs.srcAddrType == ADDR_MODE_SHORT) - { - - macCopy(fromMac, info->masterMac); - hdrLen = sizeof(struct MacFrameFromMaster); - } - - // sort out the nonce - macCopy(nonce + sizeof(uint32_t), fromMac); - *(uint32_t *)nonce = *(uint32_t *)(buf + len - sizeof(uint32_t)); - - // decrypt and auth - len -= hdrLen + AES_CCM_MIC_SIZE + sizeof(uint32_t); - - if (!aesCcmDec(buf, buf, hdrLen, len, info->encrKey, nonce)) - return COMMS_RX_ERR_MIC_FAIL; - - if (fromMacP) - macCopy(fromMacP, fromMac); - - memcpy(data, buf + hdrLen, len); - - return len; +int32_t __attribute__((noinline)) commsRxUnenc(void *data) { + if (!new_rx) + return COMMS_RX_ERR_NO_PACKETS; + memcpy(data, (uint8_t*)&rx_buffer, rx_len); + mLastLqi = 255 - new_rssi; + mLastRSSI = new_rssi; + new_rx = 0; + return rx_len; } -int32_t __attribute__((noinline)) commsRxUnenc(void *data) -{ - if (!new_rx) - return COMMS_RX_ERR_NO_PACKETS; - memcpy(data, &rx_buffer, rx_len); - mLastLqi = 255 - new_rssi; - mLastRSSI = new_rssi; - new_rx = 0; - return rx_len; -} - -void commsTxNoCpy(uint8_t *packetp) -{ - Zigbee_tx_buffer((uint8_t *)&packetp[1], (packetp[0] - 2)); +void commsTxNoCpy(uint8_t *packetp) { + Zigbee_tx_buffer((uint8_t *)&packetp[1], (packetp[0] - 2)); } diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/comms.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/comms.h index f06d30dd..a9c4d17b 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/comms.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/comms.h @@ -4,12 +4,6 @@ #include #include "ccm.h" -struct CommsInfo { - const uint8_t *myMac; - const uint8_t *masterMac; - const void *encrKey; - uint32_t *nextIV; -}; extern uint8_t mLastLqi; extern int8_t mLastRSSI; @@ -23,8 +17,6 @@ extern int8_t mLastRSSI; #define COMMS_MAX_PACKET_SZ (127 /* max phy len */ - 21 /* max mac frame with panID compression */ - 2 /* FCS len */ - AES_CCM_MIC_SIZE - COMMS_IV_SIZE) -bool commsTx(struct CommsInfo *info, bool bcast, const void *packet, uint32_t len); -int32_t commsRx(struct CommsInfo *info, void *data, uint8_t *fromMac); //returns length or COMMS_RX_ERR_* uint8_t commsGetLastPacketLQI(void); int8_t commsGetLastPacketRSSI(void); diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/compression.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/compression.c index b5b7a04a..0bc5d1d7 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/compression.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/compression.c @@ -1,7 +1,7 @@ #include "compression.h" #include "uzlib/src/uzlib.h" -#include +//#include #include #include #include diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/compression.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/compression.h index dc74075b..b059a007 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/compression.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/compression.h @@ -1,7 +1,7 @@ #pragma once #include "uzlib/src/uzlib.h" -#include +//#include #include #include #include diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/drawing.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/drawing.c index 187c733d..20b01fd7 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/drawing.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/drawing.c @@ -2,6 +2,7 @@ #include #include +#include "printf.h" #include "board.h" #include "eeprom.h" @@ -11,366 +12,22 @@ #include "util.h" #include "epd.h" -#define COMPRESSION_BITPACKED_3x5_to_7 0x62700357 // 3 pixels of 5 possible colors in 7 bits -#define COMPRESSION_BITPACKED_5x3_to_8 0x62700538 // 5 pixels of 3 possible colors in 8 bits -#define COMPRESSION_BITPACKED_3x6_to_8 0x62700368 // 3 pixels of 6 possible colors in 8 bits - -struct BitmapFileHeader -{ - uint8_t sig[2]; - uint32_t fileSz; - uint8_t rfu[4]; - uint32_t dataOfst; - uint32_t headerSz; // 40 - int32_t width; - int32_t height; - uint16_t colorplanes; // must be one - uint16_t bpp; - uint32_t compression; - uint32_t dataLen; // may be 0 - uint32_t pixelsPerMeterX; - uint32_t pixelsPerMeterY; - uint32_t numColors; // if zero, assume 2^bpp - uint32_t numImportantColors; -}; struct BitmapClutEntry { uint8_t b, g, r, x; }; -struct BitmapDrawInfo -{ - // dimensions - uint16_t w, h, effectiveW, effectiveH, stride /* 0 -> 1, 5 - >7, 255 -> 256 */; - uint8_t numColorsM1; - - // data start - uint32_t dataAddr; - - // compression state - uint8_t packetPixelDivVal; - uint8_t packetNumPixels; - uint8_t packetBitSz; - uint8_t packetBitMask; // derived from the above - - // flags - uint8_t bpp : 4; - uint8_t bottomUp : 1; -}; uint8_t mPassNo = 0; -static const uint8_t mColorMap[][6] = { - // colors are: B, DG, G, LG, W, R - // phase 0 (LUTS: B:W:R:G, purpose: BWR, prepare greys) - {1, 1, 1, 1, 0, 0}, // lo plane (B) - - {0, 0, 0, 0, 0, 1} // hi plane (R) -}; - static uint8_t mClutMap[256]; static uint8_t mClutMapRed[256]; -static struct BitmapDrawInfo mDrawInfo; - -static uint32_t drawPrvParseHeader(uint32_t addr) // return clut addr or zero on error -{ - /*struct BitmapFileHeader bmph; - uint16_t packetsPerRow; - - addr += sizeof(struct EepromImageHeader); - eepromRead(addr, &bmph, sizeof(bmph)); - - if (bmph.sig[0] != 'B' || bmph.sig[1] != 'M') - goto fail; - - if (bmph.colorplanes != 1) - goto fail; - - if ((&bmph.headerSz - 40)) // < 40 - goto fail; - - if (bmph.bpp > 8) - goto fail; - - mDrawInfo.bpp = bmph.bpp; - - if (!(&bmph.headerSz - 257)) // >= 257 - goto fail; - - if ((&bmph.numColors)) - mDrawInfo.numColorsM1 = (uint8_t)bmph.numColors - (uint8_t)1; - else - mDrawInfo.numColorsM1 = (uint8_t)((uint8_t)1 << (uint8_t)mDrawInfo.bpp) - (uint8_t)1; - - if (!(&bmph.height)) - goto fail; - - if ((&bmph.width - 1) || !(&bmph.width - 0xffff)) - goto fail; - mDrawInfo.w = bmph.width; - - if ((&bmph.height) < 0) - { - if ((&bmph.height + 0xffff)) // carries if val too negative - goto fail; - mDrawInfo.h = -bmph.height; - mDrawInfo.bottomUp = false; - } - else - { - if (!(&bmph.headerSz - 0xffff)) // no carry if val too big - goto fail; - mDrawInfo.h = bmph.height; - mDrawInfo.bottomUp = true; - } - - if (bmph.compression) - { - printf("compression is not supported ;("); - goto fail; - } - - mDrawInfo.packetPixelDivVal = 0; - mDrawInfo.packetNumPixels = 1; - if (mDrawInfo.bpp > 1) - { - mDrawInfo.packetBitSz = 2; - } - else - { - mDrawInfo.packetBitSz = 1; // mDrawInfo.bpp; - } - - // mDrawInfo.stride = mathPrvDiv32x8(mathPrvMul16x8((mDrawInfo.w + mDrawInfo.packetNumPixels - 1), mDrawInfo.packetBitSz) + 31, 32) * 4UL; - // mDrawInfo.packetBitMask = (uint8_t)(((uint8_t)1) << (uint8_t)mDrawInfo.packetBitSz) - (uint8_t)1; - - packetsPerRow = (mDrawInfo.w + mDrawInfo.packetNumPixels - 1) / (mDrawInfo.packetNumPixels); - mDrawInfo.stride = (((packetsPerRow * mDrawInfo.packetBitSz) + 31) / 32) * 4UL; - mDrawInfo.packetBitMask = (uint8_t)(((uint8_t)1) << (uint8_t)mDrawInfo.packetBitSz) - (uint8_t)1; - - // calc effective size - mDrawInfo.effectiveH = (mDrawInfo.h > SCREEN_HEIGHT) ? SCREEN_HEIGHT : mDrawInfo.h; - mDrawInfo.effectiveW = (mDrawInfo.w > SCREEN_WIDTH) ? SCREEN_WIDTH : mDrawInfo.w; - - // calc addrs - mDrawInfo.dataAddr = addr + bmph.dataOfst; - return addr + bmph.dataOfst - sizeof(struct BitmapClutEntry) * (1 + mDrawInfo.numColorsM1); - -fail: - printf("Tried to parse the bmp header, didn't work...");*/ - return 0; -} - -static void drawPrvLoadAndMapClut(uint32_t clutAddr) -{ - /*struct BitmapClutEntry clut; - uint8_t i; - - // convert clut to our understanding of color - i = 0; - do - { - uint8_t entry; - - eepromRead(clutAddr, &clut, sizeof(clut)); - clutAddr += sizeof(struct BitmapClutEntry); - - if (SCREEN_EXTRA_COLOR_INDEX >= 0 && clut.r == 0xff && (clut.g == 0xff || clut.g == 0) && clut.b == 0) // yellow/red - entry = SCREEN_EXTRA_COLOR_INDEX; - else - { - uint16_t intensity = 0; - - intensity += (0x37 * clut.r); - intensity += (0xB7 * clut.g); - intensity += (0x12 * clut.b); - // adds up to 0xff00 -> fix it - intensity += (uint8_t)(intensity >> 8); - - entry = (intensity * SCREEN_NUM_GREYS) >> 16; - entry += SCREEN_FIRST_GREY_IDX; - } - // printf("mapped clut %u (%d %d %d) -> %d\n", i, clut.r, clut.g, clut.b, entry); - mClutMap[i] = entry; - } while (i++ != mDrawInfo.numColorsM1); - - // replicate clut down if not a full 256-entry clut - if (mDrawInfo.bpp != 8) - { - uint8_t num = (uint8_t)((uint8_t)1 << (uint8_t)mDrawInfo.bpp); - - // we can use the fact that our memcpy always copies forward - memcpy(mClutMap + num, mClutMap, (uint8_t)256 - (uint8_t)num); - }*/ -} - -static void drawPrvDecodeImageOnce(void) -{ - /*uint8_t rowBuf[SCREEN_WIDTH]; - uint16_t er, c; - if (mDrawInfo.bottomUp) - er = mDrawInfo.effectiveH - 1; - else - er = 0; - while (1) - { // we account differently for loop gets compiled worse - uint8_t inIdx = 0, bitpoolInUsed = 0, bitpoolIn = 0; - uint16_t nBytesOut = 0; - -#if SCREEN_TX_BPP == 4 - uint8_t txPrev = 0; - bool emit = false; -#else - uint8_t bitpoolOutUsedUsed = 0; - uint16_t bitpoolOut = 0; -#endif - // get a row - epdDeselect(); - eepromRead((er * mDrawInfo.stride) + mDrawInfo.dataAddr, rowBuf, mDrawInfo.stride); - epdSelect(); - // convert to our format - c = mDrawInfo.effectiveW; - do - { - // uartTx('.'); - uint8_t packet, packetIdx, packetMembers = mDrawInfo.packetNumPixels; - - if (bitpoolInUsed >= mDrawInfo.packetBitSz) - { - bitpoolInUsed -= mDrawInfo.packetBitSz; - packet = bitpoolIn >> bitpoolInUsed; - } - else - { - uint8_t packetBitSz = mDrawInfo.packetBitSz; - uint8_t t = rowBuf[inIdx++]; - - packet = (bitpoolIn << (packetBitSz - bitpoolInUsed)) | (t >> (8 - (packetBitSz - bitpoolInUsed))); - bitpoolInUsed += 8 - packetBitSz; - - bitpoolIn = t; - } - packet &= mDrawInfo.packetBitMask; - - // val is now a packet - unpack it - if (packetMembers > c) - packetMembers = c; - - for (packetIdx = 0; packetIdx < packetMembers; packetIdx++) - { - uint8_t val; - - // extract - if (mDrawInfo.packetPixelDivVal) - { - val = packet % mDrawInfo.packetPixelDivVal; - packet /= mDrawInfo.packetPixelDivVal; - } - else - val = packet; - - // map - val = mClutMap[val]; - -// get bits out -#if SCREEN_TX_BPP == 4 - - if (emit) - { - emit = false; - ByteDecode(txPrev | val); - nBytesOut++; - txPrev = 0; - } - else - { - emit = true; - txPrev = val << 4; - } - -#else - bitpoolOut <<= SCREEN_TX_BPP; - bitpoolOut |= val; - bitpoolOutUsedUsed += SCREEN_TX_BPP; - if (bitpoolOutUsedUsed >= 8) - { - ByteDecode(bitpoolOut >> (bitpoolOutUsedUsed -= 8)); - bitpoolOut &= (1 << bitpoolOutUsedUsed) - 1; - nBytesOut++; - } -#endif - } - c -= packetMembers; - } while (c); - -#if SCREEN_TX_BPP == 4 - - if (emit) - { - ByteDecode(txPrev); - nBytesOut++; - } - -#else - - if (bitpoolOutUsedUsed) - { - ByteDecode(bitpoolOut); - nBytesOut++; - } - -#endif - - // if we did not produce enough bytes, do so - nBytesOut = ((long)SCREEN_WIDTH * SCREEN_TX_BPP + 7) / 8 - nBytesOut; - while (nBytesOut--) - ByteDecode(SCREEN_BYTE_FILL); - - // update row - if (mDrawInfo.bottomUp) - { - if (er) - er--; - else - break; - } - else - { - er++; - if (er == mDrawInfo.effectiveH) - break; - } - } - - // fill the rest of the screen - for (er = mDrawInfo.effectiveH - SCREEN_HEIGHT; er; er--) - { - for (c = ((long)SCREEN_WIDTH * SCREEN_TX_BPP + 7) / 8; c; c--) - { - ByteDecode(SCREEN_BYTE_FILL); - } - }*/ -} - -static uint8_t prev, step = 0; - -void ByteDecode(uint8_t byte) -{ - /*prev <<= 2; - prev |= (mColorMap[mPassNo][byte >> 4] << 1) | mColorMap[mPassNo][byte & 0x0f]; - if (++step == 4) - { - step = 0; - Display_Write_byte(prev); - }*/ -} void drawImageAtAddress(uint32_t addr, uint8_t lut) { struct EepromImageHeader *eih = (struct EepromImageHeader *)mClutMap; eepromRead(addr, mClutMap, sizeof(struct EepromImageHeader)); - uint8_t prevVal = 0; switch (eih->dataType) { case DATATYPE_IMG_RAW_1BPP: @@ -430,34 +87,6 @@ void drawImageAtAddress(uint32_t addr, uint8_t lut) display_send_stop(); epd_refresh_and_sleep(); break; - case DATATYPE_IMG_BMP:; - uint32_t clutAddr; - printf("sending BMP to EPD - "); - /*clutAddr = drawPrvParseHeader(addr); - if (!clutAddr) - return; - drawPrvLoadAndMapClut(clutAddr); - - epdSetup(); - if (lut) - selectLUT(lut); - mPassNo = 0; - beginFullscreenImage(); - beginWriteFramebuffer(EPD_COLOR_BLACK); - prev = 0; - step = 0; - drawPrvDecodeImageOnce(); - endWriteFramebuffer(); - mPassNo++; - beginFullscreenImage(); - beginWriteFramebuffer(EPD_COLOR_RED); - prev = 0; - step = 0; - drawPrvDecodeImageOnce(); - endWriteFramebuffer();*/ - - printf(" complete.\n"); - break; default: // prevent drawing from an unknown file image type printf("Image with type 0x%02X was requested, but we don't know what to do with that currently...\n", eih->dataType); return; diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.c old mode 100644 new mode 100755 index 3ba31c32..7998eccc --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.c @@ -1,431 +1,753 @@ -#include -#include -#include -#include "core_cm3.h" -#include "main.h" #include "epd.h" -#include "mz100_gpio.h" -#include "mz100_ssp.h" -#include "mz100_pinmux.h" -#include "mz100_clock.h" -#include "mz100_wdt.h" -#include "util.h" + +#include +#include +//#include +#include "printf.h" +#include +#include + +#include "12x20_horizontal_LSB_1.h" +#include "core_cm3.h" #include "gpio.h" +#include "main.h" +#include "mz100_clock.h" +#include "mz100_gpio.h" +#include "mz100_pinmux.h" +#include "mz100_ssp.h" +#include "mz100_wdt.h" +#include "stdarg.h" +#include "util.h" -void epd_reset() -{ - uint8_t v0 = 5; - while (1) - { - GPIO_WritePinOutput(EPD_RESET, GPIO_IO_HIGH); - delay(100); - GPIO_WritePinOutput(EPD_RESET, GPIO_IO_LOW); - delay(3000); - GPIO_WritePinOutput(EPD_RESET, GPIO_IO_HIGH); - delay(3000); - if (GPIO_ReadPinLevel(EPD_BUSY)) - break; - v0--; - if (!v0) - { - printf("EPD reset failure\r\n"); - break; - } - } - delay(5000); +#define EPD_PANEL_SETTING 0x00 +#define EPD_POWER_SETTING 0x01 +#define EPD_POWER_OFF 0x02 +#define EPD_POWER_OFF_SEQUENCE 0x03 +#define EPD_POWER_ON 0x04 +#define EPD_BOOSTER_SOFT_START 0x06 +#define EPD_DEEP_SLEEP 0x07 +#define EPD_START_DATA 0x10 +#define EPD_DATA_STOP 0x11 +#define EPD_REFRESH 0x12 +#define EPD_IMAGE_PROCESS 0x13 +#define EPD_LUT_VCOM 0x20 +#define EPD_LUT_B 0x21 +#define EPD_LUT_W 0x22 +#define EPD_LUT_G1 0x23 +#define EPD_LUT_G2 0x24 +#define EPD_LUT_R0 0x25 +#define EPD_LUT_R1 0x26 +#define EPD_LUT_R2 0x27 +#define EPD_LUT_R3 0x28 +#define EPD_LUT_XON 0x29 +#define EPD_PLL_CONTROL 0x30 +#define EPD_TEMP_CALIB 0x40 +#define EPD_TEMP_SELECT 0x41 +#define EPD_TEMP_WRITE 0x42 +#define EPD_TEMP_READ 0x43 +#define EPD_VCOM_DATA_INTERVAL 0x50 +#define EPD_LPD 0x51 +#define EPD_TCON_SET 0x60 +#define EPD_TRES 0x61 +#define EPD_SPI_FLASH_CONTROL 0x65 +#define EPD_REVISION 0x70 +#define EPD_GET_STATUS 0x71 +#define EPD_AUTOMEASURE_VCOM 0x80 +#define EPD_READ_VCOM 0x81 +#define EPD_VCOM_DC_SET 0x82 +#define EPD_SET_WINDOW 0x90 + +#define EPD_WAKE_EEPROM 0xAB +#define EPD_EEPROM_SLEEP 0xB9 +#define EPD_UNKNOWN_1 0xE5 + +static uint8_t EPDtempBracket = 0; + +struct epd_colorlutpart { + uint8_t repeat; + uint8_t lvl0 : 4; + uint8_t lvl1 : 4; + uint8_t lvl2 : 4; + uint8_t lvl3 : 4; + uint8_t lvl4 : 4; + uint8_t lvl5 : 4; + uint8_t lvl6 : 4; + uint8_t lvl7 : 4; + uint8_t length[8]; +} __packed; + +struct epd_colorlut { + struct epd_colorlutpart part[20]; +} __packed; + +struct epd_vcomlutpart { + uint8_t repeat; + uint8_t lvl0 : 2; + uint8_t lvl1 : 2; + uint8_t lvl2 : 2; + uint8_t lvl3 : 2; + uint8_t lvl4 : 2; + uint8_t lvl5 : 2; + uint8_t lvl6 : 2; + uint8_t lvl7 : 2; + uint8_t length[8]; +} __packed; + +struct epd_vcomlut { + struct epd_vcomlutpart part[20]; +} __packed; + +struct epd_xonlutpart { + uint8_t repeat; + uint8_t lvl0 : 1; + uint8_t lvl1 : 1; + uint8_t lvl2 : 1; + uint8_t lvl3 : 1; + uint8_t lvl4 : 1; + uint8_t lvl5 : 1; + uint8_t lvl6 : 1; + uint8_t lvl7 : 1; + uint8_t length[8]; +} __packed; + +struct epd_xonlut { + struct epd_xonlutpart part[20]; +} __packed; + +void interleaveColor(uint8_t b) { + b ^= 0xFF; + uint8_t b_out = 0; + for (uint8_t shift = 0; shift < 4; shift++) { + b_out = 0; + if ((b >> 2 * shift) & 0x01) b_out |= 0x30; + if ((b >> 2 * shift) & 0x02) b_out |= 0x03; + display_tx_byte(b_out); + } } -void EPD_cmd(char a1) -{ - unsigned int v1 = 0; +void epdWrite(uint8_t reg, uint8_t len, ...) { + va_list valist; + va_start(valist, len); + epd_pin_enable(1); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); + GPIO_WritePinOutput(EPD_DC, GPIO_IO_LOW); + SSP_SendData(SSP2_ID, reg); + for (int i = 0; i < 0xF; ++i) + __ISB(); + GPIO_WritePinOutput(EPD_DC, GPIO_IO_HIGH); + for (uint8_t i = 0; i < len; i++) { + SSP_SendData(SSP2_ID, va_arg(valist, int)); + } - GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); - GPIO_WritePinOutput(EPD_DC, GPIO_IO_LOW); - do - { - if ((a1 & 0x80) != 0) - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_HIGH); - else - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); - a1 *= 2; - v1++; - delay_us(1); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); - delay_us(1); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); - } while (v1 < 8); - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); - GPIO_WritePinOutput(EPD_DC, GPIO_IO_HIGH); - GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); - delay(1000); + for (int j = 0; j < 0xF; ++j) + __ISB(); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); + epd_pin_enable(0); + va_end(valist); } -void EPD_data(char a1) -{ - unsigned int v1 = 0; - - GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); - do - { - if ((a1 & 0x80) != 0) - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_HIGH); - else - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); - a1 *= 2; - v1++; - delay_us(1); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); - delay_us(1); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); - } while (v1 < 8); - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); - GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); - delay(1000); +void epd_reset() { + uint8_t v0 = 5; + printf("Resetting..."); + while (1) { + GPIO_WritePinOutput(EPD_RESET, GPIO_IO_HIGH); + delay(100); + GPIO_WritePinOutput(EPD_RESET, GPIO_IO_LOW); + delay(3000); + GPIO_WritePinOutput(EPD_RESET, GPIO_IO_HIGH); + delay(3000); + if (GPIO_ReadPinLevel(EPD_BUSY)) + break; + v0--; + if (!v0) { + printf("EPD reset failure\r\n"); + break; + } + } + delay(5000); + printf(" Reset complete\n"); } -void spi_soft_send_byte(char a1) -{ - uint8_t v2 = 0; - do - { - if ((a1 & 0x80) != 0) - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_HIGH); - else - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); - delay_us(1); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); - delay_us(1); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); - a1 *= 2; - v2++; - } while (v2 < 8); - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); - delay_us(1); +void EPD_cmd(char a1) { + epd_pin_enable(1); + + GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); + GPIO_WritePinOutput(EPD_DC, GPIO_IO_LOW); + SSP_SendData(SSP2_ID, a1); + for (int i = 0; i < 0xF; ++i) + __ISB(); + + epd_pin_enable(0); + + GPIO_WritePinOutput(EPD_DC, GPIO_IO_HIGH); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); } -void BUSY_wait(unsigned int a1) -{ - unsigned int v2 = 0; - while (GPIO_ReadPinLevel(EPD_BUSY) == GPIO_IO_LOW) - { - delay(10000); - v2++; - if (v2 > a1) - break; - if (!(20 * (v2 % 1000 / 10))) - WDT_RestartCounter(); - } +void EPD_data(char a1) { + epd_pin_enable(1); + + GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); + SSP_SendData(SSP2_ID, a1); + for (int i = 0; i < 0xF; ++i) + __ISB(); + + epd_pin_enable(0); + + GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); } -void spi_soft_read_buffer(char a1, uint16_t a2, uint8_t *a3, unsigned int a4) -{ - char v9; - unsigned int v10; - - GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); - GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_LOW); - spi_soft_send_byte(3); - spi_soft_send_byte(a1); - spi_soft_send_byte(a2 >> 8); - spi_soft_send_byte(a2); - delay_us(10); - for (int i = 0; i < a4; i = (uint8_t)(i + 1)) - { - v9 = 0; - v10 = 0; - do - { - v9 *= 2; - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); - delay_us(5); - if (GPIO_ReadPinLevel(EPD_MISO)) - v9 |= 1u; - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); - delay_us(5); - v10++; - } while (v10 < 8); - delay_us(5); - *a3++ = v9; - } - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); - GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_HIGH); +void spi_soft_send_byte(char a1) { + uint8_t v2 = 0; + do { + if ((a1 & 0x80) != 0) + GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_HIGH); + else + GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); + delay_us(1); + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); + delay_us(1); + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); + a1 *= 2; + v2++; + } while (v2 < 8); + GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); + delay_us(1); } -int spi_soft_read_byte() -{ - int v0; - unsigned int v1; - - v0 = 0; - GPIO_WritePinOutput(EPD_BS, GPIO_IO_HIGH); - GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); - delay_us(1); - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_HIGH); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); - delay_us(1); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); - delay_us(1); - GPIO_SetPinDir(EPD_MOSI, GPIO_INPUT); - GPIO_PinMuxFun(EPD_MOSI, 0); - delay_us(3); - v1 = 0; - do - { - v0 = (uint8_t)(2 * v0); - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); - if (GPIO_ReadPinLevel(EPD_MOSI)) - v0 |= 1u; - GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); - delay_us(1); - v1++; - } while (v1 < 8); - GPIO_SetPinDir(EPD_MOSI, GPIO_OUTPUT); - GPIO_PinMuxFun(EPD_MOSI, 0); - GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); - delay_us(1); - GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); - delay_us(1); - GPIO_WritePinOutput(EPD_BS, GPIO_IO_LOW); - return v0; +void BUSY_wait(unsigned int a1) { + unsigned int v2 = 0; + while (GPIO_ReadPinLevel(EPD_BUSY) == GPIO_IO_LOW) { + delay(10000); + v2++; + if (v2 > a1) + break; + if (((v2 % 1000) / 10) == 0) + WDT_RestartCounter(); + } } -void epd_read_write_30() -{ - uint8_t v0; - uint8_t v1; - uint8_t v4; - uint8_t v5[12]; - uint8_t v6[12]; - uint8_t v7[40]; +void spi_soft_read_buffer(char a1, uint16_t readaddress, uint8_t *target, uint16_t length) { + char v9; + unsigned int v10; - EPD_cmd(101); - EPD_data(1); - delay_us(1000); - spi_soft_read_buffer(0, 25002, v7, 10); - delay_us(1000); - spi_soft_read_buffer(0, 25039, v6, 10); - delay_us(1000); - EPD_cmd(101); - EPD_data(0); - EPD_cmd(64); - BUSY_wait(0xAu); - v0 = spi_soft_read_byte(); - v1 = (uint8_t)(2 * v0) + ((uint8_t)spi_soft_read_byte() >> 7); - for (int i = 0; i < 9; i++) - v5[i] = (((char)v1 - (uint8_t)v7[i]) & 0x80) != 0; - for (int j = 0; j < 9; j++) - { - v4 = v6[j]; - if (v5[j] == 1) - break; - } - EPD_cmd(0x30); - EPD_data(v4); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); + GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_LOW); + spi_soft_send_byte(3); + spi_soft_send_byte(a1); + spi_soft_send_byte(readaddress >> 8); + spi_soft_send_byte(readaddress); + delay_us(5); + for (uint16_t i = 0; i < length; i++) { + v9 = 0; + v10 = 0; + do { + v9 *= 2; + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); + delay_us(1); + if (GPIO_ReadPinLevel(EPD_MISO)) + v9 |= 1u; + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); + delay_us(1); + v10++; + } while (v10 < 8); + delay_us(1); + *target++ = v9; + } + + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); + GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_HIGH); } -void epd_read_write_82() -{ - uint8_t v0; - int v1; - uint8_t v4; - uint8_t v5[12]; - uint8_t v6[12]; - uint8_t v7[32]; +int spi_soft_read_byte() { + int v0; + unsigned int v1; - EPD_cmd(101); - EPD_data(1); - delay_us(1000); - spi_soft_read_buffer(0, 25002, v7, 10); - spi_soft_read_buffer(0, 25049, v6, 10); - delay_us(1000); - EPD_cmd(101); - EPD_data(0); - EPD_cmd(0x40); - BUSY_wait(0xAu); - v0 = spi_soft_read_byte(); - v1 = (char)(2 * v0 + ((unsigned int)spi_soft_read_byte() >> 7)); - for (int i = 0; i < 9; i++) - v5[i] = ((v1 - (uint8_t)v7[i]) & 0x80) != 0; - for (int j = 0; j < 9; j++) - { - v4 = v6[j]; - if (v5[j] == 1) - break; - } - EPD_cmd(0x82); - EPD_data(v4); -} -void epd_send_init() -{ - GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); - delay(1000); - EPD_cmd(4); - BUSY_wait(0x32u); - EPD_cmd(1); - EPD_data(55); - EPD_data(0); - EPD_data(5); - EPD_data(5); - EPD_cmd(0); - EPD_data(203); - EPD_data(8); - EPD_cmd(229); - EPD_data(3); - EPD_cmd(3); - EPD_data(0); - EPD_cmd(6); - EPD_data(199); - EPD_data(204); - EPD_data(45); - EPD_cmd(48); - EPD_data(60); - EPD_cmd(65); - EPD_data(0); - EPD_cmd(80); - EPD_data(119); - EPD_cmd(96); - EPD_data(34); - EPD_cmd(97); - EPD_data(2); - EPD_data(128); - EPD_data(1); - EPD_data(128); - epd_read_write_82(); - EPD_cmd(2); - BUSY_wait(0x32u); + v0 = 0; + GPIO_WritePinOutput(EPD_BS, GPIO_IO_HIGH); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); + delay_us(1); + GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_HIGH); + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); + delay_us(1); + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); + delay_us(1); + GPIO_SetPinDir(EPD_MOSI, GPIO_INPUT); + GPIO_PinMuxFun(EPD_MOSI, 0); + delay_us(3); + v1 = 0; + do { + v0 = (uint8_t)(2 * v0); + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_HIGH); + if (GPIO_ReadPinLevel(EPD_MOSI)) + v0 |= 1u; + GPIO_WritePinOutput(EPD_CLK, GPIO_IO_LOW); + delay_us(1); + v1++; + } while (v1 < 8); + GPIO_SetPinDir(EPD_MOSI, GPIO_OUTPUT); + GPIO_PinMuxFun(EPD_MOSI, 0); + GPIO_WritePinOutput(EPD_MOSI, GPIO_IO_LOW); + delay_us(1); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); + delay_us(1); + GPIO_WritePinOutput(EPD_BS, GPIO_IO_LOW); + return v0; } -void init_GPIO_EPD() -{ - SSP_CFG_Type v0; - SPI_Param_Type spiParaStruct; - GPIO_PinMuxFun(EPD_MOSI, 0); - GPIO_SetPinDir(EPD_MOSI, GPIO_OUTPUT); - GPIO_PinMuxFun(EPD_CLK, 0); - GPIO_SetPinDir(EPD_CLK, GPIO_OUTPUT); - GPIO_PinMuxFun(EPD_CS, 0); - GPIO_SetPinDir(EPD_CS, GPIO_OUTPUT); - GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); - GPIO_PinMuxFun(EPD_BUSY, 0); - GPIO_SetPinDir(EPD_BUSY, GPIO_INPUT); - GPIO_PinModeConfig(EPD_BUSY, PINMODE_PULLUP); - GPIO_PinMuxFun(EPD_RESET, 0); - GPIO_SetPinDir(EPD_RESET, GPIO_OUTPUT); - GPIO_WritePinOutput(EPD_RESET, GPIO_IO_HIGH); - GPIO_PinMuxFun(EPD_DC, 0); - GPIO_SetPinDir(EPD_DC, GPIO_OUTPUT); - GPIO_WritePinOutput(EPD_DC, GPIO_IO_HIGH); - GPIO_PinMuxFun(EPD_BS, 0); - GPIO_SetPinDir(EPD_BS, GPIO_OUTPUT); - GPIO_WritePinOutput(EPD_BS, GPIO_IO_LOW); - GPIO_PinMuxFun(EPD_HLT_CTRL, 0); - GPIO_SetPinDir(EPD_HLT_CTRL, GPIO_OUTPUT); - GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_HIGH); - GPIO_PinMuxFun(EPD_MISO, 0); - GPIO_SetPinDir(EPD_MISO, GPIO_INPUT); - GPIO_PinModeConfig(EPD_MISO, PINMODE_DEFAULT); - memset(&v0, 0, 9); - v0.timeOutVal = 0; - SSP_Init(SSP2_ID, &v0); - spiParaStruct.spiClkPhase = SPI_SCPHA_1; - spiParaStruct.spiClkPolarity = SPI_SCPOL_LOW; - SPI_Config(SSP2_ID, &spiParaStruct); - CLK_I2SClkSrc(CLK_I2S_XTAL32M); - CLK_SSPClkSrc(CLK_SSP_ID_2, CLK_SSP_I2S); - CLK_I2SClkDivider(1, 1); +uint8_t getTempBracket() { + uint8_t v0; + uint8_t v1; + uint8_t temptable[40]; + + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(1); + delay_us(1000); + spi_soft_read_buffer(0, 25002, temptable, 10); + delay_us(1000); + delay_us(1000); + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(0); + + EPD_cmd(EPD_TEMP_CALIB); + BUSY_wait(0xAu); + v0 = spi_soft_read_byte(); + v1 = (uint8_t)(2 * v0) + ((uint8_t)spi_soft_read_byte() >> 7); + + uint8_t bracket = 0; + for (int i = 0; i < 9; i++) { + if ((((char)v1 - (uint8_t)temptable[i]) & 0x80) != 0) { + bracket = i; + break; + } + } + return bracket; } -void epd_refresh_and_sleep() -{ - EPD_cmd(0x12); - delay(100000); - do_sleeped_epd_refresh(); - init_GPIO_EPD(); - epd_reset(); - epd_reset(); - EPD_cmd(1); - EPD_data(2); - EPD_data(0); - EPD_data(0); - EPD_data(0); - delay_us(500000); - EPD_cmd(2); - delay_us(1000000); - BUSY_wait(0x32u); - EPD_cmd(0x65); - EPD_data(1); - GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_LOW); - spi_soft_send_byte(0xB9); - GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_HIGH); - EPD_cmd(0x65); - EPD_data(0); - EPD_cmd(7); - EPD_data(0xA5); +void loadFrameRatePLL(uint8_t bracket) { + uint8_t pllvalue; + uint8_t plltable[12]; + + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(1); + delay_us(1000); + spi_soft_read_buffer(0, 25039, plltable, 10); + delay_us(1000); + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(0); + + pllvalue = plltable[bracket]; + + EPD_cmd(EPD_PLL_CONTROL); + EPD_data(pllvalue); } -void epd_pin_enable(int a1) -{ - if (a1) - { - GPIO_PinMuxFun(EPD_CLK, GPIO22_SSP2_SCK); - GPIO_PinMuxFun(EPD_MOSI, GPIO12_SSP2_TXD); - GPIO_PinMuxFun(EPD_MISO, GPIO13_SSP2_RXD); - SSP_Enable(SSP2_ID); - } - else - { - SSP_Disable(SSP2_ID); - GPIO_PinMuxFun(EPD_MOSI, 0); - GPIO_SetPinDir(EPD_MOSI, GPIO_OUTPUT); - GPIO_PinMuxFun(EPD_CLK, 0); - GPIO_SetPinDir(EPD_CLK, GPIO_OUTPUT); - GPIO_PinMuxFun(EPD_MISO, 0); - GPIO_SetPinDir(EPD_MISO, GPIO_INPUT); - GPIO_PinModeConfig(EPD_MISO, PINMODE_DEFAULT); - } +extern void dump(const uint8_t *a, const uint16_t l); + +void loadTempVCOMDC(uint8_t bracket) { + uint8_t vcomvalue; + uint8_t vcomtable[12]; + + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(1); + delay_us(1000); + spi_soft_read_buffer(0, 25049, vcomtable, 10); + delay_us(1000); + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(0); + + vcomvalue = vcomtable[bracket]; + + EPD_cmd(EPD_VCOM_DC_SET); + EPD_data(vcomvalue); } -void display_tx_byte(uint8_t data) -{ - SSP_SendData(SSP2_ID, data); - for (int i = 0; i < 0xF; ++i) - __ISB(); +uint8_t *loadLUT(uint8_t index, uint8_t bracket) { + uint16_t adr = 0; + uint16_t len = 0; + uint8_t *lutBuffer; + switch (index) { + case EPD_LUT_VCOM: + // VCOM LUT + adr = 20800 + (220 * bracket); + len = 220; + break; + case EPD_LUT_B: + case EPD_LUT_W: + case EPD_LUT_G1: + case EPD_LUT_G2: + case EPD_LUT_R0: + case EPD_LUT_R1: + case EPD_LUT_R2: + case EPD_LUT_R3: + adr = (bracket * 2080); + adr += (index - 0x21) * 260; + len = 260; + break; + case EPD_LUT_XON: + // XON LUT + adr = 23000 + (200 * bracket); + len = 200; + break; + } + + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(1); + delay_us(1000); + lutBuffer = malloc(len); + if (lutBuffer) spi_soft_read_buffer(0, adr, lutBuffer, len); + delay_us(1000); + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(0); + return lutBuffer; } -void display_send_start(uint8_t inverted) -{ - EPD_cmd(0); - if(inverted) - EPD_data(207); - else - EPD_data(203); - EPD_cmd(0x10); - epd_pin_enable(1); - GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); +void shiftRightByX(uint8_t *data, int X, int N) { + if (X < 0 || X >= 8) { + // Invalid shift value, X should be between 0 and 7 (inclusive) + return; + } + + // Perform the shift operation on each byte in the range + for (int i = 0; i < N; i++) { + data[i] = (data[i] >> X) | ((data[i + 1] & ((1 << X) - 1)) << (8 - X)); + } +} +void shiftLeftByX(uint8_t *data, int X, int N) { + if (X < 0 || X >= 8) { + // Invalid shift value, X should be between 0 and 7 (inclusive) + return; + } + + // Perform the shift operation on each byte in the range + for (int i = N - 1; i >= 0; i--) { + data[i] = (data[i] << X) | ((data[i - 1] >> (8 - X)) & ((1 << X) - 1)); + } +} +#define CHAR_WIDTH_BYTES 2 +#define CHAR_HEIGHT 20 +#define CHAR_WIDTH 12 +#define SCREEN_WIDTH 640 + +uint8_t buffer[CHAR_HEIGHT][640 / 8]; +uint8_t charbuffer[CHAR_HEIGHT][CHAR_WIDTH_BYTES + 1]; + +// uint16_t curX = 0; +#define CHAR_SPACING 1 +#define EMPTY_SPACING 3 + +uint16_t loadCharacter(uint8_t currentChar, uint16_t curX, bool first) { + + currentChar-=0x20; + + memset(charbuffer, 0, sizeof(charbuffer)); + for (uint8_t d = 0; d < CHAR_HEIGHT; d++) { + for (uint8_t c = 0; c < CHAR_WIDTH_BYTES; c++) { + charbuffer[d][c] = font[currentChar][c + (2 * d)]; + } + } + + // find amount of left whitespace and compensate + uint8_t leftShift = 0; + for (uint8_t left = 0; left < CHAR_WIDTH; left++) { + bool leftAdjusted = false; + for (uint8_t height = 0; height < CHAR_HEIGHT; height++) { + if (charbuffer[height][0] & 0x01) { + leftAdjusted = true; + break; + } + } + if (leftAdjusted) { + break; + } else { + for (uint8_t height = 0; height < CHAR_HEIGHT; height++) { + shiftRightByX(&(charbuffer[height][0]), 1, CHAR_WIDTH_BYTES); + } + leftShift++; + } + } + + // find width for character + uint8_t width = 0; + for (int8_t curBit = CHAR_WIDTH + 1; curBit > 0; curBit--) { + bool widthFound = false; + for (uint8_t height = 0; height < CHAR_HEIGHT; height++) { + if (charbuffer[height][curBit / 8] & (1 << (curBit % 8))) { + widthFound = true; + break; + } + } + if (widthFound) { + width = curBit + 1; + break; + } + } + + if (!first) { + curX += CHAR_SPACING; + } + + for (uint8_t height = 0; height < CHAR_HEIGHT; height++) { + shiftLeftByX(&(charbuffer[height][0]), curX % 8, CHAR_WIDTH_BYTES + 1); + } + + for (uint8_t d = 0; d < CHAR_HEIGHT; d++) { + for (uint8_t c = 0; c < CHAR_WIDTH_BYTES + 1; c++) { + buffer[d][(curX / 8) + c] |= charbuffer[d][c]; + } + } + if (width == 0) width = EMPTY_SPACING; + curX += width; + return curX; } -void display_send_stop() -{ - GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); - epd_pin_enable(0); +void dumpBuffer(uint16_t xloc, uint16_t yloc, uint16_t width) { + xloc = SCREEN_WIDTH - xloc; + + setDisplayWindow(xloc - width, yloc, xloc, yloc + CHAR_HEIGHT); + display_send_start(0); + for (uint8_t curY = 0; curY < CHAR_HEIGHT; curY++) { + for (uint16_t curX = 0; curX < width; curX += 8) { + interleaveColor(buffer[curY][curX / 8]); + } + } + display_send_stop(); } -void init_epd(void) -{ - init_GPIO_EPD(); - epd_reset(); - epd_reset(); - EPD_cmd(0x65); - EPD_data(1); - GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_LOW); - spi_soft_send_byte(0xAB); - GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_HIGH); - EPD_cmd(0x65); - EPD_data(0); - delay(1000); - epd_send_init(); - delay(1000); - EPD_cmd(4); - BUSY_wait(0x32u); - epd_read_write_30(); +void loadLUTSfromEEPROM() { + uint8_t bracket = getTempBracket(); + for (uint8_t c = EPD_LUT_B; c <= EPD_LUT_R3; c++) { + struct epd_colorlut *colorlut = (struct epd_colorlut *)loadLUT(c, bracket); + for (uint8_t part = 0; part < 20; part++) { + if (colorlut->part[part].repeat) colorlut->part[part].repeat = 1; + } + lutBeginTX(c); + for (uint16_t d = 0; d < 260; d++) { + display_tx_byte(((uint8_t *)colorlut)[d]); + } + lutEndTX(); + if (colorlut) free(colorlut); + } + + struct epd_vcomlut *vcomlut = (struct epd_vcomlut *)loadLUT(EPD_LUT_VCOM, bracket); + for (uint8_t part = 0; part < 20; part++) { + if (vcomlut->part[part].repeat) vcomlut->part[part].repeat = 1; + } + lutBeginTX(EPD_LUT_VCOM); + for (uint16_t d = 0; d < 220; d++) { + display_tx_byte(((uint8_t *)vcomlut)[d]); + } + lutEndTX(); + if (vcomlut) free(vcomlut); + + struct epd_xonlut *xonlut = (struct epd_xonlut *)loadLUT(EPD_LUT_XON, bracket); + for (uint8_t part = 0; part < 20; part++) { + if (xonlut->part[part].repeat) xonlut->part[part].repeat = 1; + } + lutBeginTX(EPD_LUT_XON); + for (uint16_t d = 0; d < 200; d++) { + display_tx_byte(((uint8_t *)xonlut)[d]); + } + lutEndTX(); + if (xonlut) free(xonlut); } + +void init_GPIO_EPD() { + SSP_CFG_Type v0; + SPI_Param_Type spiParaStruct; + GPIO_PinMuxFun(EPD_MOSI, 0); + GPIO_SetPinDir(EPD_MOSI, GPIO_OUTPUT); + GPIO_PinMuxFun(EPD_CLK, 0); + GPIO_SetPinDir(EPD_CLK, GPIO_OUTPUT); + GPIO_PinMuxFun(EPD_CS, 0); + GPIO_SetPinDir(EPD_CS, GPIO_OUTPUT); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); + GPIO_PinMuxFun(EPD_BUSY, 0); + GPIO_SetPinDir(EPD_BUSY, GPIO_INPUT); + GPIO_PinModeConfig(EPD_BUSY, PINMODE_PULLUP); + GPIO_PinMuxFun(EPD_RESET, 0); + GPIO_SetPinDir(EPD_RESET, GPIO_OUTPUT); + GPIO_WritePinOutput(EPD_RESET, GPIO_IO_HIGH); + GPIO_PinMuxFun(EPD_DC, 0); + GPIO_SetPinDir(EPD_DC, GPIO_OUTPUT); + GPIO_WritePinOutput(EPD_DC, GPIO_IO_HIGH); + GPIO_PinMuxFun(EPD_BS, 0); + GPIO_SetPinDir(EPD_BS, GPIO_OUTPUT); + GPIO_WritePinOutput(EPD_BS, GPIO_IO_LOW); + GPIO_PinMuxFun(EPD_HLT_CTRL, 0); + GPIO_SetPinDir(EPD_HLT_CTRL, GPIO_OUTPUT); + GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_HIGH); + GPIO_PinMuxFun(EPD_MISO, 0); + GPIO_SetPinDir(EPD_MISO, GPIO_INPUT); + GPIO_PinModeConfig(EPD_MISO, PINMODE_DEFAULT); + memset(&v0, 0, 9); + v0.timeOutVal = 0; + SSP_Init(SSP2_ID, &v0); + spiParaStruct.spiClkPhase = SPI_SCPHA_1; + spiParaStruct.spiClkPolarity = SPI_SCPOL_LOW; + SPI_Config(SSP2_ID, &spiParaStruct); + CLK_I2SClkSrc(CLK_I2S_XTAL32M); + CLK_SSPClkSrc(CLK_SSP_ID_2, CLK_SSP_I2S); + CLK_I2SClkDivider(1, 1); +} + +void fillWindow(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye, uint8_t color) { + setDisplayWindow(x, y, xe, ye); + display_send_start(0); + + for (uint32_t c = 0; c < (xe - x) * (ye - y) / 8; c++) { + interleaveColor(0x00); + } + + display_send_stop(); +} + +void epd_refresh_and_sleep() { + loadLUTSfromEEPROM(EPDtempBracket); + + // epdPrintf(50,100,false,"Blaat! Dit is een test %d", 6); + + EPD_cmd(EPD_REFRESH); + delay(100000); + do_sleeped_epd_refresh(); + init_GPIO_EPD(); + epd_reset(); + epd_reset(); + EPD_cmd(EPD_POWER_SETTING); + EPD_data(2); + EPD_data(0); + EPD_data(0); + EPD_data(0); + delay_us(500000); + EPD_cmd(EPD_POWER_OFF); + delay_us(1000000); + BUSY_wait(0x32u); + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(1); + GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_LOW); + spi_soft_send_byte(EPD_EEPROM_SLEEP); + GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_HIGH); + EPD_cmd(EPD_SPI_FLASH_CONTROL); + EPD_data(0); + EPD_cmd(EPD_DEEP_SLEEP); + EPD_data(0xA5); +} + +void epd_pin_enable(int a1) { + if (a1) { + GPIO_PinMuxFun(EPD_CLK, GPIO22_SSP2_SCK); + GPIO_PinMuxFun(EPD_MOSI, GPIO12_SSP2_TXD); + GPIO_PinMuxFun(EPD_MISO, GPIO13_SSP2_RXD); + SSP_Enable(SSP2_ID); + } else { + SSP_Disable(SSP2_ID); + GPIO_PinMuxFun(EPD_MOSI, 0); + GPIO_SetPinDir(EPD_MOSI, GPIO_OUTPUT); + GPIO_PinMuxFun(EPD_CLK, 0); + GPIO_SetPinDir(EPD_CLK, GPIO_OUTPUT); + GPIO_PinMuxFun(EPD_MISO, 0); + GPIO_SetPinDir(EPD_MISO, GPIO_INPUT); + GPIO_PinModeConfig(EPD_MISO, PINMODE_DEFAULT); + } +} + +void lutBeginTX(uint8_t reg) { + EPD_cmd(reg); + epd_pin_enable(1); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); +} + +void lutEndTX() { + GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); + epd_pin_enable(0); +} + +void setDisplayWindow(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye) { + x &= 0xFFF8; // byte boundary + xe = (xe - 1) | 0x0007; // byte boundary - 1 + EPD_cmd(0x91); + epdWrite(0x90, 9, x / 256, x % 256, xe / 256, xe % 256, y / 256, y % 256, ye / 256, ye % 256, 0x01); +} + +void display_tx_byte(uint8_t data) { + SSP_SendData(SSP2_ID, data); +} + +void display_send_start(uint8_t inverted) { + EPD_cmd(EPD_START_DATA); + epd_pin_enable(1); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_LOW); +} + +void display_send_stop() { + for (int i = 0; i < 0xF; ++i) + __ISB(); + GPIO_WritePinOutput(EPD_CS, GPIO_IO_HIGH); + epd_pin_enable(0); +} + +void init_epd(void) { + printf("EPD Powerup begin\n"); + init_GPIO_EPD(); + epd_reset(); + + EPD_cmd(EPD_POWER_ON); + BUSY_wait(0x32u); + + // wake the eeprom + epdWrite(EPD_SPI_FLASH_CONTROL, 1, 0x01); + GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_LOW); + epdWrite(EPD_WAKE_EEPROM, 0x00); + GPIO_WritePinOutput(EPD_HLT_CTRL, GPIO_IO_HIGH); + epdWrite(EPD_SPI_FLASH_CONTROL, 1, 0x00); + + epdWrite(EPD_POWER_SETTING, 4, 0x37, 0x00, 0x05, 0x05); // 0x37 - 00- 05 05 + epdWrite(EPD_PANEL_SETTING, 2, 0xC3, 0x88); // CB-88 // CB-08 // C3-reverse + + // epdWrite(EPD_UNKNOWN_1, 1, 0x03); + + epdWrite(EPD_POWER_OFF_SEQUENCE, 1, 0x00); + epdWrite(EPD_BOOSTER_SOFT_START, 0x03, 199, 204, 45); + + epdWrite(EPD_PLL_CONTROL, 0x01, 60); + epdWrite(EPD_TEMP_SELECT, 0x01, 0x00); + epdWrite(EPD_VCOM_DATA_INTERVAL, 0x01, 119); + epdWrite(EPD_TCON_SET, 0x01, 34); + epdWrite(EPD_TRES, 0x04, 2, 128, 1, 128); + + //setDisplayWindow(96, 32, 496, 332); + + EPDtempBracket = getTempBracket(); + loadFrameRatePLL(EPDtempBracket); + loadTempVCOMDC(EPDtempBracket); + printf("EPD Powerup complete\n"); +} + +void epdPrintf(uint16_t x, uint16_t y, bool color, const char *c, ...) { + // Render the text + char out_buffer[96]; + + uint16_t curX = 0; + memset(buffer, 0, sizeof(buffer)); + memset(charbuffer, 0, sizeof(charbuffer)); + va_list lst; + va_start(lst, c); + vsnprintf(out_buffer,256, c, lst); + va_end(lst); + + curX = x % 8; + + char *text = (char *)out_buffer; + memset(charbuffer, 0, sizeof(charbuffer)); + curX = loadCharacter(*text, curX, true); + text++; + + while (*text != '\0') { + memset(charbuffer, 0, sizeof(charbuffer)); + curX = loadCharacter(*text, curX, false); + text++; + } + + x /= 8; + x *= 8; + dumpBuffer(x, y, curX); +} \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.h index 5cebf061..9b68d899 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.h @@ -1,7 +1,8 @@ #pragma once -#include +//#include #include #include +#include #define DISPLAY_WIDTH (640) #define DISPLAY_HEIGHT (384) @@ -16,6 +17,18 @@ #define FORE_COLOR_2 4 #define FORE_COLOR_3 0 +#define EPD_LUT_DEFAULT 0 +#define EPD_LUT_NO_REPEATS 1 +#define EPD_LUT_FAST_NO_REDS 2 +#define EPD_LUT_FAST 3 + +#define EPD_DIRECTION_X false +#define EPD_DIRECTION_Y true +#define EPD_SIZE_SINGLE false +#define EPD_SIZE_DOUBLE true +#define EPD_COLOR_RED true +#define EPD_COLOR_BLACK false + void init_GPIO_EPD(); void display_send_buffer(); @@ -25,5 +38,16 @@ void display_tx_byte(uint8_t data); void display_send_start(uint8_t inverted); void display_send_stop(); +void setDisplayWindow(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye); + void init_epd(); -void refresh_epd(); \ No newline at end of file +void refresh_epd(); + +void lutBeginTX(uint8_t reg); +void lutEndTX(); + +void epd_pin_enable(int a1); + + +void fillWindow(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye, uint8_t color); +void epdPrintf(uint16_t x, uint16_t y, bool color, const char* c, ...); \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/gpio.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/gpio.c index 04da951c..8d1c19cb 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/gpio.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/gpio.c @@ -1,6 +1,6 @@ #include "nfc.h" -#include +//#include #include #include #include @@ -12,6 +12,8 @@ #include "mz100_pinmux.h" #include "mz100_gpio.h" #include "util.h" +#include "printf.h" + void NVIC_some_IRQ1(unsigned int a1) { diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.c old mode 100644 new mode 100755 index 2d83700d..41be818d --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.c @@ -1,38 +1,38 @@ -#include -#include -#include -#include -#include "core_cm3.h" #include "main.h" -#include "mz100_pinmux.h" -#include "mz100_gpio.h" -#include "mz100_uart.h" -#include "mz100_clock.h" -#include "mz100_sleep.h" -#include "mz100_flash.h" -#include "epd.h" -#include "mz100_ssp.h" -#include "mz100_pmu.h" -#include "core_cminstr.h" -#include "zigbee.h" -#include "util.h" -#include "settings.h" -#include "eeprom.h" -#include "proto.h" -#include "comms.h" -#include "chars.h" -#include "mz100.h" -#include "timer.h" -#include "util.h" -#include "ccm.h" -#include "nfc.h" -#include "gpio.h" -#include "compression.h" -#include "syncedproto.h" -#include "proto.h" -#include "powermgt.h" -#define SW_VER_CURRENT (0x0000011300000000ull) // top 16 bits are off limits, xxxx.VV.tt.vvvv.mmmm means version V.t.v.m +#include +#include +// #include +#include + +#include "ccm.h" +#include "chars.h" +#include "comms.h" +#include "core_cm3.h" +#include "eeprom.h" +#include "epd.h" +#include "gpio.h" +#include "mz100.h" +#include "mz100_aon_ram.h" +#include "mz100_clock.h" +#include "mz100_flash.h" +#include "mz100_gpio.h" +#include "mz100_pinmux.h" +#include "mz100_pmu.h" +#include "mz100_sleep.h" +#include "mz100_ssp.h" +#include "mz100_uart.h" +#include "powermgt.h" +#include "printf.h" +#include "proto.h" +#include "settings.h" +#include "syncedproto.h" +#include "timer.h" +#include "userinterface.h" +#include "util.h" +#include "zigbee.h" + +#define SW_VER_CURRENT (0x0000011300000000ull) // top 16 bits are off limits, xxxx.VV.tt.vvvv.mmmm means version V.t.v.m #define SW_DEFAULT_MAC (0x0000000000000014ull) uint64_t __attribute__((section(".ver"))) mCurVersionExport = SW_VER_CURRENT; @@ -40,21 +40,25 @@ uint64_t __attribute__((section(".default_mac"))) default_mac = SW_DEFAULT_MAC; char macStr[32]; char macStr1[32]; -uint8_t mSelfMac[8]; +// uint8_t mSelfMac[8]; -void prvApplyUpdateIfNeeded() -{ +#define TAG_MODE_CHANSEARCH 0 +#define TAG_MODE_ASSOCIATED 1 + +__attribute__((section(".aon"))) uint8_t currentTagMode = TAG_MODE_CHANSEARCH; +__attribute__((section(".aon"))) volatile struct zigbeeCalibDataStruct zigbeeCalibData; + +void prvApplyUpdateIfNeeded() { uint32_t ofst, now, size, pieceSz = 0x2000; uint8_t chunkStore[0x2000]; - (*(volatile unsigned int *)0x130000) = 0; // Invalidate RAM in any case so the next boot will be a full one + (*(volatile unsigned int *)0x130000) = 0; // Invalidate RAM in any case so the next boot will be a full one (*(volatile unsigned int *)0x130400) = 0; printf("Applying update\r\n"); qspiEraseRange(EEPROM_OS_START, EEPROM_OS_LEN); size = EEPROM_OS_LEN; - for (ofst = 0; ofst < size; ofst += now) - { + for (ofst = 0; ofst < size; ofst += now) { now = size - ofst; if (now > pieceSz) now = pieceSz; @@ -64,138 +68,38 @@ void prvApplyUpdateIfNeeded() WDT_RestartCounter(); } - printf("Erz IMAGES\r\n"); - qspiEraseRange(EEPROM_IMG_START, EEPROM_IMG_LEN); - printf("Erz update\r\n"); - qspiEraseRange(EEPROM_UPDATE_START, EEPROM_UPDATE_LEN); + // printf("Erz IMAGES\r\n"); + // qspiEraseRange(EEPROM_IMG_START, EEPROM_IMG_LEN); + // printf("Erz update\r\n"); + // qspiEraseRange(EEPROM_UPDATE_START, EEPROM_UPDATE_LEN); sleep_with_with_wakeup(1000); } -static const char *fwVerString(void) -{ - static char fwVer[64] = {}; - sprintf(fwVer, "FW v%u.%u.%u.%u Batt: %imV Temp: %iC", - (uint8_t)(mCurVersionExport >> 40), - (uint8_t)(mCurVersionExport >> 32), - (uint16_t)(mCurVersionExport >> 16), - (uint16_t)(mCurVersionExport), - measureBattery(), - measureTemp()); - - return fwVer; -} - -uint8_t mScreenRow[DISPLAY_WIDTH / 2]; -void uiPrvFullscreenMsg(const char *str, const char *line2, const char *line3) -{ - struct CharDrawingParams cdp; - uint16_t i, r, textRow, textRowEnd; - static const char zero = 0; - uint8_t rowIdx; - const char *strA[] = {str ? str : "", line2 ? line2 : "", line3 ? line3 : ""}; - - printf("MESSAGE: '%s', '%s', '%s'\r\n", strA[0], strA[1], strA[2]); - - if (NO_GUI == 1) - return; // Make everything faster for debugging.!!!! - init_epd(); - display_send_start(false); - - rowIdx = 0; - - cdp.magnify = MAGNIFY1; - cdp.str = strA[0]; - cdp.x = 1; - - cdp.foreColor = FORE_COLOR_1; - cdp.backColor = BACK_COLOR; - textRow = 10; - - textRowEnd = textRow + CHAR_HEIGHT * cdp.magnify; - for (r = 0; r < DISPLAY_HEIGHT; r++) - { - - // clear the row - for (i = 0; i < sizeof(mScreenRow); i++) - mScreenRow[i] = 0x33; - - if (r >= textRowEnd) - { - - switch (rowIdx) - { - case 0: - rowIdx = 1; - - textRow = textRowEnd + 20; - cdp.magnify = MAGNIFY2; - cdp.foreColor = FORE_COLOR_2; - cdp.str = strA[1]; - cdp.x = 1; - textRowEnd = textRow + CHAR_HEIGHT * cdp.magnify; - break; - - case 1: - rowIdx = 2; - - textRow = DISPLAY_HEIGHT - CHAR_HEIGHT; - cdp.magnify = MAGNIFY3; - cdp.foreColor = FORE_COLOR_3; - cdp.str = strA[2]; - cdp.x = 1; - textRowEnd = textRow + CHAR_HEIGHT; - break; - - case 2: - cdp.str = (const char *)&zero; - break; - } - } - else if (r > textRow) - { - cdp.imgRow = r - textRow; - charsDrawString(&cdp); - } - - for (i = 0; i < sizeof(mScreenRow); i++) - display_tx_byte(mScreenRow[i]); - } - WDT_RestartCounter(); - - display_send_stop(); - epd_refresh_and_sleep(); -} - -void prvEepromIndex(struct EepromContentsInfo *eci) -{ +void prvEepromIndex(struct EepromContentsInfo *eci) { struct EepromImageHeader eih; uint32_t addr; - for (addr = EEPROM_IMG_START; addr - EEPROM_IMG_START < EEPROM_IMG_LEN; addr += EEPROM_IMG_EACH) - { - + for (addr = EEPROM_IMG_START; addr - EEPROM_IMG_START < EEPROM_IMG_LEN; addr += EEPROM_IMG_EACH) { uint32_t *addrP, *szP = NULL; uint64_t *verP = NULL; FLASH_Read(0, addr, (uint8_t *)&eih, sizeof(struct EepromImageHeader)); printf("DATA slot 0x%06x: type 0x%08x ver 0x%08x%08x\r\n", addr, eih.validMarker, (uint32_t)(eih.version >> 32), (uint32_t)eih.version); - switch (eih.validMarker) - { - case EEPROM_IMG_INPROGRESS: - verP = &eci->latestInprogressImgVer; - addrP = &eci->latestInprogressImgAddr; - break; + switch (eih.validMarker) { + case EEPROM_IMG_INPROGRESS: + verP = &eci->latestInprogressImgVer; + addrP = &eci->latestInprogressImgAddr; + break; - case EEPROM_IMG_VALID: - verP = &eci->latestCompleteImgVer; - addrP = &eci->latestCompleteImgAddr; - szP = &eci->latestCompleteImgSize; - break; + case EEPROM_IMG_VALID: + verP = &eci->latestCompleteImgVer; + addrP = &eci->latestCompleteImgAddr; + szP = &eci->latestCompleteImgSize; + break; } - if (verP && eih.version >= *verP) - { + if (verP && eih.version >= *verP) { *verP = eih.version; *addrP = addr; if (szP) @@ -203,10 +107,9 @@ void prvEepromIndex(struct EepromContentsInfo *eci) } } } -void prvWriteNewHeader(struct EepromImageHeaderOld *eih, uint32_t addr, uint32_t eeSize, uint64_t ver, uint32_t size) -{ +void prvWriteNewHeader(struct EepromImageHeaderOld *eih, uint32_t addr, uint32_t eeSize, uint64_t ver, uint32_t size) { qspiEraseRange(addr, eeSize); - bzero(eih, sizeof(struct EepromImageHeaderOld)); + // bzero(eih, sizeof(struct EepromImageHeaderOld)); eih->version = ver; eih->validMarker = EEPROM_IMG_INPROGRESS; eih->size = size; @@ -214,26 +117,10 @@ void prvWriteNewHeader(struct EepromImageHeaderOld *eih, uint32_t addr, uint32_t FLASH_Write(false, addr, (uint8_t *)eih, sizeof(struct EepromImageHeaderOld)); } -static void uiPrvDrawImageAtAddress(uint32_t addr, uint32_t size) -{ - printf("Drawing image now\r\n"); - if (size < 6) // we need enough size to even sort out what this is, that needs 6 bytes - return; - - // uiPrvDrawBitmap(addr + sizeof(struct EepromImageHeader), size); -} -void uiPrvDrawLatestImage(const struct EepromContentsInfo *eci) -{ - if (eci->latestCompleteImgAddr) - uiPrvDrawImageAtAddress(eci->latestCompleteImgAddr, eci->latestCompleteImgSize); -} - -static void prvGetSelfMac(void) -{ +static void prvGetSelfMac(void) { FLASH_Read(0, EEPROM_MAC_INFO_START, mSelfMac, 8); - if ((((uint32_t *)mSelfMac)[0] | ((uint32_t *)mSelfMac)[1]) == 0 || (((uint32_t *)mSelfMac)[0] & ((uint32_t *)mSelfMac)[1]) == 0xffffffff) - { // fastest way to check for all ones or all zeroes + if ((((uint32_t *)mSelfMac)[0] | ((uint32_t *)mSelfMac)[1]) == 0 || (((uint32_t *)mSelfMac)[0] & ((uint32_t *)mSelfMac)[1]) == 0xffffffff) { // fastest way to check for all ones or all zeroes printf("mac unknown\r\n"); // Write a blank mac to have something to work with. @@ -243,35 +130,14 @@ static void prvGetSelfMac(void) } } -static void showVersionAndVerifyMatch(void) -{ - // the &mCurVersionExport access is necessary to make sure mCurVersionExport is referenced - /* printf("Booting FW ver 0x%08x%08x (at 0x%08x)\r\n", - (unsigned)(mCurVersionExport >> 32), (unsigned)mCurVersionExport, &mCurVersionExport); - if (((uint32_t)&mCurVersionExport) - 0x100000 != HW_TYPE_74_INCH_BWR_ROM_VER_OFST - 0x20) - { - printf("ver loc mismatch\r\n"); - sleep_with_with_wakeup(0); - } - if (mCurVersionExport & ~VERSION_SIGNIFICANT_MASK) - { - printf("ver num @ red zone\r\n"); - sleep_with_with_wakeup(0); - }*/ -} - -uint8_t showChannelSelect() -{ // returns 0 if no accesspoints were found +uint8_t showChannelSelect() { // returns 0 if no accesspoints were found uint8_t result[sizeof(channelList)]; memset(result, 0, sizeof(result)); powerUp(INIT_RADIO); // uiPrvFullscreenMsg("Scanning", NULL, NULL); - for (uint8_t i = 0; i < 4; i++) - { - for (uint8_t c = 0; c < sizeof(channelList); c++) - { - if (detectAP(channelList[c])) - { + for (uint8_t i = 0; i < 4; i++) { + for (uint8_t c = 0; c < sizeof(channelList); c++) { + if (detectAP(channelList[c])) { if (mLastLqi > result[c]) result[c] = mLastLqi; printf("Channel: %d - LQI: %d RSSI %d\n", channelList[c], mLastLqi, mLastRSSI); @@ -281,10 +147,8 @@ uint8_t showChannelSelect() uint8_t highestLqi = 0; uint8_t highestSlot = 0; - for (uint8_t c = 0; c < sizeof(result); c++) - { - if (result[c] > highestLqi) - { + for (uint8_t c = 0; c < sizeof(result); c++) { + if (result[c] > highestLqi) { highestSlot = channelList[c]; highestLqi = result[c]; } @@ -293,17 +157,13 @@ uint8_t showChannelSelect() return highestSlot; } -uint8_t channelSelect() -{ // returns 0 if no accesspoints were found +uint8_t channelSelect() { // returns 0 if no accesspoints were found uint8_t result[16]; memset(result, 0, sizeof(result)); - for (uint8_t i = 0; i < 2; i++) - { - for (uint8_t c = 0; c < sizeof(channelList); c++) - { - if (detectAP(channelList[c])) - { + for (uint8_t i = 0; i < 2; i++) { + for (uint8_t c = 0; c < sizeof(channelList); c++) { + if (detectAP(channelList[c])) { if (mLastLqi > result[c]) result[c] = mLastLqi; } @@ -312,10 +172,8 @@ uint8_t channelSelect() uint8_t highestLqi = 0; uint8_t highestSlot = 0; - for (uint8_t c = 0; c < sizeof(result); c++) - { - if (result[c] > highestLqi) - { + for (uint8_t c = 0; c < sizeof(result); c++) { + if (result[c] > highestLqi) { highestSlot = channelList[c]; highestLqi = result[c]; } @@ -325,139 +183,51 @@ uint8_t channelSelect() return highestSlot; } -void __attribute__((interrupt)) NMIException(void) -{ +void __attribute__((interrupt)) NMIException(void) { printf("-----------> NMIException\r\n"); PMU->CLK_SRC.BF.MAIN_CLK_SOURCE = 1; PMU->PWR_MODE.BF.PWR_MODE = 0; NVIC_SystemReset(); } -void __attribute__((interrupt)) HardFaultException(void) -{ +void __attribute__((interrupt)) HardFaultException(void) { printf("-----------> HardFaultException\r\n"); PMU->CLK_SRC.BF.MAIN_CLK_SOURCE = 1; PMU->PWR_MODE.BF.PWR_MODE = 0; NVIC_SystemReset(); } -void __attribute__((interrupt)) MemManageException(void) -{ +void __attribute__((interrupt)) MemManageException(void) { printf("-----------> MemManageException\r\n"); PMU->CLK_SRC.BF.MAIN_CLK_SOURCE = 1; PMU->PWR_MODE.BF.PWR_MODE = 0; NVIC_SystemReset(); } -void __attribute__((interrupt)) BusFaultException(void) -{ +void __attribute__((interrupt)) BusFaultException(void) { printf("-----------> BusFaultException\r\n"); PMU->CLK_SRC.BF.MAIN_CLK_SOURCE = 1; PMU->PWR_MODE.BF.PWR_MODE = 0; NVIC_SystemReset(); } -void __attribute__((interrupt)) UsageFaultException(void) -{ +void __attribute__((interrupt)) UsageFaultException(void) { printf("-----------> UsageFaultException\r\n"); PMU->CLK_SRC.BF.MAIN_CLK_SOURCE = 1; PMU->PWR_MODE.BF.PWR_MODE = 0; NVIC_SystemReset(); } -void __attribute__((interrupt)) SVCHandler(void) -{ +void __attribute__((interrupt)) SVCHandler(void) { } -void __attribute__((interrupt)) DebugMonitor(void) -{ +void __attribute__((interrupt)) DebugMonitor(void) { } -void __attribute__((interrupt)) PendSVC(void) -{ +void __attribute__((interrupt)) PendSVC(void) { } -extern struct blockRequest curBlock; // used by the block-requester, contains the next request that we'll send -extern struct AvailDataInfo curDataInfo; // last 'AvailDataInfo' we received from the AP -extern bool requestPartialBlock; // if we should ask the AP to get this block from the host or not -int main(void) -{ - uint8_t currentChannel = 0; - struct Settings settings; - uint32_t sleepDuration = 5000; - struct CommsInfo ci; - uint32_t reset_reason = PMU_GetLastResetCause(); - uint8_t pwr_mode_at_boot = PMU->PWR_MODE.BF.PWR_MODE; - - (*(volatile unsigned int *)0x20124000) = 0x100004; // On WARM RESET: Goto this address. -> entry - (*(volatile unsigned int *)0xE000ED08) = 0x20100000; // Vector table in RAM and offset 0x4000 - (*(volatile unsigned int *)0xE000E41A) = 0x40; - - CLK_SystemClkInit(CLK_SYS_XTAL64M, CLK_SYS_64M); - CLK_Xtal32MEnable(CLK_OSC_INTERN); - while (CLK_GetClkStatus(CLK_OUT_XTAL64M) != 1) - ; - - // UART 1 DEBUG OUT - GPIO_PinModeConfig(UART_TX, PINMODE_DEFAULT); - GPIO_PinModeConfig(UART_RX, PINMODE_DEFAULT); - GPIO_PinMuxFun(UART_TX, GPIO4_UART2_TXD); // UART - GPIO_PinMuxFun(UART_RX, GPIO6_UART2_RXD); // UART - UART_CFG_Type uartcfg; - uartcfg.baudRate = 115200; - uartcfg.dataBits = UART_DATABITS_8; - uartcfg.stopBits = 1; - uartcfg.parity = UART_PARITY_NONE; - uartcfg.autoFlowControl = DISABLE; - UART_Init(1, &uartcfg); - - UART_FIFO_Type uartFifo; - uartFifo.FIFO_ResetRx = 1; - uartFifo.FIFO_ResetTx = 1; - uartFifo.FIFO_Function = 1; - uartFifo.FIFO_RcvrTrigger = 2; - uartFifo.FIFO_TxEmptyTrigger = 3; - UART_FIFOConfig(1, &uartFifo); - // UART 1 DEBUG OUT - - if (!(~(*(volatile unsigned int *)0x4A080000) << 30)) - { - NVIC_EnableIRQ(ExtPin5_IRQn); - NVIC_EnableIRQ(RTC_IRQn); - } - - (*(volatile unsigned int *)0x4A070004) = ((*(volatile unsigned int *)0x4A070004) & 0xFFFFFFE0) + 2; - PMU->PWR_MODE.BF.PWR_MODE = 2; - uint32_t v0 = FLASH_WordRead(FLASH_NORMAL_READ, 4u); - char v1; - if (!(~v0 << 25)) - { - CLK_RC32MEnable(); - while (CLK_GetClkStatus(CLK_OUT_RC32M) != 1) - ; - v1 = CLK_RC32MCalibration(CLK_AUTO_CAL, 0); - FLASH_WordWrite(FLASH_PROGRAM_NORMAL, 4u, (v0 & 0xFFFFFF00) | (v1 & 0x7F)); - } - - //** WATCHDOG - CLK_ModuleClkEnable(CLK_WDT); - WDT_SetMode(WDT_MODE_RESET); - WDT_SetResetPulseLen(WDT_RESET_PULSE_LEN_256); - WDT_SetTimeoutVal(30); - WDT_RestartCounter(); - WDT_Enable(); - //** WATCHDOG - - //** GPIOS - init_GPIO_boot(); - // NFC POWER Should be on if NFC is wanted to be used - GPIO_PinOutputModeConfig(NFC_POWER, PIN_OUTPUT_MODE_NORMAL_FUNCTION); - GPIO_PinModeConfig(NFC_POWER, PINMODE_DEFAULT); - GPIO_PinMuxFun(NFC_POWER, 0); - GPIO_SetPinDir(NFC_POWER, GPIO_OUTPUT); - GPIO_WritePinOutput(NFC_POWER, 1); // Better power NFC up so IRQ will work unpowered later - //** GPIOS - //** RTC +void setupRTC() { CLK_Xtal32MEnable(CLK_OSC_INTERN); while (!CLK_GetClkStatus(CLK_OUT_XTAL64M)) ; @@ -480,277 +250,287 @@ int main(void) NVIC_ClearPendingIRQ(RTC_IRQn); RTC_IntMask(RTC_INT_CNT_UPP, UNMASK); NVIC_EnableIRQ(RTC_IRQn); - //*** RTC +} - //** Get the real wakeup reason - printf("Rst reason: %i\r\n", reset_reason); - uint32_t real_reason = 0; - if ((*(volatile unsigned int *)0x130400) != 0x11223344) - { - (*(volatile unsigned int *)0x130400) = 0x11223344; - currentChannel = 0; - (*(volatile unsigned int *)0x130404) = currentChannel; +void setupUART() { + // UART 1 DEBUG OUT + GPIO_PinModeConfig(UART_TX, PINMODE_DEFAULT); + GPIO_PinModeConfig(UART_RX, PINMODE_DEFAULT); + GPIO_PinMuxFun(UART_TX, GPIO4_UART2_TXD); // UART + GPIO_PinMuxFun(UART_RX, GPIO6_UART2_RXD); // UART + UART_CFG_Type uartcfg; + uartcfg.baudRate = 115200; + uartcfg.dataBits = UART_DATABITS_8; + uartcfg.stopBits = 1; + uartcfg.parity = UART_PARITY_NONE; + uartcfg.autoFlowControl = DISABLE; + UART_Init(1, &uartcfg); - nfc_i2c_init(); // This is only needed on a complete reboot + UART_FIFO_Type uartFifo; + uartFifo.FIFO_ResetRx = 1; + uartFifo.FIFO_ResetTx = 1; + uartFifo.FIFO_Function = 1; + uartFifo.FIFO_RcvrTrigger = 2; + uartFifo.FIFO_TxEmptyTrigger = 3; + UART_FIFOConfig(1, &uartFifo); + // UART 1 DEBUG OUT +} - if (reset_reason == 5) - { - if (pwr_mode_at_boot) - { - real_reason = 1; // POR Reset - } - else - { - real_reason = 3; // WDT Reset - } - } - else - { - if (reset_reason != 4) - { - if (real_reason == 1 && !pwr_mode_at_boot) - { - real_reason = 2; // System Reset - } - else - { - real_reason = 1; // POR Reset - } - } - else if (pwr_mode_at_boot != 1) - { - real_reason = 1; // POR Reset - } - } - } - else - { - currentChannel = (*(volatile unsigned int *)0x130404); - memcpy((uint8_t *)&curBlock, (uint8_t *)&(*(volatile unsigned int *)0x130500), sizeof(struct blockRequest)); - memcpy((uint8_t *)&curDataInfo, (uint8_t *)&(*(volatile unsigned int *)0x130600), sizeof(struct AvailDataInfo)); - memset(curBlock.requestedParts, 0x00, BLOCK_REQ_PARTS_BYTES); - requestPartialBlock = false; - } - - showVersionAndVerifyMatch(); - timerInit(); +void setupWDT() { + //** WATCHDOG + CLK_ModuleClkEnable(CLK_WDT); + WDT_SetMode(WDT_MODE_RESET); + WDT_SetResetPulseLen(WDT_RESET_PULSE_LEN_256); + WDT_SetTimeoutVal(30); WDT_RestartCounter(); - prvGetSelfMac(); - initializeProto(); + WDT_Enable(); + //** WATCHDOG +} - printf("Booot OpenEPaperLink\r\n"); - if (real_reason == 1) - { +void setupGPIO() { + //** GPIOS + init_GPIO_boot(); + // NFC POWER Should be on if NFC is wanted to be used + GPIO_PinOutputModeConfig(NFC_POWER, PIN_OUTPUT_MODE_NORMAL_FUNCTION); + GPIO_PinModeConfig(NFC_POWER, PINMODE_DEFAULT); + GPIO_PinMuxFun(NFC_POWER, 0); + GPIO_SetPinDir(NFC_POWER, GPIO_OUTPUT); + GPIO_WritePinOutput(NFC_POWER, 1); // Better power NFC up so IRQ will work unpowered later + //** GPIOS + if (!(~(*(volatile unsigned int *)0x4A080000) << 30)) { + NVIC_EnableIRQ(ExtPin5_IRQn); + NVIC_EnableIRQ(RTC_IRQn); + } +} + +void setupCLKCalib() { + (*(volatile unsigned int *)0x4A070004) = ((*(volatile unsigned int *)0x4A070004) & 0xFFFFFFE0) + 2; + PMU->PWR_MODE.BF.PWR_MODE = 2; + uint32_t v0 = FLASH_WordRead(FLASH_NORMAL_READ, 4u); + char v1; + if (!(~v0 << 25)) { + CLK_RC32MEnable(); + while (CLK_GetClkStatus(CLK_OUT_RC32M) != 1) + ; + v1 = CLK_RC32MCalibration(CLK_AUTO_CAL, 0); + FLASH_WordWrite(FLASH_PROGRAM_NORMAL, 4u, (v0 & 0xFFFFFF00) | (v1 & 0x7F)); + } +} + +void TagAssociated() { + // associated + struct AvailDataInfo *avail; + // Is there any reason why we should do a long (full) get data request (including reason, status)? + if ((longDataReqCounter > LONG_DATAREQ_INTERVAL) || wakeUpReason != WAKEUP_REASON_TIMED) { + // check if we should do a voltage measurement (those are pretty expensive) + if (voltageCheckCounter == VOLTAGE_CHECK_INTERVAL) { + doVoltageReading(); + voltageCheckCounter = 0; + } else { + powerUp(INIT_TEMPREADING); + } + voltageCheckCounter++; + + // check if the battery level is below minimum, and force a redraw of the screen + + if ((lowBattery && !lowBatteryShown && tagSettings.enableLowBatSymbol) || (noAPShown && tagSettings.enableNoRFSymbol)) { + printf("For some reason, we're going to redraw the image. lowbat=%d, lowbatshown=%d, noAPShown=%d\n", lowBattery, lowBatteryShown, noAPShown); + // Check if we were already displaying an image + /* + if (curImgSlot != 0xFF) { + powerUp(INIT_EEPROM | INIT_EPD); + wdt60s(); + drawImageFromEeprom(curImgSlot); + powerDown(INIT_EEPROM | INIT_EPD); + } else { + powerUp(INIT_EPD); + showAPFound(); + powerDown(INIT_EPD); + } + */ + } + + powerUp(INIT_RADIO); + avail = getAvailDataInfo(); + powerDown(INIT_RADIO); + + if (avail != NULL) { + // we got some data! + longDataReqCounter = 0; + // since we've had succesful contact, and communicated the wakeup reason succesfully, we can now reset to the 'normal' status + wakeUpReason = WAKEUP_REASON_TIMED; + } + if (tagSettings.enableTagRoaming) { + uint8_t roamChannel = channelSelect(); + if (roamChannel) currentChannel = roamChannel; + } + } else { + powerUp(INIT_RADIO); + avail = getShortAvailDataInfo(); + powerDown(INIT_RADIO); + } + + addAverageValue(); + + if (avail == NULL) { + // no data :( this means no reply from AP + nextCheckInFromAP = 0; // let the power-saving algorithm determine the next sleep period + } else { + nextCheckInFromAP = avail->nextCheckIn; + // got some data from the AP! + if (avail->dataType != DATATYPE_NOUPDATE) { + // data transfer + if (processAvailDataInfo(avail)) { + // succesful transfer, next wake time is determined by the NextCheckin; + } else { + // failed transfer, let the algorithm determine next sleep interval (not the AP) + nextCheckInFromAP = 0; + } + } else { + // no data transfer, just sleep. + } + } + + uint16_t nextCheckin = getNextSleep(); + longDataReqCounter += nextCheckin; + + if (nextCheckin == INTERVAL_AT_MAX_ATTEMPTS) { + // We've averaged up to the maximum interval, this means the tag hasn't been in contact with an AP for some time. + if (tagSettings.enableScanForAPAfterTimeout) { + currentTagMode = TAG_MODE_CHANSEARCH; + return; + } + } + + // if the AP told us to sleep for a specific period, do so. + if (nextCheckInFromAP) { + sleep_with_with_wakeup(nextCheckInFromAP * 60000UL); + } else { + sleep_with_with_wakeup(getNextSleep() * 1000UL); + } +} + +void TagChanSearch() { + // not associated + if (((scanAttempts != 0) && (scanAttempts % VOLTAGEREADING_DURING_SCAN_INTERVAL == 0)) || (scanAttempts > (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS))) { + doVoltageReading(); + } + + // try to find a working channel + currentChannel = channelSelect(); + + // Check if we should redraw the screen with icons, info screen or screensaver + if ((!currentChannel && !noAPShown && tagSettings.enableNoRFSymbol) || + (lowBattery && !lowBatteryShown && tagSettings.enableLowBatSymbol) || + (scanAttempts == (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS - 1))) { + powerUp(INIT_EPD); + wdt60s(); + if (curImgSlot != 0xFF) { + powerUp(INIT_EEPROM); + drawImageFromEeprom(curImgSlot); + powerDown(INIT_EEPROM); + } else if ((scanAttempts >= (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS - 1))) { + showLongTermSleep(); + } else { + showNoAP(); + } + powerDown(INIT_EPD); + } + + // did we find a working channel? + if (currentChannel) { + // now associated! set up and bail out of this loop. + scanAttempts = 0; + wakeUpReason = WAKEUP_REASON_NETWORK_SCAN; + initPowerSaving(INTERVAL_BASE); + doSleep(getNextSleep() * 1000UL); + currentTagMode = TAG_MODE_ASSOCIATED; + return; + } else { + // still not associated + doSleep(getNextScanSleep(true) * 1000UL); + } +} + +int main(void) { + (*(volatile unsigned int *)0x20124000) = 0x100004; // On WARM RESET: Goto this address. -> entry + (*(volatile unsigned int *)0xE000ED08) = 0x20100000; // Vector table in RAM and offset 0x4000 + (*(volatile unsigned int *)0xE000E41A) = 0x40; // ?? + + timerInit(); + + CLK_SystemClkInit(CLK_SYS_XTAL64M, CLK_SYS_64M); + CLK_Xtal32MEnable(CLK_OSC_INTERN); + while (CLK_GetClkStatus(CLK_OUT_XTAL64M) != 1) + ; + + if (!loadValidateAonRam() || PMU_GetLastResetCause()) { + setupWDT(); + setupGPIO(); + setupCLKCalib(); + setupUART(); + printf("Rst reason: %i\r\n", PMU_GetLastResetCause()); + printf("AON is not valid!\n"); + setupRTC(); + clearAonRam(); + currentChannel = 0; + zigbeeCalibData.isValid = false; + wakeUpReason = WAKEUP_REASON_FIRSTBOOT; + prvGetSelfMac(); + initializeProto(); printf("Erz data\r\n"); + initPowerSaving(INTERVAL_BASE); + loadDefaultSettings(); + doVoltageReading(); + qspiEraseRange(EEPROM_IMG_START, EEPROM_IMG_LEN); qspiEraseRange(EEPROM_UPDATE_START, EEPROM_UPDATE_LEN); qspiEraseRange(EEPROM_SETTINGS_AREA_START, EEPROM_SETTINGS_AREA_LEN); sprintf(macStr, "(" MACFMT ")", MACCVT(mSelfMac)); - // uiPrvFullscreenMsg("HELLO OTA", macStr, fwVerString()); currentChannel = showChannelSelect(); - (*(volatile unsigned int *)0x130404) = currentChannel; - WDT_RestartCounter(); - - if (currentChannel) - { + if (currentChannel) { printf("AP Found\r\n"); + showAPFound(); sprintf(macStr1, "OpenEPaperLink Ch: %i", currentChannel); - uiPrvFullscreenMsg("AP Found", macStr1, macStr); timerDelay(TIMER_TICKS_PER_MSEC * 1000); - } - else - { + currentTagMode = TAG_MODE_ASSOCIATED; + } else { printf("No AP found\r\n"); - uiPrvFullscreenMsg("No AP Found", "OpenEPaperLink", macStr); sleep_with_with_wakeup(120000UL); + currentTagMode = TAG_MODE_CHANSEARCH; } + + } else { + // setupWDT(); + setupGPIO(); + // setupCLKCalib(); + // setupUART(); + // setupRTC(); + memset(curBlock.requestedParts, 0x00, BLOCK_REQ_PARTS_BYTES); } - if (nfc_handle()) // If an image was uploaded via NFC lets display it. - { - struct EepromContentsInfo eci; - memset(&eci, 0x00, sizeof(eci)); - prvEepromIndex(&eci); - uiPrvDrawLatestImage(&eci); - sleep_with_with_wakeup(30 * 1000); - } - - while (1 == 1) - { + while (1) { + powerUp(INIT_UART); wdt10s(); - if (currentChannel) - { - // associated - - struct AvailDataInfo *avail; - // Is there any reason why we should do a long (full) get data request (including reason, status)? - if ((longDataReqCounter > LONG_DATAREQ_INTERVAL) || wakeUpReason != WAKEUP_REASON_TIMED) - { - // check if we should do a voltage measurement (those are pretty expensive) - /*if (voltageCheckCounter == VOLTAGE_CHECK_INTERVAL) - { - powerUp(INIT_RADIO); // load down the battery using the radio to get a good reading - powerUp(INIT_TEMPREADING | INIT_EPD_VOLTREADING); - powerDown(INIT_RADIO); - voltageCheckCounter = 0; - } - else - { - powerUp(INIT_TEMPREADING); - } - voltageCheckCounter++;*/ - - // check if the battery level is below minimum, and force a redraw of the screen - /*if ((lowBattery && !lowBatteryShown) || (noAPShown)) - { - // Check if we were already displaying an image - if (curImgSlot != 0xFF) - { - powerUp(INIT_EEPROM | INIT_EPD); - wdt60s(); - drawImageFromEeprom(curImgSlot); - powerDown(INIT_EEPROM | INIT_EPD); - } - else - { - powerUp(INIT_EPD); - showAPFound(); - powerDown(INIT_EPD); - } - }*/ - powerUp(INIT_RADIO); - avail = getAvailDataInfo(); - powerDown(INIT_RADIO); - - if (avail != NULL) - { - // we got some data! - longDataReqCounter = 0; - // since we've had succesful contact, and communicated the wakeup reason succesfully, we can now reset to the 'normal' status - wakeUpReason = WAKEUP_REASON_TIMED; - } - } - else - { - powerUp(INIT_RADIO); - avail = getShortAvailDataInfo(); - powerDown(INIT_RADIO); - } - - addAverageValue(); - - if (avail == NULL) - { - // no data :( - sleep_with_with_wakeup(60 * 1000UL); - nextCheckInFromAP = 0; // let the power-saving algorithm determine the next sleep period - } - else - { - nextCheckInFromAP = avail->nextCheckIn; - // got some data from the AP! - if (avail->dataType != DATATYPE_NOUPDATE) - { - // data transfer - if (processAvailDataInfo(avail)) - { - // succesful transfer, next wake time is determined by the NextCheckin; - } - else - { - // failed transfer, let the algorithm determine next sleep interval (not the AP) - nextCheckInFromAP = 0; - } - } - else - { - // no data transfer, just sleep. - } - } - - uint16_t nextCheckin = getNextSleep(); - longDataReqCounter += nextCheckin; - if (nextCheckin == INTERVAL_AT_MAX_ATTEMPTS) - { - // disconnected, obviously... - currentChannel = 0; - (*(volatile unsigned int *)0x130404) = currentChannel; - } - - // if the AP told us to sleep for a specific period, do so. - if (nextCheckInFromAP) - { - sleep_with_with_wakeup(nextCheckInFromAP * 60000UL); - } - else - { - sleep_with_with_wakeup(getNextSleep() * 1000UL); - } - } - else - { - // not associated - if (((scanAttempts != 0) && (scanAttempts % VOLTAGEREADING_DURING_SCAN_INTERVAL == 0)) || (scanAttempts > (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS))) - { - powerUp(INIT_RADIO); // load down the battery using the radio to get a good reading - powerUp(INIT_EPD_VOLTREADING); - powerDown(INIT_RADIO); - } - // try to find a working channel - powerUp(INIT_RADIO); - currentChannel = channelSelect(); - (*(volatile unsigned int *)0x130404) = currentChannel; - powerDown(INIT_RADIO); - - /*if ((!currentChannel && !noAPShown) || (lowBattery && !lowBatteryShown) || (scanAttempts == (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS - 1))) - { - powerUp(INIT_EPD); - wdt60s(); - if (curImgSlot != 0xFF) - { - powerUp(INIT_EEPROM); - drawImageFromEeprom(curImgSlot); - powerDown(INIT_EEPROM); - } - else if ((scanAttempts >= (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS - 1))) - { - showLongTermSleep(); - } - else - { - showNoAP(); - } - powerDown(INIT_EPD); - }*/ - - // did we find a working channel? - if (currentChannel) - { - // now associated! - scanAttempts = 0; - wakeUpReason = WAKEUP_REASON_NETWORK_SCAN; - initPowerSaving(INTERVAL_BASE); - sleep_with_with_wakeup(getNextSleep() * 1000UL); - } - else - { - // still not associated - //sleep_with_with_wakeup(getNextScanSleep(true) * 1000UL); - sleep_with_with_wakeup(10 * 60 * 1000UL); - } + switch (currentTagMode) { + case TAG_MODE_ASSOCIATED: + TagAssociated(); + break; + case TAG_MODE_CHANSEARCH: + TagChanSearch(); + break; } } - printf("sleep: %u\r\n", sleepDuration); - if (sleepDuration == 0) - sleepDuration = 60 * 1000 * 15; // never sleep forever! - sleep_with_with_wakeup(sleepDuration); return 0; } -int _write(int file, char *ptr, int len) -{ +int _write(int file, char *ptr, int len) { UART_SendBytes(1, ptr, len); return len; +} + +void _putchar(char c) { + _write(0, &c, 1); } \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.h index 6aa0b408..ab44cabd 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.h @@ -1,5 +1,5 @@ #pragma once -#include +//#include #include #include #include diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100.ld b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100.ld index 6139bc9a..bfbcf36c 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100.ld +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100.ld @@ -4,8 +4,9 @@ GROUP(-lgcc -lc -lnosys) MEMORY { FLASH (rx) : ORIGIN = 0x100000, LENGTH = 80k - RAM (rwx) : ORIGIN = 0x20100000 + 80k, LENGTH = 160k - 80k - 16k - RAM1 (rwx) : ORIGIN = 0x20124000 , LENGTH = 16k + RAM (rwx) : ORIGIN = 0x20100000 + 80k, LENGTH = 160k - 80k - 2k + AONSHADOW (rwx) : ORIGIN = 0x20128000 - 2k, LENGTH = 2k + AON (rwx) : ORIGIN = 0x20130000 , LENGTH = 4k } ENTRY(Reset_Handler) @@ -123,6 +124,27 @@ SECTIONS /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + +.aon (NOLOAD): + { + . = ALIGN(4); + _start_of_aon = .; + *(.aon) + *(.aon.*) + . = ALIGN(4); + _end_of_aon = .; + + } > AON +.aonshadow (NOLOAD): + { + . = ALIGN(4); + _start_of_aonshadow = .; + *(.aonshadow) + *(.aonshadow.*) + . = ALIGN(4); + _end_of_aonshadow = .; + + } > AONSHADOW } diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_aon_ram.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_aon_ram.c new file mode 100644 index 00000000..bdd147ff --- /dev/null +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_aon_ram.c @@ -0,0 +1,43 @@ +#include "mz100_aon_ram.h" + +#include + +#include "stdint.h" + +__attribute__((section(".aon"))) volatile uint32_t aonChecksum; +__attribute__((section(".aon"))) volatile uint8_t aonShadow[AONSHADOW_SIZE]; + +bool aonRamValid = false; + +void clearAonRam() { + memset((void *)0x130000, 0, 4096); +} + +bool loadValidateAonRam() { + uint32_t testchecksum = aonChecksum; + aonChecksum = 0x00000000; + uint32_t checksum = 0xABBA5FF5; + for (uint32_t c = 0x130000; c < 0x131000; c += 4) { + checksum += *(uint32_t *)c; + } + if (checksum == testchecksum) { + // immediately invalidate the checksum; if we reboot, we want a clean reboot + aonChecksum = 0x5445A00A; + memcpy((void *)(0x128000 - AONSHADOW_SIZE), (uint8_t*)aonShadow, AONSHADOW_SIZE); + return true; + } else { + clearAonRam(); + memset((void *)(0x128000 - AONSHADOW_SIZE), 0, AONSHADOW_SIZE); + return false; + } +} + +void saveAonRam() { + memcpy((uint8_t*)aonShadow, (void *)(0x128000 - AONSHADOW_SIZE), AONSHADOW_SIZE); + aonChecksum = 0x00000000; + uint32_t checksum = 0xABBA5FF5; + for (uint32_t c = 0x130000; c < 0x131000; c += 4) { + checksum += *(uint32_t *)c; + } + aonChecksum = checksum; +} diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_aon_ram.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_aon_ram.h new file mode 100644 index 00000000..4ad25f19 --- /dev/null +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_aon_ram.h @@ -0,0 +1,11 @@ +#include "stdint.h" +#include "stdbool.h" + + +#define AONSHADOW_SIZE 2048 + +extern bool aonRamValid; + +bool loadValidateAonRam(); +void saveAonRam(); +void clearAonRam(); \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_sleep.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_sleep.c index 888a81ce..58eaf6c6 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_sleep.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_sleep.c @@ -1,4 +1,4 @@ -#include +//#include #include #include "core_cm3.h" #include "mz100_gpio.h" @@ -14,6 +14,9 @@ #include "gpio.h" #include "main.h" #include "proto.h" +#include "printf.h" + +extern void saveAonRam(); void AON_level_VDD(int state) { @@ -90,9 +93,11 @@ extern struct AvailDataInfo curDataInfo; // last 'AvailDataInfo' we received fro extern bool requestPartialBlock; // if we should ask the AP to get this block from the host or not void sleep_with_with_wakeup(uint32_t sleep_time_ms) { - memcpy((uint8_t *)&(*(volatile unsigned int *)0x130500), (uint8_t *)&curBlock, sizeof(struct blockRequest)); - memcpy((uint8_t *)&(*(volatile unsigned int *)0x130600), (uint8_t *)&curDataInfo, sizeof(struct AvailDataInfo)); - printf("sleep: %u\r\n", sleep_time_ms); + saveAonRam(); + //memcpy((uint8_t *)&(*(volatile unsigned int *)0x130500), (uint8_t *)&curBlock, sizeof(struct blockRequest)); + //memcpy((uint8_t *)&(*(volatile unsigned int *)0x130600), (uint8_t *)&curDataInfo, sizeof(struct AvailDataInfo)); + //sleep_time_ms = 10000; + printf("sleep! %u\n", sleep_time_ms); uint32_t sleep_time_ms_1; AON_level_VDD(7); AON_level_VAA(0); diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_sleep.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_sleep.h index bb997583..2ea346fb 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_sleep.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/mz100_sleep.h @@ -1,6 +1,6 @@ #pragma once -#include +//#include #include #include "core_cm3.h" #include "mz100_gpio.h" diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.c index 668b6a3a..0f85e977 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.c @@ -1,5 +1,5 @@ #include "nfc.h" -#include +//#include #include #include #include @@ -17,6 +17,8 @@ #include "timer.h" #include "epd.h" #include "proto.h" +#include "printf.h" + #define WHO_AM_I 0x04 uint8_t i2c_address = 0xAA; diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.h index 16708ad5..de8d121b 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/nfc.h @@ -1,5 +1,5 @@ #pragma once -#include +//#include #include #include #include diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.c old mode 100644 new mode 100755 index 33bf4b10..2b74a077 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.c @@ -3,64 +3,69 @@ #include #include #include -#include +// #include #include -#include "mz100_sleep.h" -#include "zigbee.h" -#include "eeprom.h" + #include "board.h" -#include "screen.h" +#include "eeprom.h" #include "main.h" -#include "util.h" +#include "mz100_sleep.h" +#include "printf.h" +#include "screen.h" #include "syncedproto.h" -#include +#include "util.h" +#include "zigbee.h" -uint16_t dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in -uint8_t dataReqAttemptArrayIndex = 0; -uint8_t dataReqLastAttempt = 0; -uint16_t nextCheckInFromAP = 0; -uint8_t wakeUpReason = 0; -uint8_t scanAttempts = 0; +__attribute__((section(".aonshadow"))) uint16_t dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in +__attribute__((section(".aonshadow"))) uint8_t dataReqAttemptArrayIndex = 0; +__attribute__((section(".aonshadow"))) uint8_t dataReqLastAttempt = 0; +__attribute__((section(".aonshadow"))) uint16_t nextCheckInFromAP = 0; +__attribute__((section(".aonshadow"))) uint8_t wakeUpReason = 0; +__attribute__((section(".aonshadow"))) uint8_t scanAttempts = 0; -int8_t temperature = 0; -uint16_t batteryVoltage = 0; -bool lowBattery = false; -uint16_t longDataReqCounter = 0; -uint16_t voltageCheckCounter = 0; +__attribute__((section(".aonshadow"))) int8_t temperature = 0; +__attribute__((section(".aonshadow"))) uint16_t batteryVoltage = 0; +__attribute__((section(".aonshadow"))) bool lowBattery = false; +__attribute__((section(".aonshadow"))) uint16_t longDataReqCounter = 0; +__attribute__((section(".aonshadow"))) uint16_t voltageCheckCounter = 0; -uint8_t capabilities = 0; +__attribute__((section(".aonshadow"))) uint8_t capabilities = 0; bool spiActive = false; bool uartActive = false; bool eepromActive = false; bool i2cActive = false; -extern int8_t adcSampleTemperature(void); // in degrees C -uint8_t checkButtonOrJig() -{ +extern int8_t adcSampleTemperature(void); // in degrees C + +uint8_t checkButtonOrJig() { return DETECT_P1_0_NOTHING; } -void setupPortsInitial() -{ +void setupPortsInitial() { } -void initPowerSaving(const uint16_t initialValue) -{ - for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) - { +uint16_t doVoltageReading() { + batteryVoltage = (uint16_t)measureBattery(); + if (batteryVoltage < BATTERY_VOLTAGE_MINIMUM) { + lowBattery = true; + } else { + lowBattery = false; + } + return batteryVoltage; +} + +void initPowerSaving(const uint16_t initialValue) { + for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) { dataReqAttemptArr[c] = initialValue; } } -static void configSPI(const bool setup) -{ - +static void configSPI(const bool setup) { spiActive = setup; } -static void configUART(const bool setup) -{ +static void configUART(const bool setup) { /* if (setup == uartActive) return; uartActive = setup; @@ -70,69 +75,64 @@ static void configUART(const bool setup) Serial.end();*/ } -static void configEEPROM(const bool setup) -{ +static void configEEPROM(const bool setup) { } -static void configI2C(const bool setup) -{ +static void configI2C(const bool setup) { } -void powerUp(const uint8_t parts) -{ - printf("Power up: %d\r\n", parts); - if (parts & INIT_RADIO) - { +void powerUp(const uint8_t parts) { + // printf("Power up: %d\r\n", parts); + if (parts & INIT_RADIO) { radioInit(); // radioRxFilterCfg(mSelfMac, 0x10000, PROTO_PAN_ID); // radioSetTxPower(10); - if (currentChannel >= 11 && currentChannel <= 27) - { + if (currentChannel >= 11 && currentChannel <= 27) { radioSetChannel(currentChannel); - } - else - { + } else { radioSetChannel(RADIO_FIRST_CHANNEL); } } + if (parts & INIT_UART) { + configUART(true); + } + if (parts & INIT_EPD) { + configSPI(true); + } + if (parts & INIT_EEPROM) { + configEEPROM(true); + } + if (parts & INIT_I2C) { + configI2C(true); + } } -void powerDown(const uint8_t parts) -{ - printf("Power down: %d\r\n", parts); +void powerDown(const uint8_t parts) { + // printf("Power down: %d\r\n", parts); } -void doSleep(const uint32_t t) -{ +void doSleep(const uint32_t t) { printf("Sleeping for: %d ms\r\n", t); // sleepForMs(t); delay(t); } -uint32_t getNextScanSleep(const bool increment) -{ - if (increment) - { +uint32_t getNextScanSleep(const bool increment) { + if (increment) { if (scanAttempts < 255) scanAttempts++; } - if (scanAttempts < INTERVAL_1_ATTEMPTS) - { + if (scanAttempts < INTERVAL_1_ATTEMPTS) { return INTERVAL_1_TIME; - } - else if (scanAttempts < (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS)) - { + } else if (scanAttempts < (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS)) { return INTERVAL_2_TIME; - } - else - { + } else { return INTERVAL_3_TIME; } } -void addAverageValue() -{ +void addAverageValue() { uint16_t curval = INTERVAL_AT_MAX_ATTEMPTS - INTERVAL_BASE; curval *= dataReqLastAttempt; curval /= DATA_REQ_MAX_ATTEMPTS; @@ -141,14 +141,11 @@ void addAverageValue() dataReqAttemptArrayIndex++; } -uint16_t getNextSleep() -{ - /*uint16_t avg = 0; - for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) - { +uint16_t getNextSleep() { + uint16_t avg = 0; + for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) { avg += dataReqAttemptArr[c]; } avg /= POWER_SAVING_SMOOTHING; - return avg;*/ - return 30; + return avg; } \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.h index 06ead2fe..eed0e059 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.h @@ -60,20 +60,22 @@ extern void doSleep(const uint32_t t); extern void addAverageValue(); extern uint16_t getNextSleep(); +extern uint16_t doVoltageReading(); + extern uint32_t getNextScanSleep(const bool increment); extern void initPowerSaving(const uint16_t initialValue); extern uint8_t wakeUpReason; -extern uint8_t capabilities; +extern __attribute__((section(".aonshadow"))) uint8_t capabilities; extern uint16_t nextCheckInFromAP; -extern uint8_t dataReqLastAttempt; -extern int8_t temperature; -extern uint16_t batteryVoltage; -extern bool lowBattery; -extern uint8_t scanAttempts; -extern uint16_t longDataReqCounter; -extern uint16_t voltageCheckCounter; +extern __attribute__((section(".aonshadow"))) uint8_t dataReqLastAttempt; +extern __attribute__((section(".aonshadow"))) int8_t temperature; +extern __attribute__((section(".aonshadow"))) uint16_t batteryVoltage; +extern __attribute__((section(".aonshadow"))) bool lowBattery; +extern __attribute__((section(".aonshadow"))) uint8_t scanAttempts; +extern __attribute__((section(".aonshadow"))) uint16_t longDataReqCounter; +extern __attribute__((section(".aonshadow"))) uint16_t voltageCheckCounter; #endif \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/printf.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/printf.c new file mode 100644 index 00000000..93029741 --- /dev/null +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/printf.c @@ -0,0 +1,914 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "printf.h" + + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + + +// internal _putchar wrapper +static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)buffer; (void)idx; (void)maxlen; + if (character) { + _putchar(character); + } +} + + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) { + } + else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } + else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } + else if (*format == 'o') { + base = 8U; + } + else if (*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); + } + else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char* format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int sprintf_(char* buffer, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int snprintf_(char* buffer, size_t count, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + + +int vprintf_(const char* format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + + +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = { out, arg }; + const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/printf.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/printf.h new file mode 100644 index 00000000..821655b5 --- /dev/null +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/printf.h @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ +void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ +int printf_(const char* format, ...); + + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ +int sprintf_(char* buffer, const char* format, ...); + + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ +int vprintf_(const char* format, va_list va); + + +/** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); + + +#ifdef __cplusplus +} +#endif + + +#endif // _PRINTF_H_ diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/proto.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/proto.h index 19cd3fa2..e87d7326 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/proto.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/proto.h @@ -4,49 +4,49 @@ #include /* - All communications are direct from tag to station, EXCEPT association (tag will broadcast). - All comms shall be encrypted and authenticated with AES-CCM. Shared key shall be burned into the firmware. - Master shall provision new key at association. All non-bcast packets shall have pan id compression. - Master may skip "from" field. Tag checking in confirms it got the master's provisioning reply. - - Sadly filtering on MZ100 fails for long addr with no src addr. so short addr for src is used - - T = tag, S = station - - PACKET TYPE USE PAYLOAD STRUCT NOTES - ASSOC_REQ T2bcast TagInfo tag's info and assoc request (encrypted with shared key) - ASSOC_RESP S2T AssocInfo tag's association info (encrypted with shared key) - CHECKIN T2S CheckinInfo tag checking in occasionally - CHECKOUT S2T PendingInfo station's checkin reply telling tag what we have for it - CHUNK_REQ T2S ChunkReqInfo tag requesting a piece of data - CHUNK_RESP S2T ChunkInfo station provides chunk + All communications are direct from tag to station, EXCEPT association (tag will broadcast). + All comms shall be encrypted and authenticated with AES-CCM. Shared key shall be burned into the firmware. + Master shall provision new key at association. All non-bcast packets shall have pan id compression. + Master may skip "from" field. Tag checking in confirms it got the master's provisioning reply. + + Sadly filtering on MZ100 fails for long addr with no src addr. so short addr for src is used + + T = tag, S = station + + PACKET TYPE USE PAYLOAD STRUCT NOTES + ASSOC_REQ T2bcast TagInfo tag's info and assoc request (encrypted with shared key) + ASSOC_RESP S2T AssocInfo tag's association info (encrypted with shared key) + CHECKIN T2S CheckinInfo tag checking in occasionally + CHECKOUT S2T PendingInfo station's checkin reply telling tag what we have for it + CHUNK_REQ T2S ChunkReqInfo tag requesting a piece of data + CHUNK_RESP S2T ChunkInfo station provides chunk */ -#define PROTO_PRESHARED_KEY {0x34D906D3, 0xE3E5298E, 0x3429BF58, 0xC1022081} +#define PROTO_PRESHARED_KEY \ + { 0x34D906D3, 0xE3E5298E, 0x3429BF58, 0xC1022081 } -#define PROTO_PAN_ID (0x4447) //PAN ID compression shall be used +#define PROTO_PAN_ID (0x4447) // PAN ID compression shall be used -#define PKT_ASSOC_REQ (0xF0) -#define PKT_ASSOC_RESP (0xF1) -#define PKT_CHECKIN (0xF2) -#define PKT_CHECKOUT (0xF3) -#define PKT_CHUNK_REQ (0xF4) -#define PKT_CHUNK_RESP (0xF5) +#define PKT_ASSOC_REQ (0xF0) +#define PKT_ASSOC_RESP (0xF1) +#define PKT_CHECKIN (0xF2) +#define PKT_CHECKOUT (0xF3) +#define PKT_CHUNK_REQ (0xF4) +#define PKT_CHUNK_RESP (0xF5) -#define PROTO_VER_0 (0) -#define PROTO_VER_CURRENT (PROTO_VER_0) +#define PROTO_VER_0 (0) +#define PROTO_VER_CURRENT (PROTO_VER_0) -#define PROTO_COMPR_TYPE_LZ (0x0001) -#define PROTO_COMPR_TYPE_BITPACK (0x0002) - -#define PROTO_MAX_DL_LEN (88) +#define PROTO_COMPR_TYPE_LZ (0x0001) +#define PROTO_COMPR_TYPE_BITPACK (0x0002) +#define PROTO_MAX_DL_LEN (88) //////////////// NEW #include "tag_types.h" - +/* // power saving algorithm #define INTERVAL_BASE 40 // interval (in seconds) (when 1 packet is sent/received) for target current (7.2µA) #define INTERVAL_AT_MAX_ATTEMPTS 600 // interval (in seconds) (at max attempts) for target average current @@ -68,7 +68,7 @@ #define INTERVAL_2_TIME 7200UL // Try every 2 hours #define INTERVAL_2_ATTEMPTS 12 // for 12 attempts (an additional day) #define INTERVAL_3_TIME 86400UL // Finally, try every day - +*/ #pragma pack(1) enum TagScreenType { TagScreenEink_BW_1bpp, @@ -96,8 +96,6 @@ enum TagScreenType { TagScreenTypeOther = 0x7f, }; - - #define RADIO_MAX_PACKET_LEN (125) // useful payload, not including the crc #define ADDR_MODE_NONE (0) @@ -122,7 +120,7 @@ struct MacFcs { uint8_t destAddrType : 2; uint8_t frameVer : 2; uint8_t srcAddrType : 2; -} ; +}; struct MacFrameFromMaster { struct MacFcs fcs; @@ -130,7 +128,7 @@ struct MacFrameFromMaster { uint16_t pan; uint8_t dst[8]; uint16_t from; -} ; +}; struct MacFrameNormal { struct MacFcs fcs; @@ -138,7 +136,7 @@ struct MacFrameNormal { uint16_t pan; uint8_t dst[8]; uint8_t src[8]; -} ; +}; struct MacFrameBcast { struct MacFcs fcs; @@ -147,7 +145,7 @@ struct MacFrameBcast { uint16_t dstAddr; uint16_t srcPan; uint8_t src[8]; -} ; +}; #define PKT_AVAIL_DATA_SHORTREQ 0xE3 #define PKT_AVAIL_DATA_REQ 0xE5 @@ -175,7 +173,7 @@ struct AvailDataReq { uint8_t currentChannel; uint8_t customMode; uint8_t reserved[8]; -} ; +}; struct oldAvailDataReq { uint8_t checksum; @@ -186,7 +184,7 @@ struct oldAvailDataReq { uint8_t hwType; uint8_t wakeupReason; uint8_t capabilities; -} ; +}; struct AvailDataInfo { uint8_t checksum; @@ -195,31 +193,31 @@ struct AvailDataInfo { uint8_t dataType; uint8_t dataTypeArgument; // extra specification or instruction for the tag (LUT to be used for drawing image) uint16_t nextCheckIn; // when should the tag check-in again? Measured in minutes -} ; +} __attribute__((packed)) ; struct pendingData { struct AvailDataInfo availdatainfo; uint16_t attemptsLeft; uint8_t targetMac[8]; -} ; +}; struct blockPart { uint8_t checksum; uint8_t blockId; uint8_t blockPart; uint8_t data[]; -} ; +}; struct blockData { uint16_t size; uint16_t checksum; uint8_t data[]; -} ; +}; struct burstMacData { uint16_t offset; uint8_t targetMac[8]; -} ; +}; #define BLOCK_PART_DATA_SIZE 99 #define BLOCK_MAX_PARTS 42 @@ -238,140 +236,131 @@ struct blockRequest { struct blockRequestAck { uint8_t checksum; uint16_t pleaseWaitMs; -} ; +}; struct espBlockRequest { uint8_t checksum; uint64_t ver; uint8_t blockId; uint8_t src[8]; -} ; +}; struct espXferComplete { uint8_t checksum; uint8_t src[8]; -} ; +}; struct espAvailDataReq { uint8_t checksum; uint8_t src[8]; struct AvailDataReq adr; -} ; +}; struct espSetChannelPower { uint8_t checksum; uint8_t channel; uint8_t power; -} ; +}; #pragma pack(0) ///////////////// NEW END #ifndef __packed -#define __packed __attribute__((packed)) +#define __packed __attribute__((packed)) #endif struct TagState { - uint64_t swVer; - uint16_t hwType; - uint16_t batteryMv; + uint64_t swVer; + uint16_t hwType; + uint16_t batteryMv; } __packed; struct TagInfo { - uint8_t protoVer; //PROTO_VER_* - struct TagState state; - uint8_t rfu1[1]; //shall be ignored for now - uint16_t screenPixWidth; - uint16_t screenPixHeight; - uint16_t screenMmWidth; - uint16_t screenMmHeight; - uint16_t compressionsSupported; //COMPR_TYPE_* bitfield - uint16_t maxWaitMsec; //how long tag will wait for packets before going to sleep - uint8_t screenType; //enum TagScreenType - uint8_t rfu[11]; //shall be zero for now + uint8_t protoVer; // PROTO_VER_* + struct TagState state; + uint8_t rfu1[1]; // shall be ignored for now + uint16_t screenPixWidth; + uint16_t screenPixHeight; + uint16_t screenMmWidth; + uint16_t screenMmHeight; + uint16_t compressionsSupported; // COMPR_TYPE_* bitfield + uint16_t maxWaitMsec; // how long tag will wait for packets before going to sleep + uint8_t screenType; // enum TagScreenType + uint8_t rfu[11]; // shall be zero for now } __packed; struct AssocInfo { - uint32_t checkinDelay; //space between checkins, in msec - uint32_t retryDelay; //if download fails mid-way wait thi smany msec to retry (IFF progress was made) - uint16_t failedCheckinsTillBlank; //how many fails till we go blank - uint16_t failedCheckinsTillDissoc; //how many fails till we dissociate - uint32_t newKey[4]; - uint8_t rfu[8]; //shall be zero for now + uint32_t checkinDelay; // space between checkins, in msec + uint32_t retryDelay; // if download fails mid-way wait thi smany msec to retry (IFF progress was made) + uint16_t failedCheckinsTillBlank; // how many fails till we go blank + uint16_t failedCheckinsTillDissoc; // how many fails till we dissociate + uint32_t newKey[4]; + uint8_t rfu[8]; // shall be zero for now } __packed; -#define CHECKIN_TEMP_OFFSET 0x7f +#define CHECKIN_TEMP_OFFSET 0x7f struct CheckinInfo { - struct TagState state; - uint8_t lastPacketLQI; //zero if not reported/not supported to be reported - int8_t lastPacketRSSI; //zero if not reported/not supported to be reported - uint8_t temperature; //zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C - uint8_t rfu[6]; //shall be zero for now + struct TagState state; + uint8_t lastPacketLQI; // zero if not reported/not supported to be reported + int8_t lastPacketRSSI; // zero if not reported/not supported to be reported + uint8_t temperature; // zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C + uint8_t rfu[6]; // shall be zero for now } __packed; struct PendingInfo { - uint64_t imgUpdateVer; - uint32_t imgUpdateSize; - uint64_t osUpdateVer; //version of OS update avail - uint32_t osUpdateSize; - uint8_t rfu[8]; //shall be zero for now + uint64_t imgUpdateVer; + uint32_t imgUpdateSize; + uint64_t osUpdateVer; // version of OS update avail + uint32_t osUpdateSize; + uint8_t rfu[8]; // shall be zero for now } __packed; struct ChunkReqInfo { - uint64_t versionRequested; - uint32_t offset; - uint8_t len; - uint8_t osUpdatePlz : 1; - uint8_t rfu[6]; //shall be zero for now + uint64_t versionRequested; + uint32_t offset; + uint8_t len; + uint8_t osUpdatePlz : 1; + uint8_t rfu[6]; // shall be zero for now } __packed; struct ChunkInfo { - uint32_t offset; - uint8_t osUpdatePlz : 1; - uint8_t rfu; //shall be zero for now - uint8_t data[]; //no data means request is out of bounds of this version no longer exists + uint32_t offset; + uint8_t osUpdatePlz : 1; + uint8_t rfu; // shall be zero for now + uint8_t data[]; // no data means request is out of bounds of this version no longer exists } __packed; +#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" +#define MACCVT(x) ((const uint8_t*)(x))[7], ((const uint8_t*)(x))[6], ((const uint8_t*)(x))[5], ((const uint8_t*)(x))[4], ((const uint8_t*)(x))[3], ((const uint8_t*)(x))[2], ((const uint8_t*)(x))[1], ((const uint8_t*)(x))[0] +#define VERSION_SIGNIFICANT_MASK (0x0000ffffffffffffull) -#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" -#define MACCVT(x) ((const uint8_t*)(x))[7], ((const uint8_t*)(x))[6], ((const uint8_t*)(x))[5], ((const uint8_t*)(x))[4], ((const uint8_t*)(x))[3], ((const uint8_t*)(x))[2], ((const uint8_t*)(x))[1], ((const uint8_t*)(x))[0] +#define HW_TYPE_42_INCH_SAMSUNG (1) +#define HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST (0xEFF8) +#define HW_TYPE_74_INCH_DISPDATA (2) +#define HW_TYPE_74_INCH_DISPDATA_FRAME_MODE (3) +#define HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST (0x008b) -#define VERSION_SIGNIFICANT_MASK (0x0000ffffffffffffull) +#define HW_TYPE_ZBD_EPOP50 (4) +#define HW_TYPE_ZBD_EPOP50_ROM_VER_OFST (0x008b) -#define HW_TYPE_42_INCH_SAMSUNG (1) -#define HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST (0xEFF8) +#define HW_TYPE_ZBD_EPOP900 (5) +#define HW_TYPE_ZBD_EPOP900_ROM_VER_OFST (0x008b) -#define HW_TYPE_74_INCH_DISPDATA (2) -#define HW_TYPE_74_INCH_DISPDATA_FRAME_MODE (3) -#define HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST (0x008b) - -#define HW_TYPE_ZBD_EPOP50 (4) -#define HW_TYPE_ZBD_EPOP50_ROM_VER_OFST (0x008b) - -#define HW_TYPE_ZBD_EPOP900 (5) -#define HW_TYPE_ZBD_EPOP900_ROM_VER_OFST (0x008b) - -#define HW_TYPE_29_INCH_DISPDATA (6) -#define HW_TYPE_29_INCH_DISPDATA_FRAME_MODE (7) -#define HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST (0x008b) - -#define HW_TYPE_29_INCH_ZBS_026 (8) -#define HW_TYPE_29_INCH_ZBS_026_FRAME_MODE (9) -#define HW_TYPE_29_INCH_ZBS_025 (10) -#define HW_TYPE_29_INCH_ZBS_025_FRAME_MODE (11) -#define HW_TYPE_29_INCH_ZBS_ROM_VER_OFST (0x008b) - -#define HW_TYPE_74_INCH_BWR (40) -#define HW_TYPE_74_INCH_BWR_ROM_VER_OFST (0x0160) -#define HW_TYPE_58_INCH_BWR (41) -#define HW_TYPE_58_INCH_BWR_ROM_VER_OFST (0x0160) -#define HW_TYPE_42_INCH_BWR (42) -#define HW_TYPE_42_INCH_BWR_ROM_VER_OFST (0x0160) +#define HW_TYPE_29_INCH_DISPDATA (6) +#define HW_TYPE_29_INCH_DISPDATA_FRAME_MODE (7) +#define HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST (0x008b) +#define HW_TYPE_29_INCH_ZBS_026 (8) +#define HW_TYPE_29_INCH_ZBS_026_FRAME_MODE (9) +#define HW_TYPE_29_INCH_ZBS_025 (10) +#define HW_TYPE_29_INCH_ZBS_025_FRAME_MODE (11) +#define HW_TYPE_29_INCH_ZBS_ROM_VER_OFST (0x008b) +#define HW_TYPE_74_INCH_BWR (40) +#define HW_TYPE_74_INCH_BWR_ROM_VER_OFST (0x0160) +#define HW_TYPE_58_INCH_BWR (41) +#define HW_TYPE_58_INCH_BWR_ROM_VER_OFST (0x0160) +#define HW_TYPE_42_INCH_BWR (42) +#define HW_TYPE_42_INCH_BWR_ROM_VER_OFST (0x0160) #endif - - - - diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/settings.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/settings.c index fb452c15..44636885 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/settings.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/settings.c @@ -3,143 +3,24 @@ #include "eeprom.h" #include "util.h" #include "mz100_flash.h" +#include "powermgt.h" + + __attribute__((section(".aonshadow"))) struct tagsettings tagSettings = {0}; +extern uint8_t blockXferBuffer[]; +uint8_t* infopageTempBuffer = 1024 + blockXferBuffer; -#define EEPROM_NUM_SETTINGS_PAGES (EEPROM_SETTINGS_AREA_LEN / EEPROM_PAGE_SIZE) -#define SETTINGS_MAGIC (0x31415926) - - - -static uint32_t mCurSettingsAddr; - -static void settingsPrvDoWriteAtLocation(uint32_t addr, struct Settings* settings) -{ - settings->hdr.revision++; - mCurSettingsAddr = addr; - FLASH_Write(false, addr, (uint8_t*)settings, sizeof(struct Settings)); -} - -//this is impossible to call before calling read. thus mCurSettingsAddr will be set -void settingsPrvDoWrite(struct Settings* settings) -{ - struct SettingsHeader sh; - uint32_t i, addr; - uint8_t byte; - - - //first we try to fit in the current page, after current (latest) settings - if (mCurSettingsAddr) { - - FLASH_Read(0, mCurSettingsAddr, (uint8_t*)&sh, sizeof(struct SettingsHeader)); - addr = mCurSettingsAddr + sh.structSize; - - //is there space? - if (addr % EEPROM_PAGE_SIZE != 0 && addr % EEPROM_PAGE_SIZE + sizeof(struct Settings) <= EEPROM_PAGE_SIZE) { - - //is it erased - for (i = 0; i < sizeof(struct Settings); i++) { - - FLASH_Read(0, addr, &byte, 1); - if (byte != 0xff) - break; - } - - if (i == sizeof(struct Settings)) { - - settingsPrvDoWriteAtLocation(addr, settings); - return; - } - } - } - - //we need to erase - use next page (or 0th page if no current page at all) - if (mCurSettingsAddr) { - - addr = (mCurSettingsAddr + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE; - if (addr == EEPROM_SETTINGS_AREA_START + EEPROM_SETTINGS_AREA_LEN) - addr = EEPROM_SETTINGS_AREA_START; - } - else - addr = EEPROM_SETTINGS_AREA_START; - - qspiEraseRange(addr, EEPROM_PAGE_SIZE); - settingsPrvDoWriteAtLocation(addr, settings); -} - -void settingsRead(struct Settings* settings) -{ - uint32_t bestAddr = 0, page, ofst; - uint64_t bestRevision = 0; - struct SettingsHeader sh; - bool doWrite = true; - - for (page = 0; page < EEPROM_NUM_SETTINGS_PAGES; page++) { - - for (ofst = 0; ofst < EEPROM_PAGE_SIZE - sizeof(struct SettingsHeader); ofst += sh.structSize) { - - uint32_t addr = EEPROM_SETTINGS_AREA_START + page * EEPROM_PAGE_SIZE + ofst; - - FLASH_Read(0, addr, (uint8_t*)&sh, sizeof(struct SettingsHeader)); - - //sanity checks. struct is only allowed to grow in size... - if (sh.magic != SETTINGS_MAGIC || ofst + sh.structSize > EEPROM_PAGE_SIZE || sh.structSize > sizeof(struct Settings)) - break; - - if (sh.revision > bestRevision) { - bestRevision = sh.revision; - bestAddr = addr; - } - } - } - - if (bestAddr) { - FLASH_Read(0, bestAddr, (uint8_t*)&sh, sizeof(struct SettingsHeader)); //to get size - FLASH_Read(0, bestAddr, (uint8_t*)settings, sh.structSize); - mCurSettingsAddr = bestAddr; - } - else { - settings->hdr.structVersion = SETTINGS_VER_NONE; - settings->hdr.revision = 1; - mCurSettingsAddr = 0; - } - - //migrate - switch (settings->hdr.structVersion) { - - //current version here - mark as such - case SETTINGS_CURRENT: - doWrite = false; - break; - - case SETTINGS_VER_NONE: //migrate to v1 - memset(settings, 0, sizeof(*settings)); - settings->hdr.magic = SETTINGS_MAGIC; - //fallthrough - - case SETTINGS_VER_1: //migrate to v2 - settings->prevDlProgress = 0xffff; - //fallthrough - - case SETTINGS_VER_2: //migrate to v3 - settings->lastRxedRSSI = 0; - //fallthrough - - //new migrations here in order from lower vers to higher vers - - settings->hdr.structVersion = SETTINGS_CURRENT; - settings->hdr.structSize = sizeof(struct Settings); - break; - } - - if (doWrite) - settingsPrvDoWrite(settings); -} - -void settingsWrite(struct Settings* settings) -{ - struct Settings s; - - settingsRead(&s); - if (memcmp(&s, settings, sizeof(struct Settings))) - settingsPrvDoWrite(settings); +void loadDefaultSettings() { + tagSettings.settingsVer = SETTINGS_STRUCT_VERSION; + tagSettings.enableFastBoot = DEFAULT_SETTING_FASTBOOT; + tagSettings.enableRFWake = DEFAULT_SETTING_RFWAKE; + tagSettings.enableTagRoaming = DEFAULT_SETTING_TAGROAMING; + tagSettings.enableScanForAPAfterTimeout = DEFAULT_SETTING_SCANFORAP; + tagSettings.enableLowBatSymbol = DEFAULT_SETTING_LOWBATSYMBOL; + tagSettings.enableNoRFSymbol = DEFAULT_SETTING_NORFSYMBOL; + tagSettings.customMode = 0; + tagSettings.fastBootCapabilities = 0; + tagSettings.minimumCheckInTime = INTERVAL_BASE; + tagSettings.fixedChannel = 0; + tagSettings.batLowVoltage = BATTERY_VOLTAGE_MINIMUM; } \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/settings.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/settings.h index 9da5fff7..d45efc05 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/settings.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/settings.h @@ -1,65 +1,41 @@ -#ifndef _SETTINGS_H_ -#define _SETTINGS_H_ +#ifndef SETTINGS_H +#define SETTINGS_H #include -#define SETTINGS_VER_NONE (0x00000000) -#define SETTINGS_VER_1 (0x00000001) -#define SETTINGS_VER_2 (0x00000002) -#define SETTINGS_VER_3 (0x00000003) +#define FW_VERSION 20 // version number (max 2.5.5 :) ) +#define FW_VERSION_SUFFIX "-75" // suffix, like -RC1 or whatever. +// #define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers +// #define PRINT_LUT // uncomment if you want the tag to print the LUT for the current temperature bracket -struct SettingsHeader { - uint32_t magic; - uint64_t revision; - uint8_t structVersion; - uint8_t structSize; //incl this header +#define SETTINGS_STRUCT_VERSION 0x01 + +#define DEFAULT_SETTING_FASTBOOT 0 +#define DEFAULT_SETTING_RFWAKE 0 +#define DEFAULT_SETTING_TAGROAMING 0 +#define DEFAULT_SETTING_SCANFORAP 1 +#define DEFAULT_SETTING_LOWBATSYMBOL 1 +#define DEFAULT_SETTING_NORFSYMBOL 1 + +struct tagsettings { + uint8_t settingsVer; // the version of the struct as written to the infopage + uint8_t enableFastBoot; // default 0; if set, it will skip splashscreen + uint8_t enableRFWake; // default 0; if set, it will enable RF wake. This will add about ~0.9µA idle power consumption + uint8_t enableTagRoaming; // default 0; if set, the tag will scan for an accesspoint every few check-ins. This will increase power consumption quite a bit + uint8_t enableScanForAPAfterTimeout; // default 1; if a the tag failed to check in, after a few attempts it will try to find a an AP on other channels + uint8_t enableLowBatSymbol; // default 1; tag will show 'low battery' icon on screen if the battery is depleted + uint8_t enableNoRFSymbol; // default 1; tag will show 'no signal' icon on screen if it failed to check in for a longer period of time + uint8_t fastBootCapabilities; // holds the byte with 'capabilities' as detected during a normal tag boot; allows the tag to skip detecting buttons and NFC chip + uint8_t customMode; // default 0; if anything else, tag will bootup in a different 'mode' + uint16_t batLowVoltage; // Low battery threshold voltage (2450 for 2.45v). defaults to BATTERY_VOLTAGE_MINIMUM from powermgt.h + uint16_t minimumCheckInTime; // defaults to BASE_INTERVAL from powermgt.h + uint8_t fixedChannel; // default 0; if set to a valid channel number, the tag will stick to that channel } __attribute__((packed)); -enum SettingsThingType { - SettingsThingTypeNone, - SettingsThingTypeImage, - SettingsThingTypeUpdate, -}; +extern __attribute__((section(".aonshadow")))struct tagsettings tagSettings; -#define SETTING_CHANNEL_OFFSET 11 - -struct Settings { //V1 - struct SettingsHeader hdr; - - //master address - uint8_t masterMac[8]; - - //encryption things - uint32_t encrKey[4]; - uint32_t nextIV; - - //checkin tracking - uint32_t checkinDelay; //space between checkins, in msec - uint32_t retryDelay; - uint16_t failedCheckinsTillBlank; //how many fails till we go blank - uint16_t failedCheckinsTillDissoc; //how many fails till we dissociate - uint16_t numFailedCheckins; - - //state - uint8_t lastRxedLQI; - uint8_t isPaired : 1; - uint8_t channel : 4; //minus SETTING_CHANNEL_OFFSET - uint8_t reserved : 3; - - uint16_t prevDlProgress; - - int8_t lastRxedRSSI; - uint32_t helperInit; - -} __attribute__((packed)); - -#define SETTINGS_CURRENT SETTINGS_VER_3 - - -void settingsRead(struct Settings* settings); -void settingsWrite(struct Settings* settings); - - - - -#endif +void loadDefaultSettings(); +void writeSettings(); +void loadSettings(); +void loadSettingsFromBuffer(uint8_t* p); +#endif \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/syncedproto.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/syncedproto.c index 916d4556..75c2b0da 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/syncedproto.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/syncedproto.c @@ -1,127 +1,111 @@ -#include "main.h" -#include "timer.h" -#include "zigbee.h" -#include "util.h" -#include "mz100_sleep.h" -#include "proto.h" #include "syncedproto.h" -#include "comms.h" + #include "board.h" -#include "eeprom.h" -#include "powermgt.h" -#include "util.h" +#include "comms.h" #include "drawing.h" +#include "eeprom.h" +#include "main.h" +#include "mz100_sleep.h" +#include "powermgt.h" +#include "proto.h" +#include "timer.h" +#include "util.h" +#include "zigbee.h" +#include "printf.h" // download-stuff uint8_t blockXferBuffer[BLOCK_XFER_BUFFER_SIZE] = {0}; -struct blockRequest curBlock = {0}; // used by the block-requester, contains the next request that we'll send -struct AvailDataInfo curDataInfo = {0}; // last 'AvailDataInfo' we received from the AP -bool requestPartialBlock = false; // if we should ask the AP to get this block from the host or not +__attribute__((section(".aonshadow"))) struct blockRequest curBlock = {0}; // used by the block-requester, contains the next request that we'll send +__attribute__((section(".aonshadow"))) struct AvailDataInfo curDataInfo = {0}; // last 'AvailDataInfo' we received from the AP +bool requestPartialBlock = false; // if we should ask the AP to get this block from the host or not #define BLOCK_TRANSFER_ATTEMPTS 10 -uint8_t prevImgSlot = 0xFF; -uint8_t curImgSlot = 0xFF; -static uint32_t curHighSlotId = 0; -static uint8_t nextImgSlot = 0; -static uint8_t imgSlots = 0; -uint8_t drawWithLut = 0; +__attribute__((section(".aonshadow"))) uint8_t prevImgSlot = 0xFF; +__attribute__((section(".aonshadow"))) uint8_t curImgSlot = 0xFF; +__attribute__((section(".aonshadow"))) static uint32_t curHighSlotId = 0; +__attribute__((section(".aonshadow"))) static uint8_t nextImgSlot = 0; +__attribute__((section(".aonshadow"))) static uint8_t imgSlots = 0; +__attribute__((section(".aonshadow"))) uint8_t drawWithLut = 0; // stuff we need to keep track of related to the network/AP uint8_t APmac[8] = {0}; uint16_t APsrcPan = 0; -uint8_t mSelfMac[8] = {0}; -static uint8_t seq = 0; -uint8_t currentChannel = 0; +__attribute__((section(".aonshadow"))) uint8_t mSelfMac[8] = {0}; +__attribute__((section(".aonshadow"))) static uint8_t seq = 0; +__attribute__((section(".aonshadow"))) volatile uint8_t currentChannel = 0; // buffer we use to prepare/read packets static uint8_t inBuffer[128] = {0}; static uint8_t outBuffer[128] = {0}; +// #define DEBUGBLOCKS 1 + // tools -static uint8_t getPacketType(const void *buffer) -{ +static uint8_t getPacketType(const void *buffer) { const struct MacFcs *fcs = buffer; - if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) - { + if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) { // broadcast frame uint8_t type = ((uint8_t *)buffer)[sizeof(struct MacFrameBcast)]; return type; - } - else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) - { + } else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) { // normal frame uint8_t type = ((uint8_t *)buffer)[sizeof(struct MacFrameNormal)]; return type; } return 0; } -static bool pktIsUnicast(const void *buffer) -{ +static bool pktIsUnicast(const void *buffer) { const struct MacFcs *fcs = buffer; - if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) - { + if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) { return false; - } - else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) - { + } else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) { // normal frame return true; } // unknown type... return false; } -void dump(const uint8_t *a, const uint16_t l) -{ +void dump(const uint8_t *a, const uint16_t l) { printf("\n "); #define ROWS 16 - for (uint8_t c = 0; c < ROWS; c++) - { + for (uint8_t c = 0; c < ROWS; c++) { printf(" %02X", c); } printf("\n--------"); - for (uint8_t c = 0; c < ROWS; c++) - { + for (uint8_t c = 0; c < ROWS; c++) { printf("---"); } - for (uint16_t c = 0; c < l; c++) - { - if ((c % ROWS) == 0) - { + for (uint16_t c = 0; c < l; c++) { + if ((c % ROWS) == 0) { printf("\n0x%04X | ", c); } printf("%02X ", a[c]); } printf("\n--------"); - for (uint8_t c = 0; c < ROWS; c++) - { + for (uint8_t c = 0; c < ROWS; c++) { printf("---"); } printf("\n"); } -static bool checkCRC(const void *p, const uint8_t len) -{ +static bool checkCRC(const void *p, const uint8_t len) { uint8_t total = 0; - for (uint8_t c = 1; c < len; c++) - { + for (uint8_t c = 1; c < len; c++) { total += ((uint8_t *)p)[c]; } // printf("CRC: rx %d, calc %d\n", ((uint8_t *)p)[0], total); return ((uint8_t *)p)[0] == total; } -static void addCRC(void *p, const uint8_t len) -{ +static void addCRC(void *p, const uint8_t len) { uint8_t total = 0; - for (uint8_t c = 1; c < len; c++) - { + for (uint8_t c = 1; c < len; c++) { total += ((uint8_t *)p)[c]; } ((uint8_t *)p)[0] = total; } // radio stuff -static void sendPing() -{ +static void sendPing() { struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); memset(outBuffer, 0, sizeof(struct MacFrameBcast) + 2 + 4); outBuffer[0] = sizeof(struct MacFrameBcast) + 1 + 2; @@ -137,26 +121,20 @@ static void sendPing() txframe->srcPan = PROTO_PAN_ID; commsTxNoCpy(outBuffer); } -uint8_t detectAP(const uint8_t channel) -{ +uint8_t detectAP(const uint8_t channel) { uint32_t t; radioRxEnable(false); radioSetChannel(channel); radioRxFlush(); radioRxEnable(true); - for (uint8_t c = 1; c <= MAXIMUM_PING_ATTEMPTS; c++) - { + for (uint8_t c = 1; c <= MAXIMUM_PING_ATTEMPTS; c++) { sendPing(); t = timerGet() + (TIMER_TICKS_PER_MSEC * 5); - while (timerGet() < t) - { + while (timerGet() < t) { int8_t ret = commsRxUnenc(inBuffer); - if (ret > 1) - { - if ((inBuffer[sizeof(struct MacFrameNormal) + 1] == channel) && (getPacketType(inBuffer) == PKT_PONG)) - { - if (pktIsUnicast(inBuffer)) - { + if (ret > 1) { + if ((inBuffer[sizeof(struct MacFrameNormal) + 1] == channel) && (getPacketType(inBuffer) == PKT_PONG)) { + if (pktIsUnicast(inBuffer)) { // dump(inBuffer,32); struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; memcpy(APmac, f->src, 8); @@ -165,29 +143,27 @@ uint8_t detectAP(const uint8_t channel) } } } - timerDelay(TIMER_TICKS_PER_MSEC * 1); + timerDelay(TIMER_TICKS_PER_MSEC * 2); } } return 0; } // data xfer stuff -static void sendShortAvailDataReq() -{ +static void sendShortAvailDataReq() { struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); outBuffer[0] = sizeof(struct MacFrameBcast) + 1 + 2; outBuffer[sizeof(struct MacFrameBcast) + 1] = PKT_AVAIL_DATA_SHORTREQ; memcpy(txframe->src, mSelfMac, 8); outBuffer[1] = 0x21; - outBuffer[2] = 0xC8; // quickly set txframe fcs structure for broadcast packet + outBuffer[2] = 0xC8; // quickly set txframe fcs structure for broadcast packet txframe->seq = seq++; txframe->dstPan = PROTO_PAN_ID; txframe->dstAddr = 0xFFFF; txframe->srcPan = PROTO_PAN_ID; commsTxNoCpy(outBuffer); } -static void sendAvailDataReq() -{ +static void sendAvailDataReq() { struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); memset(outBuffer, 0, sizeof(struct MacFrameBcast) + sizeof(struct AvailDataReq) + 2 + 4); struct AvailDataReq *availreq = (struct AvailDataReq *)(outBuffer + 2 + sizeof(struct MacFrameBcast)); @@ -203,7 +179,7 @@ static void sendAvailDataReq() txframe->dstAddr = 0xFFFF; txframe->srcPan = PROTO_PAN_ID; // TODO: send some (more) meaningful data - availreq->hwType = HW_TYPE; + availreq->hwType = 5; availreq->wakeupReason = wakeUpReason; availreq->lastPacketRSSI = mLastRSSI; availreq->lastPacketLQI = mLastLqi; @@ -213,27 +189,22 @@ static void sendAvailDataReq() addCRC(availreq, sizeof(struct AvailDataReq)); commsTxNoCpy(outBuffer); } -struct AvailDataInfo *getAvailDataInfo() -{ +struct AvailDataInfo *getAvailDataInfo() { radioRxEnable(true); uint32_t t; - for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) - { + for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) { sendAvailDataReq(); - t = timerGet() + (TIMER_TICKS_PER_MSEC * DATA_REQ_RX_WINDOW_SIZE); - while (timerGet() < t) - { + t = timerGet() + (TIMER_TICKS_PER_MSEC * (DATA_REQ_RX_WINDOW_SIZE + 2)); + while (timerGet() < t) { int8_t ret = commsRxUnenc(inBuffer); - if (ret > 1) - { - if (getPacketType(inBuffer) == PKT_AVAIL_DATA_INFO) - { - if (checkCRC(inBuffer + sizeof(struct MacFrameNormal) + 1, sizeof(struct AvailDataInfo))) - { + if (ret > 1) { + if (getPacketType(inBuffer) == PKT_AVAIL_DATA_INFO) { + if (checkCRC(inBuffer + sizeof(struct MacFrameNormal) + 1, sizeof(struct AvailDataInfo))) { struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; memcpy(APmac, f->src, 8); APsrcPan = f->pan; dataReqLastAttempt = c; + printf("%d", dataReqLastAttempt); return (struct AvailDataInfo *)(inBuffer + sizeof(struct MacFrameNormal) + 1); } } @@ -243,28 +214,23 @@ struct AvailDataInfo *getAvailDataInfo() dataReqLastAttempt = DATA_REQ_MAX_ATTEMPTS; return NULL; } -struct AvailDataInfo *getShortAvailDataInfo() -{ +struct AvailDataInfo *getShortAvailDataInfo() { radioRxEnable(true); uint32_t t; - for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) - { + for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) { sendShortAvailDataReq(); // sendAvailDataReq(); t = timerGet() + (TIMER_TICKS_PER_MSEC * 25); - while (timerGet() < t) - { + while (timerGet() < t) { int8_t ret = commsRxUnenc(inBuffer); - if (ret > 1) - { - if (getPacketType(inBuffer) == PKT_AVAIL_DATA_INFO) - { - if (checkCRC(inBuffer + sizeof(struct MacFrameNormal) + 1, sizeof(struct AvailDataInfo))) - { + if (ret > 1) { + if (getPacketType(inBuffer) == PKT_AVAIL_DATA_INFO) { + if (checkCRC(inBuffer + sizeof(struct MacFrameNormal) + 1, sizeof(struct AvailDataInfo))) { struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; memcpy(APmac, f->src, 8); APsrcPan = f->pan; dataReqLastAttempt = c; + printf("%d", dataReqLastAttempt); return (struct AvailDataInfo *)(inBuffer + sizeof(struct MacFrameNormal) + 1); } } @@ -274,13 +240,11 @@ struct AvailDataInfo *getShortAvailDataInfo() dataReqLastAttempt = DATA_REQ_MAX_ATTEMPTS; return NULL; } -static bool processBlockPart(const struct blockPart *bp) -{ +static bool processBlockPart(const struct blockPart *bp) { uint16_t start = bp->blockPart * BLOCK_PART_DATA_SIZE; uint16_t size = BLOCK_PART_DATA_SIZE; // validate if it's okay to copy data - if (bp->blockId != curBlock.blockId) - { + if (bp->blockId != curBlock.blockId) { // printf("got a packet for block %02X\n", bp->blockId); return false; } @@ -288,36 +252,28 @@ static bool processBlockPart(const struct blockPart *bp) return false; if (bp->blockPart > BLOCK_MAX_PARTS) return false; - if ((start + size) > sizeof(blockXferBuffer)) - { + if ((start + size) > sizeof(blockXferBuffer)) { size = sizeof(blockXferBuffer) - start; } - if (checkCRC(bp, sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE)) - { + if (checkCRC(bp, sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE)) { // copy block data to buffer memcpy((void *)(blockXferBuffer + start), (const void *)bp->data, size); // we don't need this block anymore, set bit to 0 so we don't request it again curBlock.requestedParts[bp->blockPart / 8] &= ~(1 << (bp->blockPart % 8)); return true; - } - else - { + } else { return false; } } -static bool blockRxLoop(const uint32_t timeout) -{ +static bool blockRxLoop(const uint32_t timeout) { uint32_t t; bool success = false; radioRxEnable(true); t = timerGet() + (TIMER_TICKS_PER_MSEC * (timeout + 20)); - while (timerGet() < t) - { + while (timerGet() < t) { int8_t ret = commsRxUnenc(inBuffer); - if (ret > 1) - { - if (getPacketType(inBuffer) == PKT_BLOCK_PART) - { + if (ret > 1) { + if (getPacketType(inBuffer) == PKT_BLOCK_PART) { struct blockPart *bp = (struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1); success = processBlockPart(bp); } @@ -327,25 +283,20 @@ static bool blockRxLoop(const uint32_t timeout) radioRxFlush(); return success; } -static struct blockRequestAck *continueToRX() -{ +static struct blockRequestAck *continueToRX() { struct blockRequestAck *ack = (struct blockRequestAck *)(inBuffer + sizeof(struct MacFrameNormal) + 1); ack->pleaseWaitMs = 0; return ack; } -static void sendBlockRequest() -{ +static void sendBlockRequest() { memset(outBuffer, 0, sizeof(struct MacFrameNormal) + sizeof(struct blockRequest) + 2 + 2); struct MacFrameNormal *f = (struct MacFrameNormal *)(outBuffer + 1); struct blockRequest *blockreq = (struct blockRequest *)(outBuffer + 2 + sizeof(struct MacFrameNormal)); outBuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct blockRequest) + 2 + 2; - if (requestPartialBlock) - { + if (requestPartialBlock) { ; outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_PARTIAL_REQUEST; - } - else - { + } else { outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_REQUEST; } memcpy(f->src, mSelfMac, 8); @@ -365,37 +316,32 @@ static void sendBlockRequest() addCRC(blockreq, sizeof(struct blockRequest)); commsTxNoCpy(outBuffer); } -static struct blockRequestAck *performBlockRequest() -{ +static struct blockRequestAck *performBlockRequest() { uint32_t t; radioRxEnable(true); radioRxFlush(); - for (uint8_t c = 0; c < 30; c++) - { + for (uint8_t c = 0; c < 30; c++) { sendBlockRequest(); t = timerGet() + (TIMER_TICKS_PER_MSEC * (7UL + c / 10)); - do - { + do { int8_t ret = commsRxUnenc(inBuffer); - if (ret > 1) - { - switch (getPacketType(inBuffer)) - { - case PKT_BLOCK_REQUEST_ACK: - if (checkCRC((inBuffer + sizeof(struct MacFrameNormal) + 1), sizeof(struct blockRequestAck))) - return (struct blockRequestAck *)(inBuffer + sizeof(struct MacFrameNormal) + 1); - break; - case PKT_BLOCK_PART: - // block already started while we were waiting for a get block reply - // printf("!"); - // processBlockPart((struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1)); - return continueToRX(); - break; - case PKT_CANCEL_XFER: - return NULL; - default: - printf("pkt w/type %02X\n", getPacketType(inBuffer)); - break; + if (ret > 1) { + switch (getPacketType(inBuffer)) { + case PKT_BLOCK_REQUEST_ACK: + if (checkCRC((inBuffer + sizeof(struct MacFrameNormal) + 1), sizeof(struct blockRequestAck))) + return (struct blockRequestAck *)(inBuffer + sizeof(struct MacFrameNormal) + 1); + break; + case PKT_BLOCK_PART: + // block already started while we were waiting for a get block reply + // printf("!"); + // processBlockPart((struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1)); + return continueToRX(); + break; + case PKT_CANCEL_XFER: + return NULL; + default: + printf("pkt w/type %02X\n", getPacketType(inBuffer)); + break; } } @@ -404,8 +350,7 @@ static struct blockRequestAck *performBlockRequest() return continueToRX(); // return NULL; } -static void sendXferCompletePacket() -{ +static void sendXferCompletePacket() { memset(outBuffer, 0, sizeof(struct MacFrameNormal) + 2 + 4); struct MacFrameNormal *f = (struct MacFrameNormal *)(outBuffer + 1); outBuffer[0] = sizeof(struct MacFrameNormal) + 2 + 2; @@ -424,21 +369,16 @@ static void sendXferCompletePacket() f->seq = seq++; commsTxNoCpy(outBuffer); } -static void sendXferComplete() -{ +static void sendXferComplete() { radioRxEnable(true); - for (uint8_t c = 0; c < 16; c++) - { + for (uint8_t c = 0; c < 16; c++) { sendXferCompletePacket(); uint32_t start = timerGet(); - while ((timerGet() - start) < (TIMER_TICKS_PER_MSEC * 6UL)) - { + while ((timerGet() - start) < (TIMER_TICKS_PER_MSEC * 6UL)) { int8_t ret = commsRxUnenc(inBuffer); - if (ret > 1) - { - if (getPacketType(inBuffer) == PKT_XFER_COMPLETE_ACK) - { + if (ret > 1) { + if (getPacketType(inBuffer) == PKT_XFER_COMPLETE_ACK) { printf("XFC ACK\n"); return; } @@ -448,25 +388,21 @@ static void sendXferComplete() printf("XFC NACK!\n"); return; } -static bool validateBlockData() -{ +static bool validateBlockData() { struct blockData *bd = (struct blockData *)blockXferBuffer; // printf("expected len = %04X, checksum=%04X\n", bd->size, bd->checksum); uint16_t t = 0; - for (uint16_t c = 0; c < bd->size; c++) - { + for (uint16_t c = 0; c < bd->size; c++) { t += bd->data[c]; } return bd->checksum == t; } // EEprom related stuff -static uint32_t getAddressForSlot(const uint8_t s) -{ +static uint32_t getAddressForSlot(const uint8_t s) { return EEPROM_IMG_START + (EEPROM_IMG_EACH * s); } -static void getNumSlots() -{ +static void getNumSlots() { printf("Checking slots\n"); imgSlots = 1; @@ -487,40 +423,29 @@ static void getNumSlots() imgSlots = nSlots;*/ printf("Got %i nr of slots\n", imgSlots); } -static uint8_t findSlot(const uint8_t *ver) -{ +static uint8_t findSlot(const uint8_t *ver) { // return 0xFF; // remove me! This forces the tag to re-download each and every upload without checking if it's already in the eeprom somewhere uint32_t markerValid = EEPROM_IMG_VALID; - for (uint8_t c = 0; c < imgSlots; c++) - { + for (uint8_t c = 0; c < imgSlots; c++) { struct EepromImageHeader *eih = (struct EepromImageHeader *)blockXferBuffer; eepromRead(getAddressForSlot(c), eih, sizeof(struct EepromImageHeader)); - if (!memcmp(&eih->validMarker, &markerValid, 4)) - { - if (!memcmp(&eih->version, (void *)ver, 8)) - { + if (!memcmp(&eih->validMarker, &markerValid, 4)) { + if (!memcmp(&eih->version, (void *)ver, 8)) { return c; } } } return 0xFF; } -static void eraseUpdateBlock() -{ - eepromErase(EEPROM_UPDATA_AREA_START, EEPROM_UPDATE_AREA_LEN); +static void eraseUpdateBlock() { + eepromErase(EEPROM_UPDATA_AREA_START, (uint16_t)EEPROM_UPDATE_AREA_LEN); } -static void eraseImageBlock(const uint8_t c) -{ - eepromErase(getAddressForSlot(c), EEPROM_IMG_EACH); -} -static void saveUpdateBlockData(uint8_t blockId) -{ +static void saveUpdateBlockData(uint8_t blockId) { printf("EEPROM writing UpdateBlock %i\n", blockId); if (!eepromWrite(EEPROM_UPDATA_AREA_START + (blockId * BLOCK_DATA_SIZE), blockXferBuffer + sizeof(struct blockData), BLOCK_DATA_SIZE)) printf("EEPROM write failed\n"); } -static void saveImgBlockData(const uint8_t imgSlot, const uint8_t blockId) -{ +static void saveImgBlockData(const uint8_t imgSlot, const uint8_t blockId) { printf("EEPROM writing Slot: %i ImageBlock %i\n", imgSlot, blockId); uint32_t length = EEPROM_IMG_EACH - (sizeof(struct EepromImageHeader) + (blockId * BLOCK_DATA_SIZE)); if (length > 4096) @@ -529,13 +454,11 @@ static void saveImgBlockData(const uint8_t imgSlot, const uint8_t blockId) if (!eepromWrite(getAddressForSlot(imgSlot) + sizeof(struct EepromImageHeader) + (blockId * BLOCK_DATA_SIZE), blockXferBuffer + sizeof(struct blockData), length)) printf("EEPROM write failed\n"); } -void drawImageFromEeprom(const uint8_t imgSlot) -{ +void drawImageFromEeprom(const uint8_t imgSlot) { drawImageAtAddress(getAddressForSlot(imgSlot), drawWithLut); - drawWithLut = 0; // default back to the regular ol' stock/OTP LUT + drawWithLut = 0; // default back to the regular ol' stock/OTP LUT } -static uint32_t getHighSlotId() -{ +static uint32_t getHighSlotId() { uint32_t temp = 0; /*uint32_t markerValid = EEPROM_IMG_VALID; for (uint8_t c = 0; c < imgSlots; c++) @@ -556,47 +479,37 @@ static uint32_t getHighSlotId() } static uint8_t partsThisBlock = 0; -static uint8_t blockAttempts = 0; // these CAN be local to the function, but for some reason, they won't survive sleep? - // they get overwritten with 7F 32 44 20 00 00 00 00 11, I don't know why. +static uint8_t blockAttempts = 0; // these CAN be local to the function, but for some reason, they won't survive sleep? + // they get overwritten with 7F 32 44 20 00 00 00 00 11, I don't know why. -static bool getDataBlock(const uint16_t blockSize) -{ +static bool getDataBlock(const uint16_t blockSize) { blockAttempts = BLOCK_TRANSFER_ATTEMPTS; - if (blockSize == BLOCK_DATA_SIZE) - { + if (blockSize == BLOCK_DATA_SIZE) { partsThisBlock = BLOCK_MAX_PARTS; memset(curBlock.requestedParts, 0xFF, BLOCK_REQ_PARTS_BYTES); - } - else - { + } else { partsThisBlock = (sizeof(struct blockData) + blockSize) / BLOCK_PART_DATA_SIZE; if (blockSize % BLOCK_PART_DATA_SIZE) partsThisBlock++; memset(curBlock.requestedParts, 0x00, BLOCK_REQ_PARTS_BYTES); - for (uint8_t c = 0; c < partsThisBlock; c++) - { + for (uint8_t c = 0; c < partsThisBlock; c++) { curBlock.requestedParts[c / 8] |= (1 << (c % 8)); } } - requestPartialBlock = false; // this forces the AP to request the block data from the host + requestPartialBlock = false; // this forces the AP to request the block data from the host - while (blockAttempts--) - { + while (blockAttempts--) { #ifndef DEBUGBLOCKS printf("REQ %d ", curBlock.blockId); #else printf("REQ %d[", curBlock.blockId); - for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) - { + for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { if ((c != 0) && (c % 8 == 0)) printf("]["); - if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) - { + if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) { printf("R"); - } - else - { + } else { printf("_"); } } @@ -605,34 +518,26 @@ static bool getDataBlock(const uint16_t blockSize) powerUp(INIT_RADIO); struct blockRequestAck *ack = performBlockRequest(); - if (ack == NULL) - { + if (ack == NULL) { printf("Cancelled request\n"); return false; } - if (ack->pleaseWaitMs) - { // SLEEP - until the AP is ready with the data + if (ack->pleaseWaitMs) { // SLEEP - until the AP is ready with the data timerDelay(TIMER_TICKS_PER_MSEC * ack->pleaseWaitMs); - } - else - { + } else { // immediately start with the reception of the block data } - blockRxLoop(270); // BLOCK RX LOOP - receive a block, until the timeout has passed + blockRxLoop(270); // BLOCK RX LOOP - receive a block, until the timeout has passed powerDown(INIT_RADIO); #ifdef DEBUGBLOCKS printf("RX %d[", curBlock.blockId); - for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) - { + for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { if ((c != 0) && (c % 8 == 0)) printf("]["); - if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) - { + if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) { printf("."); - } - else - { + } else { printf("R"); } } @@ -640,34 +545,26 @@ static bool getDataBlock(const uint16_t blockSize) #endif // check if we got all the parts we needed, e.g: has the block been completed? bool blockComplete = true; - for (uint8_t c = 0; c < partsThisBlock; c++) - { + for (uint8_t c = 0; c < partsThisBlock; c++) { if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) blockComplete = false; } - if (blockComplete) - { + if (blockComplete) { #ifndef DEBUGBLOCKS printf("- COMPLETE\n"); #endif - if (validateBlockData()) - { + if (validateBlockData()) { // block download complete, validated return true; - } - else - { - for (uint8_t c = 0; c < partsThisBlock; c++) - { + } else { + for (uint8_t c = 0; c < partsThisBlock; c++) { curBlock.requestedParts[c / 8] |= (1 << (c % 8)); } requestPartialBlock = false; printf("blk failed validation!\n"); } - } - else - { + } else { #ifndef DEBUGBLOCKS printf("- INCOMPLETE\n"); #endif @@ -679,15 +576,11 @@ static bool getDataBlock(const uint16_t blockSize) return false; } uint16_t dataRequestSize = 0; -static bool downloadFWUpdate(const struct AvailDataInfo *avail) -{ +static bool downloadFWUpdate(const struct AvailDataInfo *avail) { // check if we already started the transfer of this information & haven't completed it - if (!memcmp((const void *)&avail->dataVer, (const void *)&curDataInfo.dataVer, 8) && curDataInfo.dataSize) - { + if (!memcmp((const void *)&avail->dataVer, (const void *)&curDataInfo.dataVer, 8) && curDataInfo.dataSize) { // looks like we did. We'll carry on where we left off. - } - else - { + } else { // start, or restart the transfer from 0. Copy data from the AvailDataInfo struct, and the struct intself. This forces a new transfer curBlock.blockId = 0; memcpy(&(curBlock.ver), &(avail->dataVer), 8); @@ -696,30 +589,23 @@ static bool downloadFWUpdate(const struct AvailDataInfo *avail) eraseUpdateBlock(); } - while (curDataInfo.dataSize) - { + while (curDataInfo.dataSize) { wdt10s(); - if (curDataInfo.dataSize > BLOCK_DATA_SIZE) - { + if (curDataInfo.dataSize > BLOCK_DATA_SIZE) { // more than one block remaining dataRequestSize = BLOCK_DATA_SIZE; - } - else - { + } else { // only one block remains dataRequestSize = curDataInfo.dataSize; } - if (getDataBlock(dataRequestSize)) - { + if (getDataBlock(dataRequestSize)) { // succesfully downloaded datablock, save to eeprom powerUp(INIT_EEPROM); saveUpdateBlockData(curBlock.blockId); powerDown(INIT_EEPROM); curBlock.blockId++; curDataInfo.dataSize -= dataRequestSize; - } - else - { + } else { // failed to get the block we wanted, we'll stop for now, maybe resume later return false; } @@ -729,17 +615,13 @@ static bool downloadFWUpdate(const struct AvailDataInfo *avail) } uint16_t imageSize = 0; -static bool downloadImageDataToEEPROM(const struct AvailDataInfo *avail) -{ +static bool downloadImageDataToEEPROM(const struct AvailDataInfo *avail) { // check if we already started the transfer of this information & haven't completed it - if (!memcmp((const void *)&avail->dataVer, (const void *)&curDataInfo.dataVer, 8) && curDataInfo.dataSize) - { + if (!memcmp((const void *)&avail->dataVer, (const void *)&curDataInfo.dataVer, 8) && curDataInfo.dataSize) { // looks like we did. We'll carry on where we left off. printf("restarting image download"); curImgSlot = nextImgSlot; - } - else - { + } else { // go to the next image slot nextImgSlot++; if (nextImgSlot >= imgSlots) @@ -749,17 +631,10 @@ static bool downloadImageDataToEEPROM(const struct AvailDataInfo *avail) drawWithLut = avail->dataTypeArgument; powerUp(INIT_EEPROM); uint8_t attempt = 5; - while (attempt--) - { - if (eepromErase(getAddressForSlot(curImgSlot), EEPROM_IMG_EACH)) + while (attempt--) { + if (eepromErase(getAddressForSlot(curImgSlot), (uint16_t)EEPROM_IMG_EACH)) goto eraseSuccess; } - eepromFail: - powerDown(INIT_RADIO); - powerUp(INIT_EPD); - uiPrvFullscreenMsg("No EEPROM :(", NULL, NULL); - powerDown(INIT_EEPROM | INIT_EPD); - doSleep(-1); NVIC_SystemReset(); eraseSuccess: printf("new download, writing to slot %d\n", curImgSlot); @@ -768,25 +643,22 @@ static bool downloadImageDataToEEPROM(const struct AvailDataInfo *avail) curBlock.blockId = 0; memcpy(&(curBlock.ver), &(avail->dataVer), 8); curBlock.type = avail->dataType; + memcpy(&curDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); + imageSize = curDataInfo.dataSize; } - while (curDataInfo.dataSize) - { + while (curDataInfo.dataSize) { wdt10s(); - if (curDataInfo.dataSize > BLOCK_DATA_SIZE) - { + if (curDataInfo.dataSize > BLOCK_DATA_SIZE) { // more than one block remaining dataRequestSize = BLOCK_DATA_SIZE; - } - else - { + } else { // only one block remains dataRequestSize = curDataInfo.dataSize; } - if (getDataBlock(dataRequestSize)) - { + if (getDataBlock(dataRequestSize)) { // succesfully downloaded datablock, save to eeprom powerUp(INIT_EEPROM); #ifdef DEBUGBLOCKS @@ -796,9 +668,7 @@ static bool downloadImageDataToEEPROM(const struct AvailDataInfo *avail) powerDown(INIT_EEPROM); curBlock.blockId++; curDataInfo.dataSize -= dataRequestSize; - } - else - { + } else { // failed to get the block we wanted, we'll stop for now, probably resume later return false; } @@ -823,112 +693,98 @@ static bool downloadImageDataToEEPROM(const struct AvailDataInfo *avail) return true; } -bool processAvailDataInfo(struct AvailDataInfo *avail) -{ - switch (avail->dataType) - { - case DATATYPE_IMG_BMP: - case DATATYPE_IMG_DIFF: - case DATATYPE_IMG_RAW_1BPP: - case DATATYPE_IMG_RAW_2BPP: - // check if this download is currently displayed or active - if (curDataInfo.dataSize == 0 && !memcmp((const void *)&avail->dataVer, (const void *)&curDataInfo.dataVer, 8)) - { - // we've downloaded this already, we're guessing it's already displayed - printf("currently shown image, send xfc\n"); - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - } +bool processAvailDataInfo(struct AvailDataInfo *avail) { + switch (avail->dataType) { + case DATATYPE_IMG_BMP: + case DATATYPE_IMG_DIFF: + case DATATYPE_IMG_RAW_1BPP: + case DATATYPE_IMG_RAW_2BPP: + // check if this download is currently displayed or active + if (curDataInfo.dataSize == 0 && !memcmp((const void *)&avail->dataVer, (const void *)&curDataInfo.dataVer, 8)) { + // we've downloaded this already, we're guessing it's already displayed + printf("currently shown image, send xfc\n"); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + } - // check if we've seen this version before - powerUp(INIT_EEPROM); - curImgSlot = findSlot((uint8_t *)&(avail->dataVer)); - powerDown(INIT_EEPROM); - if (curImgSlot != 0xFF) - { - // found a (complete)valid image slot for this version - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - - printf("already seen, drawing from eeprom slot %d\n", curImgSlot); - - // mark as completed and draw from EEPROM - memcpy(&curDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); - curDataInfo.dataSize = 0; // mark as transfer not pending - - drawWithLut = avail->dataTypeArgument; - wdt60s(); - powerUp(INIT_EPD | INIT_EEPROM); - drawImageFromEeprom(curImgSlot); - powerDown(INIT_EPD | INIT_EEPROM); - return true; - } - else - { - // not found in cache, prepare to download - printf("downloading to imgslot\n"); - drawWithLut = avail->dataTypeArgument; + // check if we've seen this version before powerUp(INIT_EEPROM); - if (downloadImageDataToEEPROM(avail)) - { - printf("download complete!\n"); + curImgSlot = findSlot((uint8_t *)&(avail->dataVer)); + powerDown(INIT_EEPROM); + if (curImgSlot != 0xFF) { + // found a (complete)valid image slot for this version powerUp(INIT_RADIO); sendXferComplete(); powerDown(INIT_RADIO); + printf("already seen, drawing from eeprom slot %d\n", curImgSlot); + + // mark as completed and draw from EEPROM + memcpy(&curDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); + curDataInfo.dataSize = 0; // mark as transfer not pending + + drawWithLut = avail->dataTypeArgument; wdt60s(); powerUp(INIT_EPD | INIT_EEPROM); drawImageFromEeprom(curImgSlot); powerDown(INIT_EPD | INIT_EEPROM); return true; + } else { + // not found in cache, prepare to download + printf("downloading to imgslot\n"); + drawWithLut = avail->dataTypeArgument; + powerUp(INIT_EEPROM); + if (downloadImageDataToEEPROM(avail)) { + printf("download complete!\n"); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + + wdt60s(); + powerUp(INIT_EPD | INIT_EEPROM); + drawImageFromEeprom(curImgSlot); + powerDown(INIT_EPD | INIT_EEPROM); + return true; + } else { + powerDown(INIT_EEPROM); + return false; + } } - else - { - powerDown(INIT_EEPROM); + break; + case DATATYPE_FW_UPDATE: + powerUp(INIT_EEPROM); + if (downloadFWUpdate(avail)) { + printf("firmware download complete, doing update.\n"); + + powerUp(INIT_EPD); + // uiPrvFullscreenMsg("Updating", NULL, NULL); + + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + + powerUp(INIT_EEPROM); + wdt60s(); + prvApplyUpdateIfNeeded(); + } else { return false; } - } - break; - case DATATYPE_FW_UPDATE: - powerUp(INIT_EEPROM); - if (downloadFWUpdate(avail)) - { - printf("firmware download complete, doing update.\n"); - - powerUp(INIT_EPD); - uiPrvFullscreenMsg("Updating", NULL, NULL); - - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - - powerUp(INIT_EEPROM); - wdt60s(); - prvApplyUpdateIfNeeded(); - } - else - { + break; + case DATATYPE_NFC_URL_DIRECT: + case DATATYPE_NFC_RAW_CONTENT: { return false; + break; } - break; - case DATATYPE_NFC_URL_DIRECT: - case DATATYPE_NFC_RAW_CONTENT: - { - return false; - break; - } - case DATATYPE_CUSTOM_LUT_OTA: - return false; - break; + case DATATYPE_CUSTOM_LUT_OTA: + return false; + break; } return true; } -void initializeProto() -{ +void initializeProto() { getNumSlots(); curHighSlotId = getHighSlotId(); } \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/syncedproto.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/syncedproto.h index 0066fa73..f0eafddc 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/syncedproto.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/syncedproto.h @@ -1,14 +1,19 @@ #pragma once -#include #include +#include + #include "settings.h" -extern uint8_t mSelfMac[]; -extern uint8_t currentChannel; +extern __attribute__((section(".aonshadow"))) uint8_t mSelfMac[]; +extern __attribute__((section(".aonshadow"))) volatile uint8_t currentChannel; +extern __attribute__((section(".aonshadow"))) struct blockRequest curBlock; // used by the block-requester, contains the next request that we'll send +extern __attribute__((section(".aonshadow"))) struct AvailDataInfo curDataInfo; // last 'AvailDataInfo' we received from the AP // __attribute__((section(".aon"))) + + extern uint8_t APmac[]; -extern uint8_t curImgSlot; +extern __attribute__((section(".aonshadow"))) uint8_t curImgSlot; extern void setupRadio(void); extern void killRadio(void); diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/userinterface.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/userinterface.c new file mode 100644 index 00000000..fc674ab1 --- /dev/null +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/userinterface.c @@ -0,0 +1,181 @@ +#include "userinterface.h" + +#include +#include + +#include "bitmaps.h" +#include "board.h" +#include "comms.h" +#include "epd.h" +#include "font.h" +#include "powermgt.h" +#include "printf.h" +#include "proto.h" +#include "screen.h" +#include "settings.h" +#include "syncedproto.h" // for APmac / Channel +#include "timer.h" + +const uint16_t fwVersion = FW_VERSION; +const char fwVersionSuffix[] = FW_VERSION_SUFFIX; + +extern uint8_t capabilities; + +bool __attribute__((section(".aonshadow"))) lowBatteryShown = false; +bool __attribute__((section(".aonshadow"))) noAPShown = false; + +void addCapabilities() { + // if (capabilities) epdpr("Options: "); + if (capabilities & CAPABILITY_HAS_NFC) { + // epdpr("-NFC"); + if (capabilities & CAPABILITY_NFC_WAKE) { + // epdpr("+WAKE"); + } else { + // epdpr(" "); + } + } + if (capabilities & CAPABILITY_HAS_WAKE_BUTTON) { + // epdpr("-WAKE BUTTON"); + } +} + +void addOverlay() { + if ((currentChannel == 0) && (tagSettings.enableNoRFSymbol)) { + // loadRawBitmap(ant, SCREEN_WIDTH - 24, 6, EPD_COLOR_BLACK); + // loadRawBitmap(cross, SCREEN_WIDTH - 16, 13, EPD_COLOR_RED); + noAPShown = true; + } else { + noAPShown = false; + } + if ((batteryVoltage < tagSettings.batLowVoltage) && (tagSettings.enableLowBatSymbol)) { + // loadRawBitmap(battery, SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, EPD_COLOR_BLACK); + lowBatteryShown = true; + } else { + lowBatteryShown = false; + } +} + +void afterFlashScreenSaver() { + // selectLUT(EPD_LUT_DEFAULT); + // clearScreen(); + +#if (SCREEN_WIDTH == 400) // 4.2" + epdPrintBegin(3, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK); + epdpr("OpenEPaperLink"); + epdPrintEnd(); +#endif + // drawWithSleep(); +} + +void showSplashScreen() { + // selectLUT(EPD_LUT_NO_REPEATS); + // clearScreen(); +#if (SCREEN_WIDTH == 400) // 4.2" + epdPrintBegin(3, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK); + epdpr("Starting"); + epdPrintEnd(); + + epdPrintBegin(2, 252, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK); + addCapabilities(); + epdPrintEnd(); + + epdPrintBegin(3, 268, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK); + epdpr("zbs42v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix); + epdPrintEnd(); + epdPrintBegin(3, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_RED); + epdpr("MAC: %02X:%02X", mSelfMac[7], mSelfMac[6]); + epdpr(":%02X:%02X", mSelfMac[5], mSelfMac[4]); + epdpr(":%02X:%02X", mSelfMac[3], mSelfMac[2]); + epdpr(":%02X:%02X", mSelfMac[1], mSelfMac[0]); + epdPrintEnd(); + + loadRawBitmap(oepli, 136, 22, EPD_COLOR_BLACK); + loadRawBitmap(cloud, 136, 10, EPD_COLOR_RED); + + uint8_t __xdata buffer[17]; + spr(buffer, "%02X%02X", mSelfMac[7], mSelfMac[6]); + spr(buffer + 4, "%02X%02X", mSelfMac[5], mSelfMac[4]); + spr(buffer + 8, "%02X%02X", mSelfMac[3], mSelfMac[2]); + spr(buffer + 12, "%02X%02X", mSelfMac[1], mSelfMac[0]); + printBarcode(buffer, 392, 264); + printBarcode(buffer, 384, 264); + +#endif + // drawWithSleep(); +} + +void showApplyUpdate() { + // selectLUT(1); + // clearScreen(); +#if (SCREEN_WIDTH == 400) + epdPrintBegin(136, 134, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK); +#endif + + // epdpr("Updating!"); + // epdPrintEnd(); + // drawNoWait(); +} + +void showAPFound() { + + init_epd(); + fillWindow(0, 0, 640, 384, 1); + epdPrintf(10, 10, 1, "OpenEPaperLink"); + epdPrintf(10, 40, 1, "AP Found at channel %d", currentChannel); + epdPrintf(10, 60, 1, "AP MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", APmac[7], APmac[6], APmac[5], APmac[4], APmac[3], APmac[2], APmac[1], APmac[0]); + + epdPrintf(10, 330, 1, "Battery: %d.%dV", batteryVoltage / 1000, batteryVoltage % 1000); + epdPrintf(10, 350, 1, "Tag MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", mSelfMac[7], mSelfMac[6], mSelfMac[5], mSelfMac[4], mSelfMac[3], mSelfMac[2], mSelfMac[1], mSelfMac[0]); + epd_refresh_and_sleep(); +} + +void showNoAP() { + init_epd(); + fillWindow(0, 0, 640, 384, 1); + epdPrintf(10, 10, 1, "OpenEPaperLink "); + epdPrintf(10, 40, 1, "No AP found... We'll try again in a little while though!"); + epdPrintf(10, 350, 1, "Tag MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", mSelfMac[7], mSelfMac[6], mSelfMac[5], mSelfMac[4], mSelfMac[3], mSelfMac[2], mSelfMac[1], mSelfMac[0]); + + epd_refresh_and_sleep(); +} + +void showLongTermSleep() { + // selectLUT(EPD_LUT_NO_REPEATS); + // clearScreen(); + + // epdPrintBegin(2, SCREEN_HEIGHT - 16, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK); + // epdpr("zZ"); + // epdPrintEnd(); + + addOverlay(); + // drawWithSleep(); +} +void showNoEEPROM() { + // selectLUT(EPD_LUT_NO_REPEATS); + // clearScreen(); +#if (SCREEN_WIDTH == 400) // 4.2" + epdPrintBegin(50, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK); + epdpr("EEPROM FAILED :("); + epdPrintEnd(); + loadRawBitmap(failed, 176, 126, EPD_COLOR_RED); + epdPrintBegin(100, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK); + epdpr("Sleeping forever :'("); + epdPrintEnd(); +#endif + // drawWithSleep(); +} + +void showNoMAC() { + // selectLUT(EPD_LUT_NO_REPEATS); + // clearScreen(); +#if (SCREEN_WIDTH == 400) // 4.2" + epdPrintBegin(100, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK); + epdpr("NO MAC SET :("); + epdPrintEnd(); + loadRawBitmap(failed, 176, 126, EPD_COLOR_RED); + epdPrintBegin(100, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK); + epdpr("Sleeping forever :'("); + epdPrintEnd(); +#endif + // drawWithSleep(); +} \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/userinterface.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/userinterface.h new file mode 100644 index 00000000..d9d69079 --- /dev/null +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/userinterface.h @@ -0,0 +1,22 @@ +#ifndef _UI_H_ +#define _UI_H_ +#include +#include + +void addOverlay(); + +void afterFlashScreenSaver(); +void showSplashScreen(); +void showApplyUpdate(); +void showAPFound(); +void showNoAP(); +void showLongTermSleep(); +void showNoEEPROM(); +void showNoMAC(); + +extern const uint16_t fwVersion; +extern const char fwVersionSuffix[]; +extern __attribute__((section(".aon"))) bool lowBatteryShown; +extern __attribute__((section(".aon"))) bool noAPShown; + +#endif \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/util.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/util.c index 86503a53..e17bdc90 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/util.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/util.c @@ -1,213 +1,185 @@ -#include -#include -#include "eeprom.h" -#include "timer.h" -#include "mz100.h" #include "util.h" -#include "mz100_flash.h" + +#include +//#include +#include "printf.h" + +#include "eeprom.h" +#include "mz100.h" #include "mz100_adc.h" +#include "mz100_flash.h" #include "mz100_wdt.h" +#include "timer.h" -void wdt10s() -{ +void wdt10s() { WDT_RestartCounter(); } -void wdt30s() -{ +void wdt30s() { WDT_RestartCounter(); } -void wdt60s() -{ +void wdt60s() { WDT_RestartCounter(); } -void delay(int cnt) -{ - volatile unsigned int i; - for (i = 107 * cnt; i; --i) - ; +void delay(int cnt) { + volatile unsigned int i; + for (i = 107 * cnt; i; --i) + ; } -void delay_us(unsigned int result) -{ - volatile unsigned int i; +void delay_us(unsigned int result) { + volatile unsigned int i; - for (i = 0; i < result; ++i) - ; + for (i = 0; i < result; ++i) + ; } -uint16_t crc16(uint16_t cur_crc, uint8_t data) -{ - cur_crc ^= data; - for (uint8_t i = 8; i > 0; i--) - { - if ((cur_crc & 0x001) != 0) - { - cur_crc >>= 1; - cur_crc ^= 0x8005; // poly - } - else - { - cur_crc >>= 1; - } - } - return cur_crc; +uint16_t crc16(uint16_t cur_crc, uint8_t data) { + cur_crc ^= data; + for (uint8_t i = 8; i > 0; i--) { + if ((cur_crc & 0x001) != 0) { + cur_crc >>= 1; + cur_crc ^= 0x8005; // poly + } else { + cur_crc >>= 1; + } + } + return cur_crc; } -uint32_t measureTemp(void) -{ - uint32_t result = 0; - ADC_CFG_Type adc_config; - adc_config.adcResolution = ADC_RESOLUTION_14BIT; - adc_config.adcVrefSource = ADC_VREF_INTERNAL; // 1.2V - adc_config.adcGainSel = ADC_GAIN_1; - adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4; - adc_config.adcBiasMode = ADC_BIAS_FULL; +uint32_t measureTemp(void) { + uint32_t result = 0; + ADC_CFG_Type adc_config; + adc_config.adcResolution = ADC_RESOLUTION_14BIT; + adc_config.adcVrefSource = ADC_VREF_INTERNAL; // 1.2V + adc_config.adcGainSel = ADC_GAIN_1; + adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4; + adc_config.adcBiasMode = ADC_BIAS_FULL; - ADC_Reset(); - ADC_ModeSelect(ADC_MODE_TSENSOR); - ADC_TSensorConfig(ADC_TEMPP, ADC_SENSOR_INTERNAL); - ADC_Init(&adc_config); - ADC_Enable(); - for (int i = 0; i < 32; i++) - { - ADC_ConversionStart(); - ADC_IntClr(ADC_RDY); - while (!ADC_GetStatus(ADC_STATUS_RDY)) - ; - ADC_ConversionStop(); - } - for (int i = 0; i < 128; i++) - { - ADC_ConversionStart(); - ADC_IntClr(ADC_RDY); - while (!ADC_GetStatus(ADC_STATUS_RDY)) - ; - ADC_ConversionStop(); - result += (ADC_GetConversionResult() - 458) / 1.7; - } - result /= 128; - printf("Temp: %iC\r\n", result); - return result; + ADC_Reset(); + ADC_ModeSelect(ADC_MODE_TSENSOR); + ADC_TSensorConfig(ADC_TEMPP, ADC_SENSOR_INTERNAL); + ADC_Init(&adc_config); + ADC_Enable(); + for (int i = 0; i < 32; i++) { + ADC_ConversionStart(); + ADC_IntClr(ADC_RDY); + while (!ADC_GetStatus(ADC_STATUS_RDY)) + ; + ADC_ConversionStop(); + } + for (int i = 0; i < 128; i++) { + ADC_ConversionStart(); + ADC_IntClr(ADC_RDY); + while (!ADC_GetStatus(ADC_STATUS_RDY)) + ; + ADC_ConversionStop(); + result += (ADC_GetConversionResult() - 458) / 1.7; + } + result /= 128; + printf("Temp: %iC\r\n", result); + return result; } -uint32_t measureBattery(void) -{ - uint32_t result = 0; - ADC_CFG_Type adc_config; - adc_config.adcResolution = ADC_RESOLUTION_16BIT; - adc_config.adcVrefSource = ADC_VREF_VCAU; // 1.8V - adc_config.adcGainSel = ADC_GAIN_1; // range 0 - 1.8V - adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4; - adc_config.adcBiasMode = ADC_BIAS_FULL; +uint32_t measureBattery(void) { + uint32_t result = 0; + ADC_CFG_Type adc_config; + adc_config.adcResolution = ADC_RESOLUTION_16BIT; + adc_config.adcVrefSource = ADC_VREF_VCAU; // 1.8V + adc_config.adcGainSel = ADC_GAIN_1; // range 0 - 1.8V + adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4; + adc_config.adcBiasMode = ADC_BIAS_FULL; - ADC_Reset(); - ADC_ModeSelect(ADC_MODE_ADC); - ADC_ChannelConfig(ADC_VBATS); // 0.33 of Actual Voltage - ADC_Init(&adc_config); - ADC_Enable(); - ADC_ConversionStart(); - ADC_IntClr(ADC_RDY); - for (int i = 0; i < 32; i++) - { - ADC_ConversionStart(); - ADC_IntClr(ADC_RDY); - while (!ADC_GetStatus(ADC_STATUS_RDY)) - ; - ADC_ConversionStop(); - } - for (int i = 0; i < 128; i++) - { - ADC_ConversionStart(); - ADC_IntClr(ADC_RDY); - while (!ADC_GetStatus(ADC_STATUS_RDY)) - ; - ADC_ConversionStop(); - result += ADC_GetConversionResult() * 5940 / 32768; - } - result /= 128; - printf("Voltage: %imV\r\n", result); - return result; + ADC_Reset(); + ADC_ModeSelect(ADC_MODE_ADC); + ADC_ChannelConfig(ADC_VBATS); // 0.33 of Actual Voltage + ADC_Init(&adc_config); + ADC_Enable(); + ADC_ConversionStart(); + ADC_IntClr(ADC_RDY); + for (int i = 0; i < 32; i++) { + ADC_ConversionStart(); + ADC_IntClr(ADC_RDY); + while (!ADC_GetStatus(ADC_STATUS_RDY)) + ; + ADC_ConversionStop(); + } + for (int i = 0; i < 128; i++) { + ADC_ConversionStart(); + ADC_IntClr(ADC_RDY); + while (!ADC_GetStatus(ADC_STATUS_RDY)) + ; + ADC_ConversionStop(); + result += ADC_GetConversionResult() * 5940 / 32768; + } + result /= 128; + printf("Voltage: %imV\r\n", result); + return result; } -void qspiEraseRange(uint32_t addr, uint32_t len) -{ - uint64_t time; - // round starting address down - if (addr % EEPROM_PAGE_SIZE) - { - len += addr % EEPROM_PAGE_SIZE; - addr = addr / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE; - } +void qspiEraseRange(uint32_t addr, uint32_t len) { + uint64_t time; + // round starting address down + if (addr % EEPROM_PAGE_SIZE) { + len += addr % EEPROM_PAGE_SIZE; + addr = addr / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE; + } - // round length up - len = (len + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE; + // round length up + len = (len + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE; - while (len) - { + while (len) { + uint32_t now; + bool ok; - uint32_t now; - bool ok; + WDT_RestartCounter(); + if (!(addr % 0x10000) && len >= 0x10000) { + ok = FLASH_Block64KErase(addr / 0x10000); + now = 0x10000; + } else if (!(addr % 0x8000) && len >= 0x8000) { + ok = FLASH_Block32KErase(addr / 0x8000); + now = 0x8000; + } else { + ok = FLASH_SectorErase(addr / 0x1000); + now = 0x1000; + } - WDT_RestartCounter(); - if (!(addr % 0x10000) && len >= 0x10000) - { - ok = FLASH_Block64KErase(addr / 0x10000); - now = 0x10000; - } - else if (!(addr % 0x8000) && len >= 0x8000) - { - ok = FLASH_Block32KErase(addr / 0x8000); - now = 0x8000; - } - else - { - ok = FLASH_SectorErase(addr / 0x1000); - now = 0x1000; - } + if (!ok) + printf("ERZ fail at 0x%08x + %u\r\n", addr, now); - if (!ok) - printf("ERZ fail at 0x%08x + %u\r\n", addr, now); - - addr += now; - len -= now; - if (len) - { - // let the caps recharge - time = timerGet(); - while (timerGet() - time < TIMER_TICKS_PER_SEC / 10) - ; - } - } - WDT_RestartCounter(); + addr += now; + len -= now; + if (len) { + // let the caps recharge + time = timerGet(); + while (timerGet() - time < TIMER_TICKS_PER_SEC / 10) + ; + } + } + WDT_RestartCounter(); } -bool eepromWrite(uint32_t addr, const void *srcP, uint16_t len) -{ - FLASH_Write(false, addr, srcP, len); - return true; +bool eepromWrite(uint32_t addr, const void *srcP, uint16_t len) { + FLASH_Write(false, addr, (void*)srcP, len); + return true; } -bool eepromErase(uint32_t addr, uint16_t nSec) -{ - qspiEraseRange(addr, nSec); - return true; +bool eepromErase(uint32_t addr, uint16_t nSec) { + qspiEraseRange(addr, nSec); + return true; } -void eepromRead(uint32_t addr, void *dstP, uint16_t len) -{ - uint8_t *dst = (uint8_t *)dstP; - FLASH_Read(0, addr, dst, len); +void eepromRead(uint32_t addr, void *dstP, uint16_t len) { + uint8_t *dst = (uint8_t *)dstP; + FLASH_Read(0, addr, dst, len); } -uint32_t eepromGetSize(void) -{ - return EEPROM_IMG_LEN; +uint32_t eepromGetSize(void) { + return EEPROM_IMG_LEN; } -void radioShutdown(void) -{ - // i have no idea what these do, determined by writing random registers and watching the current drawn - *(volatile uint32_t *)0x4C000000 = 0; - *(volatile uint32_t *)0x4C010000 = 0; - *(volatile uint32_t *)0x4C010004 = 0x10000000; +void radioShutdown(void) { + // i have no idea what these do, determined by writing random registers and watching the current drawn + *(volatile uint32_t *)0x4C000000 = 0; + *(volatile uint32_t *)0x4C010000 = 0; + *(volatile uint32_t *)0x4C010004 = 0x10000000; } \ No newline at end of file diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/zigbee.c b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/zigbee.c index 18410137..59f5b80b 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/zigbee.c +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/zigbee.c @@ -1,7 +1,8 @@ -#include +//#include #include #include "util.h" #include "zigbee.h" +#include "printf.h" volatile uint8_t calibration_irq_ocoured = 0; volatile uint8_t zigbee_tx_done = 0; @@ -145,24 +146,29 @@ void fill_rx_regs() ; } -void sub_1021E6() +void load_calib() { int v0; unsigned int v1; unsigned int i; v0 = (*(volatile unsigned int *)0x4C01000C); - v1 = get_register(0x130004); + v1 = zigbeeCalibData.len; (*(volatile unsigned int *)0x4C010000) |= 4u; while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) == 0) ; for (i = 0; i < v1; ++i) - set_register(v0 + 4 * i + 0x4C014000, *(uint32_t *)(4 * i + 0x130008)); + set_register(v0 + 4 * i + 0x4C014000, zigbeeCalibData.data[i]); (*(volatile unsigned int *)0x4C010000) &= ~4u; while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) != 0) ; } + + +// It is from 0x130000 up to 0x1301000 But you can only use 0x130404 up to the 0x1301000 + + void save_calib_in_ram() { int v0; @@ -171,14 +177,14 @@ void save_calib_in_ram() v0 = (*(volatile unsigned int *)0x4C01000C) + 0x4C014000; v1 = ((unsigned int)(uint8_t)((*(volatile unsigned int *)0x4C010008) >> 2) + 3) >> 2; - set_register(0x130000u, 0x464C4147); - set_register(0x130004u, v1); + zigbeeCalibData.isValid = true; (*(volatile unsigned int *)0x4C01001C) = -5; (*(volatile unsigned int *)0x4C010000) |= 2u; while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) == 0) ; - for (i = 0; i < v1; ++i) - set_register(4 * i + 0x130008, *(uint32_t *)(v0 + 4 * i)); + for (i = 0; i < v1; ++i){ + zigbeeCalibData.data[i] = *(uint32_t *)(v0 + 4 * i); + } (*(volatile unsigned int *)0x4C010000) &= ~2u; while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) != 0) ; @@ -189,12 +195,12 @@ int inner_calibration() int is_in_ram; (*(volatile unsigned int *)0x4C010000) |= 0x20u; - if (get_register(0x130000) == 0x464C4147) + if(zigbeeCalibData.isValid) { is_in_ram = 1; (*(volatile unsigned int *)0x4C010000) |= 8u; (*(volatile unsigned int *)0x4C010000) &= ~0x10u; - sub_1021E6(); + load_calib(); } else { diff --git a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/zigbee.h b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/zigbee.h index eeb50011..7dae8ac2 100644 --- a/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/zigbee.h +++ b/ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/zigbee.h @@ -1,4 +1,5 @@ #pragma once +#include extern uint8_t channelList[6]; @@ -10,4 +11,11 @@ uint8_t Zigbee_tx_buffer(uint8_t *tx_buffer, int len); void radioInit(); void radioSetChannel(uint8_t channel); void radioRxEnable(uint8_t channel); -void radioRxFlush(); \ No newline at end of file +void radioRxFlush(); + +struct zigbeeCalibDataStruct { + uint16_t len; + bool isValid; + uint32_t data[30]; +}; +extern __attribute__((section(".aon"))) volatile struct zigbeeCalibDataStruct zigbeeCalibData;