From 66c7ad6140969de1fd0538643d56d4b0fa0f54b1 Mon Sep 17 00:00:00 2001 From: VstudioLAB <57193516+VstudioLAB@users.noreply.github.com> Date: Wed, 29 Nov 2023 00:26:15 +0100 Subject: [PATCH] =?UTF-8?q?Added=20ST=E2=80=90GM29XXF=202.9"=20Support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added ST‐GM29XXF 2.9" support --- ESP32_AP-Flasher/data/tagtypes/21.json | 74 ++ .../data/www/content_cards.json.gz | Bin 3260 -> 3284 bytes ESP32_AP-Flasher/wwwroot/content_cards.json | 57 +- oepl-definitions.h | 1 + zbs243_Tag_FW/powermgt.c | 6 +- zbs243_Tag_FW/syncedproto.c | 4 +- zbs243_shared/board/zbs29_BW_ssd1619/board.c | 163 ++++ zbs243_shared/board/zbs29_BW_ssd1619/board.h | 36 + zbs243_shared/board/zbs29_BW_ssd1619/make.mk | 7 + .../board/zbs29_BW_ssd1619/old_screen.c | 1 + zbs243_shared/board/zbs29_BW_ssd1619/screen.c | 834 ++++++++++++++++++ zbs243_shared/board/zbs29_BW_ssd1619/screen.h | 26 + 12 files changed, 1186 insertions(+), 23 deletions(-) create mode 100644 ESP32_AP-Flasher/data/tagtypes/21.json create mode 100644 zbs243_shared/board/zbs29_BW_ssd1619/board.c create mode 100644 zbs243_shared/board/zbs29_BW_ssd1619/board.h create mode 100644 zbs243_shared/board/zbs29_BW_ssd1619/make.mk create mode 100644 zbs243_shared/board/zbs29_BW_ssd1619/old_screen.c create mode 100644 zbs243_shared/board/zbs29_BW_ssd1619/screen.c create mode 100644 zbs243_shared/board/zbs29_BW_ssd1619/screen.h diff --git a/ESP32_AP-Flasher/data/tagtypes/21.json b/ESP32_AP-Flasher/data/tagtypes/21.json new file mode 100644 index 00000000..e380a7f4 --- /dev/null +++ b/ESP32_AP-Flasher/data/tagtypes/21.json @@ -0,0 +1,74 @@ +{ + "name": "ST‐GM29XXF 2.9\"", + "width": 296, + "height": 128, + "rotatebuffer": 1, + "bpp": 1, + "colors": 2, + "colortable": { + "white": [255, 255, 255], + "black": [0, 0, 0] + }, + "shortlut": 0, + "options": ["button", "customlut"], + "contentids": [ 0, 1, 2, 3, 4, 8, 16, 9, 7, 19, 10, 11, 21 ], + "usetemplate": 1, + "template": { + "1": { + "weekday": [148, 10, "fonts/calibrib60"], + "date": [148, 73, "fonts/calibrib50"] + }, + "16": { + "location": [ 5, 5, "fonts/bahnschrift30" ], + "title": [ 247, 11, "glasstown_nbp_tf" ], + "cols": [ 1, 125, 12, "glasstown_nbp_tf" ], + "bars": [ 5, 111, 10 ] + }, + "2": { + "fonts": ["fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120", "fonts/calibrib100"], + "xy": [148, 74] + }, + "4": { + "location": [5, 5, "fonts/bahnschrift30"], + "wind": [280, 5, "fonts/bahnschrift30"], + "temp": [5, 65, "fonts/bahnschrift70"], + "icon": [285, 20, 70, 2], + "dir": [235, -12, 40], + "umbrella": [190, -50, 25] + }, + "8": { + "location": [5, 12, "t0_14b_tf"], + "column": [5, 59], + "day": [30, 18, "fonts/twcondensed20", 41, 108], + "icon": [30, 55, 30], + "wind": [18, 26], + "line": [20, 128] + }, + "9": { + "title": [5, 3, "fonts/bahnschrift20"], + "items": 8, + "line": [5, 34, 13], + "font": "glasstown_nbp_tf" + }, + "10": { + "title": [10, 5, "fonts/bahnschrift20"], + "pos": [149, 25] + }, + "11": { + "title": [5, 2, "fonts/bahnschrift20"], + "date": [290, 2], + "items": 7, + "red": [0, 21, 296, 14], + "line": [5, 32, 15, "t0_14b_tf", 50] + }, + "21": [ + { "text": [ 5, 5, "OpenEpaperLink AP", "bahnschrift20", 1, 0, 0 ] }, + { "text": [ 5, 50, "IP address:", "t0_14b_tf", 1, 0, 0 ] }, + { "text": [ 120, 50, "{ap_ip}", "t0_14b_tf", 1, 0, 0 ] }, + { "text": [ 5, 70, "Channel:", "t0_14b_tf", 1, 0, 0 ] }, + { "text": [ 120, 70, "{ap_ch}", "t0_14b_tf", 1, 0, 0 ] }, + { "text": [ 5, 90, "Tag count:", "t0_14b_tf", 1, 0, 0 ] }, + { "text": [ 120, 90, "{ap_tagcount}", "t0_14b_tf", 1, 0, 0 ] } + ] + } +} diff --git a/ESP32_AP-Flasher/data/www/content_cards.json.gz b/ESP32_AP-Flasher/data/www/content_cards.json.gz index 263bdfb2be2caf4681dd5def77f004a76c6f9caf..982947461dd2cabd8928780eae43614aa105d514 100644 GIT binary patch delta 3191 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 delta 3149 zcmV-T46^go8N3;gJpt>HJ|BPWC^M0H+$D)!?DsBy`t}<_e!)M4tnqm5d$QRpe+il~ z_`NnV)bNP#j852^2gXcC7C4nNO(@Ydq#j9>%t*phM)X`8mi0(ZxiCx-sR+A)3qez& zWuYS0C#UoZ5DUeKkwh`g{{>f5=3a*P@JA$-Vv#|(Obf%M=vRxG&dh&2$F=_CZjg!y zLbj@_L3OoTUG2PC++1L8y>eGS`w!$)(X9UdU;G6Cuh<+pf#h12^=dHB!=n(;^YBDQ zzzzRAk-`$3a${1KXc8-az5HNF?b_#jf={h^(67B))@OLej7*#F@+-Uq@TELyg8dqY znJk1M>lqh?VfrO3ks5#a!*#h$GoGd>1#@yuQy%xpCD35#gk*d&HAG0eb-5HV$ru&0 zDK0!j6v)Zl2#PtdNd<{{Ye8I0g0x%~N{}f{6C(v9vByQmgLuj$fyE;VH+X9{Wde|N zp3*sD-?vo?HH6Y8nN9M91V`fmkQ%0{raua+_kUY&&5aUrLkjzn>H9%`r!u?Ie zZ@dR?Z`s>oZE^G0JMk-5ER{6wV3?&3=|2ykdCqw>A!mqMzL6wwol+#f(CksDECq4YO}u7c7|_(yNv|ePVDf32 z_E6Y#&LR$W^07qhOsb5gA+~-v{+^6rH}#Z^BE^^paTk$JByc#ALcoXNLm?(3+VC8V zMIqt_e8;I@!YoKzSP#9bGU$3r;{_E0N9_U%GgW^m9%$wzmAu8@-PoV?GO*f?c|$$liWrz^H8q`}QFs^Y>)272}@x^gI9 z5jDnRh=0b=bflmv+Um^4qc4Bb7 z8~Fbvgu~d+f$yT3+ECI^!{RJS#GUZ2Kqr z50&Yymbv|l#twfr%knf-vTq6Y=Q{fTw}3B^+9xB%Y_4^f)10XjF0Melu6QM=PJ-=2 zPWIsuY=R{aX%JWod4B;H!MTI`OXBL|3rE%9!tX^TK798BsTFMzeIodKN;!#Q*A^NJxC+XQj2Z`@Oig1pfD266muQ^}6F%4SqCP@3SJ0%(JS*onBL z#I8?1qIS3$n93#g$3POgwLg#_0cy>L;?b4U34DJMPZrNnGE-QXzz6zIx5@JuQ>y)h83Pn|jFC&slZ07gI*cO7 zRLHrkwIXX9)47&bhkOJERkMjT#I2Qlz+x4~R52SDlL^Q>WHMlQi7oWGZ0Q!USg~ct z1>1iLZY4sN2aKwfzJ~uEXfW{YgYNGRI==e|U)}Eh>h?pZ{eoQXgk<6D=pa}-7tUi7CkMqNegaaiyv7;6i5B8`3$F4%t! ziy$`m6Zj6S4@r!`3vV)k3>>yA>=by+u30Mcj0y9EM|`sISk1xdTf)OLHN&XWOWr5R zd>yhxm%!u$%)uvtJptC@CX|gdXMoBdW%yE#ZV`;2(453>OyV_Q{6;So?kZ0-YRL~EjX^~R=~ zjwa2;E_#^oNejI10J%zL5cCEaaB)OFHepriC27*G3&=~AA@CHw(J{c3vO;6*N0QC{ zoB#ZKxEKyrboMyxfh>)VQx=DKb0{3>zMKKtl%!en)Jl~y0_hh4c#T8MeYt;EvC3Af zbEdeAIS$g#iN-VnZQk(wg)R%{SO9`#8??z<%thoHO{owpMbD2XyfD9j$UyUnZAf58 zxG}Q+JrtCl){q_9z?J#+VFoo>IKiztj0e)O8{nh+|`K&d$f;^Ux8T7v#|`P=p)Ar zFOv1{>I*dbWA};|j$g~v*s~;l&J+0$9)saTD8+*66Xzk=3SCPeg1mp*w;{z0E3Ww7*+E*@#_YFHTAbQM-6x3+?3qu_6YV~$^&t(~#A zTiEvS6#Q-6wg;x*@BM#gwg#})axY$#Vt-G~7s_moy>Vq@xL92l@tbd$~M0U535Q?<` nlnOeAFsj~EO6UHlBvuM)FTI +#include "printf.h" +#include "screen.h" +#include "board.h" +#include "flash.h" +#include "uart.h" +#include "spi.h" +#include "cpu.h" +#include "wdt.h" +#include "i2c.h" + +//extern uint8_t __xdata* tempBuffer; +uint8_t __xdata tempBuffer[320]; + +void powerPortsDownForSleep(void) +{ + P0FUNC = 0; + P1FUNC = 0; + P2FUNC = 0; + P0DIR = 0; + P0 = 0; + P0PULL = 0; + P1DIR = 0; + P1 = 2; + P1PULL = 0; + P2DIR = 2; + P2 =1; + P2PULL = 0; +} + +void boardInit(void) +{ + pr("board init\n"); + //set up pins for spi(0.0,0.1,0.2), UART (0.6) + P0FUNC |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 6); + P0DIR = (P0DIR &~ ((1 << 0) | (1 << 1) | (1 << 6))) | (1 << 2); + + //pulls for spi in + P0PULL = (P0PULL &~ ((1 << 0) | (1 << 1) | (1 << 6))) | (1 << 2); + + //setup 1.1(eeprom_nCS), 1.2(eink_BS1), 1.6(flash write protect) 1.7(eink_nCS) + P1FUNC &=~ ((1 << 1) | (1 << 2) | (1 << 5) | (1 << 6) | (1 << 7)); + P1DIR &= ~((1 << 1) | (1 << 2) | (1 << 5) | (1 << 6) | (1 << 7)); + + //setup 2.0(eink_nRST), 2.1(eink_BUSY), 2.2(eink_D/nC) + P2FUNC &= ~((1 << 0) | (1 << 1) | (1 << 2)); + P2DIR = (P2DIR &~ ((1 << 0) | (1 << 2))) | (1 << 1); + + //raise chip select(s) + P1_1 = 1; + P1_7 = 1; + + //BS1 = low + P1_2 = 1; + P1_5 = 0; + P1_6 = 1; + + spiInit(); +} + + +__bit boardGetOwnMac(uint8_t __xdata *mac) +{ + return flashRead(FLASH_INFOPAGE_ADDR + 0x10, mac, 8); +} + +#pragma callee_saves prvUpdateApplierGet +static uint32_t prvUpdateApplierGet(void) __naked +{ + __asm__( + " mov DPTR, #00098$ \n" + " mov A, #00099$ \n" + " clr C \n" + " subb A, DPL \n" + " mov B, A \n" + " ret \n" + + ///actual updater code + "00098$: \n" + + + //copied to last page of flash for updating, called with ints off and eeprom ready to read update + //flashes 63 flash pages, uses xram for buffer. uses combined erase+flash flash op + + " mov _CLKSPEED, #0x21 \n" + " mov _CFGPAGE, #0x04 \n" + " mov R0, #0 \n" + + "00001$: \n" + + //read a page of update + " mov DPTR, #0xe000 \n" + " mov R1, #0x04 \n" + " mov R2, #0x00 \n" + "000010$: \n" + " mov _SPITX, #0x00 \n" + " mov _SPICFG, #0xa0 \n" + "000011$: \n" + " mov A, _SPICFG \n" + " jb A.5, 000011$ \n" + " mov A, _SPIRX \n" + " movx @DPTR, A \n" + " inc DPTR \n" + " djnz R2, 000010$ \n" + " djnz R1, 000010$ \n" + + //flash it + " clr A \n" + " orl _SETTINGS, #0x38 \n" + " mov _FWRTHREE, #0x03 \n" + " mov _FPGNO, R0 \n" + " mov _FWRDSTL, A \n" + " mov _FWRDSTH, A \n" + " mov _FWRLENL, #0xff \n" + " mov _FWRLENH, #0x03 \n" + " mov _FWRSRCL, A \n" + " mov _FWRSRCH, #0xe0 \n" + " orl _TRIGGER, #0x08 \n" + "00050$: \n" + " mov A, _TCON2 \n" + " jnb A.3, 00050$ \n" + " anl _TCON2, #~0x48 \n" + " anl _SETTINGS, #~0x10 \n" + + //go do next page + " inc R0 \n" + " cjne R0, #63, 00001$ \n" + + //done? reset + " mov _WDTCONF, #0x80 \n" + " mov _WDTENA, #0x01 \n" + " mov A, #0xff \n" + " mov _WDTRSTVALH, A \n" + " mov _WDTRSTVALM, A \n" + " mov _WDTRSTVALL, A \n" + "00090$: \n" + " sjmp 00090$ \n" + + "00099$: \n" + ); +} + +void selfUpdate(void) +{ + uint32_t updaterInfo = prvUpdateApplierGet(); + uint8_t __code *src = (uint8_t __code*)updaterInfo; + uint8_t i, len = updaterInfo >> 16; + uint8_t __xdata *dst = tempBuffer; + + for (i = len; i ; i--) + *dst++ = *src++; + + if (!flashWrite(0xfc00, tempBuffer, len, true)) + pr("failed to write updater\n"); + + IEN_EA = 0; //ints off + + __asm__( + " mov dptr, #0xfc00 \n" + " clr a \n" + " jmp @a+dptr \n" + ); +} \ No newline at end of file diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/board.h b/zbs243_shared/board/zbs29_BW_ssd1619/board.h new file mode 100644 index 00000000..4684a303 --- /dev/null +++ b/zbs243_shared/board/zbs29_BW_ssd1619/board.h @@ -0,0 +1,36 @@ +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include + +#define __packed + +#include "../oepl-definitions.h" +#include "spi.h" + +#define eepromByte spiByte +#define eepromPrvSelect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 0; __asm__("nop\nnop\nnop\n"); } while(0) +#define eepromPrvDeselect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 1; __asm__("nop\nnop\nnop\n"); } while(0) + +//eeprom map +#define EEPROM_SETTINGS_AREA_START (0x01000UL) +#define EEPROM_SETTINGS_AREA_LEN (0x03000UL) +#define EEPROM_UPDATA_AREA_START (0x04000UL) +#define EEPROM_UPDATE_AREA_LEN (0x10000UL) +#define EEPROM_IMG_START (0x14000UL) +#define EEPROM_IMG_EACH (0x04000UL) +//till end of eeprom really. do not put anything after - it will be erased at pairing time!!! +#define EEPROM_PROGRESS_BYTES (128) + +#define HAS_EEPROM 1 +#define HAS_SCREEN 1 +#define NFC_TYPE -1 +#define AP_EMULATE_TAG 1 + +//hw types +#define HW_TYPE SOLUM_29_BW_SSD1619 + +#include "../boardCommon.h" + + +#endif diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/make.mk b/zbs243_shared/board/zbs29_BW_ssd1619/make.mk new file mode 100644 index 00000000..d1083181 --- /dev/null +++ b/zbs243_shared/board/zbs29_BW_ssd1619/make.mk @@ -0,0 +1,7 @@ +FLAGS += --code-size 0xfc00 + +SOC = zbs243 + +BARCODE = datamatrix + +# 0xfc00 and not 0x10000 to leave some space for update header and updater in flash \ No newline at end of file diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/old_screen.c b/zbs243_shared/board/zbs29_BW_ssd1619/old_screen.c new file mode 100644 index 00000000..9494884c --- /dev/null +++ b/zbs243_shared/board/zbs29_BW_ssd1619/old_screen.c @@ -0,0 +1 @@ +#include "../ssd1619.c" diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/screen.c b/zbs243_shared/board/zbs29_BW_ssd1619/screen.c new file mode 100644 index 00000000..abc290f3 --- /dev/null +++ b/zbs243_shared/board/zbs29_BW_ssd1619/screen.c @@ -0,0 +1,834 @@ +#include "../ssd1619.h" + +#include +#include + +#include "asmUtil.h" +#include "barcode.h" +#include "board.h" +#include "cpu.h" +#include "font.h" +#include "lut.h" +#include "printf.h" +#include "screen.h" +// #include "settings.h" +#include "sleep.h" +#include "spi.h" +#include "timer.h" +#include "wdt.h" + +#define CMD_DRV_OUTPUT_CTRL 0x01 +#define CMD_SOFT_START_CTRL 0x0C +#define CMD_ENTER_SLEEP 0x10 +#define CMD_DATA_ENTRY_MODE 0x11 +#define CMD_SOFT_RESET 0x12 +#define CMD_SOFT_RESET2 0x13 +#define CMD_SETUP_VOLT_DETECT 0x15 +#define CMD_TEMP_SENSOR_CONTROL 0x18 +#define CMD_ACTIVATION 0x20 +#define CMD_DISP_UPDATE_CTRL 0x21 +#define CMD_DISP_UPDATE_CTRL2 0x22 +#define CMD_WRITE_FB_BW 0x24 +#define CMD_WRITE_FB_RED 0x26 +#define CMD_UNKNOWN_1 0x2B +#define CMD_LOAD_OTP_LUT 0x31 +#define CMD_WRITE_LUT 0x32 +#define CMD_BORDER_WAVEFORM_CTRL 0x3C +#define CMD_WINDOW_X_SIZE 0x44 +#define CMD_WINDOW_Y_SIZE 0x45 +#define CMD_WRITE_PATTERN_RED 0x46 +#define CMD_WRITE_PATTERN_BW 0x47 +#define CMD_XSTART_POS 0x4E +#define CMD_YSTART_POS 0x4F +#define CMD_ANALOG_BLK_CTRL 0x74 +#define CMD_DIGITAL_BLK_CTRL 0x7E +// added for OTA LUT-support +#define CMD_GATE_LEVEL 0x03 +#define CMD_SOURCE_LEVEL 0x04 +#define CMD_DUMMY_PERIOD 0x3A +#define CMD_GATE_LINE_WIDTH 0x3B + +#define SCREEN_CMD_CLOCK_ON 0x80 +#define SCREEN_CMD_CLOCK_OFF 0x01 +#define SCREEN_CMD_ANALOG_ON 0x40 +#define SCREEN_CMD_ANALOG_OFF 0x02 +#define SCREEN_CMD_LATCH_TEMPERATURE_VAL 0x20 +#define SCREEN_CMD_LOAD_LUT 0x10 +#define SCREEN_CMD_USE_MODE_2 0x08 // modified commands 0x10 and 0x04 +#define SCREEN_CMD_REFRESH 0xC7 + +#define commandEnd() \ + do { \ + P1_7 = 1; \ + } while (0) + +#define markCommand() \ + do { \ + P2_2 = 0; \ + } while (0) + +#define markData() \ + do { \ + P2_2 = 1; \ + } while (0) + +extern void dump(uint8_t* __xdata a, uint16_t __xdata l); // remove me when done + +static uint8_t __xdata epdCharSize = 1; // character size, 1 or 2 (doubled) +static bool __xdata directionY = true; // print direction, X or Y (true) +static uint8_t __xdata rbuffer[32]; // used to rotate bits around +static uint16_t __xdata fontCurXpos = 0; // current X value we're working with +static uint16_t __xdata fontCurYpos = 0; // current Y value we're working with +static uint8_t __xdata currentLut = 0; +uint8_t __xdata dispLutSize = 0; // we'll need to expose this in the 'capabilities' flag + +static bool __xdata isInited = false; + +bool __xdata epdGPIOActive = false; + +#define LUT_BUFFER_SIZE 128 +static uint8_t waveformbuffer[LUT_BUFFER_SIZE]; +uint8_t __xdata customLUT[LUT_BUFFER_SIZE] = {0}; + +struct waveform10* __xdata waveform10 = (struct waveform10*)waveformbuffer; // holds the LUT/waveform +struct waveform* __xdata waveform7 = (struct waveform*)waveformbuffer; // holds the LUT/waveform + +#pragma callee_saves epdBusySleep +#pragma callee_saves epdBusyWait +static void epdBusySleep(uint32_t timeout) { + uint8_t tmp_P2FUNC = P2FUNC; + uint8_t tmp_P2DIR = P2DIR; + uint8_t tmp_P2PULL = P2PULL; + uint8_t tmp_P2LVLSEL = P2LVLSEL; + P2FUNC &= 0xfd; + P2DIR |= 2; + P2PULL |= 2; + P2LVLSEL |= 2; + + P2CHSTA &= 0xfd; + P2INTEN |= 2; + P2CHSTA &= 0xfd; + sleepForMsec(timeout); + wdtOn(); + P2CHSTA &= 0xfd; + P2INTEN &= 0xfd; + + P2FUNC = tmp_P2FUNC; + P2DIR = tmp_P2DIR; + P2PULL = tmp_P2PULL; + P2LVLSEL = tmp_P2LVLSEL; + eepromPrvDeselect(); +} +static void epdBusyWait(uint32_t timeout) { + uint32_t __xdata start = timerGet(); + + while (timerGet() - start < timeout) { + if (!P2_1) + return; + } + pr("screen timeout %lu ticks :(\n", timerGet() - start); + while (1) + ; +} +static void commandReadBegin(uint8_t cmd) { + epdSelect(); + markCommand(); + spiByte(cmd); // dump LUT + + P0DIR = (P0DIR & ~(1 << 0)) | (1 << 1); + P0 &= ~(1 << 0); + P0FUNC &= ~((1 << 0) | (1 << 1)); + P2_2 = 1; +} +static void commandReadEnd() { + // set up pins for spi (0.0,0.1,0.2) + P0FUNC |= (1 << 0) | (1 << 1); + epdDeselect(); +} +#pragma callee_saves epdReadByte +static uint8_t epdReadByte() { + uint8_t val = 0, i; + + for (i = 0; i < 8; i++) { + P0_0 = 1; + __asm__("nop\nnop\nnop\nnop\nnop\nnop\n"); + val <<= 1; + if (P0_1) + val++; + P0_0 = 0; + __asm__("nop\nnop\nnop\nnop\nnop\nnop\n"); + } + + return val; +} +static void shortCommand(uint8_t cmd) { + epdSelect(); + markCommand(); + spiTXByte(cmd); + epdDeselect(); +} +static void shortCommand1(uint8_t cmd, uint8_t arg) { + epdSelect(); + markCommand(); + spiTXByte(cmd); + markData(); + spiTXByte(arg); + epdDeselect(); +} +static void shortCommand2(uint8_t cmd, uint8_t arg1, uint8_t arg2) { + epdSelect(); + markCommand(); + spiTXByte(cmd); + markData(); + spiTXByte(arg1); + spiTXByte(arg2); + epdDeselect(); +} +static void commandBegin(uint8_t cmd) { + epdSelect(); + markCommand(); + spiTXByte(cmd); + markData(); +} +static void epdReset() { + timerDelay(TIMER_TICKS_PER_SECOND / 100); + P2_0 = 0; + timerDelay(TIMER_TICKS_PER_SECOND / 100); + P2_0 = 1; + timerDelay(TIMER_TICKS_PER_SECOND / 100); + + shortCommand(CMD_SOFT_RESET); // software reset + timerDelay(TIMER_TICKS_PER_SECOND / 100); + //shortCommand(CMD_SOFT_RESET2); + //timerDelay(TIMER_TICKS_PER_SECOND / 100); +} +void epdConfigGPIO(bool setup) { + // data / _command: 2.2 + // busy 2.1 + // reset 2.0 + // _select 1.7 + // bs1 1.2 + + // GENERIC SPI BUS PINS + // spi.clk 0.0 + // spi.mosi 0.1 + if (epdGPIOActive == setup) return; + if (setup) { + P2DIR |= (1 << 1); // busy as input + P2DIR &= ~((1 << 2) | (1 << 0)); // D/C and Reset as output + P1DIR &= ~((1 << 7) | (1 << 2) | (1 << 5)); // select and bs1 as output + //P1_2 = 0; // select 4-wire SPI / BS1 = low + P1_5 = 0; + P1_7 = 1; // deselect EPD + } else { + P2DIR |= ((1 << 2) | (1 << 0)); // DC and Reset as input + P2 &= ~((1 << 2) | (1 << 0)); + P1DIR |= ((1 << 7) | (1 << 2)); // Select and BS1 as input + P2 &= ~((1 << 7)); + } + epdGPIOActive = setup; +} +void epdEnterSleep() { + P2_0 = 0; + timerDelay(10); + P2_0 = 1; + timerDelay(50); + shortCommand(CMD_SOFT_RESET2); + epdBusyWait(TIMER_TICKS_PER_MS * 15); + shortCommand1(CMD_ENTER_SLEEP, 0x03); + isInited = false; +} +void epdSetup() { + epdReset(); + shortCommand1(CMD_ANALOG_BLK_CTRL, 0x54); + shortCommand1(CMD_DIGITAL_BLK_CTRL, 0x3B); + shortCommand2(CMD_UNKNOWN_1, 0x04, 0x63); + + commandBegin(CMD_SOFT_START_CTRL); + epdSend(0x8f); + epdSend(0x8f); + epdSend(0x8f); + epdSend(0x3f); + commandEnd(); + + commandBegin(CMD_DRV_OUTPUT_CTRL); + epdSend((SCREEN_HEIGHT - 1) & 0xff); + epdSend((SCREEN_HEIGHT - 1) >> 8); + epdSend(0x00); + commandEnd(); + + // shortCommand1(CMD_DATA_ENTRY_MODE, 0x03); + // shortCommand1(CMD_BORDER_WAVEFORM_CTRL, 0xC0); // blurry edges + shortCommand1(CMD_BORDER_WAVEFORM_CTRL, 0x01); + shortCommand1(CMD_TEMP_SENSOR_CONTROL, 0x80); + shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1 (i2C) + // shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB9); // mode 2? + shortCommand(CMD_ACTIVATION); + epdBusyWait(TIMER_TICKS_PER_SECOND); + isInited = true; + currentLut = EPD_LUT_DEFAULT; +} +static uint8_t epdGetStatus() { + uint8_t sta; + commandReadBegin(0x2F); + sta = epdReadByte(); + commandReadEnd(); + return sta; +} +uint16_t epdGetBattery(void) { + uint16_t voltage = 2600; + uint8_t val; + + epdReset(); + + shortCommand1(CMD_DISP_UPDATE_CTRL2, SCREEN_CMD_CLOCK_ON | SCREEN_CMD_ANALOG_ON); + shortCommand(CMD_ACTIVATION); + epdBusyWait(TIMER_TICKS_PER_MS * 1000); + + for (val = 3; val < 8; val++) { + shortCommand1(CMD_SETUP_VOLT_DETECT, val); + epdBusyWait(TIMER_TICKS_PER_MS * 1000); + if (epdGetStatus() & 0x10) { // set if voltage is less than threshold ( == 1.9 + val / 10) + voltage = 1850 + mathPrvMul8x8(val, 100); + break; + } + } + //shortCommand(CMD_SOFT_RESET2); + //epdBusyWait(TIMER_TICKS_PER_MS * 1000); + shortCommand1(CMD_ENTER_SLEEP, 0x03); + return voltage; +} + +void loadFixedTempOTPLUT() { + shortCommand1(0x18, 0x48); // external temp sensor + shortCommand2(0x1A, 0x05, 0x00); // < temp register + shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1 (i2C) + shortCommand(CMD_ACTIVATION); + epdBusyWait(TIMER_TICKS_PER_SECOND); +} +static void writeLut() { + commandBegin(CMD_WRITE_LUT); + for (uint8_t i = 0; i < (dispLutSize * 10); i++) + epdSend(waveformbuffer[i]); + commandEnd(); +} +static void readLut() { + commandReadBegin(0x33); + uint16_t checksum = 0; + uint16_t ident = 0; + uint16_t shortl = 0; + for (uint16_t c = 0; c < LUT_BUFFER_SIZE; c++) { + waveformbuffer[c] = epdReadByte(); + } + commandReadEnd(); +} +static uint8_t getLutSize() { + uint8_t ref = 0; + for (uint8_t c = (LUT_BUFFER_SIZE - 4); c > 16; c--) { + uint8_t check = waveformbuffer[c]; + for (uint8_t d = 1; d < 4; d++) { + if (waveformbuffer[c + d] != check) { + ref = c; + goto end; + } + } + } +end:; + return ref + 1; +} +static void lutGroupDisable(uint8_t group) { + if (dispLutSize == 7) { + memset(&(waveform7->group[group]), 0x00, 5); + } else { + memset(&(waveform10->group[group]), 0x00, 5); + } +} +static void lutGroupSpeedup(uint8_t group, uint8_t speed) { + if (dispLutSize == 7) { + for (uint8_t i = 0; i < 4; i++) { + waveform7->group[group].phaselength[i] = 1 + (waveform7->group[group].phaselength[i] / speed); + } + } else { + for (uint8_t i = 0; i < 4; i++) { + waveform10->group[group].phaselength[i] = 1 + (waveform10->group[group].phaselength[i] / speed); + } + } +} +static void lutGroupRepeat(uint8_t group, uint8_t repeat) { + if (dispLutSize == 7) { + waveform7->group[group].repeat = repeat; + } else { + waveform10->group[group].repeat = repeat; + } +} +static void lutGroupRepeatReduce(uint8_t group, uint8_t factor) { + if (dispLutSize == 7) { + waveform7->group[group].repeat = waveform7->group[group].repeat / factor; + } else { + waveform10->group[group].repeat = waveform10->group[group].repeat / factor; + } +} +void selectLUT(uint8_t lut) { + if (currentLut == lut) { + return; + } + + if (currentLut != EPD_LUT_DEFAULT) { + // load the 'default' LUT for the current temperature in the EPD lut register + shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1? + shortCommand(CMD_ACTIVATION); + epdBusyWait(TIMER_TICKS_PER_SECOND); + } + + currentLut = lut; + + // if we're going to be using the default LUT, we're done here. + if (lut == EPD_LUT_DEFAULT) { + return; + } + + // download the current LUT from the waveform buffer + readLut(); + + if (dispLutSize == 0) { + dispLutSize = getLutSize(); + dispLutSize /= 10; + pr("lut size = %d\n", dispLutSize); +#ifdef PRINT_LUT + dump(waveformbuffer, LUT_BUFFER_SIZE); +#endif + memcpy(customLUT, waveformbuffer, dispLutSize * 10); + } + + switch (lut) { + default: + case EPD_LUT_NO_REPEATS: + lutGroupDisable(LUTGROUP_NEGATIVE); + lutGroupDisable(LUTGROUP_FASTBLINK); + lutGroupRepeat(LUTGROUP_SLOWBLINK, 0); + lutGroupSpeedup(LUTGROUP_SET, 2); + lutGroupSpeedup(LUTGROUP_IMPROVE_SHARPNESS, 2); + lutGroupRepeatReduce(LUTGROUP_IMPROVE_SHARPNESS, 2); + lutGroupSpeedup(LUTGROUP_IMPROVE_REDS, 2); + lutGroupRepeatReduce(LUTGROUP_IMPROVE_REDS, 2); + lutGroupDisable(LUTGROUP_UNUSED); + break; + case EPD_LUT_FAST_NO_REDS: + lutGroupDisable(LUTGROUP_NEGATIVE); + lutGroupDisable(LUTGROUP_FASTBLINK); + lutGroupDisable(LUTGROUP_SLOWBLINK); + lutGroupSpeedup(LUTGROUP_SET, 2); + lutGroupDisable(LUTGROUP_IMPROVE_REDS); + lutGroupDisable(LUTGROUP_IMPROVE_SHARPNESS); + lutGroupDisable(LUTGROUP_UNUSED); + break; + case EPD_LUT_FAST: + lutGroupDisable(LUTGROUP_NEGATIVE); + lutGroupDisable(LUTGROUP_FASTBLINK); + lutGroupDisable(LUTGROUP_SLOWBLINK); + lutGroupRepeat(LUTGROUP_SET, 1); + lutGroupSpeedup(LUTGROUP_SET, 2); + lutGroupDisable(LUTGROUP_IMPROVE_SHARPNESS); + lutGroupDisable(LUTGROUP_IMPROVE_REDS); + lutGroupDisable(LUTGROUP_UNUSED); + break; + } + + // Handling if we received an OTA LUT + if (lut == EPD_LUT_OTA) { + memcpy(waveformbuffer, customLUT, dispLutSize * 10); + writeLut(); + shortCommand1(CMD_GATE_LEVEL, customLUT[70]); + commandBegin(CMD_SOURCE_LEVEL); + epdSend(customLUT[71]); + epdSend(customLUT[72]); + epdSend(customLUT[73]); + commandEnd(); + shortCommand1(CMD_DUMMY_PERIOD, customLUT[74]); + shortCommand1(CMD_GATE_LINE_WIDTH, customLUT[75]); + currentLut = lut; + return; + } + + if (dispLutSize == 10) { + lutGroupDisable(LUTGROUP_UNUSED); + lutGroupDisable(LUTGROUP_UNKNOWN); + lutGroupDisable(LUTGROUP_UNUSED3); + lutGroupDisable(LUTGROUP_UNUSED4); + } + writeLut(); +} + +void setWindowX(uint16_t start, uint16_t end) { + shortCommand2(CMD_WINDOW_X_SIZE, start / 8, end / 8 - 1); +} +void setWindowY(uint16_t start, uint16_t end) { + commandBegin(CMD_WINDOW_Y_SIZE); + epdSend((start)&0xff); + epdSend((start) >> 8); + epdSend((end - 1) & 0xff); + epdSend((end - 1) >> 8); + commandEnd(); +} +void setPosXY(uint16_t x, uint16_t y) { + shortCommand1(CMD_XSTART_POS, (uint8_t)(x / 8)); + commandBegin(CMD_YSTART_POS); + epdSend((y)&0xff); + epdSend((y) >> 8); + commandEnd(); +} +void setColorMode(uint8_t red, uint8_t bw) { + shortCommand1(CMD_DISP_UPDATE_CTRL, (red << 4) | bw); +} +void fillWindowWithPattern(bool color) { + if (color == EPD_COLOR_RED) { + shortCommand1(CMD_WRITE_PATTERN_RED, 0x00); + } else { + shortCommand1(CMD_WRITE_PATTERN_BW, 0x00); + } +} +void clearWindow(bool color) { + if (color == EPD_COLOR_RED) { + shortCommand1(CMD_WRITE_PATTERN_RED, 0x66); + } else { + shortCommand1(CMD_WRITE_PATTERN_BW, 0x66); + } +} +void clearScreen() { + setWindowX(0, SCREEN_WIDTH); + setWindowY(0, SCREEN_HEIGHT); + setPosXY(0, 0); + shortCommand1(CMD_DATA_ENTRY_MODE, 3); // was 3 + shortCommand1(CMD_WRITE_PATTERN_BW, 0x66); + epdBusyWait(TIMER_TICKS_PER_MS * 100); + shortCommand1(CMD_WRITE_PATTERN_RED, 0x66); + epdBusyWait(TIMER_TICKS_PER_MS * 100); +} +void draw() { + shortCommand1(0x22, 0xCF); + // shortCommand1(0x22, SCREEN_CMD_REFRESH); + shortCommand(0x20); + epdBusyWait(TIMER_TICKS_PER_SECOND * 120); +} +void drawNoWait() { + shortCommand1(0x22, 0xCF); + // shortCommand1(0x22, SCREEN_CMD_REFRESH); + shortCommand(0x20); +} +void drawWithSleep() { + shortCommand1(0x22, 0xCF); + // shortCommand1(0x22, SCREEN_CMD_REFRESH); + shortCommand(0x20); + uint8_t tmp_P2FUNC = P2FUNC; + uint8_t tmp_P2DIR = P2DIR; + uint8_t tmp_P2PULL = P2PULL; + uint8_t tmp_P2LVLSEL = P2LVLSEL; + P2FUNC &= 0xfd; + P2DIR |= 2; + P2PULL |= 2; + P2LVLSEL |= 2; + + P2CHSTA &= 0xfd; + P2INTEN |= 2; + P2CHSTA &= 0xfd; + sleepForMsec(TIMER_TICKS_PER_SECOND * 120); + wdtOn(); + P2CHSTA &= 0xfd; + P2INTEN &= 0xfd; + + P2FUNC = tmp_P2FUNC; + P2DIR = tmp_P2DIR; + P2PULL = tmp_P2PULL; + P2LVLSEL = tmp_P2LVLSEL; + eepromPrvDeselect(); +} +void epdWaitRdy() { + epdBusyWait(TIMER_TICKS_PER_SECOND * 120); +} +void drawLineHorizontal(bool color, uint16_t x1, uint16_t x2, uint16_t y) { + setWindowX(x1, x2); + setWindowY(y, y + 1); + if (color) { + shortCommand1(CMD_WRITE_PATTERN_RED, 0xE6); + } else { + shortCommand1(CMD_WRITE_PATTERN_BW, 0xE6); + } + epdBusyWait(TIMER_TICKS_PER_MS * 100); +} +void drawLineVertical(bool color, uint16_t x, uint16_t y1, uint16_t y2) { + setWindowY(y1, y2); + setWindowX(x, x + 8); + shortCommand1(CMD_DATA_ENTRY_MODE, 3); + setPosXY(x, y1); + if (color) { + commandBegin(CMD_WRITE_FB_RED); + } else { + commandBegin(CMD_WRITE_FB_BW); + } + uint8_t __xdata c = 0x80; + c >>= (x % 8); + for (; y1 < y2; y1++) { + epdSend(c); + } + commandEnd(); +} +void beginFullscreenImage() { + setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT); + setWindowX(0, SCREEN_WIDTH); + setWindowY(0, SCREEN_HEIGHT); + shortCommand1(CMD_DATA_ENTRY_MODE, 3); + setPosXY(0, 0); +} +void beginWriteFramebuffer(bool color) { + if (color == EPD_COLOR_RED) { + commandBegin(CMD_WRITE_FB_RED); + } else { + commandBegin(CMD_WRITE_FB_BW); + } + epdDeselect(); +} +void endWriteFramebuffer() { + commandEnd(); +} +void loadRawBitmap(uint8_t* bmp, uint16_t x, uint16_t y, bool color) { + uint16_t xsize = bmp[0] / 8; + if (bmp[0] % 8) xsize++; + uint16_t size = xsize * bmp[1]; + setWindowX(x, x + (xsize * 8)); + setWindowY(y, bmp[1] + y); + setPosXY(x, y); + shortCommand1(CMD_DATA_ENTRY_MODE, 3); + if (color) { + commandBegin(CMD_WRITE_FB_RED); + } else { + commandBegin(CMD_WRITE_FB_BW); + } + bmp += 2; + while (size--) { + epdSend(*(bmp++)); + } + commandEnd(); +} +void printBarcode(const uint8_t* string, uint16_t x, uint16_t y) { + setWindowY(y, 1); + setWindowX(x, x + 8); + setPosXY(x, y); + shortCommand1(CMD_DATA_ENTRY_MODE, 1); + commandBegin(CMD_WRITE_FB_BW); + struct BarcodeInfo __xdata bci = { + .str = string, + }; + while (!barcodeIsDone(&bci)) { + if (barcodeNextBar(&bci)) { + epdSend(0xFF); + } else { + epdSend(0x00); + } + } + commandEnd(); +} +// stuff for printing text +static void pushXFontBytesToEPD(uint8_t byte1, uint8_t byte2) { + if (epdCharSize == 1) { + uint8_t offset = 7 - (fontCurXpos % 8); + for (uint8_t c = 0; c < 8; c++) { + if (byte2 & (1 << (7 - c))) rbuffer[c] |= (1 << offset); + } + for (uint8_t c = 0; c < 8; c++) { + if (byte1 & (1 << (7 - c))) rbuffer[8 + c] |= (1 << offset); + } + fontCurXpos++; + } else { + uint8_t offset = 6 - (fontCurXpos % 8); + // double font size + for (uint8_t c = 0; c < 8; c++) { + if (byte2 & (1 << (7 - c))) { + rbuffer[c * 2] |= (3 << offset); + rbuffer[(c * 2) + 1] |= (3 << offset); + } + } + for (uint8_t c = 0; c < 8; c++) { + if (byte1 & (1 << (7 - c))) { + rbuffer[(c * 2) + 16] |= (3 << offset); + rbuffer[(c * 2) + 17] |= (3 << offset); + } + } + fontCurXpos += 2; + } + if (fontCurXpos % 8 == 0) { + // next byte, flush current byte to EPD + for (uint8_t i = 0; i < (16 * epdCharSize); i++) { + epdSend(rbuffer[i]); + } + memset(rbuffer, 0, 32); + } +} +static void bufferByteShift(uint8_t byte) { + /* + rbuffer[0] = 0; // previous value + rbuffer[1] = y%8; // offset + rbuffer[2] = 0; // current byte counter; + rbuffer[3] = 1+(epdCharsize*2); + */ + + if (rbuffer[1] == 0) { + epdSend(byte); + } else { + uint8_t offset = rbuffer[1]; + rbuffer[0] |= (byte >> offset); + epdSend(rbuffer[0]); + // epdSend(byte); + rbuffer[0] = (byte << (8 - offset)); + rbuffer[2]++; + if (rbuffer[2] == rbuffer[3]) { + epdSend(rbuffer[0]); + rbuffer[0] = 0; + rbuffer[2] = 0; + } + } +} +static void pushYFontBytesToEPD(uint8_t byte1, uint8_t byte2) { + if (epdCharSize == 2) { + for (uint8_t j = 0; j < 2; j++) { + uint8_t c = 0; + for (uint8_t i = 7; i != 255; i--) { + if (byte1 & (1 << i)) c |= (0x03 << ((i % 4) * 2)); + if ((i % 4) == 0) { + bufferByteShift(c); + c = 0; + } + } + for (uint8_t i = 7; i != 255; i--) { + if (byte2 & (1 << i)) c |= (0x03 << ((i % 4) * 2)); + if ((i % 4) == 0) { + bufferByteShift(c); + c = 0; + } + } + } + } else { + bufferByteShift(byte1); + bufferByteShift(byte2); + } +} +void writeCharEPD(uint8_t c) { + // Writes a single character to the framebuffer + bool empty = true; + for (uint8_t i = 0; i < 20; i++) { + if (font[c][i]) empty = false; + } + if (empty) { + for (uint8_t i = 0; i < 8; i++) { + if (directionY) { + pushYFontBytesToEPD(0x00, 0x00); + } else { + pushXFontBytesToEPD(0x00, 0x00); + } + } + return; + } + + uint8_t begin = 0; + while (font[c][begin] == 0x00 && font[c][begin + 1] == 0x00) { + begin += 2; + } + + uint8_t end = 20; + while (font[c][end - 1] == 0x00 && font[c][end - 2] == 0x00) { + end -= 2; + } + + for (uint8_t pos = begin; pos < end; pos += 2) { + if (directionY) { + pushYFontBytesToEPD(font[c][pos + 1], font[c][pos]); + } else { + pushXFontBytesToEPD(font[c][pos], font[c][pos + 1]); + } + } + + // spacing between characters + if (directionY) { + pushYFontBytesToEPD(0x00, 0x00); + } else { + pushXFontBytesToEPD(0x00, 0x00); + } +} + +// Print text to the EPD. Origin is top-left +void epdPrintBegin(uint16_t x, uint16_t y, bool direction, bool fontsize, bool color) { + directionY = direction; + epdCharSize = 1 + fontsize; + if (directionY) { + uint8_t extra = 0; + + // provisions for dealing with font in Y direction, byte-unaligned + if (x % 8) { + extra = 8; + rbuffer[0] = 0; // previous value + rbuffer[1] = x % 8; // offset + rbuffer[2] = 0; // current byte counter; + rbuffer[3] = (epdCharSize * 2); + } else { + rbuffer[1] = 0; + } + + setWindowY(y, 1); + if (epdCharSize == 2) { + setWindowX(x, x + 32 + extra); + setPosXY(x, y); + } else { + setWindowX(x, x + 16 + extra); + setPosXY(x, y); + } + shortCommand1(CMD_DATA_ENTRY_MODE, 1); // was 3 + } else { + if (epdCharSize == 2) { + x /= 2; + x *= 2; + setWindowY(y, y + 32); + } else { + setWindowY(y, y + 16); + } + setPosXY(x, y); + fontCurXpos = x; + setWindowX(x, SCREEN_WIDTH); + shortCommand1(CMD_DATA_ENTRY_MODE, 7); + memset(rbuffer, 0, 32); + } + + if (color) { + commandBegin(CMD_WRITE_FB_RED); + } else { + commandBegin(CMD_WRITE_FB_BW); + } +} +void epdPrintEnd() { + if (!directionY && ((fontCurXpos % 8) != 0)) { + for (uint8_t i = 0; i < (16 * epdCharSize); i++) { + epdSend(rbuffer[i]); + } + } + commandEnd(); +} + +extern uint8_t __xdata blockXferBuffer[]; + +void readRam() { + setWindowY(296, 0); + setWindowX(0, 8); + setPosXY(0, 296); + shortCommand1(CMD_DATA_ENTRY_MODE, 1); // was 3 + shortCommand1(0x41, 0x00); + commandReadBegin(0x27); + epdReadByte(); + + for (uint16_t c = 0; c < 293; c++) { + blockXferBuffer[c] = epdReadByte() | 0x10; + } + commandReadEnd(); + commandBegin(CMD_WRITE_FB_BW); + for (uint16_t c = 0; c < 296; c++) { + epdSend(blockXferBuffer[c]); + } + commandEnd(); +} diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/screen.h b/zbs243_shared/board/zbs29_BW_ssd1619/screen.h new file mode 100644 index 00000000..e724816e --- /dev/null +++ b/zbs243_shared/board/zbs29_BW_ssd1619/screen.h @@ -0,0 +1,26 @@ +#ifndef _SCREEN_H_ +#define _SCREEN_H_ + +#include +#include +#include "../ssd1619.h" + + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 296 + +#define SCREEN_NUM_GREYS 5 +#define SCREEN_FIRST_GREY_IDX 0 +#define SCREEN_EXTRA_COLOR_INDEX -1 //set to negative if nonexistent +#define SCREEN_TX_BPP 4 //in transit + +#define SCREEN_WIDTH_MM 29 +#define SCREEN_HEIGHT_MM 67 + +#define SCREEN_BYTE_FILL 0x44 //white + +#define SCREEN_TYPE TagScreenEink_BW_1bpp + +#define SCREEN_DATA_PASSES 2 + +#endif