From d84c5628cff672689a9ffab2e6ac84333af7a0da Mon Sep 17 00:00:00 2001 From: James Bowman Date: Sun, 10 Feb 2019 06:46:36 -0800 Subject: [PATCH] Firmware initial import --- firmware/README.md | 7 + firmware/assets/IBMPlexSans-SemiBold.otf | Bin 0 -> 106020 bytes firmware/assets/arrow.png | Bin 0 -> 185 bytes firmware/assets/dot.png | Bin 0 -> 86 bytes firmware/assets/go | 3 + firmware/assets/hex4x5.png | Bin 0 -> 242 bytes firmware/assets/label-scl.png | Bin 0 -> 243 bytes firmware/assets/label-sda.png | Bin 0 -> 238 bytes firmware/assets/larrow.png | Bin 0 -> 94 bytes firmware/assets/mkfont.py | 131 +++ firmware/assets/pf_ronda_seven_bold.ttf | Bin 0 -> 72864 bytes firmware/assets/plasma.png | Bin 0 -> 413 bytes firmware/assets/symbol-b.png | Bin 0 -> 184 bytes firmware/assets/symbol-p.png | Bin 0 -> 185 bytes firmware/assets/symbol-s.png | Bin 0 -> 191 bytes firmware/capture.fs | 259 ++++++ firmware/font.fs | 131 +++ firmware/fontsize.fs | 1 + firmware/main.fs | 1010 ++++++++++++++++++++++ firmware/st7735.fs | 698 +++++++++++++++ 20 files changed, 2240 insertions(+) create mode 100644 firmware/README.md create mode 100644 firmware/assets/IBMPlexSans-SemiBold.otf create mode 100644 firmware/assets/arrow.png create mode 100644 firmware/assets/dot.png create mode 100755 firmware/assets/go create mode 100644 firmware/assets/hex4x5.png create mode 100644 firmware/assets/label-scl.png create mode 100644 firmware/assets/label-sda.png create mode 100644 firmware/assets/larrow.png create mode 100755 firmware/assets/mkfont.py create mode 100644 firmware/assets/pf_ronda_seven_bold.ttf create mode 100644 firmware/assets/plasma.png create mode 100644 firmware/assets/symbol-b.png create mode 100644 firmware/assets/symbol-p.png create mode 100644 firmware/assets/symbol-s.png create mode 100644 firmware/capture.fs create mode 100644 firmware/font.fs create mode 100644 firmware/fontsize.fs create mode 100644 firmware/main.fs create mode 100644 firmware/st7735.fs diff --git a/firmware/README.md b/firmware/README.md new file mode 100644 index 0000000..3b46f9b --- /dev/null +++ b/firmware/README.md @@ -0,0 +1,7 @@ +This is the firmware for the SPIDriver. +It uses the [MyForth](http://www.kiblerelectronics.com/myf/myf.shtml) compiler written by +Charley Shattuck and Bob Nash. + +To compile the fonts go into ``st7735s`` +and unpack the [IBM Plex OpenType](https://github.com/IBM/plex/releases) font set. +then run ``mkfont.py``. This builds the font sources in ``fonts.fs`` diff --git a/firmware/assets/IBMPlexSans-SemiBold.otf b/firmware/assets/IBMPlexSans-SemiBold.otf new file mode 100644 index 0000000000000000000000000000000000000000..c0a3f534609baaeea7e229140cedbee18b6242e8 GIT binary patch literal 106020 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
    *N+nku}@PIwO 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 literal 0 HcmV?d00001 diff --git a/firmware/assets/arrow.png b/firmware/assets/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..4c106c5193ec73e17a53c44be223596e4649e735 GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQk(@Ik;M!Q+`=Ht$S`Y;1W=H@ z#M9T6{ShYzqrUVp_k)Z;A;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB4bY%#}JO|tOp$h84Ng>H@q`b{&CPagw5YhVX^nSGdp(A^nCSBnUkU6jLOfl?W;W8 bSWfa!sFzi}IU^|^sGY&n)z4*}Q$iB}BtA2H literal 0 HcmV?d00001 diff --git a/firmware/assets/dot.png b/firmware/assets/dot.png new file mode 100644 index 0000000000000000000000000000000000000000..bf63333a555b923a4ec6fafa0db65d57355854df GIT binary patch literal 86 zcmeAS@N?(olHy`uVBq!ia0vp^EFjDQBp7;T9b5#YWIbIRLn`JZ8{FJ-yf^Xk{{PQs j?2wS$A#vwiY(E=A&|mh$_6lddfi!r!`njxgN@xNA#kCtS literal 0 HcmV?d00001 diff --git a/firmware/assets/go b/firmware/assets/go new file mode 100755 index 0000000..1effea9 --- /dev/null +++ b/firmware/assets/go @@ -0,0 +1,3 @@ +set -e + +python2 mkfont.py diff --git a/firmware/assets/hex4x5.png b/firmware/assets/hex4x5.png new file mode 100644 index 0000000000000000000000000000000000000000..2e6771621caa6a2461f442f1a5d0e25d965a0ffd GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^0YJ>k0VEi{Tbh^xDb50q$YKTtZeb8+WSBKa0w~B{ z;_2(k{)m}NOh@w9mT87SA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zq8v{b#}J9jtrHyim<>6Y(|;TP_&6zHrWh{Ra9{KzkWHUHx3vIVCg!093_My#N3J literal 0 HcmV?d00001 diff --git a/firmware/assets/label-scl.png b/firmware/assets/label-scl.png new file mode 100644 index 0000000000000000000000000000000000000000..c6b30e02373468400d7b8a03d00fa7698e8bf595 GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~c!3HEhl+{lMQfx`y?k+$Y2!1;6t_M<_1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPd4=0nT^u#ZoCO{#{64!_l=ltB<)VvY~=c3falGGH1 z^30M91$R&1fbd2>aiF4TPZ!4!jq^(HLfT|IKYxC1zc6W?IDaVx3xu zWd)-Q?+zp3QZ|S5nVbivUkvE&U|q#y_UfD%d+67oXc05t`Q|rP{aiF3wPZ!4!jq_V4267!R;BbyVt+)95{`hak-F)25MvfQSRT$e< z3Imuegl43*ExGD&^DIw8jkr$V*Ci2iBhSb(o7~+So~N~3=TG--y@OLl@9y2P_ojaQ a4#tKHh4o)*r_2W0z~JfX=d#Wzp$P!jl1k

    > 8)), + 16 * (0xf & (rgb >> 4)), + 16 * (0xf & (rgb >> 0))) + +font2 = ImageFont.truetype("IBMPlexSans-SemiBold.otf", 13) +fontSP = ImageFont.truetype("pf_ronda_seven_bold.ttf", 8) + +def pad2(s): + if len(s) % 2: + s.append(s[0]) + return s + +def rfont2(c): + im = Image.new("L", (128, 160)) + dr = ImageDraw.Draw(im) + dr.text((10,40), c, font=font2, fill=255) + # im.save("out.png") + extents = im.getbbox() + assert 10 <= extents[0] + assert 45 <= extents[1] + if c in "0123456789": + extents = (0, 0, 10 + 8, 45 + 9) + im = im.crop((10, 45) + extents[2:]) + (w,h) = im.size + nyb = pad2((np.array(im).astype(int).flatten() * 15 / 255).tolist()) + return [w,h] + nyb + +def rf(c, font): + im = Image.new("L", (128, 160)) + dr = ImageDraw.Draw(im) + dr.text((10,40), c, font=font, fill=255) + # im.save("out.png") + extents = im.getbbox() + im = im.crop(extents) + (w,h) = im.size + nyb = (np.array(im).astype(int).flatten() * 15 / 255).tolist() + return [w,h] + nyb + +fs = open("../font.fs", "wt") +fb = 0 + +if __name__ == '__main__': + fs.write('here constant tplan\n') + fs.write('%d , %d , $%x%x , $%x , ," %s"\n' % (60, 0, 0xf, 0xf, 0xf, "V")) + fb += 5 + len("V") + # fs.write("[ %d ]\n" % fb) + fs.write('%d , %d , $%x%x , $%x , ," %s"\n' % (108, 0, 0xf, 0xf, 0xf, "mA")) + fb += 5 + len("mA") + # fs.write("[ %d ]\n" % fb) + + fs.write('0 ,\n') + fb += 1 + # im.save("out.png") + +uniq = "".join(sorted(set("0123456789.mAVDMCS"))) +f2 = sum([rfont2(c) for c in uniq], []) +print "font2 %s takes %d bytes" % (uniq, len(f2) / 2) +def nybbles(nn): + s = len(nn) + assert s % 2 == 0 + b = ["$%x%x ," % tuple(nn[i:i+2]) for i in range(0, s, 2)] + return b +fs.write('here constant font\n') +for c in uniq: + bb = nybbles(rfont2(c)) + print >>fs, "'%s' , " % c, " ".join(bb) + fb += 1 + len(bb) + +# See http://angband.pl/font/tinyfont.html +fs.write("\nhere constant micro\n") +if 1: + tiny = Image.open("hex4x5.png").convert("L") + for i in range(16): + x = 5 * i + im = tiny.crop((x, 0, x + 4, 5)) + rim = im.transpose(Image.ROTATE_90) + ch = ((np.array(rim)).flatten() * 15.99 / 255).astype(np.uint8).tolist() + fs.write(" ".join(nybbles(ch)) + "\n") + fb += len(ch) / 2 + +if 1: + # Image.open("arrow.png").transpose(Image.FLIP_LEFT_RIGHT).save("larrow.png") + for n in ("symbol-s", "symbol-p", "symbol-b", "arrow", "larrow", "dot", "label-sda", "label-scl"): + im = Image.open(n + ".png").convert("L") + if n.startswith("label-"): + im = im.point([0] + 254 * [64] + [255]) + rim = im.transpose(Image.ROTATE_90) + (w,h) = rim.size + ch = [w,h] + pad2(((np.array(rim)).flatten() * 15.99 / 255).astype(np.uint8).tolist()) + fs.write("here constant %s \n" % n) + fs.write(" ".join(nybbles(ch)) + "\n") + fb += len(ch) / 2 + +if 1: + w = 72 + gpng = "plasma.png" + grad = Image.open(gpng).convert("RGB").resize((w, 1), Image.BILINEAR).load() + fs.write("\nHERE constant grad\n") + for x in range(w): + (r,g,b) = grad[x,0] + r = (r * 15) // 255 + g = (g * 15) // 255 + b = (b * 15) // 255 + fs.write('$%x%x , $%x ,\n' % (r, g, b)) + fb += 2 * w + +fs.close() + +fs = open("../fontsize.fs", "wt") +fs.write("&%d constant FONTDATA_SIZE\n" % fb) +fs.close() diff --git a/firmware/assets/pf_ronda_seven_bold.ttf b/firmware/assets/pf_ronda_seven_bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3d3ae5fce8b1c6419893c5f87d37b0dff2ddbbf6 GIT binary patch literal 72864 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 literal 0 HcmV?d00001 diff --git a/firmware/assets/plasma.png b/firmware/assets/plasma.png new file mode 100644 index 0000000000000000000000000000000000000000..51e4ebbfe98750ea150921956e12998a685cb93f GIT binary patch literal 413 zcmV;O0b>4%P)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{009L_L_t(I%axQ%a>FnTLm%kO zbklS9*kKp`h!T;+*~tI|fRac0{rO9P2V%VmsC+7CF9S;-x zW^}tp?L_f4SdMk&!eB~G{MvWzqe(aRReofxzJ*igLg#uPAXN5f@lakK!)su19IgZr zr!Hr`^1?2EIgdoqvA6m~7eT{Dc3Ms`_m$G-cm3-g7K=X1M~|CXXMdN)t!{j&&+8|# zeQwN*nGbW{ilSG(@t}DSpSxksjhlMRIn!q9zC`w!qk8a~Gj~AsUuURZrib^S>#j$| zbUgT+9;@sR5|C5oi5Z<1zHyp4L57;H?Va?c6M&v%JOTIzPe-2#7!+`P00000NkvXX Hu0mjf69=fl literal 0 HcmV?d00001 diff --git a/firmware/assets/symbol-b.png b/firmware/assets/symbol-b.png new file mode 100644 index 0000000000000000000000000000000000000000..8a2d45a3e262d2a2e36ea9c590fb5e43002f5019 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CN0VEjmq>Y<_RK2H*V@SoEy0*|tOfzv7(#`&UVt{b?`!H$OQj zp@CU)*>jF_+RM(`OG-$9lqUSx-RhWg*uG<4z55hNiID5HAjAI4SN8%HNVH7;Zc|rg c5#QCw$dJ+Pz?pN6^*zW*p00i_>zopr00K2jp#T5? literal 0 HcmV?d00001 diff --git a/firmware/assets/symbol-p.png b/firmware/assets/symbol-p.png new file mode 100644 index 0000000000000000000000000000000000000000..9a3528a4cd7ccdbca315c4c0bfed6f3c8f52278f GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CN0VEjmq>Y<_RD-9BV@Smu)8iYt7#SHF9{%q; zn-I`;dO=CI(agQAHy<(DG>C7E^o=o`*LkxnHGGPZK-}V*|ApasOe_-|m2DVTY<_RGX)ZV@Smu)iVpZ4h2ZGJ^ViZ zVuFMOADg3?@9BH3Hy^Q9Fy8ApyK4zE`?Q%Rvu%>ZbJ$!RQ?2JGe&y_UtTvI`ruX_2 z194wIH7Ut#Z`Id|dlZGjW~EOZP(wvjT>kHw&Gwer4kuHOoMZ*L%+uA+Wt~$(699AxMn(Vt literal 0 HcmV?d00001 diff --git a/firmware/capture.fs b/firmware/capture.fs new file mode 100644 index 0000000..80e06b4 --- /dev/null +++ b/firmware/capture.fs @@ -0,0 +1,259 @@ + +here constant CAPTURE-START + +[ +: fake ( a - u ) + dup 0= ] here [ and or ; +] + +:m :: ( - ) + [ >in @ label >in ! + create ] here [ , hide + does> @ ] m; + +\ jump if bit is 0 or 1 ( addr bit ) +:m j1 ( addr bit ) [ swap fake swap ] 0=until. m; +:m j0 ( addr bit ) [ swap fake swap ] until. m; +:m j ( addr ) fake again m; + +:m tx SBUF0 (#!) clra TMR3H (#!) m; +:m SDA0 ( a ) SDA j0 m; +:m SDA1 ( a ) SDA j1 m; + +fwd L00.0 +fwd L00.1 +fwd L00.2 +fwd H00.0 +fwd H00.1 +fwd H00.2 +fwd HS +fwd HP +fwd LP +fwd LS + +:m escape RI0 if. RI0 clr ; then m; + +:: L11 begin LS SDA0 SCL 0=until. +:: Lidle begin begin SDA until. SCL until. + L11 j + +:: L10.0 3 .t set + begin L00.0 SDA0 SCL until. begin LS SDA0 SCL 0=until. 2 .t set +:: L10.1 begin L00.1 SDA0 SCL until. begin LS SDA0 SCL 0=until. 1 .t set +:: L10.2 begin L00.2 SDA0 SCL until. begin LS SDA0 SCL 0=until. 0 .t set + tx +:: H10.0 7 .t set +:: klak begin H00.0 SDA0 SCL until. begin HS SDA0 SCL 0=until. 6 .t set +:: H10.1 begin H00.1 SDA0 SCL until. begin HS SDA0 SCL 0=until. 5 .t set +:: H10.2 begin H00.2 SDA0 SCL until. begin HS SDA0 SCL 0=until. 4 .t set + L10.0 j + +:: LS $f0 # and $01 # ior tx ( start ) +:: LS2 begin HP SDA1 SCL 0=until. + H00.0 j + +:: HS $10 (#) ( start ) +:: HS2 begin LP SDA1 SCL 0=until. +:: L00.0 3 .t set + begin L10.0 SDA1 SCL until. begin LP SDA1 SCL 0=until. +:: L00.1 begin L10.1 SDA1 SCL until. begin LP SDA1 SCL 0=until. +:: L00.2 begin L10.2 SDA1 SCL until. begin LP SDA1 SCL 0=until. + tx +:: H00.0 7 .t set + begin H10.0 SDA1 SCL until. begin HP SDA1 SCL 0=until. +:: H00.1 begin H10.1 SDA1 SCL until. begin HP SDA1 SCL 0=until. +:: H00.2 begin H10.2 SDA1 SCL until. begin HP SDA1 SCL 0=until. + L00.0 j + +:: HP $20 (#) + \ L11 j + begin SDA 0=until. + escape + HS2 j + +: (warm) +:: H11 begin HS SDA0 SCL 0=until. +:: Hidle begin begin SDA until. SCL until. + H11 j + +:: LP $f0 # and $02 # ior tx + \ H11 j + begin SDA 0=until. + escape + LS2 j + +: /timer3 + $80 # EIE1 ior! \ Timer 3 interrupt enable +; +: timer3\ + $80 ~# EIE1 and! \ Timer 3 interrupt disable +; + +:m timer3 + SBUF0 (#!) clra + $7f # TMR3CN and! + RI0 if. + RI0 clr + [ sp dec ] + [ sp dec ] + then + [ reti ] m; + +: capture + [ IE push ] + [ ET2 clr ] \ Timer 2 interrupt disable + [ ES0 clr ] \ UART interrupt disable + \i2chw + + [ clra ] + [ FL1 set ] t3+ + (warm) + t3- [ FL1 clr ] + /i2chw + [ IE pop ] + ; + +\ This code all runs in register bank 1: +\ 0 scratch for heatmap +\ 1 log +\ 2 +\ 3 prev cmd +\ 4 constant 72, for heatmap +\ 5 +\ 6 +\ 7 caller acc save +\ +\ FL0 set means this is an address byte + +fwd M00.0 +fwd M10.0 +fwd M10.1 +fwd M10.2 +fwd M10.3 +fwd M10.4 +fwd M10.5 +fwd M10.6 +fwd M10.7 +fwd M10.8 +fwd Mt +fwd MP + +:m (l!) $f3 , m; + +:m (log!) + $f3 , a+ m; + +:m wrap + $7f # 9 and! m; + +:m heat + \ byte is in t + FL0 if. + setc 2/' + 0 (#!) + 4 (#@) $f2 , + then +m; + +:m escape + 7 (#@) + [ + dirty set + PSW pop + reti + ] +m; + +:: MP + $00 (#) (log!) + $01 (#) (log!) + wrap + escape + begin SDA 0=until. +: (mismatch) +:: Mt + begin MP SDA1 SCL 0=until. + $82 # 3 #! FL0 set + M00.0 j + +:: M00.6 begin M10.6 SDA1 SCL until. begin MP SDA1 SCL 0=until. +:: M00.7 begin M10.7 SDA1 SCL until. begin MP SDA1 SCL 0=until. (l!) +:: M00.8 begin M10.8 SDA1 SCL until. begin MP SDA1 SCL 0=until. heat + a+ 3 (#@) (l!) a+ wrap + $83 # 3 #! FL0 clr + M00.0 j + +:: M10.6 begin M00.6 SDA0 SCL until. begin Mt SDA0 SCL 0=until. 1 .t set +:: M10.7 begin M00.7 SDA0 SCL until. begin Mt SDA0 SCL 0=until. 1+ (l!) +:: M10.8 begin M00.8 SDA0 SCL until. begin Mt SDA0 SCL 0=until. + a+ 3 (#@) $7f # and (l!) a+ wrap + $83 # 3 #! FL0 clr + M10.0 j + +:: MP3 MP j +:: Mt3 Mt j +:: M00.3 begin M10.3 SDA1 SCL until. begin MP3 SDA1 SCL 0=until. +:: M00.4 begin M10.4 SDA1 SCL until. begin MP3 SDA1 SCL 0=until. +:: M00.5 begin M10.5 SDA1 SCL until. begin MP3 SDA1 SCL 0=until. + M00.6 j +:: M10.3 begin M00.3 SDA0 SCL until. begin Mt3 SDA0 SCL 0=until. 4 .t set +:: M10.4 begin M00.4 SDA0 SCL until. begin Mt3 SDA0 SCL 0=until. 3 .t set +:: M10.5 begin M00.5 SDA0 SCL until. begin Mt3 SDA0 SCL 0=until. 2 .t set + M10.6 j + +:: MP0 MP j +:: Mt0 Mt j + +: (warm) +:: M00.0 clra + begin M10.0 SDA1 SCL until. begin MP0 SDA1 SCL 0=until. +:: M00.1 begin M10.1 SDA1 SCL until. begin MP0 SDA1 SCL 0=until. +:: M00.2 begin M10.2 SDA1 SCL until. begin MP0 SDA1 SCL 0=until. + M00.3 j +:: M10.0 clra + begin M00.0 SDA0 SCL until. begin Mt0 SDA0 SCL 0=until. 7 .t set +:: M10.1 begin M00.1 SDA0 SCL until. begin Mt0 SDA0 SCL 0=until. 6 .t set +:: M10.2 begin M00.2 SDA0 SCL until. begin Mt0 SDA0 SCL 0=until. 5 .t set + M10.3 j + +: /monitor + [ ET2 clr ] \ Timer 2 interrupt disable + [ ES0 clr ] \ UART interrupt disable + \i2chw + t3i- t3+ \ Timer3 running, no intr + + %00000100 # P0MASK #! + %00000100 # P0MAT #! \ SDA high + + %00010000 # P1MASK #! + %00010000 # P1MAT #! \ SCL high + + \ constants in registers + 72 # [ 4 8 + ] #! + $02 # EIE1 ior! \ EMAT + ; + +: \monitor + $02 ~# EIE1 and! \ EMAT off + [ ET2 set ] \ Timer 2 interrupt enable + [ ES0 set ] \ UART interrupt enable + t3i+ t3- \ Timer3 stopped, intr + /i2chw + ; + +:m mismatch + [ + PSW push + RS0 set + ] + 7 (#!) + Mt j +m; + +here [ +CAPTURE-START xor 11 rshift 0<> +[IF] +cr .( Capture block cannot cross a 2K boundary) +abort +[THEN] +] diff --git a/firmware/font.fs b/firmware/font.fs new file mode 100644 index 0000000..9c10255 --- /dev/null +++ b/firmware/font.fs @@ -0,0 +1,131 @@ +here constant tplan +60 , 0 , $ff , $f , ," V" +108 , 0 , $ff , $f , ," mA" +0 , +here constant font +'.' , $49 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $03 , $20 , $2f , $e0 , $0c , $b0 , +'0' , $89 , $01 , $9d , $d8 , $00 , $0c , $fc , $df , $90 , $4f , $b0 , $0d , $e1 , $7f , $70 , $0b , $f4 , $9f , $70 , $0a , $f5 , $7f , $70 , $0b , $f4 , $4f , $a0 , $0d , $e1 , $0c , $fb , $cf , $90 , $01 , $ad , $d8 , $00 , +'1' , $89 , $00 , $af , $f7 , $00 , $09 , $fd , $f7 , $00 , $6f , $a7 , $f7 , $00 , $07 , $07 , $f7 , $00 , $00 , $07 , $f7 , $00 , $00 , $07 , $f7 , $00 , $00 , $07 , $f7 , $00 , $09 , $9c , $fc , $95 , $0e , $ff , $ff , $f8 , +'2' , $89 , $02 , $ae , $d8 , $00 , $1d , $fc , $ef , $90 , $3b , $70 , $3f , $e0 , $00 , $00 , $4f , $c0 , $00 , $02 , $de , $40 , $00 , $4d , $e4 , $00 , $06 , $ed , $30 , $00 , $4f , $fb , $aa , $a2 , $5f , $ff , $ff , $f3 , +'3' , $89 , $04 , $ce , $d7 , $00 , $3e , $eb , $df , $80 , $05 , $10 , $5f , $b0 , $00 , $6b , $dd , $30 , $00 , $9f , $fc , $20 , $00 , $00 , $6f , $c0 , $2a , $10 , $2f , $e0 , $8f , $eb , $df , $80 , $07 , $ce , $c7 , $00 , +'4' , $89 , $00 , $05 , $ff , $40 , $00 , $1d , $ff , $40 , $00 , $8e , $cf , $40 , $03 , $e8 , $9f , $40 , $0c , $d0 , $9f , $40 , $6f , $50 , $9f , $40 , $bf , $ff , $ff , $f7 , $69 , $99 , $cf , $a4 , $00 , $00 , $9f , $40 , +'5' , $89 , $0c , $ff , $ff , $c0 , $0d , $eb , $bb , $90 , $0e , $a0 , $00 , $00 , $0f , $aa , $eb , $20 , $1f , $fb , $df , $d0 , $02 , $20 , $0d , $f3 , $07 , $40 , $0d , $f2 , $2e , $eb , $df , $b0 , $03 , $be , $d8 , $10 , +'6' , $89 , $00 , $1a , $fc , $10 , $00 , $cf , $90 , $00 , $09 , $f8 , $00 , $00 , $1e , $da , $eb , $30 , $5f , $fb , $bf , $d0 , $7f , $a0 , $0c , $f4 , $5f , $a0 , $0c , $f3 , $0c , $eb , $bf , $b0 , $01 , $ad , $d9 , $10 , +'7' , $89 , $5f , $ff , $ff , $f2 , $5f , $ba , $ae , $f2 , $5f , $50 , $3f , $c0 , $14 , $10 , $9f , $50 , $00 , $01 , $ee , $00 , $00 , $07 , $f8 , $00 , $00 , $0d , $e2 , $00 , $00 , $5f , $a0 , $00 , $00 , $bf , $40 , $00 , +'8' , $89 , $03 , $be , $d9 , $10 , $1e , $ea , $bf , $c0 , $2f , $a0 , $0d , $e0 , $09 , $e9 , $ae , $60 , $06 , $ef , $fd , $40 , $5f , $b1 , $2d , $e2 , $7f , $80 , $0c , $f4 , $2e , $ea , $bf , $d0 , $03 , $be , $da , $20 , +'9' , $89 , $02 , $ae , $d8 , $00 , $1d , $eb , $cf , $90 , $6f , $90 , $0d , $f1 , $7f , $80 , $0d , $f3 , $3e , $ea , $bf , $f2 , $05 , $ce , $af , $d0 , $00 , $00 , $af , $50 , $00 , $1b , $f9 , $00 , $02 , $df , $80 , $00 , +'A' , $99 , $00 , $0e , $fa , $00 , $00 , $04 , $ff , $e1 , $00 , $00 , $9f , $9f , $50 , $00 , $0e , $c2 , $fb , $00 , $05 , $f7 , $0c , $e1 , $00 , $af , $ff , $ff , $60 , $1e , $eb , $bb , $fb , $06 , $f8 , $00 , $0d , $f1 , $bf , $40 , $00 , $8f , $70 , +'C' , $99 , $00 , $5b , $ec , $70 , $00 , $6f , $ec , $ef , $80 , $1e , $e2 , $01 , $c7 , $04 , $fa , $00 , $00 , $00 , $6f , $90 , $00 , $00 , $04 , $fa , $00 , $00 , $00 , $1e , $e1 , $01 , $d8 , $00 , $7f , $ec , $ef , $80 , $00 , $6c , $ec , $60 , $00 , +'D' , $99 , $1f , $ff , $eb , $50 , $01 , $fe , $cc , $ef , $60 , $1f , $d0 , $02 , $ee , $01 , $fd , $00 , $0b , $f3 , $1f , $d0 , $00 , $af , $51 , $fd , $00 , $0b , $f3 , $1f , $d0 , $01 , $ee , $11 , $fe , $bc , $ef , $60 , $1f , $ff , $eb , $50 , $01 , +'M' , $a9 , $1f , $f5 , $00 , $0a , $fa , $1f , $fc , $00 , $3f , $fa , $1f , $ff , $60 , $bf , $fa , $1f , $dd , $d5 , $fb , $fa , $1f , $c6 , $fe , $e4 , $fa , $1f , $c0 , $cf , $63 , $fa , $1f , $c0 , $4d , $03 , $fa , $1f , $c0 , $02 , $03 , $fa , $1f , $c0 , $00 , $03 , $fa , +'S' , $89 , $02 , $ad , $da , $20 , $0d , $fd , $df , $e1 , $4f , $b0 , $04 , $30 , $2e , $e8 , $51 , $00 , $05 , $df , $ff , $80 , $00 , $02 , $5d , $f4 , $08 , $30 , $0a , $f5 , $5f , $fd , $df , $d1 , $04 , $be , $da , $20 , +'V' , $99 , $cf , $30 , $00 , $de , $17 , $f8 , $00 , $3f , $b0 , $2f , $d0 , $08 , $f6 , $00 , $cf , $20 , $de , $10 , $06 , $f7 , $2f , $b0 , $00 , $1f , $b6 , $f6 , $00 , $00 , $be , $ce , $10 , $00 , $06 , $ff , $a0 , $00 , $00 , $1e , $f5 , $00 , $0c , +'m' , $b9 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $00 , $2f , $ba , $ea , $1a , $ea , $12 , $ff , $bd , $fe , $bd , $f8 , $2f , $c0 , $3f , $c0 , $3f , $b2 , $fb , $02 , $fb , $02 , $fc , $2f , $b0 , $2f , $b0 , $2f , $c2 , $fb , $02 , $fb , $02 , $fc , $2f , $b0 , $2f , $b0 , $2f , $c0 , + +here constant micro +$4f , $ff , $4f , $00 , $0f , $f0 , $00 , $f4 , $ff , $f4 , +$00 , $00 , $0f , $ff , $ff , $f0 , $00 , $00 , $00 , $00 , +$4f , $40 , $ff , $0f , $0f , $f0 , $f0 , $ff , $04 , $ff , +$4f , $4f , $4f , $0f , $0f , $f0 , $f0 , $ff , $04 , $0f , +$ff , $ff , $f0 , $0f , $00 , $00 , $f0 , $0f , $ff , $00 , +$f0 , $4f , $4f , $0f , $0f , $f0 , $f0 , $ff , $ff , $0f , +$40 , $4f , $4f , $0f , $0f , $f0 , $f0 , $f4 , $ff , $f4 , +$ff , $00 , $0f , $0f , $00 , $f0 , $0f , $ff , $00 , $00 , +$4f , $4f , $4f , $0f , $0f , $f0 , $f0 , $f4 , $f4 , $f4 , +$4f , $ff , $4f , $0f , $0f , $f0 , $f0 , $f4 , $f4 , $04 , +$4f , $ff , $ff , $0f , $00 , $f0 , $f0 , $04 , $ff , $ff , +$4f , $4f , $4f , $0f , $0f , $f0 , $f0 , $ff , $ff , $ff , +$4f , $0f , $4f , $00 , $0f , $f0 , $00 , $f4 , $ff , $f4 , +$4f , $ff , $4f , $00 , $0f , $f0 , $00 , $ff , $ff , $ff , +$00 , $00 , $0f , $0f , $0f , $f0 , $f0 , $ff , $ff , $ff , +$00 , $00 , $0f , $0f , $00 , $f0 , $f0 , $0f , $ff , $ff , +here constant symbol-s +$bc , $00 , $17 , $ce , $d8 , $20 , $00 , $2b , $ff , $ff , $fc , $30 , $0a , $ff , $ff , $ff , $fb , $15 , $fd , $0f , $d0 , $0d , $f6 , $af , $00 , $f0 , $00 , $0f , $cc , $f0 , $ff , $0f , $f0 , $fe , $cf , $0f , $f0 , $ff , $0f , $e9 , $f0 , $00 , $0f , $00 , $fb , $4e , $d0 , $0d , $f0 , $df , $60 , $9f , $ff , $ff , $ff , $a1 , $01 , $9f , $ff , $ff , $b2 , $00 , $00 , $5b , $db , $61 , $00 , +here constant symbol-p +$bc , $00 , $17 , $ce , $d8 , $20 , $00 , $2b , $ff , $ff , $fc , $30 , $0a , $ff , $ff , $ff , $fb , $15 , $fd , $00 , $df , $ff , $f6 , $af , $00 , $00 , $ff , $ff , $cc , $f0 , $ff , $0f , $ff , $fe , $cf , $0f , $f0 , $ff , $ff , $e9 , $f0 , $00 , $00 , $00 , $fb , $4e , $00 , $00 , $00 , $0f , $60 , $9f , $ff , $ff , $ff , $a1 , $01 , $9f , $ff , $ff , $b2 , $00 , $00 , $5b , $db , $61 , $00 , +here constant symbol-b +$bc , $00 , $17 , $ce , $d8 , $20 , $00 , $2b , $ff , $ff , $fc , $30 , $0a , $ff , $ff , $ff , $fb , $15 , $ff , $ff , $ff , $ff , $f6 , $af , $ff , $ff , $ff , $ff , $cc , $f0 , $00 , $00 , $f0 , $fe , $cf , $00 , $00 , $0f , $0f , $e9 , $ff , $ff , $ff , $ff , $fb , $4e , $ff , $ff , $ff , $ff , $60 , $9f , $ff , $ff , $ff , $a1 , $01 , $9f , $ff , $ff , $b2 , $00 , $00 , $5b , $db , $61 , $00 , +here constant arrow +$35 , $08 , $03 , $b3 , $8f , $8b , $fb , $ef , $e0 , +here constant larrow +$35 , $ef , $eb , $fb , $8f , $83 , $b3 , $08 , $0e , +here constant dot +$44 , $4c , $d5 , $bf , $fd , $bf , $fc , $3b , $b4 , +here constant label-sda +$810 , $f0 , $00 , $00 , $00 , $f0 , $00 , $00 , $00 , $f0 , $4f , $ff , $f0 , $f0 , $f0 , $f0 , $00 , $f0 , $f0 , $f0 , $00 , $f0 , $4f , $ff , $f0 , $f0 , $00 , $00 , $00 , $f0 , $4f , $ff , $40 , $f0 , $f0 , $00 , $f0 , $f0 , $f0 , $00 , $f0 , $f0 , $ff , $ff , $f0 , $f0 , $00 , $00 , $00 , $f0 , $f0 , $4f , $40 , $f0 , $f0 , $f0 , $f0 , $f0 , $f0 , $f0 , $f0 , $f0 , $4f , $40 , $f0 , +here constant label-scl +$810 , $f0 , $00 , $00 , $00 , $f0 , $00 , $00 , $00 , $f0 , $00 , $00 , $00 , $f0 , $00 , $00 , $f0 , $f0 , $00 , $00 , $f0 , $f0 , $ff , $ff , $f0 , $f0 , $00 , $00 , $00 , $f0 , $4f , $0f , $40 , $f0 , $f0 , $00 , $f0 , $f0 , $f0 , $00 , $f0 , $f0 , $4f , $ff , $40 , $f0 , $00 , $00 , $00 , $f0 , $f0 , $4f , $40 , $f0 , $f0 , $f0 , $f0 , $f0 , $f0 , $f0 , $f0 , $f0 , $4f , $40 , $f0 , + +HERE constant grad +$00 , $7 , +$10 , $8 , +$10 , $8 , +$20 , $8 , +$20 , $8 , +$30 , $8 , +$30 , $9 , +$30 , $9 , +$40 , $9 , +$40 , $9 , +$40 , $9 , +$50 , $9 , +$50 , $9 , +$50 , $9 , +$60 , $9 , +$60 , $9 , +$60 , $9 , +$70 , $9 , +$70 , $9 , +$70 , $9 , +$70 , $9 , +$80 , $9 , +$80 , $9 , +$81 , $9 , +$91 , $9 , +$91 , $9 , +$91 , $8 , +$92 , $8 , +$a2 , $8 , +$a2 , $8 , +$a2 , $8 , +$a2 , $8 , +$b3 , $7 , +$b3 , $7 , +$b3 , $7 , +$b3 , $7 , +$b4 , $7 , +$c4 , $6 , +$c4 , $6 , +$c4 , $6 , +$c5 , $6 , +$c5 , $6 , +$d5 , $5 , +$d5 , $5 , +$d6 , $5 , +$d6 , $5 , +$d6 , $5 , +$d6 , $5 , +$d7 , $4 , +$e7 , $4 , +$e7 , $4 , +$e7 , $4 , +$e8 , $4 , +$e8 , $3 , +$e8 , $3 , +$e9 , $3 , +$e9 , $3 , +$e9 , $3 , +$e9 , $3 , +$ea , $2 , +$ea , $2 , +$ea , $2 , +$eb , $2 , +$eb , $2 , +$eb , $2 , +$ec , $2 , +$ec , $2 , +$ec , $2 , +$ed , $2 , +$ed , $2 , +$ed , $2 , +$ee , $2 , diff --git a/firmware/fontsize.fs b/firmware/fontsize.fs new file mode 100644 index 0000000..69d92e3 --- /dev/null +++ b/firmware/fontsize.fs @@ -0,0 +1 @@ +&1385 constant FONTDATA_SIZE diff --git a/firmware/main.fs b/firmware/main.fs new file mode 100644 index 0000000..86035d8 --- /dev/null +++ b/firmware/main.fs @@ -0,0 +1,1010 @@ +24500000 constant SYSCLK + +0 constant CLOSEUP + +\ P0.0 SDA 2K2 +\ P0.1 SDA 4K3 +\ P0.2 SDA +\ P0.3 SDA 4K7 +\ P0.4 RX +\ P0.5 TX +\ P0.6 A.V +\ P0.7 A.C + +\ P1.0 RS/DC +\ P1.1 DATA +\ P1.2 CLOCK +\ P1.3 RESET +\ P1.4 SCL +\ P1.5 SCL 2K2 +\ P1.6 SCL 4K3 + +\ P2.0 SCL 4K7 + +0 [if] +There are 3 threads: + + 1000Hz tick. Increments the BCD milliscond timer. + Timer 2 interrupt. + + UART/SPI service. Runs the transport. + UART and SPI interrupts. + [DPTR, R0] + + graphics. renders the main image. + Main thread. + [DPTR, R0-7] + ADC drive. Runs ADC conversions, stores results in adc-. + ADC end of conversion interrupt. + +[then] +[ : ," '"' parse dup ] , [ + bounds do + i c@ ] , [ + loop +; +] + +:m t3+ %00000100 # TMR3CN #! m; \ Timer 3 enable +:m t3- %00000000 # TMR3CN #! m; \ Timer 3 enable +:m t3i+ $80 # EIE1 ior! m; \ Timer 3 interrupt enable +:m t3i- $80 ~# EIE1 and! m; \ Timer 3 interrupt disable + +$0090 org +: 2dup |over +: over |over ; +: tuck swap over ; +:m p>r [ dpl push dph push ] m; +: r>p [ dph pop dpl pop ] ; \ MUST be followed by ; +: @p |@p ; +: @p+ |@p+ ; +: * |* ; +: um* |um* ; +:m #+! [ dup add ] #! m; +: dnegate + swap invert swap invert +: d1+ + swap 1 # + swap 0 # +' ; +: d+ push swap push + pop pop +' ; +:m d2/ clrc 2/' swap 2/' swap m; +: - negate + ; + +: twist ( a b c d -- a c b d ) + push swap pop ; + +:m /uart + REN0 set \ Receive enable + + TR1 set + $20 # TMOD #! + $18 # CKCON #! \ Use system clock (T1,T2) + [ SYSCLK 2/ 1000000 / negate ] # + TH1 #! \ speed + ES0 set +m; + +[ : array create , does> @ + ; ] + +$10 cpuORG + +$09 constant log \ logging ring pointer + +cpuHERE constant tempr 2 cpuALLOT \ temperature ADC +cpuHERE constant currr 2 cpuALLOT \ current ADC +cpuHERE constant currd 2 cpuALLOT \ decimal +cpuHERE constant slowc 1 cpuALLOT \ slow refresh counter +cpuHERE constant charc 1 cpuALLOT \ character counter +cpuHERE constant convs 1 cpuALLOT \ converter state (2 bit) +cpuHERE constant other 1 cpuALLOT \ context SP save +cpuHERE array clock 6 cpuALLOT + +cpuHERE $20 <> throw + +cpuHERE constant flags 1 cpuALLOT $00 constant dirty + $01 constant prev. + $02 constant talked. + $03 constant ptalked. + $04 constant fade. + $05 constant risen. + $06 constant modechange. + $07 constant timeout +cpuHERE constant ftemp 1 cpuALLOT $08 constant f.0 + $09 constant f.1 + $0a constant f.2 + $0b constant f.3 + $0c constant f.4 + $0d constant f.5 + $0e constant f.6 + $0f constant f.7 +cpuHERE constant flags2 1 cpuALLOT $10 constant weighing + +cpuHERE constant mode 1 cpuALLOT +cpuHERE constant tempd 2 cpuALLOT \ decimal +cpuHERE constant vbusr 2 cpuALLOT \ voltage ADC +cpuHERE constant vbusd 2 cpuALLOT \ decimal + +cpuHERE constant talk0 1 cpuALLOT +cpuHERE constant talk1 1 cpuALLOT +cpuHERE constant talker 1 cpuALLOT +cpuHERE constant ptalker 1 cpuALLOT +cpuHERE constant slashx 1 cpuALLOT +cpuHERE constant story 16 cpuALLOT +cpuHERE constant guard 2 cpuALLOT + +[ +cpuHERE constant red 1 cpuALLOT +cpuHERE constant grn 1 cpuALLOT +cpuHERE constant blu 1 cpuALLOT +] +3 constant x \ graphics x coordinate +4 constant y \ graphics y coordinate +cpuHERE constant i2cb 64 cpuALLOT + +cr .( RAM used ) cpuHERE . .( bytes ) + +: swapctx [ + 0 push + 1 push + t push + psw push + dph push + dpl push + ] + SP (#@) + [ other xch ] + SP (#!) + [ + dpl pop + dph pop + psw pop + t pop + 1 pop + 0 pop + ] + ; + +: 0# dup [ clra ] ; +: key begin TI0 clr RI0 0=while. swapctx repeat RI0 clr SBUF0 #@ ; \ XXX compare with spidriver +: emit SBUF0 (#!) begin swapctx RI0 clr TI0 until. TI0 clr [ charc dec ] +: _drop drop ; + +:m drop; _drop ; m; + +: umax + clrc $96 , $26 , \ C set if u>t + if' drop; then nip ; +: umin + clrc $96 , $26 , \ C set if u>t + 0=if' drop; then nip ; + +: depth S #@ invert ; +0 [if] +include debug.fs +[then] +: 1ms + 1 # +: ms + slowc (#!) + begin slowc (#@) 0=until drop; + +: 5µs + 5 # +: µs + 1 #for + nop nop nop nop + nop nop nop nop + 1 #next + ; + +\ ---------------------------------------- DECIMAL +5 constant d.l \ decimal accumulator +6 constant d.h + +: decimal ( u. -- d. ) \ d is the BCD of u + 0 # d.l #! + 0 # d.h #! + 16 # 7 #for + swap 2*' swap 2*' + d.l #@ [ d.l addc da ] d.l #! + d.h #@ [ d.h addc da ] d.h #! + 7 #next + 2drop d.l #@ d.h #@ ; + +: 10trunc + swap $f0 # and swap ; +: 5trunc + swap + dup $f # and 5 # crc) CRC0IN (#!) m; + +: hdigit + dup +: (hdigit) + [swap] +: digit + $f # and + -10 # + -if -39 # + then 97 # + emit ; +: dd hdigit digit ; + +: >i2c + MASTER 0=if. drop; then + (>crc) + SMB0DAT #! +: i2c + [ SI clr ] +: (i2c) + [ timeout clr ] +: i2c-wait + $01 # EIE1 ior! \ ESMB0 + t3+ + begin swapctx SI until. + t3- + $01 ~# EIE1 and! \ ESMB0 + ; +: i2c-start + [ STA set ] + i2c + [ STA clr ] ; +: i2c-stop + [ STO set SI clr ] ; +: i2c> + MASTER 0=if. $ff # ; then + i2c SMB0DAT #@ (>crc) ; + +: i2c-leave + SMB0CF #@ + 7 .t 0=if. drop; then \ already turned off + 5 .t if. i2c-stop 10 # µs then + \i2chw + $04 ~# XBR0 and! drop; + +: setport ( u ) \ SCL SCL_DIR SDA SDA_DIR + + P0MDOUT ftemp mov + 2/' f.2 movcb + ftemp P0MDOUT mov + 2/' P0.2 movcb + P1MDOUT ftemp mov + 2/' f.4 movcb + ftemp P1MDOUT mov + 2/' P1.4 movcb ; + +: i2c-restore + %1010 # setport drop + /i2chw + $04 # XBR0 ior! ; + +: i2c-reset + i2c-leave + [ SDA set SCL clr ] + 10 # 2 #for + [ SCL set ] 5µs + [ SCL clr ] 5µs + 2 #next + \ a STOP signal (SDA from low to high while CLK is high) + [ SDA clr ] 5µs + [ SCL set ] 2 # µs + [ SDA set ] 2 # µs + i2c-restore + STO clr ; + +: bitbang + i2c-leave + begin + key + '@' # =if drop; then + setport + + 2/' if' + 0# + P1.4 movbc 2*' + P0.2 movbc 2*' + emit + then + drop + again + +: doconv + startconv + begin AD0INT until. AD0INT clr ; +: measure ( - ) \ + doconv + ADC0H #@ emit ; + +: startweigh ( u ) + weighing set + i2c-leave + [ SCL set SDA set ] + %00000100 ~# P0MDIN and! + %00010000 ~# P1MDIN and! +: pulldir ( u ) [ \ Set pullup/down direction (1=up, 0=down) + 2/' P0.0 movcb + 2/' P0.1 movcb + 2/' P0.3 movcb + 2/' P1.5 movcb + 2/' P1.6 movcb + 2/' P2.0 movcb + ] drop; + +: weigh + 0# [ weighing movbc ] 2*' \ 1=pending, 0=ready + dup emit + 0=if + 0# µs + + 3.3v + %0010 # ADC0MX #! measure + %1100 # ADC0MX #! measure + + %00000100 # P0MDIN ior! + %00010000 # P1MDIN ior! + + dup /converter convs #! + %111111 # pulldir + i2c-restore + then drop; + +\ ---------------------------------------- timer service + +: timer2 + [ psw push t push ] + [ + slowc dec + setc + clra 0 clock dup addc da (#!) + clra 1 clock dup addc da (#!) + clra 2 clock dup addc da (#!) + clra 3 clock dup addc da (#!) + clra 4 clock dup addc da (#!) + clra 5 clock dup addc da (#!) + ] [ t pop psw pop ] ; + +: timer3a + \i2chw + timeout set + SI set + /i2chw + [ reti ] + + +\ ---------------------------------------- CRC16 +:m /crc + %1100 # CRC0CN0 #! + m; + +:m crc16 + CRC0DAT #@ + CRC0DAT #@ m; + +\ ---------------------------------------- pullups + +\ P0.0 SDA 2K2 +\ P0.1 SDA 4K3 +\ P0.2 SDA +\ P0.3 SDA 4K7 + +\ P1.4 SCL +\ P1.5 SCL 2K2 +\ P1.6 SCL 4K3 + +\ P2.0 SCL 4K7 + +: SDA_2k2 %00000001 # P0MDOUT ior! ; +: SDA_4k3 %00000010 # P0MDOUT ior! ; +: SDA_4k7 %00001000 # P0MDOUT ior! ; + +: SCL_2k2 %00100000 # P1MDOUT ior! ; +: SCL_4k3 %01000000 # P1MDOUT ior! ; +: SCL_4k7 %00000001 # P2MDOUT ior! ; + +\ 5 4 3 2 1 0 +\ SCL_4k7 SCL_4k3 SCL_2k2 SDA_4k7 SDA_4k3 SDA_2k2 +: pull@ ( - u ) + 0# [ + P2MDOUT ftemp mov + f.0 movbc 2*' + P1MDOUT ftemp mov + f.6 movbc 2*' + f.5 movbc 2*' + P0MDOUT ftemp mov + f.3 movbc 2*' + f.1 movbc 2*' + f.0 movbc 2*' ] ; +: pull! ( u ) [ + P0MDOUT ftemp mov + 2/' f.0 movcb + 2/' f.1 movcb + 2/' f.3 movcb + ftemp P0MDOUT mov + P1MDOUT ftemp mov + 2/' f.5 movcb + 2/' f.6 movcb + ftemp P1MDOUT mov + P2MDOUT ftemp mov + 2/' f.0 movcb + ftemp P2MDOUT mov + ] drop; + +: release + %00001011 ~# P0MDOUT and! + %01100000 ~# P1MDOUT and! + %00000001 ~# P2MDOUT and! +; +: weak + release + %00001000 # P0MDOUT ior! + %00000001 # P2MDOUT ior! + ; + +here constant "devname ," i2cdriver1" + +: heatmap ( u - ) \ heatmap address in x + $80 # + dpl #! ; + +: ishot ( u ) + heatmap 72 # !x ; + +: snap + [ log dpl mov ] + story # a! + dup + 16 # 7 #for + [ dpl dec ] + $7f # dpl and! + (@x) + (!+) + 7 #next + drop; + +: type + @p+ 2 #for @p+ emit 2 #next ; + +: hdigit + dup +: (hdigit) + [swap] +: digit + $f # and + -10 # + -if -39 # + then 97 # + emit ; +: dd hdigit digit ; +: dh. dd +: h. dd +: space + 32 # emit ; +: point + '.' # emit ; +: d.d + hdigit point digit ; + +: .' \ print carry + [ '0' 2/ ] # 2*' +: emit_ + emit space ; + +: i2c-speed + TH0 #@ + I2C_400 # =if 4 (#) ; then + 1 (#) ; + +: modechar + 'I' # + SMB0CF ftemp mov + f.7 if. ; then + 'B' (#) ; + +: bracket + 79 # charc #! + '[' # emit ; +: info + bracket + "devname ##p! type space + "serial ##p! type space + + 5 clock #@ dd + 4 clock #@ dd + 3 clock #@ dd + 2 clock #@ dd + 1 clock #@ (hdigit) + space + + vbusd #2@ d.d dd space + currd #2@ digit dd space + tempd #2@ digit d.d space + + modechar emit_ + + [ SDA movbc ] .' + [ SCL movbc ] .' + + i2c-speed digit $00 # dd space + + pull@ dd space + + crc16 dd dd +: pad + charc #@ begin + space 1- + 0=until + drop + ']' # emit + ; + +: introspect + bracket + + $93 # h. + 0 #@ h. + SP #@ h. + SMB0CF #@ h. + SMB0CN #@ h. + TMR2L #2@ dh. + TMR3L #2@ dh. + IE #@ h. + EIE1 #@ h. + + P0 #@ h. P0MDIN #@ h. P0MDOUT #@ h. + P1 #@ h. P1MDIN #@ h. P1MDOUT #@ h. + P2 #@ h. P2MDOUT #@ h. + + convs #@ h. + + pad ; + +\ Commands are: +\ e echo next byte +\ s select +\ u unselect +\ 80-bf read 1-64 bytes +\ c0-ff write 1-64 bytes + +: count ( u -- u) + 63 # and 1+ ; + +CLOSEUP [IF] +:m acmd \ Copy ACK into T.7 for a command byte + [ 7 .t set ] m; +[ELSE] +:m acmd \ Copy ACK into T.7 for a command byte + [ ACK movbc 7 .t movcb ] m; +[THEN] + +: b>log ( arg - arg ) + 3 # acmd over +: >log ( cmd arg ) + [ dirty set ] + [ log dpl mov ] + !x+ !x+ + $7f # dpl and! + [ dpl log mov ] + ; + +: alert ( u ) + [ timeout set ] + drop 4 # 0 # >log ; + +: rdN ( n -- ) + [ ACK set ] + 2 #for + [ $b8 2 + ] , [ 1 cond ] \ Clear ACK on final byte when R2 is 1 + [ ACK clr ] + then + i2c> + (>crc) b>log emit + 2 #next ; + +: rdNA ( n -- ) \ don't NACK final byte + [ ACK set ] + 2 #for + i2c> b>log emit + 2 #next ; + +: report + [ '0' 2/ 2/ 2/ ] # + [ ARBLOST movbc ] 2*' + [ timeout movbc ] 2*' + [ ACK movbc ] 2*' emit ; + +: flame ( u - u ) + dup clrc 2/' ishot ; + +: do-start + key +: log-start ( u ) + SDA 0=if. alert ; then + i2c-start + dup >i2c + timeout if. alert ; then + ACK if. + flame + then + 2 # acmd + swap >log ; + +: log-stop + 1 # 0# >log i2c-stop ; + +: i2c-regrd \ expect (dev, reg, len) + key 2* key key push ( dev reg R: len ) + over log-start \ S/W + b>log >i2c \ reg + 1+ log-start \ S/R + pop rdN + log-stop ; + +: dmode + 'D' # +: newmode + mode #! modechange. set ; + +here constant WIP +: device-scan + 8 # + 112 # 2 #for + i2c-start dup 2* >i2c + ACK if. dup ishot then + report + i2c-stop + 1+ + 2 #next drop ; + +: service + key + -if + 6 .t if. + count dup + i2cb # a! 2 #for + key + b>log + !+ + 2 #next + i2cb # a! 2 #for + @+ >i2c + 2 #next + report + ; + then + count rdN ; + then + '?' # =if info then + '1' # =if 100Khz then + '4' # =if 400Khz then + 'a' # =if key rdNA then + 'b' # =if bitbang then + 'c' # =if 'C' # newmode then + 'd' # =if device-scan then + 'e' # =if key emit then + 'f' # =if fade. set 'X' # emit then + '_' # =if $10 # RSTSRC #! then + 'i' # =if i2c-restore then + 'm' # =if 'M' # newmode then + 'p' # =if log-stop then + 'r' # =if i2c-regrd then + 's' # =if do-start report then + 'u' # =if key pull! then + 'v' # =if key startweigh then + 'w' # =if weigh then + 'x' # =if + i2c-reset + [ '0' 2/ 2/ ] # + [ SDA movbc ] 2*' + [ SCL movbc ] 2*' + emit + then + 'J' # =if introspect then + drop ; + +: thread2 + 0 # 2 #for + 0# !x+ + 2 #next + \ '@' # emit + begin + service + again ; + +here constant _cap +[ : fwd 0 constant ; ] +include capture.fs +_cap org +[ : fwd bl word find 0= throw execute 0= throw ; ] +include capture.fs + +0 constant Y_V +29 constant X_V +80 constant X_MA + +include st7735.fs + +: hdigit dup [swap] +: digit $f # and '0' # + ch ; +: dd hdigit digit ; +: d3 ( d. ) \ 3-digit space padded + if digit dd ; then \ ### + drop blch + 10 # body @ ] ##p! + [ + dpl push + dph push + 0 push + ] + [ STACKS 8 + ] # other #! + + $100 SP! $c0 RP! + + %11001011 # P0SKIP #! \ TX,RX,SDA + %00010000 # P0MDOUT #! \ + %00111111 # P0MDIN #! \ analog P0.6 P0.7 + + %11101111 # P1SKIP #! \ SCL + %00001111 # P1MDOUT #! + /uart + /adc + /crc + + [ ticks/ms negate ] # TMR2RLL #! + [ ticks/ms negate 8 rshift ] # TMR2RLH #! + + dmode + + [ ET2 set ] \ Timer 2 interrupt enable + [ TR2 set ] \ Timer 2 enable + [ EA set ] + t3i+ + + release + weak + \ SDA_2k2 + \ SDA_4k3 + \ SDA_4k7 + + \ SCL_2k2 + \ SCL_4k3 + \ SCL_4k7 + + /i2c 100Khz + + [ dirty set ] + swapctx + + /st7735 fixed + 25 # slowc #! + + /converter + + begin + conversions + + dirty if. + snap + [ dirty clr ] + waves + then + AD0INT if. + AD0INT clr + convs #@ converter convs #! + then + modechange. if. + [ modechange. clr ] + drawmode + mode #@ + 'C' # =if + capture + dmode + then + 'M' # =if + /monitor + then + drop + then + + mode #@ + 'M' # =if + TMR3CN #@ -if + $7f # TMR3CN and! + cool + then drop + RI0 if. + RI0 clr + \monitor + dmode + then + then + drop + + slowc #@ \ 4 Hz + 0=if + results + then +CLOSEUP [IF] + fade. if. + cool [ fade. clr ] + then +[ELSE] + $1f # and 0=if \ 32 Hz + cool + then +[THEN] + drop + again +here + +\ Reset +$000 org go ; + +\ UART interrupt +$023 org + swapctx [ reti ] + +\ Timer 2 overflow +$02b org [ + ] timer2 [ + TF2H clr + reti +] + +\ I2C +$03b org + swapctx [ reti ] + +\ Port mismatch +$043 org + mismatch + +\ Timer 3 overflow +$073 org + FL1 if. timer3 then + timer3a ; + +org diff --git a/firmware/st7735.fs b/firmware/st7735.fs new file mode 100644 index 0000000..dd389f7 --- /dev/null +++ b/firmware/st7735.fs @@ -0,0 +1,698 @@ +$00 constant NOP $2B constant RASET $C2 constant PWCTR3 +$01 constant SWRESET $2C constant RAMWR $C3 constant PWCTR4 +$04 constant RDDID $2E constant RAMRD $C4 constant PWCTR5 +$09 constant RDDST $30 constant PTLAR $C5 constant VMCTR1 +$10 constant SLPIN $36 constant MADCTL $DA constant RDID1 +$11 constant SLPOUT $3A constant COLMOD $DB constant RDID2 +$12 constant PTLON $B1 constant FRMCTR1 $DC constant RDID3 +$13 constant NORON $B2 constant FRMCTR2 $DD constant RDID4 +$20 constant INVOFF $B3 constant FRMCTR3 $E0 constant GMCTRP1 +$21 constant INVON $B4 constant INVCTR $E1 constant GMCTRN1 +$28 constant DISPOFF $B6 constant DISSET5 $FC constant PWCTR6 +$29 constant DISPON $C0 constant PWCTR1 +$2A constant CASET $C1 constant PWCTR2 +$80 constant DELAY + +here constant init-table + SWRESET , DELAY , \ Software reset, 0 args, w/delay + 60 , + SLPOUT , DELAY , \ Out of sleep mode, 0 args, w/delay + 60 , + FRMCTR1 , 3 , \ Frame rate ctrl - normal mode, 3 args: + 0x01 , 0x2C , 0x2D , \ Rate = fosc/(1x2+40) * (LINE+2C+2D) + FRMCTR2 , 3 , \ Frame rate control - idle mode, 3 args: + 0x01 , 0x2C , 0x2D , \ Rate = fosc/(1x2+40) * (LINE+2C+2D) + FRMCTR3 , 6 , \ Frame rate ctrl - partial mode, 6 args: + 0x01 , 0x2C , 0x2D , \ Dot inversion mode + 0x01 , 0x2C , 0x2D , \ Line inversion mode + PWCTR1 , 3 , \ Power control, 3 args: + 0xA2 , + 0x02 , \ -4.6V + 0x84 , \ AUTO mode + PWCTR2 , 1 , \ Power control, 1 arg: + 0xC5 , \ VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD + PWCTR3 , 2 , \ Power control, 2 args: + 0x0A , \ Opamp current small + 0x00 , \ Boost frequency + PWCTR4 , 2 , \ Power control, 2 args: + 0x8A , \ BCLK/2, Opamp current small & Medium low + 0x2A , + PWCTR5 , 2 , \ Power control, 2 args: + 0x8A , 0xEE , + VMCTR1 , 1 , \ Power control, 1 arg: + 0x0E , + MADCTL , 1 , \ Memory access control (directions), 1 arg: + 0xC8 , \ row addr/col addr, bottom to top refresh + COLMOD , 1 , \ set color mode, 1 arg: + 0x03 , \ 12-bit color + GMCTRP1 , 16 , \ Gamma + polarity Correction Characterstics + 0x02 , 0x1c , 0x07 , 0x12 , + 0x37 , 0x32 , 0x29 , 0x2d , + 0x29 , 0x25 , 0x2B , 0x39 , + 0x00 , 0x01 , 0x03 , 0x10 , + GMCTRN1 , 16 , \ Gamma - polarity Correction Characterstics + 0x03 , 0x1d , 0x07 , 0x06 , + 0x2E , 0x2C , 0x29 , 0x2D , + 0x2E , 0x2E , 0x37 , 0x3F , + 0x00 , 0x00 , 0x02 , 0x10 , + NORON , 0 , \ Normal display on + 0 , + +:m clk [ 2 .p1 set 2 .p1 clr ] m; +:m 1bit 2*' 1 .p1 movcb clk m; +:m /C/ [ 0 .p1 clr ] m; +:m /D/ [ 0 .p1 set ] m; + +: (>st) 1bit 1bit 1bit 1bit +: _4 1bit 1bit 1bit 1bit 2*' ; +: (4>st) 2*' 2*' 2*' 2*' _4 ; +: 4>st (4>st) drop ; + +: write-cmd ( b ) /C/ +: 1>st ( b ) 1bit 1bit 1bit 1bit 1bit 1bit 1bit 1bit drop ; +: write-data ( b ) /D/ 1>st ; +: data16 ( b ) 0# write-data write-data ; + +: args + begin + 0=if drop; then + @p+ write-data + 1- + again + +: coldregs + init-table ##p! + begin + @p+ + 0=if drop; then + write-cmd + @p+ + dup $7f # and args + -if @p+ ms then + drop + again + +here [ $1000 > throw ] +$1000 org +: dim ( x w ) + over data16 + 1- data16 ; +: rect ( x y w h ) + twist ( x w y h ) + RASET # write-cmd dim + CASET # write-cmd dim +: writing + RAMWR # write-cmd + /D/ +; + +: full + blu #@ (4>st) + grn (#@) (4>st) red (#@) 4>st ; + +:m |4>st 1bit 1bit 1bit 1bit m; + +: half 10 # +: gray + 0=if + drop +: dark + 1 .p1 clr + clk clk clk clk + clk clk clk clk + clk clk clk clk ; + then + 5 (#!) [ blu b mov mul ] $f # + |4>st + 5 (#@) [ grn b mov mul ] $f # + |4>st + 5 (#@) [ red b mov mul ] $f # + |4>st drop ; + +: ndark + 7 #for dark 7 #next ; + +: cls ( ) + 0# 0# 128 # 160 # + rect + + 160 # 6 #for + 128 # ndark + 6 #next ; + +: /st7735 + [ 3 .p1 clr ] + 1 # ms + [ 3 .p1 set ] + coldregs + + cls +: white + $f # +: setgray + red (#!) grn (#!) blu #! ; +: black + 0# setgray ; + +$1fff constant TOPMEM +947 here +include fontsize.fs +[ TOPMEM FONTDATA_SIZE - ] org +include font.fs +here TOPMEM <> throw +org 947 <> throw + + +:m 4.4r ( - l h ) + dup clra + dup $93 , $a3 , \ |@p+ + xchd [swap] m; + +: 4.4 ( - h l ) + 4.4r swap ; + +: skip + 4.4 * 1+ clrc 2/' +: +p + [ dpl add ] dpl (#!) + [ clra dph addc ] dph (#!) + drop; + +: seek ( c - ) \ p points to the data for character c + font ##p! + begin + dup @p+ xor 0=if 2drop ; then + drop skip + again + +: xy! y #! x #! ; +: xy@ x #@ y #@ ; +: adv x #+! ; \ advance cursor + +: preloop ( l h - i j ) + swap if 1u+ then ; + +\ Fill rect with current color +: wash ( x y w h ) + 2dup um* d1+ d2/ preloop 7 #! 6 #! + rect + begin begin + full full + 7 #next 6 #next ; + +: ch ( c - ) + p>r + seek + xy@ + 4.4 ( w h ) + over adv + 2dup * push ( w h r: w*h ) + rect + pop 1+ 2/ 7 #for + 4.4r gray gray + 7 #next + + r>p ; + +: blch + black + xy@ 8 # 9 # wash + 8 # adv white ; + +: str + @p+ 6 #for + @p+ + ch + 6 #next ; + +: setcolor + 4.4 grn #! red #! @p+ blu #! ; + +: hex1 ( h - ) + x #@ 3 # + $7f # xor 4 # + RASET # write-cmd dim + + RAMWR # write-cmd + /D/ + + micro ##p! + $f # and 10 # b #! [ mul ] +p + 10 # 7 #for 4.4r gray gray 7 #next + 5 # x #+! ; + +: drawhex ( hh - ) + y #@ 5 # + CASET # write-cmd dim + + dup [swap] hex1 hex1 ; + +:m gap [ y inc ] m; + +: clip + y #@ +: (clip) + -if + $7f # and negate + ; + then + drop; + +: preblank ( w ) + dup y #@ + (clip) + dup push x #@ -4 # + y #@ + 16 # pop rect + 6 #for 16 # ndark 6 #next ; + +: bitmap + 0 # +: +bitmap ( o ) + x #@ + y #@ + -if 2drop ; then + 4.4 ( w h ) +: (bitmap) ( x y w h ) + dup y #+! + clip + 2dup * push ( w h r: w*h ) + rect + pop 1+ clrc 2/' 7 #for + 4.4r gray gray + 7 #next ; + +: (hex2) + micro ##p! + $f # and 10 # * +p + y #@ -if drop; then drop + x #@ 3 # + y #@ + 5 # 4 # + (bitmap) ; + +: hex2 ( u - ) + dup (hex2) + gap + [swap] (hex2) + ; + +: acknak + 0=if' + $c # red #! + $2 # grn #! + $2 # blu #! ; + then + $2 # red #! + $c # grn #! + $2 # blu #! ; + +: d-byte-ack + acknak + 18 # preblank + gap + dot ##p! + 7 # +bitmap + + gap + + white + hex2 + gap gap gap ; + +: barpoint ( u - ) \ update the slash bar bounds + -if drop; then + dup + talk0 #@ umin talk0 #! + talk1 #@ umax talk1 #! ; + +: slashcolor 8 # setgray ; + +here constant DRAW-SEGMENT \ This block must all be in the same 2K segment + +: startwave + 128 # 7 #! + 0 # 8 # 128 # rect + story # a! + [ + SP x mov + x dec + x dec + 0 y mov + ] ; + +: column + $df cond +: bail + [ + x SP mov + y 0 mov + ] then ; + +: hi full dark dark dark dark dark dark dark column ; +: lo dark dark dark dark dark dark dark full column ; +: change + full full full full full full full full column ; +: undef + half half half half half half half half column ; + +: d-stop + drop a+ + 0 # red #! + 7 # grn #! + 7 # blu #! + symbol-p ##p! +: (d-stop) + 12 # preblank + bitmap ; + +:m y; \ return if y>127 + $bc , 128 , 0 , \ CJNE R4,#128,+0 + 0=if' ; then m; + +: d-direction + arrow ##p! + if' + larrow ##p! + then + $f # red #! + $e # grn #! + $2 # blu #! + -5 # y #+! + bitmap ; + +: slashv ( u - ) \ draw the bottom slash segment + $08 # madctl + MADCTL # write-cmd write-data ; +: ltr + %11001000 # >madctl ; + +: drawmode + black + 0# 0# 2dup 10 # 9 # wash + white xy! mode #@ ch ; + +: fixed + rtl + 3 # setgray + $08 # + 112 # 6 #for + dup d-addr + 1+ + 6 #next + + ltr + + drawmode + + tplan ##p! + begin + @p+ 0=if drop; then + @p+ xy! + setcolor + str + again + +: d-slash + talked. if. + ptalked. if. + ptalker #@ talker @=if drop; then + unslash + then + talker #@ slash ; + then + ptalker #@ unslash ; + +: cool1 ( addr - ) + \ talker @=if talked. if. drop; then then + dup heatmap @x if ( addr h ) + 1- (!x) ingrad + d-addr ; + then + 2drop ; + +: cool + rtl + $08 # + 112 # 6 #for + dup cool1 + 1+ + 6 #next + drop + ltr ; + +\ talked. is true when talker is valid +\ ptalked. and ptalker hold previous values +\ slashx is set to the X of the slash line + +: waves + \ pinkwash + + rtl + + 122 # 0 # xy! + + $ff # talk0 #! + $00 # talk1 #! + talked. clr + + story # a! + l-dispatch + + d-sda + d-scl + + ltr + + d-slash + d-slashbar + + talker #@ ptalker #! + [ talked. movbc ptalked. movcb ] + + DISPON # write-cmd + ;