From ce5b01c139d06bda2a6bdcad988aaa27a419e018 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 4 May 2019 11:10:52 +0200 Subject: [PATCH] initial commit --- README.md | 31 +++ src/dump_otp/README.md | 15 ++ src/dump_otp/main.c | 76 +++++++ src/glyph_mapping/README.md | 4 + src/glyph_mapping/board_config.h | 19 ++ src/glyph_mapping/cp437_8x8.h | 261 +++++++++++++++++++++ src/glyph_mapping/dvp_ov.bin | Bin 0 -> 75584 bytes src/glyph_mapping/lcd.c | 138 +++++++++++ src/glyph_mapping/lcd.h | 81 +++++++ src/glyph_mapping/main.c | 378 +++++++++++++++++++++++++++++++ src/glyph_mapping/nt35310.c | 105 +++++++++ src/glyph_mapping/nt35310.h | 113 +++++++++ src/glyph_mapping/ov2640.c | 229 +++++++++++++++++++ src/glyph_mapping/ov2640.h | 26 +++ src/glyph_mapping/ov5640.c | 78 +++++++ src/glyph_mapping/ov5640.h | 41 ++++ src/glyph_mapping/ov5640cfg.h | 278 +++++++++++++++++++++++ 17 files changed, 1873 insertions(+) create mode 100644 README.md create mode 100644 src/dump_otp/README.md create mode 100644 src/dump_otp/main.c create mode 100644 src/glyph_mapping/README.md create mode 100644 src/glyph_mapping/board_config.h create mode 100644 src/glyph_mapping/cp437_8x8.h create mode 100644 src/glyph_mapping/dvp_ov.bin create mode 100644 src/glyph_mapping/lcd.c create mode 100644 src/glyph_mapping/lcd.h create mode 100644 src/glyph_mapping/main.c create mode 100644 src/glyph_mapping/nt35310.c create mode 100644 src/glyph_mapping/nt35310.h create mode 100644 src/glyph_mapping/ov2640.c create mode 100644 src/glyph_mapping/ov2640.h create mode 100644 src/glyph_mapping/ov5640.c create mode 100644 src/glyph_mapping/ov5640.h create mode 100644 src/glyph_mapping/ov5640cfg.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..3dd4d4c --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +Maix Go / K210 stuff +===================== + +First, get the Kendryte C toolchain and copy or symlink the contents of the +`src/` folder to a checkout of `https://github.com/sipeed/LicheeDan_K210_examples.git`. + +Then to build a certain project do: + +```bash +mkdir build && cd build +cmake .. -DPROJ= -DTOOLCHAIN=/opt/riscv-toolchain/bin && make +``` + +You will get 2 files, `build/` and `build/.bin`. The former +is an ELF executable, the latter a raw binary that can be flashed or written to +0x80000000 in SRAM and directly executed. + +Projects +========= + +glyph_mapping +------------- + +Variation of the `DVP` sample that processes the camera input through a simple +DOS 8×8 font glyph-mapping algorithm and shows it on the display. + +dump_otp +-------- + +Dumps the contents of the OTP (One-Time Programmable memory) of the K210 CPU to +serial output in Intel HEX format. diff --git a/src/dump_otp/README.md b/src/dump_otp/README.md new file mode 100644 index 0000000..13d931f --- /dev/null +++ b/src/dump_otp/README.md @@ -0,0 +1,15 @@ +otp-dump +======== + +Dump the contents of OTP (One-Time Programmable memory) in hexadecimal format +to serial. + +Output is in Intel HEX format. Make sure your parser ignores non-':' prefixed lines +as there can be warnings, or do + + grep "^:" /tmp/otp1.txt > /tmp/otp1.ihx + objcopy -I ihex -O binary /tmp/otp1.ihx kendryte_otp1.dat + +Note that the OTP contains a serial number (at least 0x3d9c..0x3d9f seem to +differ between boards) so it'd be wise to treat the output as +privacy-sensitive. diff --git a/src/dump_otp/main.c b/src/dump_otp/main.c new file mode 100644 index 0000000..7c3c07e --- /dev/null +++ b/src/dump_otp/main.c @@ -0,0 +1,76 @@ +#include +#include +#include "sysctl.h" +#include "sleep.h" +#include "uarths.h" + +typedef int otp_read_func(uint32_t offset, uint8_t *dest, uint32_t size); +static otp_read_func *otp_read_inner = (otp_read_func*)0x8800453c; /* fixed address in ROM */ + +static uint8_t ih_chksum; + +void ih_start(void) +{ + printf(":"); + ih_chksum = 0; +} + +void ih_emit(uint8_t val) +{ + printf("%02X", val); + ih_chksum += val; +} + +void ih_end() +{ + printf("%02X\n", (-ih_chksum) & 0xff); +} + +int main(void) +{ + uint64_t core_id = current_coreid(); + + if (core_id == 0) + { + int rv; + + sysctl_pll_set_freq(SYSCTL_PLL0, 800000000UL); + sysctl_pll_set_freq(SYSCTL_PLL1, 300000000UL); + sysctl_pll_set_freq(SYSCTL_PLL2, 45158400UL); + + uarths_init(); + + /* system start, sleep a bit to allow UART clients to connect */ + usleep(100000); + + /* output in Intel HEX */ + uint8_t buf[32]; + for(uint32_t base=0; base<16384; base+=sizeof(buf)) { + memset(buf, 0, sizeof(buf)); + rv = otp_read_inner(base, buf, sizeof(buf)); + if (rv != 0) { + printf("warning: non-zero status %d while reading %08x..%08x\n", rv, base, (uint32_t)(base + sizeof(buf) - 1)); + } + ih_start(); + ih_emit(sizeof(buf)); + ih_emit(base >> 8); + ih_emit(base); + ih_emit(0); /* Data */ + for (uint32_t x=0; x5hfmBqc)he}WC-^|rx=vMUTc!!g zOAJeeMhytIh!ra{by}%Lp|ZF!+No8iN;f-!08vDRmPM51f9`vUptdue`TzcZKcD!v zd(S=h+;h)e&beO#Ke}D{@s9)|(1ZV8e__Ie*I$sqPl11t79K+8^z;$0=gga+RS-(8 zl9;KTNj#^0j)>Mq6EWHtB32tqsI)5LdF}JWEbS~JP8&ze*3Ks8Xy*`fwR4GhZ9MS@ z?H}@&ynyZoq5{Y?- zekcADulMy3MZ1_7g`$X&x$D+fCnkFP+I|j_Mbba;cn03}k#ru9m*_p(W(k74>AX3w z%N6az`MciZ(Sd~S5D(E?c_@8ZAh8a~0$xY53HYAaAZ&EMHoSFYn{UVHJCF4i+e#Ws zuN`bX)OMt!{7z->%=*}-xa;vZ=d~~Fe5L2rzJ~hdrk3lsZvNEX)p@t4zb{Nic!dRf zdxeb+^a>j{(JM@*92pk;;>fVEi6g_tZ5SCQ)B1!3=lX<=-QyEB?o*#IS&eU4@I~LS zvGu-Th&89{~9SApaQ1KL+xTf&612|KpJVamfET~qu2b4Zfn%BuX~M(ELP7IuT%_=MJ<3MWPz!?mNSCy4;#liC1k93eH1(@LqQh%v^ev}368#AC+s+Q+Eh z5sw>xr+r*Z5Hj3O2tg~^+#5$;^KBj7_E<;IozUJ=+rh>|*N(K7w^eqWxO1jAu0FnL z-t~nyUul1}b6L;wzLxr1O+Q`lx_P&~zw_sy320}}&p}?1JNtOW!V>q=;Ri<^@;x%T z{ISa7;*yfm(t`&N9XfKPyu7k<=HA%-aes;b+q|-c)>kTCt!miYyualyxBm81S(o*0 zMSm5iTQi?@=@!;2`6ofh@(Dr?jm)WgGoNsM=YPsWyu|Z=dOc?nx&{A_jI;(JRFKZw zihEkRIw*1GvW8{6*Nb1Dxv^oRFu5c-c6sx1VQNWg>>JH*xUVc-8MmrsmHS(zZ^gac z^7imG2iL@}y|s4uI|ttxDLW*a7x+`)$VrDL&5QUc!Z-Lx@WPO;5Z}p1CNG@QHD&bJ z^0BWxarcSQ)5@p4GX3uK$HrBTd-bXQryh%{jC%F={l9lf_vbK0JDp37w~qST@3w3( zGo*%y5q1Q%g|^0Bi*MxWrB1i*tM`U)6UZ3ujY4Z3^(jx}d{S$iZ}ZIL3vObvh-b;c z@<1P%Ni{rIyPo%(Nd-LHT+bzDay>kM;(Fd^CN=PU!S(#1naqLbHrMlBGbs~^$sE^n zv6-9z&l=bBo|`dZ@&)IUtmryCshv-X501l=!uh0r|1>wO`~{|i2=UYd(9TtTiI)75{-b;e4Jm_ILlf! zW%)9N_31=~SSZv$TUAdX)e0_+ZLK7jKw?33KmaK~+&lGJZOkwiH*2|fia$B|-RR`k z$iyVZa5XQo60sH%Mj9q&itC;1sOzri5GOzNdX7TKpPcg1iAgUnOBjLnrc-YCDu0&} za;2eF%zX~3mrI@i4n-oN1voY9x04nVADK;0}@IU#=`C#J+CX;KLLrUW zSgSXE;VkbC`vr8QSk0&~$8xH>OG%__(luOKDn0w8(wi&s^j$93=?*7FHlLD!vQY}s zbrIg-XK=ZDP5XZmwu(2AJn~C3)h1@?dUgK<_rpQs`RucQcN@o*`pFfDamvE>y9PCx zn6AMo*;J#tZ($18t%SF*HR#%2ia<_%l7a3)`3{XbEHOz;3RYRxXZ**?rt*911GXC~oVfo3#f-U<>J6hv;> zur#|M!GMBJh?8cFP8AJ16On=+h?mP5$`(C*D8dJJVPBUd?;L zj%iM=6+cyw+j&7Lyp57j0`X&GuN1FuNQ#(TjfBE!Ou^+*ikL*M&FRrfZ;+c8=z|7J zzxwHkHJCG9ZR$TcT_JxoIV79-AfM!=LPQBxGUno`+F<=#qf5BBz6p?o%;xF`r}7w zS2sUOyZTR$(w5~6rTsA1{NsdoXk(m>j14y()Pr^j&1BmM-20u{U~h-Mi=0+kc0F9* zIrRBjkYHAp+{=Ux40&7K9IpR(=yMaBDo2lS?=DW_tt4V_--a0mS&vpAZieCPIom%261CYQz#M5eJL7lX@n6+A3fYG@j)dP-DwKtH{pKTkVM4bzREu%L1ZxIcBXSY zRMaVR&Q?A9T)2_maFSL)|PAYvo#S46sga@h zwcP`21|pdKinLMb8koI)FOT#Nme0ryl0OqDmq$*N%b!UMJgA>!nw%-0pbeH!P|D>q zUJR5+ZU}_uz~+ILd$+nvO%?^Nv8G&_7{yfDIN7Q1hF?p+o_>?g5Fy=u5t-|Vch%)@ zgA2#_7e>3p|}lfbvh$1rE0g z_h2QhaTlCVl}{K)$Rn2!S1gr12NRT@0Y(Wbzr0&TUv;1yZE|EaL5L31G9$mSm@VRnJx{(xQTS5A)CfM?&$)E|1oF-I%8{X^sqR5l3jIuSP zW}i$?$^S}MOCjx7;v|8B2qr$#soLF54l{*#FvD>#PB~m9PVzxlt-3G|6zCDvX0N0A zE2uU_RGT;{8ueJs)F-@etAbLBQ*?0@Vkr9kb3Fcd58x{knSF=z=zj`P+N%Y_hNEej zQH?QG)8DP&GnDRJ_dvz4Td06>?;CHlZ$921)i}zkoqHNe6S(cGDo)|G@lp#i-1``6 zy72~Yy0Hb}Jk#0&jP8B**KhXMKuSG2W-U(P1OH}9Bc%PhQE>|7moXGj!;}Qi-v3_h z{EolD7oIo%PD};~B;q#*Fr!c@BOc*N*uVN_tUU4ubZF<1p3=*^W8ci8FO={&ohiR#VYNnc z!oOynmm)Ma6>E+T_&9I^<^=BY2|pkv7Uw>{S?S3LkMtZ|7+~;4hky=!;mku+WR;kT zEs&#PNZoVN#~)KL-KSvsH41GUAE+Ets2(F^`6x++*?=3JQzsl+Tjle+pFg;Ov)C!$ z$tUz8T-W%`rHpwv)b{yr9vfQQ%|mPJETyOnm!g(%rA()+O8RA+0Mi}6kfZDP8q+Nx zeWBjt4Pp}0y(X1MwxY?F@}7g2$Ed=y=vS=|^gP8uOcjm95;SiXc*UpMEkfzRgot-< zBHVj%kCjJ$PW;q^`>oQ`e$g>wXwOxYviXPO{Oi#AX;^y*SPx+BF{cW6JHFyi_ds;C z2U_PP8)>F4u>B{(L#<>~X$drQ^UWZmcws>$)kKsPg7k zk!1q5FVzdfzc;Dq$2$e_$+waT*d4W0*Lbv2X8K_VpOzmC+}7YeFgWshbbO7Y`>atkE6Kq zaGKS8?{1%&@)FSIdv0J=_;R;)cQNH9gdR)L`WD=C$-VepQZU?45Q2QZvp!Y8j6qdNFgqxL+&TKNqdI=ajq3Nu1kIkoF~N3*&DBi_?3vX2Tgv+qXl>tE zTz_IVz4IQTw+};WGnu*O6oJ>;Robx1w)EcfYw1Pqs4t;k5)pTotzl&^TZ5c?V>dIG z7)~112j=*5c2+HP%BO(SPUD{KDX^o!%DpHLw?TOQ_zZ3zsVLRIo>4`8&slEAUZ5+S zc850JMOxvKJh8BQpukA+_m1f9uc$r3qvOBv<(|9xw@AdoKu>zbe&0f(g6dLKUaQDf5v1yt zk&aOj;#40*)Xq$)$aWy;HC12c;=!C zVs`#qWkp{?WygZUd$%3w?>tPh{EMul``!`X^-njdl=j6`f79`j)a4mqWBC=vUUU;i zDz$*lyayWjf?#bnz5N~v$Q}dM+rMaSc6RI%|1QStru7uN)p)Ej&NIX_o9}S*VZ~ak z@8PBsf2J*v8#vFmk6*R&xESb_c^)9>{2M%7tO!PI#HVwcd7Bxm372sGPjJ6i&pGjD z(P>gOeb|2Gs_$P(C@;j{*`C0CkMjoK0p@dJkyt1}hnnQ54vkN-ud)mzR^d|Y+ix^t ziA6YWA|i}lkV7iJI*~HnXdx5a4RrEjRux-+uP1K%%R_w?y(i_+KD`_wpPE%&UMG*|HSklw9LL^;LN%FS7HJp(9J~ z7kFghv`nkQvy_-}7$1n6-SEEvj}E5VnE~R(9{hIaxFLS)-iVqi9)8EGcg}F?Q_Z!M z4qI8dU%R`d4zH*jiz{0(Z6esDGP`T21ZA_fY7B30UwDZXK zD~5$AiP_i7;eFe{0Iahj3WkxW&-(DFXsX5Z8Px$8zXU|*y@xrzir>RXhdI9PnT_E7;n=*s<5`MGqpFlaNW(bDYah#n-T<);}UA*Z7J{ zO|x=xT51h4GOA-oNtIpi@D-`(Z4UQdcX8@l&@aG3Y;*LB7f(lGQ}+Pv=fN{?_Ccm| zSm$YB9ojqM+kUahF(4jZGit4om|Zb!yppp7xYf@Yp}e*Dv zsRevYjSLTP(xih5Typd7KBYV-o1d=N6ajnyoowCKYZ_+=dyfP571-CWjQ02QzvDutom*t9eu&q z*&k%Tz4q#R^l0)`uJ%~h<5SC0?xM@vv|(S?8dAi3PvEf^fdoL3|Dpa}Q2$rNe30WS zbe+4P!u?hgJ7;#3i-`#GZV17SFD`PdZL{Z>2{H;(4$VOHZKZ*P#;$%TkVg5Y>7< zHTMhW%$}84Q7g<>UP!+T^YVGP#-`6XNi)NhRF+--St?NuEkQ5+@&P^+*Fit#U_NT5 z?J|z4YKB$fF5;4*AX|v&#h>uk!b)U(0=F;lIT3)zMOc@Bo-KW-K8+xs=W)5WGH3`G z(8g6{EK$-4FOW5JVI{SYYr9$!%NxroKJ}2n3@aY`#L`h%2g-M0IgL?`&w7}$>|eAW zITKR`8>=XD&nI%fDx+o*yXRJB(Rm&tX}@73@iYB%0#TU(T7H_-@*Q9Ow>qx*26i=O zu-dYCUjB5^B4q{jrm`}VeyIl)c!&_aWIZ3Z))Hu~w}~HreUF4|kAFh{o{XveXrv*e zLaTaZ(R5L`ayr;R`$C!h(f(t<6#gaGW-&ki``%&ul75Vw!1azzRZ0r4_}JFH`u7J~ zSN#R(|M)DhFg4T0XMKE}CWpDFJzJ8pES2l~4XKazefsLn2WZG@LH^;m9z59VS)$~t zz_NesKzRR#UTWjHB+}~aK}yd8g9L%5{p+fe4%jeOBAOE*-%xHnjCUhoza}<4Mfp6o z>pMQ6pKlXz<3{DKki+RIebf6SQMTMF3D^tp3pPM4C;S!cg(p|jf4TIy(q&IS2mO~B zQnN;a{hcyC3-?Q`VO-C&&%JXO_j0ah#$GMA;5~y%(7Ewh_RC-18QPQ3QTlOmG_EmI z1@<)-Yd@|NNgXkoJwRt;V+r#nu%jB%hm5VfERXyLD(}I4yzT+XY0BsT(%0-k_+m}0 z@sJ#kVD$7OOBKJ08IlzIbkr5?&$Y2`F`UoB|vClyHk*^``IU+mHXP6rglPu1s) zteJKwbFP(>8y+1#wWg=eOr5TGDC|o#pVY)jzJ)dyU9Z6-43_@+RObluWd=_knT4XZ z7qhjsU#>eiwB^)=c2DxJH0hD?9p1e~?C4IfYx|oCxN?p(V7nd~Yf%1{PTbnc$XQi` z`80oA^ZEHytt0(1uwI)WB1d&Vz=E(w7GiGG*f(rl)lYcW^u44@ z(DGn6L6Bd|dJFJg1NZ~R`g2sO3t%@n9(I%CLmAjfo(nt5bAi)8q-zLo3x)UgQ&4DQ zseUURbWPYgUhjW;#w_qdvh;1w@FlQQA))v+W^bXMeBjX(ZR|wg+hFNb&yvp&2rzf@V2Bmyw)SBTrK_bjj$+cX#NCx#8OK)|cV#ehkDf&H5i&y7lt@H(Xr~u*pTXmAtI)j^>58wS-cW^xSmN$tK%PlTI`&{mlp{T{hiS5g%y~$+|2x1 zY4n(PXROXYIpbxxCV9T0jSXeeEqw{Pg#v;e(}@Zdn^bgix7!p-AtR}XglDs&k(io zkCA9tru>tqiRq%LO0CMZdJJ@LH2A!=UwZJM()mW8{L_44SVKzNyTcH$6S|WiDBow zt2uw!-c=C5fa3u!N?4Ej$z{&x3*56kk$Z0X%K2UGd_M2|{zq3GxZb4coAJE%3ulRZ z?DwYoAa(_>jPJ~7bxX=t$%8xQnJN8z`7<5!h}hg1`2@i{XWrXxE6CV=G2BzpEw|O8 z*t5>3>YI#e#@~~zTr1-KFD1OrykNAKlqoMrst0X9An&G-&x4i0-++a<4wc zhm?J>WmJ>G{?D$v79m(H!x{%*;{xc-Om^Kv9WBx8Ex0tT;t%8C4cXL*iTk_WW-SWC22A=YoCEWl7KYs1R2=pd(6v|SHkW+vfC28dsf<96JeACA1(g|H(77JOlm zJEm2-%anz^`0{5;(Uu;qs(F3v#*&S~EzK)qSCy<1zSi*8%(si*=A|{PiDE=B{=xR3 zMjNM%7SHymUTVdq@Q>qCqN7WUcG0kYK8%EJYV&4FMyCtC=rz0D;Qslu=L9c!uC5Ri z@`h#OJvXy)3~I4doZ8N(J39~=QX$v}JE4?f>B$mYTdpU5F{{N58%swdg~kG$)s`CDE{( ztdOc-K80KV#e0LTSe?1(;uF>uBmMJVi0_OQwROB_pzUzWfq$0Avy|%X&lPnw9oAKV z{3}c_OQ{jp`KpV>F**_rM(yy}Z3@K+9eV2B8nc+ugxkY|Uq8s!TMx%1cDXF>v zmt3)QL$NKicV64V)>p2*+GuEuyA$6Lo^de4EFk9*(d6vD#irIErbI1FYS=h)OYs)o zn+>aGzE=ENB4`Bvn1Ik7(4)So^w%P!SAScjOArzCTlbI-f=Fp&DshgP$E5F?l|=-L zMJA8>Dl-*Bh)DV4WWRk(XMegzt9nsYChUZ`J|QYIbRrWa&pCta76*JYQ~MrDoA@hf z<%J|f{p6}v#$s9#F#ps6J$?eLTaC>y>!4TE!E7~MW1{Riwf#g4&=$tPI(;RXxS0>W z7xN|w%CEuWin%ikk-Gfr*z(D9n$Zb~NmygHKX$n{UGw5L@M#E>2|}f-3q=7tDI#E@ zP-z||V$v^1op9^{xLYN^ANo{`1ubbGeW2eJZ-;9q&mmC^Xn!;^lOIBh$v>d_ z?Ypi0-~llvBl&yK5~Ci@kF|PJ)tC(>jpuBJ*5Ye8MvyC6(>Cko{T0^jaQ%x-8TUsD z&Ci}*_rLwbPk;#31cFCrId~>jeQX3oX7U#b5!vA76}1!J`L{r?*iqLX#5Aj8;arMv zWeD&~Mkjxt_EM*niS<6&w@|C9kBNZzVg5o2`FTZI*q-Iqj3(>t$+f-H1s4=&tw7}i zYwwz>EiWy7pmm5LQQ4@eSS@~f6yhWgv18C~rAC)D9R0Xe15?r@5ZC7437R@}L(rZ0 zR$*^wM_k*yYYQ7oO|W9SNP13p|3op*q>VlvN7u&j0~XCfH47@pW4tkO(@l&bPEW^% z^9vS4qWXjirjz$nA$cw0=^V5hVzo>}Jfoa;HH(uYI$vYe)#7)FQ(fCz&Z zOxIiW@l92(eFod#Ya;D&`TYe8qEQW7!kAeVg3nMWl$*~RTJEq$BBo*e%#Fnxd0QG* z&RkWzDiCCQQ2*?#8XWb+j_l|}ePsRpCv|yRa4^6V`|Jv zoJ(g(LiW>CZzw}L(<%yGYYj*?aPNAcp zTQkZ3r6dz6Y||VATSm#fFC+UdJQ_HouzRL%2<<4L22uPcRm zhwHBBvJOIE?cY%MzsZiSY}X|O6N#zEpmrT-L6zLQ9%UGMBz&;PCZYU27|C{Z^n2fs zk0f9&=k9FrqFXDijH+*eJ0llW&XKuSmjS>&VYkhGds%J?rUFxRwaH`3MViBN{gEW6%= zoHa5^qxlP?i8%`MP6;a6iEaIcZDL`Y8~ywd#Dsks6JUT*{GX>s>Jp*}JWIexdL|K;u<)5c>9V6-8BCjRU`~*E=pddcEjzT@#=pB?7Jx4}3IiR}l*W9MV8k}DX zTF&}|)$fp^Jt^`+0gRaRrLYm$``kSs&+UNuYBZ`B$aC;WZQtF|Klkv%@>U*aW^ID# z1SFuf4j#k`QTANNttm#Nz!FMK#S(e~B=iK;bNoTM4O0mvW2pMWQz#9rYPv_f{CA+@ zh12^(dVeYnz90d7c?Tq&0)AJ`W$UA9y&{}&o3E!Rzy&vks#Ra@coIcM4F*Wz?Uy=FQdRJ|2@A;p#oW`r|7_G}bstr$k>AJV& z_&2%I%5o;2b(L#NE!De9kfqX@AM)x`<@r50VvHRFY}@u(Er&7P({H)*+cdK}UEfjZ z9;-ZG1lB2($#BVSQxN0n#c{3J$bTsi>0S!ttvsbm2;_xk_I#0^AQr{(%^ocRdH7wP znabe1bU1Y&LofEM>DR{bFY5`>m!4iZh@(?YpJTQ<@6oHegmZ#8rpKqO1){xJ>7d1S zNn!6fM(V|fxuc21HV5W9yEYB9Z+>OFNqEVKdqMF|BK8X7=EcaVU4CBjg_V*n9AOp7S>KVC*rCu-l*|$3SxjcQ}xyn#y zM`V7em}+HhI4{OJaYIl~XlGpe!X{xKoEX0TO8t+$4R@Mh+!zm0=|`NpAzMP8K6hU? zs;9K9kK0(f(S1t`ob)VR<^EdpTd{AKy#2%w?LdnN$PvW!I9sbp?P_7P{E5@9d(HY% zldH9>*U|YR!9=VfY%LTCgPNdONSi*Cf+qO);Jq{M`?`;-I86vy?4qGiFKvhKtfr_< z2dXypxKeAL{1is!3};^3{6US7&Bxk+wSv|Pz;*_r;!KFp@ZlwxjTwA#CTK-z3$N1T z8Z}Y|K$e`G$McsYBIT;I(Pf@<___ooP)X#Spo%T?^mKDCiul^dpXsB_$pAUH^hTlZ z^t!J%W9j)tye#DIB^Ky~65X3Fx3p zJ8T*EHSjS`1J7X%WNT~N^aQ6>MhDD;u_m%w#ePF8%0Lq@9v-5h?0e1fGLH`xOOLxW z)I}XuSPgQ25gK)T3)B!xK$+u|>64-=PB#}>!2@&k$L`8Zh@=#aZ%dbq+t4Qg{}4Vq z5qM?byL;d-3IZ>Y49-!&X$fUu!_UpPTW;KP#M&lxRNf5-?VXl6Q*Zk6nFWlh0(R5# zPXBP4JG~$qE1w_|x_M=}IZIu$)p=+89X*`%WmMAYY!#h#*Y{#Ho#l=SsCxrq3S#v2 zFv7mnjm&}~o-Rx&+3E&+BAf)m!}U(PB(ev9pAyFLEWfR8^6++FE`?eEy5&a11z2D; zoB(q#UzmG+D@1q}-?L4*O77k6mNwc`BmwItCYu7BbO}D>vine;g)pNCbi-}giPs_0 z?*1&1n@bNuJw*^F%yLqMvP4_`oMnap56<_pft=Aesy`WrrNPOpEC|Yhj9!vkDnOAUSE_k(syh2f$F3JLHCu1GY zas_x!L4Rg@BP43l=j#c4NOF6|v)mZQIjfm4(qVdd&R*o~K%gKd* zsmYZ_;a8dkXNX49Jkra_e+QRJHZYh<-A})FV7Mz!QG{0I2lalS{_i(~?U|*rZ-?Dv ze>bOO(dFraZ1ke1#R|L|RY<&a46;jmo1rgwV$BAP^gs5&hSUHmtozspxb2+sxajH$ zF*mq3>I>v*%Q2v0I&i}RdTY)ach}Q@xzlzQA^WVN^Pc{$F|cTWjxOSP`5$7ok9N%s zigLX~+eaV5UU06@uA45%LgFP-WY5h1;-YA7m9zehS*|{F)>UKF>fZ@G4`){}-f)t{ zYWE>qvi!LmWWAHeTb}N&97Q*E3B(B9(!deAMSI>xx#1S$DntF0wrH>vP4&c^uH3X+H*#^TIQGQUZ@r4Z+zI1-NmrZbF4?myA{G~U(l>+Zi?rJZY^U(_|Ee%8T~ZAXUG@5r-NTun9q zVCEcv{GdBK9p{#o0S|6mON)Lu`xUlkxhtLW{ierX?D;|;c=Lzv&OCM-dcU)!6rZ{| zWvD*vx~u+d@O$-1pz4twCiic5hmqRjCV0`cp1JUUpa*@U2W+OBKdjqXz0myC?xnvs zXgL+ai67d=?6gR1q3o9_u);12q$qLeELrVr!+qL0eOoNYL0hxu9uyB#ctsd@ z6VAR+<+2SS$ERJ3$g&Vz9`yCh6nb8fAWJmOHCwqvN0Yo95!Pj}UM!D_=Qv%y|2%WL z9_9tAi=Gu{dwBNUf9=8FE$mD=#5&2DOOx1s$R!(II!6GjyW>Uk)F}HmZK8|Qj?WX8??x1B}d>lPZ2KO9tacSE%$fYgU z#g8W6b!W>PGM#P>2*w)kku@y#5=SYdJak5H2;^x#SO~teA_Brm` zb+y+A;B04~*$R8!u+nW{V{VzuJ>5^0(_Xbae8$r(gw=mD?4O6>XyRdq$Y9L*&Oej!9=IOe^i) zm)1Us?hMR3^v%fr+TDJ+hC=?O3^lRzh-pl5pTArqLj2mE0ou0?MHpu(?X`}6n7icP zbqgt^69h=^MvRmfd7w)s-F^==Ju`(E_M|*mB+ONbjkSa>#3ZECZouk=n;BJalTSDx zyu`q4#tVC!t2@$Da%;;>vy{d8@=vA^K@3F71VN-s5X8s?Laa<6oVt|3sY_Y^o3FlA z`PO4^_pg3+P30PxyUEivOo%-Am=JpiUaSgva-|iI%XV>hx@8~q2xi|Kw9p-K_f4DK zMvIc1v$OBTf{yjYDKLAIY5fLgP(>e$-3c*q-8EOMe$Or#ps8*4RQPEpl;DjH6)iEgf<=2 z>vn_?LDz*hC+X=QNZ8YX*q$QTmw^}@<~Ga%6k|S`;0EWUS-(Eg7caN}f$aTB^=t~6 z+_O!TlD`e>T;U0vQfd>Eq~-}ccMwQzL`;i1E~zwW7?g=UwKy6<*n!b+Oqt{H9w@bu zlG>!LFps@Z?2OF8aXBxc2@uP}&3_bK#mV6&SZ#6ZY9Vpc86PCu(%oKQc7(CN59$4Gj~t}R*Xpk(xy8s zyiZlq8Y0HE=iabo=9|TD^4@BAZRYCY)p!TqEI>T_tJZ^z_IkRcy zUD{?5nCl{uHVnscV^7m?D&B%p%t8wK%-0C5GlkK^jRI+s-)f%~?FGe_yjwmu@>1}= zP@W^PNL;ra!T8H3zQ~2BHiw%Tc7+R!;Jbg28c7YL#n7V9kYCbTLDI%D;dX?hf#p?oZS2)r*Je)fMOnA9S_+$k zEM9i#pWLkVC5WUUE+4;$c>%q(lKa3{0xz}T|@u2eZBiGpLp(EFD znjnib1-Gcwu^VW$J|El?ESL46^oW z^|^-Znp**NVG8oO2(caUtw3X0im+^R3d(P8FA(zaU2a&@k7@CP(p5_0Ez`C}G5pA_ z1nn>ICqvAepGHczrn}~@DTv-Jlwvw&)z1Sx+i)DF{MqjB@EakYPJ+F2f4&s_zXbm? zIJ5lbsR7!t3#{ZWMW2fP(h&}eLYS3?Wl7l6*89^nv=7~v5RtW*2;C0ro84W0Vu66i zdYoBnD0>%T#WLv;oichOL(m1&11;crGG}?_^7nh9zz-}I63O>Ca%kVx60>9upMH<> z#Su3Px8sx68V6TSm|h2|A>w6oH^Oh^&vd7AUnm{CUEeKb-%jjKUr;JuXhFHLbp08D zd0`4FFc{dVdM;)pey5m=8Sy(UBvl1hLKS5&2C$OHa+q3o&aKt!8oG{dZ8&@}mTo$O z3WPXj1atxPUG~@N`rROA#3#vXb^ETetveO;;>Y-$HcMATP}@=Q&YauQ7FL@-bDUzYpF2*N z9e&93w&xA0L;i^`@l+Z8g&U+ST#j|CuvxkS=kczXr1w9rl=!^XH^u&Lvu)V%+%Qfz zntp65D>YuNB2&_+Y1{i!)3SCP)=kDII_aHV?8ztjp_CNy+xNg@O$$9yjj9Yh_)!I9 zazw^{^vd?MQDvzg!mjLHMZ}w>=+b>pV9|P@0DiO}h9*5>_3b$jw1bGltrS5fD=UmD zB7B#`2yH9x{*!psXs)jwPdO})oK4sb)9q<0dTj!qQOP60pAYR_n9^Pl0Z|KdlLdjM z{oR4bSFZp1jj1f7GL^?ZVCg;Cz1mczvt}Z(KBHfm$7I%Q!%@ilV#|z~XuD(k4STBb z2Q%YAKz=UfZvUnVuqbd^mbDuoXSSDGyZ)-J#8@8TJ87TO5a|Hpf{H};Y->MB_F2nn z_o3aH5!7j&}oc&tS;C+hAv?g^eWGya~SLo*Vkb+?5LFzaS2D zKx`29(rvXuT^WpKhZv-*A_;yKL+C0=9>0>|@ykfQ?kK}IGmJp~Nh;zWWd`8v2a+G{ zhNvtDL|fNEymT#Jel&$A|6Ia{aYZ2ioEq3<%}Lxt?IGo#*Y?|Q056S>F6fLu?-P_k zj1v9MWu7^vYe1{&lh9`D?Zoj}{O2j?$+allB1aT))yl>)#KJqO6Y7M|DcU7n;0@EN znq2Yn@e&$GknyiNHLzGtwX;j?SykvZrgtY$(2FfVkoZ$Hha8e`{<~vl@Gv2S>dSmuDceG-FaD zr&a2oij#GdAtDb(P_twMZ=P?h{ z22ZmGjHYWVdg+9fPfNg#h^a7x*K1|QCyBfi+N}$TH#r8PcDN;zrofvU3d%&+5$)9l z-Y^dyedd7RyMi*)N+sLdkEiO)*Zm>teLA6=svlU$1Z9|e{88EUm4tjok41XyDh!(qyFh)jQYuB(78gGV+sb!^aTojxgLq%JU0-xi#AYxWg-uyHRFLA~5HYTCkiGy5Hb9pF$KSVIN!yoDGO1!0W=zFMhaq zSh}LHO}e_UgQl7RqfWpBdi3X>57vAAdfwnLT1bMw1s)`}5$Jt8%TvD?Ib*b(m{0Bj8EZ(t2raeC3Jz9rLrl+cYsr>d|D8GKN{K|T(%L^u39G(FuSDP`9;AJ1^DfrPs=Q)sW zDl=g2IXiGJe@z+Yk(F9v70H|9e%In z-C%uftkkOiKpgg4;dk9{wckxvP875{Me@#KJ}CiT+U~Ovp7}nVHP%vN5S$Ek$;wu? zrs|w&Oj+6W>ulS&=9V9`m{wK+(qOw|P9SHRPjw8Gby;AANTMmiws!yz>R)FB5zQrt z2%UHize#&{yna-JWj{M_`7(><@a=3zaBi`%s5jh*^Ltf$DGIK{Dhmvu)t|oaQ*9M` zY5IISw^@a@-(TJaQ7Z4HOEzs=x{b3?(->@_a0E~|;4`svn7>e!!bWEVdOSBa;`b0e zeZ26w+>_GkVrNv4_lC+&*IYg7>!@=zsW3N|9lCzxW_kOGo-=(>|G>5Y&QG+4H-@)u zrMHX58^$3+(`08Hr)*?#3SQA3RC_yl7|*VnpmZ5tPpE6?StMGrqSUhFnyXB_qDbRV zoM3%%6Jk)f6%dF1DeQptm6y|hWiow%*I*_VSI%4+ z3Ni69i4rj$C*-eVnLNmk>{B>B4HW2rPSls$hmoZ4O2{b<#)&ia{IxzSE|;UDd$SZ) z`mT}I(ZA_lx~9}9I>(Uw+Rg!nO7q1l{p5R5J85$*jBa@4Pr*(atf7AyFUoXC|L^dk zU>(Hn%4j4`yr@suX#E6TorvHtqworJ#}_)QiP-Hh1HvK9F1Q?_@=p#R`R50ae-jr~ z3dl`uOaFpbo${g{-mjo1wT0?o590y!r0oh9dQu^rB=|M-r1SUVST!Z|NDe(IJ(WdI zYIC6{+2#yEPip;*=tx)|OKcgqL+Fj^Lt^XgR zC$%n1`33Z(*8hLelRotSU$B!JQXarg`UqOd?fJZ4#7?UICVVKGkPABrYYbo~v9>{> z!?->F3wDx|3cIGW{TWmFZ?ThD3T@6kvNth>N3oNJ*4Bxg#M$i(_QdZ{?4()0jGZ*J z6!k`2%CBH2v2^$AG2I8SlV<%gb`qw`@s8GEst;f%|sgR-ANqdH3Cu!InuhIV=JBg#x7>Kcxxcd(S zVa4sjPRjeu*h&B5u#;T2s`?8!>F9^ClkjSg0_-F>1Ay?l^&#xjviNwsP5%u( z9*p&x{~1N;gL|PYb_^>Q^(esq1q2vzhDA~0p6x7( z(p3qhgmXb7z^m*+QDXBtMgWS^PM~5qBjQk$N*+W}VyW2rSrjF%)Fvm268lti^Pmew ziA^a}|0WbA(E}(-Y|f%@oG3~xrw-yF6eW(j?I9E;_AR=MV+oxoO2hvRiW1>OQS$mF z6s28?mL+uVeH5jC?GALJC|&&@QIx*@4Jb-;A3;${`M*F>S^y|YANp?q45dUCLunR^ zp|q66Q0jAHD0x4Gp>*@V!cdy=5Qb75#z10sRM2;3z)&jHKZv2Ufx}SB`jEp=IwJsW zMEr*^l(;rNIs`-M>Z+j_O5FD&{}DrJ$8W<>I{pBL(xU%>q4e?p1%^@Hr|n0>Y!-m`Peq7VL0F_c*DR8{{oh7y~<_Sk=qp_Gp? zl(HQ@fT8s6FJUN|_29926hmp-*ZpSwPz%@2DkRc?;r~!CI_A#i1wh zQUN`wjYCfwq^S59<|!CG=`jvHY0u}Jq(dqnMNb;cWjg@+@f*;S@V>yK=t+Zg)cZg_ z{|-HgZ9DM6JdB=H^ULT-gOsa3fcVBi^rVdK7(K}t&7mh*06j?p=t=O*p(lZs|Ci`V zgC)uGVON7iPik3wX?unKC8ZNR>1*)IW2tuaff z`i+ONlM2DhD*27rNrQA08~;1(q`{m;>wghDi5&-Io*^|D)rb*bJfSNV`6mUiOT8G7 ziZBY13p;6$mhH9QjGgquAa+ts^ds0w)i3d{SR_Wg&ZD+q+@!%$S?nYRTlM5r&|DTf zsQ{3v027bJP8#F}(Z7tH)R5xDPFjkwlPr3mrT=gPhn-XiC{MqFopc3bCk;}UEwvAG zVJE@(SB>{k6<_@_cG6(3>P17blU(_#zt{xWN%S^KKePmtu&(VZjGC0&*RoGVw+^Bv znFSa%>2|Ah6+U-vXbkPe=gnQG&nM~gZ_9&);OYJ4^X3>?=^((7gz@QD!8tJ%uz@qu z7V9ESNH<*s-anH@8(cuSBl;@~#D8Q6lp>6jAh_1kOT@^Zfv7P+)mdsaOTy8QmaOOE z4K9z7`gcSK>24P%_oXeUui_+vuJ%}6-h)HTWP>FYJmsa-G}yz}urrN|P7eaSHydlG zKIlOxV@~sMZb;3p#}t*}`cYr#=I#??~YGL}v%NXF30v z8t>v_i66#c{^_OSQJkC-*Lh(-J1RusuIR)m}KYdzm!k%s1zC~Xn;Y~Y=Jc)J>A!z>@_ z4O$z=cj2Fr!!I;7`#yXI_r z!yu2{Mva7YOlH385P^pl+NXSM)FMN2z*1;!3&Y1;^Tc{fik$VJR7VpiWP zPt2ypQ^^2EfF9OwjHX}~>fJ-ZkL2>K+M@dsN=<+T(96oeiSCuQ;G%T~I%mK{v@OLj$w@)Z@v{VbA6)NUM~g!3`mM9V5=`+0kh+C6umqQ;*p#eSx0UiP%^>HmB{Wsn?f8lOcUmFanFKpH3kl z9sCtMEENV$ZNS;1eDJ^G)hWkOI9Wf~J2*7DAz%5kF7~~2+B&p94*or$eNJpPEh`j(MQU3r z0frE{i30=-wvnx3-L1QADp|D}5P@o|wpB#iYSD1DYQ1nTm;XI83DE9-&wl$q-}C=Z zA4|@h`#JA<&wD=**9%@m>)TpJ1j-EheP9wOG|j#AzS8ak?ccUuZUnuw%er{WGQe(P$^A}>8vW5^1Uk(4 z5uzeZJK@((xBHCSI8ySDG{I^vKUPw%KuZi}st2rv6f?!qSYG9g3bIn_m=xKsg!+u) zBR?)e3H2Gbo%eXfIMO2)-e4rdr5C&Ae?9+p(2V;e9iS(j@2e!FKAtuh+OjDJY)j874X{aVEZqfae zWGi@ZHQB6?BY`r$8G!8)I!J3h*qwmoIs1d0kbD8FF8iZ|d22CZJ>>W|MbV$U|L3vw z19A5MZ+bYBTYMoulGFKEjvG43P#CS_-OB^}7aLn#^9WtlLGFqSg|VMXKmNZQ{TbWZ zEo)ikoQ{0yQK+;b)a{lUM>v(}YLH0ZM&C|vT!--p*f^vvNGHZufoDmr z6d5z~YD*S}>mYuCZ2MexrJ4wpMueBd;#mLtGxt}eR-cBrL=dy9SS-D_N+|t7i43j9{>Ez56fW=UCWV{#qo1P9`z=#-5R;%cX)cbNhDcnhKh&}GkAuE+ z`(UYoeL)JgrJ>R;^>!PS+||;D+mFI!g^dsooa5&~y4`f^$o)?j5|- zQxZE>N6grt3GpMy-%>kb^eERzlIA*=CPO5gS)5XZEmR!A`RfT?z#`AZA1$7_4D?+6 z{6N=OeGt3jB4MUGdhq+AY-~4arVDymPD$k7@IIB297^9MIhmxobMUDT?t@1P#O{Z3 zj+eU!pj_V#JN8{za^rm8x80Z9Wp}2veTO3j7$0lo6$Ht+eNFOiNCI)gfO1;Uh-s_h zHqjfwZBz4h?b`?5mc4`h5_qr(VU0Mx>HG$=IAz29w9<5l*P_!)$m`QdLRx8t7nw+S zE2H9kYszg#Hk)*{DShu=_u?v z#34esG>&!3#2XMU9R~3~8c3i9e$}eb#`vbuVjJFS+zP~iMC=PRN{TsF0dYihtd2%_ zls&@u0GHFj>Cg_mm&F6mJ|B99x3F})Y?q83RX3V-902d@WQrYAH-Y`>fSY3!br;VZ zqBh>+;Z@&2jx3&PV@GKS7DY)MUeyn9+t|BerbYaP$9ta~FRTotVQ zU$la8LyQZZ!=t*LHv|f4FYp%&fReLith8C_5Tl z^To5|bP6cDoMtJo=#?cye%b@&A5|4ne8paG!SmW*mCM1rF+yH-fcqi*hENLys|U;D)S9kAeKUq@jjeeD zyxeuW4&F;V+4$r*Y~?qr9lx9?zM2@;_%Kj?U$o_m|3gxASlVNT|1N78O$5PX@?#Tz zxw7c_pE#WoY&)j^u4^MMH~la58*!fSdmp_K-o3Tqdx+KtrxEW3e({T7OFG?NRGZVg29*b(&HujqGP~q!B(~r3vha65{qj&hAJegf z@FFIm!PH=hjxFj0uQqWEEwP+^sN_L<9fA6zDea4DeNe~XR(Pemd_>=Ox?WTlIIzFh z^!?Q@3V;9H^dhdU#B-ZTuOU#+L&`Qfg7vXML>u9&f7cql?mol>xG~(%hx96la4P?r zg7}geLc0}q&}er>fjX47bq4x7yb?WIpm07RJ&8OORY2NwWARGOP(^i+4};L(^|_8a zWzNWB;ya(1O2hvokEhF>_xmbhJd@ZxR@4(sYkdr8))432U%TO$DX=jWBo2quo_z1| zx94tq^wmc;&i<3^c1qDBt=O`X;UW-@TZ*|Bh^}kEyYVmp{au+3w2Q1}u~ML;suzp9 z<$s;9h~*9&PNfUwgnZ4?$1=nk2Q*^Iu=BSu74}$bdD;Re z9aeE=;Rc|?1WGYrm9u`mNQccF06Oe#Oo#1U_3!C0|4YB6!)8tdIt=PFZf>jo9UX?Q z{m*om`uqP49p?WPro(<6c@xLl3?Dz=@M-$2bXW^K7k2St*DlSxq6|$@9P)(ppeWSX=)AwrJpkQq?rT zEP6P|BuqifRtE8&kE1bd>>hX0sAb*jv3C=yCpj(lUo{tH5aufx;hz@x95ffn;kP^- zJjx%bwmS^&1LXzN|L#c)pP&OC+Zrb}lsZSQsSw9)`G}z+1bl^09**27u2y#D7`#@6 zEqJY1g7JkF5?3k#F$3aCskS=MURbRzqCb<;SzF=}UMrrOP2!aUx=L}9(l3U@1BwKeXQVF`_xL4;U&OzWo2w<8OT;;jl(T$Ae{R= zAl9xZERB6W3P%|f@cHqw3E;&VCjjQ=EFjI#&vE@l`8Q-f*; z52cYFDN1737|q%&(Izu_jjgK}WO}sxcuWTwq8SS9Q_?8nM~rp3%JA{CI7XH%(`x|olLh(38xRGNO76wu!+%qfljecc@TW3T+*4H= zcn49RAqha<_$G{V9}{fZIqI>-SqK9-#XDt)R|cRE zp!mZI{K5rKNANj?{(y^z{GZE^8wGamB=GHv;I_#0sV!PWX||h-s>Cuj8CqVlnP~~Z zzUyN_3VZKrZpp2bWT>;e*p7EhtD^W2Bt+hZlSVrTR7j5 zdo?!%O8fl`qihNCzhh%sbm}@=QX)eq?%*~g`94FDRH_|cFw*Xa=ZGC zLavNFXB+A-Zn(b##EAa7T7HITzyGSwuKFJ##~$2J57sRR`*|!Y(ZiCT01yMe^ z4Ry|*lX8t4nrtBwlstGV z+|lv}?=xGsBFRbj3H%fZ8TSIoBr&S3ufw~vD)u44PYEOYr?^j1ZGA{gn5<$QMABr{ z)+&O3Fac@S?Vi9)d5?U`sA6A0NmKYK2_&3vEI4meBWe`yaoWo8mqF^MP^v6*)QmACt#p z$P;%`Rab@>O~)W*6lI?{O8+#c)L1=`bd~hL<<=*M<@d+v`RPx#d%LtB3f*8<}8PxZ1Y#lx|$O@n$@k zSh{WDwwnq1M}~54h-KB^Oygv)9Ee@NUMa!6Xw^63UrA>Oy=Xa{2g2_KpaOI6U|(*D z-$k2j(ee-ocTloIAJhY1Q<8na2Rj+w{xeKGW&pY|q+_suem|ViaL@*YNEa>lTa3%g z8udX}bN#*$d-d-Zz78fSaVxH`6~Fi`4hN5rk>8Sg;ahxZtXLbzuyu~F#|GoOv{VV! z=aR*(r>E-lf%o_DE%QO<(u(+&K!iv7!&yGw6HV3l8sPp2WG5X2&v65~n(ON&Bi82Z zI`5qGeP6G1!d%CpZ*t8A9F(Dn5ZNF)m%9Z{vp%Tg{0%$q?T4jUzNZhmZ=TkQX>N&j z!-=UnzF7$oADR*09E9+it^9K8Vt#`scx+RM^_5A?K!n!i`dxsC-NNoP+?p1L*M$g> ze=;D!?*!=q#$JA3`Ypw4$G3!x^ZW9}rFbXtp7QCkdv&=le(74xKR015PjK2K zO<-TT(`QHBi!~W$CAXFO>pa}I5N(e znjC!lV*H(wHv=$!mKH+%@;c`a<90jD}~T z530-s`8mwQ3A^9Gfs;t_tYsH>2>czhyv;-AhOi6Z)M_(}vvC zmB0y0%Enp~XSo;5UY|VrF zdb*{q&kg*W=bKUk={|G43FQ690gxd+u76h_73i2K`(>wr(1lR{wS1~J>jmB`2puY% zZYg)qT0{kb+}u(=+TmF{a3Ge@2s%Uh9?9Me&ONUBp?yub|!h53uVN8yt3P<%ziJ3c0k}e+pki5F`+-^j-7!% zKuf*345U@}#wJCv8*F5Xr`WnKuSb8ZpeWoX>qGrsxR0IiUPMA}QI(6%04T;IlnqDo zSk}0r0k<>@YtvxN;K4`}k!(%}c_=N!?Z1}u5cF950) z?Ck}-fx4)fj!94S55E=D(4p6r3fc?&Vh_1FjMD)0gRd8d{iQ=>&@#xRK|g~YGkGt< z6b&mY1CHEbs)u)laVeMX8@$^)%1GxrRxvU)>4c{$Cp;xPA>kTf2~Jy2ffhp?Q*UV; ztFbt1rdKnvSZ7U5g~+W|zbn?XTqP_J&*o?6j=@fW+&$v3DfKk%tYI##HNhtxE%VwgcAZ1pJ7DSz+juu*6<_AbZpeHh>yIt+45 ztR0|q*bm~*>CLP7!g|1XX!LxOP38~n2r*`4Xd%`(n6!W$)6+LIGJk2gNw0NTN^pUd zv|~@r0ImV@O4)qV%0>FX;#2)0O@kan)S;6fmg#0=DK5P4-BdS8m}w^+ z5`;#B_xd7Ei_dFRE)g^milN4>GDLdXU`*TfVFjayC~0Btw7=A9XaQ=N$W!VBJy+}@ z({5-t3&iOu$xvo4W70|3<+R%qTV#FHoROY!-G?*Pv+=qy`SoaiehBRkaY-T;NoK;U zc{7+oDM{>RJOcp>H|Hhz=A}+HAFXiYrJ$eEw9JB2kga9A4h-YdBaEy< z>V%(iC;TjTL4*ItgoW{PX^vWoKQ#p#GT41OmZ?V{>GVONg&incbG)DfiICzr^28P# z(fycFTmc;EnlaQ2-iH&{;yO1bfDD-v{{H5Kzeo{c{SV-C6P(7jmC3mCsc){UD93jWdYpA7c1StHKZj`R>#I8Z!RZ zaqcmXjxi~mSK(%sWx&m>5J#oiEsm=Czr;~lG6)N-{}2q+q%*Qz&azytSeBY8mTlk& z%b3M%+(9WT>5xMFqSOu<+iWBESBm|l1HWD2=7gV2v92aStm~gbSO7Y?4q2tnQQXm& zt@)kBv<=oGJ#Z@{vp9Q<%MfcdO*Q=ybL5AH_~1-V!*sNdJ!X6?t2|S@g(^<+CWxa@Ur#vUXU#EFGRy7^mgMQy8QJ7sDN`^L zIp2Sr8En++*z_LKnhx<}YC!q|H}4XYM-@kd6kw=aMW9e_cMqJ!cTZDG%moljZbk&b z1wagN@#HSzbmub^iz%I6%gCg%vz#kBc=D9ci93nuk3@^Va*`%fDW zwi*SQ5YvBP`XizR;6H04$yWth3gV|u&mvJ`B2w-VymzPjj*0KY;KAru%6i(#UZ1(o}ccV?_AA>kML^pmtCt#Ys+CZ zsYqc30wbvDGiJalKwM8@6)GN^7Oz;W*B#5?w3H67gYbgoJ;4%ZY^cv_Sce(#l7-Vd z(evNW8_!vs-G)VQVnW5)?O!F%F5rPUJMY1UGxxf>308K6CnF|8Y9dq?iY96{~K4W*~Y0wGt zH$!<*ST5TiHIy5N)4xOwpaThC9x3=7Ku3J+a#E%56eh5AEZc$BCw?=O zM-kzPSBLUE2_i9WC@+8rq5B5ohH~dYx_fZ_P`(Dz_XgJwY~WNFTxJ z?x{oV*Wq--)S=aj!{_gpVt54uumr4L0#0`o9|39$b&%t$;Wm?WKtxI`D~MR0@!S|= zf+yCUyV4*LLRe-f=K4la#5p_EX)2RqT7%NDR1=BYX6X*n?>T|D2TO2#52$-V<_;}k zeL22oaVcDaR`=Nbp0mlMz=3~b$8z!T@tkHF*loL{hnBxj<4AbpQ?GGJhf@E>W2+5E znGE;pO9u>I)3FULUpI$^BD82v|$I2uUf zlLEVS4|@4=lURS<2_uGh4&%_VLjCLE;^~JR4D%YmqD{h=6ULHt7?wlm`-2@KJiLS> zyJ4x^wd`kpbYiV__*?j|+Wg`AJfQ`g6G9V%KM3t4DeJ^Dw+Fwk5=6|g3saS3;((G&rkITd9Kz!Ux-KPYW;J-Zp0wY!#z;Y!@1ngYqkid8t0Ad z`L8w4AHnPVu5P&JS8F+;Y@ha@d+w{r6Y^X=_i0D=-1nQ-Z`Vu`T7PosaO;nLsSro9 zBs>v+#k+uaLm1`RYsZDrEIQ>`xves<1F-h1Rp+eYH{8=HWhF1}#FM(=GLP0K9mg=Y zAD^nB8^7hjFwD8`gXBTZh*u>+Se24r-3Y(V`=LhMD|{#XI;Z4pn-exUqi`==e9Z}& zoB%&!JR}U4&kDQE8B11(xKo5GF^#e34WXWx#`yDw2xBjZ?LKocTc|JGO6}Rgxc7@U z+1Y)<83U^EHsOE1dtaCIkEo;?t3{wtmAJc0$fpL1AxM_0Zd9hGN3eF zvX0r_gUpvA5Z~S|p%k3dcjbrZsSG@JN{UAhl#x)n7M<2r{8MS5(xiY#7LU;p8gqRa zk)7$tYUy^;3_hTD1RF9D+a~`cnJFcti_v0NkGLn?>r)v-0PdNRHk}}xtGVcofKP9i zJ?dKjvJ2w+WBfX;0b@FNQ8*TBJ5rJ{E_Pw!MSYZJWctso3Qe^*%H>=pn?nbJjc>hk zOg({|%_U~|g zr-_~s-z^Va-)qrY0Y=2u{T~S5Uoh8)K-5YuC=@Jq=8dT5f6tX4kE%y}x7>AoH=!Fg z%6X5V?_9kYklwCv-7Z(~*S8@Sqgb_*~Z`?EJx{ zOhU(4D!{DtL>MZ30VaSNfD#9n;=A{pO`J%xqc8K51^#;`B{HDI? z`bHDqxa_cR;BS38@Us$SrVV2HzAOEsn1*%;qtJH=tXVDgA-OZG(*IS8bz9uF^9{ly>#Rx69x5dZ+Ub(XW=j>h;=#BtkO@&Un*(SAXLVIyN8a109sO zwpJow%Qqs9@ZWFB5_b`Zgp0j*1jZBxAc<6n5K`h~-4_ zAC|CWBiVVfsXvBGC0n^?!rCjJEscLUJ~Rt^2Hcap%(2%DqfW&i{r=O$q!=o-CpYYY zSU2dyEOvquPgNXr{GNIMcrm3{%^KVuH|iXGuXrw&`T|B7lgZwBn@I5$aSDBj|9%vC z%q*!L1@F@LKaunV-_rv$2weeKMPh9UA$)xzk*Y0-NSb>;@u>{n(Dk%6u?O|f_4W7= ztRhbjC=+xXApnPdUm6dKGe*mTZ!{;k#W~l54Q>jK z6E$$)&#(d|#Y*SzZ?F=A5^>nAKWF z-->g!9=kTUgJnh|>Y3(*U}sJL7lV6D@p!cjzno0~t4DFCt_Sx|OT3jh3u33Aydk!1 z$btxoLT<%}huY4^xjV-eYi5^$zChTq+MQXf>w7~n8$(IO@4Ac(w#L~biaK=Xb>|!{ zRfq1oyLM@ouRDbfrDF(SFPoUTS#-Am)bW2EJ`C!I7icxTmv)x z^!9_+^2R+43;VR)3tV2%OAmF-a%n^Ve5hxcb8IMp2p|(rz4h{2GWNg*?duyhu)qJ9 zWLHm;uoov0;aW3|d#oisO+RjcsM6Uo6m}0h7e>8CvKPxB(#>enXiEwiV?R!hmW164 zUGfvD8nuyh)PDSs(>W^XLEq63|730Q7#?B+I=(X82cIBerTrg0)PFa@wac;L!8bwT zB;=rZ!Ofq>O>pjkB{TSzxQjtsv)C2RuN8WSmb&fy{zu`vDAP%FEV0~5Np!^AbJo&9 zF0>`&#=hrRO0_!9JliJLq~FM5XE|H+=YsD(z&kIt^9~yUHfbwAY{4^hR7AGq#BWYq zV?5c!=@xX}Oxv9Htud^1-x2B=%Dy= zVXKk2Dw->Qdy>1s?(lv-P0_Y~H{Z1^#X8lrRIG67~;ea-cil$E_yM4* zyXe0D1XX-J!E+rFGXz(D4PuPilzo6x%tc=SwH-o3Jl_^2a1FlRj{sE(d_koZI5|jz z9Ys&>H}EB5Io~Pkd-tNXxrUfqk8c3xVaM1g8{geAcDtQ{|F5?*ogHH%?O?Sxc8frh z3UgUX8GSwXW6sNdCimShaZS8(oUS`Mw4e;>t5;{YUWV2{f4&{`=i9d|X%+oUJ8^9I?|AzZ# zPrKX(<+7G8Nmpphb^D2I=ZggX`M9J%@>Ng>G{zM`n|zI>k^b}i)HVgcgO9ZfJlll! z#(~EeF5~nZRQai`Jt6Qi)?EC?f*`~6M`O+iqabm);zn_7g3a1Rtkqot+D)w2)xdqX zBwY{jsz1hWG&<|PW+x)s>?Q-opSpsKMPL;$r0;tOdNPo73VF4B#v;IGO#(l-!dxE& zw|U+LqYi5hrvI&Tm-rp``uVqVXytP#69Q$5tFpmf8>e)TYWtA`iV0TiagRmj%bmoX z0YA`E)A!Eg`U!gP@bP1?O$x$Pd&TAZcqIOq-m^iCqUS2NCFCZ}Tj&{{w>mrHLXl30 zVqAMg_uo=1q(g+KTZ{WiK*d~WWR8I z&{H}0vp)vSJx&pU$KlNaeb9-VwVqi0C6ssb&50MFEm5^tA^KbU6eVta1w8smRA{%d8pJ>!pa(rE#jWX zZWr@{UM+=a48SP}{puiv>*FtY9>WpKXE~uPb`lSsX-X)~(D9qXOq|xkA*KxRVJ2@D z;Co6vQoafK(pNTy<;!Dzs>QJ-&!0XUdp0;zomdC5gmY;@@My($Xcmta-hvo0+T_=r ztAK`E4n><g!I{WxmDL|v{6RRN&aC1I8j@{*4M|y&X1Fx>IvLg1lXafAtr@i83HLN5` z7MY%a{V^I3JNnz_^*){q)9 z3YDKcLI` z`4y3Np36qFIF~h`_+qAj(8TY-AA_7Th6wox0l{KaQl4Yw++#K zYr5C9zkF}Q?ai&PJ26A@e#XbO*M_dM_~a&HtWf(4RU1|evGr#6paXxDz1jSVcGH1P zvduT&UiePwJLJ1JUtRcG>1%N?*Rv*ZZ$7+-?|7AA2I0Z_v4?i%zx?@D_EtTTv!hwi zsmsLqHSA*GoU77ylFsoyP#JD#9}TgliR4iM3j;fxpK3&v)c~ ziR<<&aox{%6g#%YN@t60xGWWyv@UCWIMW~Xuj0YO#@bbmQ~(wHN{xiS4FB5OckrE^ zJ3|jgUr?HfL2_Lgja`dND z1|D8!Ahc^OzOt^tF}K2scx47+zoY&6bPp`^*}-OX`hiW-lr>(=N^g|p#Uy$oYvO6( z1qdxy3KpI_SiX^DN6RC4Wf5{*p?k$P&)IOEBdrmv{njb3<}!4$gk1>|J{`Q!1bnyk zaxDs#)vB$gT4W8m2b#I(ZBLyoxXq4}V++(B?C$0;LdT}jkMIERgqA%;$-{Lg4ziwJ zL|9RI+CDpsYKJ4YrjOBe4T5FUK_P9Z>7%skyh7;)wQA!RKwTwEs0j9%W}q99)Tcz{ zk%3gvtw=RD89kq+lDC8OmghVW)77*i+`+Z7?(+F;GX02i9loandDC0)vZFPz8*!lD zNkmuMSU1^ZW}6ZvScGxD--w{rlMJjUmC1k^^#q#b3(4Y^Er;2XW(0G{pdaT8O^CfB zdz>ZYbLUv#+Zg#{Ai18;>4?bhlz0!e9}-$KHX)U?8}Tt7=x{or+iqhG3IsMLrXIvj z1Z!C+`3bh6%I`sri3+f2!JatA5<{sFrqqDL#EO&GkTLfi0y3lwHo4OyYNm(TG zz??+Dp~)a#VtK>Exh7*dG4DtWgW z!;0e)J-^!?<$V4N-si{L5G)vPyh2aVZU>Jv;XP;(f(%`)KPZE z0Q;ZVvV--b$F()Eo^ly$rYAGTwn63zAJ{)Vwn!>8Aolk%@NUDsvY9pBNzv=PlDzI? z8^0adsVwHhYQcg@NklRoojdxYKS^2P*^&F6&)RHNTma!ywbfsVhV7Oyf2+TsqG1ia zpyi7)DW2CXBP*3pV7$B$pSTTr=Bi+4GJD*!brjgY^|%YWn%If`4pZyVc-i{hjMfWL zJ5L$(+5+nn^$Rk-5l$V8VRAIIGy*0a#f4XjTc(z>dz%rg>^MRvz~tAmagNFI%d?De z7IvZ>UHy3&BExkvZG`Tl;{cCpMhU+3%W0`94Uf_#VYbAXX>L&E(T>>DCh|B$m*QqH z@Fs_9RX#c{El{gG@`aP8+L(bR**3K&97$TW&O6C_Z{qjU~Z@!*Dk@i9n$!l96Vamo;957%WM4c93k!U1@K z0-YO{0Z?_s@j>BT$8P$rxDFjB9La^J@fZq@uHCY(5mUk=?Y|CZZVmQ__+|WRjf7c! z27xSX2NA5={t@EWbV%@c@cCi0d%$kMU2$EBUz6mGJ{81LeMG=PmQu)AT8RVdh<Qpsv6Kr$6%oGLpC>QtSPTAqY zl7)vs^QMoamY%TTUKC6B+Qxk0+TmoKI4ZoyJpFZrF*m`c_=;DaMl8IsjOR8+R~fMh z)}GQm4>m?nU$+I`aKzu?gn9zhyk#8M?*k`jB}H0S86j$ho^Q+5?^Y!m$r?_Tp_E{5 zkA0UWf=xWvJ;=tF0j)%X9IkaR*rcNAaMdaUsY*0}g&{5Bl{C2!V(HbRLna2SR={Qg zf$b^ajMhQ;l24HZxvR7AeFB;RL(kJ})IE15@l7we3P^>%|( z-;U3c1kRF_0pS9>iUz2;sq9xKL2ZSap&NFLNoHT_!WNkBg4M9BSI$yy@}!kK1}oPqA2m}Us?WguF~CfhW_>Tf}U**jHw#=ahAF$TOg9Pacjrpp6W z+Q)z_Qs{%|C7}QYXf7FaeI2*rXv9n3B7l0cwO)c5!%X#y(flK zA0K%b1m`klPAU_vk*MARA6ZN(#dKha@DA{`+VNU0rkVQlSnmUfRmi1VW4+OKu!+F( znuK5G+|0Y_`6VltL##cBucz5th3zGhqKePqm{^W4Y6jS-%dq!%ewkx&tfvPFjbn2w zyx0eS-Q@$7cA!godmgN>KV$oUGY$px@qayze~rB!0`}@($pH5HBpLK=JM_VASv=5? z>{x|J{f)wyD@S@=c`b`kV7x^v@jO_08su&MSniojiTHs5tYysyXi41Yj-Bj{8w8 z-9G|r`S<-6YrXbQSSx(VdI4(zKL(>v=r%dA7Q|*5f~oK&|LSzJQa7~Kf1==n_s{1$ zDTd(r%V6;vB_(^Zwr8oRd{UK`4%n-lv~mR`4|uS&;BY?Mk%lN8sXSJgw367R>PkcF zvq@#JiSaNY{Vsg!*7D`~;kgH-s>VhnV5c>-9MgqV2H-Zj`)PLhJvSF_!!LWXxwm9+ z-i^0pY|+2LZM58p+Z@Zg9smY13MX3ME5$EgiMWl~yap+3%hCfy97T0afb%HpngFMI zLRU8t@q~b*!ZP;SpM5Y@y50W#^*i?M4@8XgjV*5AwclVQVT73iMoJYhQt|%{MiScP zgz@JBMuM6DD~yCo2^dLE-()riqktp-1|v!71}1T$6C(+I=mm@fR_B0`(nn$>+@4-{ z^|*+U#1*(<9fqQW<0JXFt(cDk>d@K+9Na3vNh<*-)oCPmazJOb11vLk;8Q`&tTE<5 z^rrs292rE8Ld7@vYOrfG98~+tX~gmc)7W)L@zf3DE%6MsO4&@s{AKB54*JURI|g#H z+{Az~#1TagFz|Y~P)gS92Y;umQSzNil%(W9e_xkNEZ#42arvzzn@UGuiW}3k#A0?a z?GdiKaS+#aq_&ED9C(IfJ`QY5w4l*~n?iM2Muf`ga4UEiTIYX_zR6i_1KLx@ejq1{ ze-`@nXnGQJK{DW_3K4sbezM#^EM9Btk77OlfyzyXjKvUx3@`z6MBOu>*AcqB4{_Sz zc4WZC%Y6fMHtx3>I0~@C0o}8;t_6kC&A>ml2>j!@T(HAQ_l7&Yc}HXmyH`#^yyCfR zsw_}k35X>;BPnK`5^KCDEl@_CiRhbKz=!$qz{{~lnIGnl+QhYZzZJ3B^a7?newe3Y zJSD}=+yBb?HNkuKviXFLDTLXqmCe=vRBl-PA*|hOm}RakkkjcSzEk2gJR9K<;ev#!VXu_{^f?lX>(5WuVR(ngw@cg zJ_OMEPUx4|wSPMdlHryXw8;J@1CHer`lSAM4FKrv-KLg4GI5tAdV~*{6@Hmw5u+FUHvUh**W?&l78tj*SB|0{mYcwS|wS2LJji;_w4tPZCEXKJJ`F+~uIZQEOzV*}cY zRkXPS9wyt|fosFNx2+qE2ac@F6xrjz*phtneA?|9(SxbCU)dHC<8CVROd3ch|U$Ytgo#UqL;cN9x6?V~@jIyfnm z01nL^;9tusuOj0YBE-+M7_|=Hs{?ngVD7Boh$zib0B08W2>~y*&}Rc|;vHcTFeL+7lK;NW-FYpdHV^6o` z^3g!yXhAlknfmk5pFpoJ`#PL`p_v3>w4h_3xA&7%P%^wy%TB2Ujd~0*>8HcWf|itI zO0&ZA4ET&P)a*sRvigcpx=F1n1PNAs3wR+yJi$}g_I!K|ryXyp)NZwSffnw!V*suc z#*mc9)-)sP=e<8+EzP7h_!`chZH8z~m?o4+LV(z*kk1uP)cVSXBF*7(# zpgCc@Dyc<%2*ax|R?ABx#|p3BlX@-CASje;5&B7r!sDHzU!h!|J>at%BrWrZ;!|52 z#{EXp!qc&f*l${7@K z$9GaHz=9Ueu;esgU_llXBLnTJC;_5q=28eMushtldT~##(Iwb8t>dh;Zy()*f7MC* zuGxtQSj$Mgwpic`rpEJEC`ppCKc+I3xd`vXcY|cIy8}Ti7u1p4pVz;eEru`g2hkHP zfJ>B&#K*#VI*_WR7EQum-rorF-ENQ!Rs)|>huA{7P+AqzfEI87696BWulb#2YJ8HC z-PKCswulSNs&3LDl@H*rl!o(tmc0{Vx#W=zY$N`65YO`(Pc%xtZzm4ntSVRzufw1 zR{2gW>z^9mzmoqRLq}z@wd*IOpLr*X`MyJvT^qa;V;WWpUePh%Nphr`+${-Q#CZ>j zx|-$gX3Z=^#4|+Xk%QKj4^ePhQ`JWmz^#erDVP9!?>WfJDkNqa+W4xOWazIYzB@wz zQ*^1V`4I#}xZX4Sv~UMEr$DcYfA)i zwF-P3i9?xVo(Ye1RMFi@G2yAI_FFIqrp^s!pxV}inhpGSy^^O+`BE}G_R(1X&tLvQ zqH1hLTM{VUG9^WU)XMZEdaMA;tsVur)w+UyuP3ys#lI(bxhG;@xjas(#TFA<@13$w z!BhUz!jyEdw_l=PN{{WrHSe7p2*@|~N1Somt`s{w$I zF^6g02|JZ)K+H>S;9E+QR{Ijd`sqWYdixgGs#!Um#GEr0qOg(3N)M7M`bXeDwG!q# ziac33e-E3`3v_gfGR%bQFZckrsM>l9G)quBoB0m6DIrx|4n#7ucq`WeE!-kiZr?#G zog`mRk;e+lP2k5ZaIg#Z;8(es>onPstLmylDN4RhN**(HWHB*PG9gPf3Uk05aKBB^ zjtMy6QRFn!v0)ClfT@S_1;V^K8%|DNQ~GC_jVPN$eV0#(?hum&thIJM>I0 zm7KSs->asOjsAKx)0K$kfh3aIC%rSRrK~&KBG+(po8F(82t=AezH5 zL%_i`8G&iv8eTTQeKe|mGTcXF>Ld>|x1WIa*BZr9Ge{c)*#i9G8AkIewBK=}Eqa18;kUZ4TssZV%iy=07jZT6T{z1D$b zr*KpWM;2Xi&ZcTIph@5#B&<^!UTfCSg9_mP*tTG*Wxr0?v$_yycP6x}*H)LqH*fjC zIorMsxSxKnp7N0xq_TV@6}6(_;S8X1SMcAG#J)F}i*RdC;jhRf%vF#qTm)duTn@!dnTK*@Ow?tdI3PCUj6KH6>@zw1WQZeH&`nrdE)&HLX4z!86PWO+ zalQtau?(QYZ`K@kU4uJtTyhnZ@%fqcsQRPzZWF=k-QVO*b)d2A&0%Y3X{E025Vo3{ zj~@SW^@8#P}6;a1fjkYWJ0XG7VC!Pp>(5Kl@ItIPP?nTUaPm^ zvEVdB4ZOfkkjPa{O~m*+HAXh?i&(r;Y$p9OZ@LU)1&xT857_jw6U)%H4-OYv!+s<> z3wv{FjN?Iz`-POR?Ha_E@xq8t(+Xk4XK0E6SPZ48aBIw1LY2QCsiyo5v=^$U8wk}K zjfCn7MQr~v-uzZ5q$*FJ^_yelmXd+xK6N}?)sz#hnMs;?^9panPO@pe00kpaa&ypPn(F_r_ zLg>v%KPv3yXLkuJmyUQ7P@EU(<^9p;fv3Kcqt|hfOy_%O^<7p*KhAV1k^V32lq!;? zs8xJ@fP~BBZA!G8W=l3ARWpoUCW~eYFC%u53{~oUs;&4NFPn=YXy2xOpD5rJ9R**2 zoZyU|m7XVi-&dv8A#3?vLN)dhz{#M5zdjk^y?J2dqo=@a9BIot4fwI}5@LN6=z$Tm zyj~e^buWB49S!Bs#Xmm7aXfzkq*I|wH~+!Xc6DC zcqgst_38whBjt;3O#NX_^=fvK%{L8B=P>+cv?VM!2@*H3DUrd8c!7+uQPAl_3-$%BvFx=ACq*Hj@vD=vXCwH^ z51$csf}H%5(J_HK&uRB<(k~+-&wBOyRO;p&gp;ycJSh%>`U>abJpHnM9|q&x7iplw z0q26%2Ag`uFDYgjmVxE~ZC-F0PEi<0f>m*KM0N^W+=imE^;$(Wr#cA-NJ)a(+R0D|ODlCf_wrL+gP6v7 z%ya_?bFgdpq2Gov3c%SB{I|vkb`IKe@DKm~lM(n_tv>P`&>kQ=pdafWwWx ziQitBcSOIe_$$D4MS$tBmBTt-(K0v(OVP}i1SH; z_`45|R`XOd5e_)DT*_Zo68mnRRGxu|zLahuSa)qn)CX#U=6#?7Io0ay@$tTN7kOM( zueR3LBTPROS7M8-wck+MsTM4i2-j})_d=^7^1Udvdg9#wD76xH4LzN=!E9)jgo514 z3_J+XgUU+?_^4>GNV_B>w&aTPD?Ao1g$Man2gt8LMm9o%b!MoyB@Hi7R0CcM8=Z`N zS0_Mh+qPWLe?kON^U4nJ@dk|vNX?jprm@5>IE9qr@oiGQPH$l+C`hn%_{s&n$@9!~ zlwbkF)4LXga%Et%F|{IuRs$DCX_k57TW^~*GHW-Db z$jD8&d@=r zhN9ud{+BPv1CQ$13oYn`&iH{Z?J2=LzRIH!;eD#DP<+q^H;LyUmIM+J9k!XLDd4|D zBsX$!n_)`6+zYJzrpE7<{mCOqIW;aRI?0odk`bG77b^R+uVzyz10MIYtFuA!2J+5n zoH!ROIh-1=*Ci>B*d7LADeS(DAmys3Buqy4Aafb)6tj8LJdh{R1Rqr=xt87Ijb~@= zRucL^9%8YxnR2f!?=kM~>ILtE9B>EweXZi7Q-G7ro#dGFAk}qm0L!3)#YPR^>J8e@ za&NGG%P99QZXRj*MqJ=qA3=(P;Du`H?Avr%uVdW7nrAWAgn$eRQSNYifqvkwmrFl@ z(^x-n7wJWdE^Sf_lQ6JjPU;HJp4|7m)@DDS&1T6xwpzSF63Z^C7{${See81s?)YA2 zBwxc^RFbN2jM024chGpnOMT_@5SFwkKCqhH2~|B%reLFTHP;F>*|}U|+5ImWlM?X> ziNKK*pEGLM>~gnk4Y!z0FL#6aBmj3PSn`dhU3MG9vOG)2s^b|4L5>UBuqzNVW-%*! z2(J^(ld6iEkZR)hz1TMWbUEb=#q!?=oH9 z=*$UgOC3S9;L&|-%f0~okrMXjN`M4Q@pjAk`ty?A)U|A|>nr|Fc`g^PCD1^2xFc;( zJ(x;o+qlI~TyFwu^4<>p;w>i}<7KV;=p!c_V`R-({%<@FRF$b2Db9-fH`AifIn2!i zuYJe2u0pGiq$KlnvP2cr0s5V;u|AF)%8p+!H83gH3g5s*2&jR$zC%J30<5t}Nq%Ar zA44si)(vz6T5`{KbwoI}V+d?tc6&vutPlI~MmL(la^YFsA_N^pl z=p&v5>x0=6wk|{hyQ#+3p5my~>O4qt0$9Ihpq-Yv2DR{UhZFa z&ZpPax8=#2(*nr?LUjRJ@JMn}b!mi|X8O+?JGG11gQUw0Yu zU9~Lyi7nu}x??zgsISDk;@Rf_eq|7wdQ~UEYTp>gu9C<@6mUD^JxdHE>T*xi+URkp zUy0-V9;fc9&*drs#=+n9YE7Drk|P{nAxD0!=uWOa!J2Rqr5=@o<_GY=t`wy;fkW4_ zA+DTo78<;R8>hvc=gj%-g!47V_W!57GXaaL-2eE?Dw-PZ3#LaAMI#*+1r=lvP%)QD z+^$P7&I~X)z&JA~_LmwF6-^Dq#EiP!%H`TdYEkCZao28_C^OB}D3`RGUGiSdik$!V zJ?HnboT$6_f1dmAc|1HHzVG+l&pYp&ZD!6Xnd;-Y^G1-z;Whc4%&YgWX_%GDjEqFv zd66Tgr)zgEyo_&i<9_C!1x&~G9PZrOXK*bC`=*12ju$-e3w`;Rq7rvDod4vA6Y1LU z^mG=`7T^At-;-Zft@Ycx(QB6<%kR8oT}h|5o+sB}e=GM)doS-b=6SE5U2QdmM0TOdQ|u2KaVyO^DyVEWpo)86G_7ZAkX@ti)%9 z*PhIqzNG(>L_?>`4EN-U#l7%xXXgAa=hpB$tcnpis{MYgQuDQs`Rg#^4eccnX!5;j<&kTC~4eC;xgtUSkROj^w|0d&c+F-n{7}rk}z){t8>t@00U8 zaHmZT9!%FcZW->qdYFw^?&I-{FRq#W-4}NNDM|d!{Vnd;{in+9%a)Ax80|s72Ytc6 zbPV6}wfKzx5;3mbk|y`Ts_91Ck;+0tbY}6v{8Eg(d;}&9@Mx!`rSHa_;3F`zyc2fc zx0*j!qfNoyf_4`h_WOAqOORm;e(R85dZ|*B>++^3=Xisc8&|_iE=d@<(y*bxQ>nZE zE~VzayY5>}X}>R!pNJxU(OFU&f#>3u8J2nHmwsN^*nP53S+n#Hwl6C*bTRPXxCRC( zas8|1xOn;Z<30IZss|QyN#uKzWuFZCI_%2Oi@yJ$rwOMiJz24Jqv83qQE5vfSM>VA z#P0~&LJExjuII)vd5MOUOH3)f`J_^++>kewp9%3Zpf<-kbc$pnp9+jR?C-U$MX1l{ z5uGn%e|(JJRW&p)>ZF7#6F7QZx@Ao0)U~)LsD?k$yJd^EE!H31U8>X@RK5BFJ*D_z zkHk6n4c@e`x35ytOjZ0@+B2_+?;<~ZanLFyuQW^P`_X5XzKtWB2bb4U8t(f$(}#`l zc50QGmwJ-z&zln9mpExSzIYjmag|tvRcBYyFn3qIvJ?$XiyP63Cwo*lm6YTVAL1tmX&Y+Z^|C4NDA| zdQ$7N^0Jlx?#mhdan~l->tpy6UX`s1FCVyf>o%__KgLUi`*?YX9w=o; zm0aj(s+05W-nwPfvd{6n^v3;U-p##c((b1HEYk=2-(Y2?z;^hbG<*Nqq`-~{O$h9C zgPola*csvZfnD%_QID&8^wC$#=0soGW9o5jPiBuBdvbdGl<#D0e0+jr&NAaSfXb3E zP1lTfm~6Hjt3xtmXQi3b+aRlbk=Nqx}b$IUZ@DV?bWby4f`wv?;ILkp}}=gllMb-@+EIHY?i3jb~%oEH=t(+>+i; z_tgeP^~;})Pqjz)X&tS%&UG&Kk(|LA3(It7F{|hN_4fTS5i_T^*LG>wHNG3`{$NI&gFBHF4e5m+T@s(m}9V_uI zxlrPRKRg2+H2T}XO8rW^m3o%8F6~>YDGe^|hw#QyXDQ~!!D}zFZR~KnBMEMHjh7s_IR8p`o{BLq4sB>?Q-xHsG~UE zKF>G1QJZHe%;V)3rT$-CY<*A;cxjvZ+p$R#@!>lbb5@$H6XPbznx)T@RTCN(9x-HS zWYjSIT!Yc1WI{?qeB7w`ghZth9xs<-Oj0cx)gn#QPn3{XdLRjJX>0%%aR=YjGGvqE z6N8Mhy4|nTN(5J{NY#EKWZP8bn$_q5tEW%t(q4Udt>9x6Z2+xY+3Oq*Yq=+^S3V zA9-KD5OyMLevc0WKMngPsc7b~kBUVC<}Wu8&)?`C8ZW>ATktOMIp0 z>+i3RoxgAAA6G8Q*gI(atj{ML+PnU#%g19U#KbJ#J@o!I2Y#xn(igN__})twW2f%l z_Tofs2mQel_nW$XF{4Z62j9lFI`x7zrpdosv443_z5J+p?P+A3ZaIE|Y2CPS&e%@l zk8diKR(VcNJrtA^_Q>R_O<`5R6SiKQ6LWaZh$o&~H`6k?O83OK!7*)2U0UZXS)Q}t z_}B?)Ww&PN_Dq>=dhbYa+byq_cf0Vy=$^|ezq-9Kpy-`*YtDcF?2mtaZ2Lp;Lp&or zULR@+&pz_m>Z{JRhffHKsnn|_KzG1z8+bf5k zHP^?joUw>YJVKs<8cMl?jxi%*=~0*+OdJhD2}(!^IdGzzLDDJ@qF}En+-{| z+0ew(OQf5WoHPUBLF5|>2J=N-jU*NNH(k8z7mAOQ7msEZCBrCr@oZ+{>5}Nje_3;~ zGW1zSi^+(iC^~AAdt}JAnIvOQrggX!WN*`o#p`qICW-H~Lx);&L!?Aqe7!?5vzgLo z1!=;JbAp5Lh0d7zv=Hf+Qg|uG(tas9I4EsEi1e$;yoNIim2J3MA9=0$c#-e=tG}K; zYTd7=zx9)n?jo%wJxzLrw3ZZm9VcI3KhmzG5~+qXid3iaGgLm6d!^RET9qHG@;a5Dq4KFJpP}+rmCq&bB;Ba;<>a@K9#i>h@~25_ zRsNRBGcms@-cRNIRlX~Ei8M;(wJJYW<#j4QL*-LdK11cLDxa(JPV$AM63#2F**3k+Y{E%!nj=(>PjiHkhLc8+4j~;%8mXwgzCOEt0XE0jE}C)5 zYU3x?SkmlNuf9H?-j<$|Y07fg2ir`J99x#OKyS(6eUS6(MSi6p=x2TY#u?M})IUA- zPfz{RQ~&g$fA#ewH{U<1$4>RwsUADkV;A*ANpk<}!$?ErQvj@As2uN5($U{34dW`s z>n#?O4H?oLRL()=97?%IO}1=a;aI)R_`pK54I{{-m`#seJK`rRL!>tvZ6-W$mD#OI ze6mAtm?v4Y<-H&BrBnPEOZHs7r6IvV@l!HvCcUvyyhf>~+0LRqOb5ArOb1!*n!#yS z#5Wzp!D%*qrm3M;^pDCl&BqvCez~5}IavmWIXkQ2Rc&q-AV1Zkr}lqduJLR@{&1y! zOb59gOb11Wl13_Or}~=q&(7>r9@9WsUn-AjpmY=7&01p zOSax9TS~V(aM&bAm@!||F@elkc84uTeyQa6OtW2YF{fu?el}Ewlxb3MkR@jT4z`9% zjq>mYuqiNPFEH^bt#?R4nkY-|Ajx9N8qSZmLimhE2$3P!WGHb)y+e$}HS$~b7~c;)W#l`s!vB9ezQQJIM0jXsVx!ICq#TEoohD_P zGP7-q+O$$OV#&Wtj*lBZp2_*cGp9Gok4ye3rd%^V1>gq{MrrOMd>(+a+8~QjT4;7; z@N-m4uBb@fIy0r7rtMD6vgcTFgy%pVsFT;m>liNSH8H3lMACq!s*x)Gcg0sJ z;H@f$uctIDo}LZiI}Nq?cPjAumotxCqRhKJw7>04zF?UWpSoPp;iN9gS4HXNkGtJ( zf<52>_z2X1TL7QnxO;$pAPS5E<}9NrSBjRxsE30yvNKJCb4`}P4~B(m23vFHTFi#Q z#&&xSTOO$jJB0%JmAR+FD!_ zEn*(P6L|Gv%saM%`2b(g0{DTJpcS|Wv__sbpe?u;__HFfcF^`H-vQbY+6mx`$gB(K z+N+3l1KmLY=mG8nJwY!Zf!?4G2n2mW5apBLE1xa8Wco2*S6Tn2|nFMs; zAut)^kPMxIw5h0L8ki101N;umgr5awgE`<~#HD~#pa*k-0T_V^>1iMxWB@bb9s%=! z1!RIOkPWOzpAT%n4jhQf0SmxFkc$@@H?C!mg8u=FK^}O_|7rF(aDpWuA1nn0D6pcHHbPou6);2BT`o(0c=--B}S z2k<=dy#QVWo52>a74h4^AHjC;5_lQB0$v5LfePe%9qa%*!7lIy;@T-1;@Z~@Gkf>cn|yqRDt(FHTVF02u^^Hz)AGsV{i(b z2A^Pr&h&bkeTwi|a1NZuYw#Jg2Khe+Uw{kXBDe%DgD=4qa20$7{tCVZ*T6U6Z=e=@ z3$BB|gMWY<;3oJE+ydW&AHa{G4*Ude12?dmv#^yJqf44JHaTTN+>8`m(!`0A82it+ zH|bV$c7`3B9Ca?%jvCCCM8nDKpz>lFW7~E88K}@0$koQA*-1 zr7X#c^wp7{!j+#Iq|7xtQg8y1j(yd-Fnt4aCuF1Njl!U}- zN%4t%eR&y?);p$tbBlf^Tg}p_9J>@IO+@#Sm0f3V(K&THLC6<_%f-~kpY<;Y-TU?Q zs3z$P^;kE%bH7JgcYW{8)m#U1?3~PpZu8qF89^TOH2C-ScR6YY$c%yJzL; z(j6a`MI+u>a&}i7*LONR5_k2U-e3OY{#&e~PsU>V!LP{i>m9gYuuu{kj(`7sf{%_& zHb=I7a5hc@vK+QW@}_k(Vkb|TMBBz@kM|o6`IJ)(d6jaf=SpaOIZ#pjJ_E8 zm^>Ctd`zu5Sl!XXsl?&bLS8(Y2*!;faQeX)50OpL8hn?YM?Xi(!+i1UuGH%y%?+q; zN6GLT|1SomZTMeaifEsC`RnsO?4IBFI^FI5-4VDu0(VE??g;#LMnKa;*{=%Kk{7BY zFEo|BP%C+%PVz#F$O|neFSLTZP#1Zj)#QcNkQZ7@UMRbdjuS}zRVtAesv$2_OJ1ms zywFtgLapS5I>`$yA}_R@ywD2rLS5vAR+AT6Ltbbtd7-Q)tv{*1N+t3_HROeA$qUty z7n(|5sFl1>CwZYoD7^(XaLsYG6=hP+TMd7(P; zLQ}~LwUQU=BrmjxywGy;LMzA%b&(fZO@!UZ|G5P#t-p zspN%P$qRLo7g|JKXgPVI7377w$P29|FSLfd&|30BSsz+|Qh$|7ehx*&dS1Dq_!`n%KFYY=sk)mixZJ_wlH-$-M6=E4KTB^~gje4*=FZ&zb$xf% fn}*Wjx{?@9Mwgdd7kr&OmmhFm8`*`2?_~L3?=pWd literal 0 HcmV?d00001 diff --git a/src/glyph_mapping/lcd.c b/src/glyph_mapping/lcd.c new file mode 100644 index 0000000..b52f41d --- /dev/null +++ b/src/glyph_mapping/lcd.c @@ -0,0 +1,138 @@ +/* Copyright 2018 Canaan Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "lcd.h" +#include "nt35310.h" +#include "board_config.h" + +static lcd_ctl_t lcd_ctl; + +void lcd_polling_enable(void) +{ + lcd_ctl.mode = 0; +} + +void lcd_interrupt_enable(void) +{ + lcd_ctl.mode = 1; +} + +void lcd_init(void) +{ + uint8_t data = 0; + + tft_hard_init(); + /*soft reset*/ + tft_write_command(SOFTWARE_RESET); + usleep(100000); + /*exit sleep*/ + tft_write_command(SLEEP_OFF); + usleep(100000); + /*pixel format*/ + tft_write_command(PIXEL_FORMAT_SET); + data = 0x55; + tft_write_byte(&data, 1); + lcd_set_direction(DIR_XY_LRUD); + + /*display on*/ + tft_write_command(DISPLAY_ON); + lcd_polling_enable(); +} + +void lcd_set_direction(lcd_dir_t dir) +{ +#if BOARD_LICHEEDAN +#else + dir |= 0x08; +#endif + lcd_ctl.dir = dir; + if (dir & DIR_XY_MASK) + { + lcd_ctl.width = LCD_Y_MAX - 1; + lcd_ctl.height = LCD_X_MAX - 1; + } + else + { + lcd_ctl.width = LCD_X_MAX - 1; + lcd_ctl.height = LCD_Y_MAX - 1; + } + + tft_write_command(MEMORY_ACCESS_CTL); + tft_write_byte((uint8_t *)&dir, 1); +} + +void lcd_set_area(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) +{ + uint8_t data[4] = {0}; + + data[0] = (uint8_t)(x1 >> 8); + data[1] = (uint8_t)(x1); + data[2] = (uint8_t)(x2 >> 8); + data[3] = (uint8_t)(x2); + tft_write_command(HORIZONTAL_ADDRESS_SET); + tft_write_byte(data, 4); + + data[0] = (uint8_t)(y1 >> 8); + data[1] = (uint8_t)(y1); + data[2] = (uint8_t)(y2 >> 8); + data[3] = (uint8_t)(y2); + tft_write_command(VERTICAL_ADDRESS_SET); + tft_write_byte(data, 4); + + tft_write_command(MEMORY_WRITE); +} + +void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color) +{ + lcd_set_area(x, y, x, y); + tft_write_half(&color, 1); +} + +void lcd_clear(uint16_t color) +{ + uint32_t data = ((uint32_t)color << 16) | (uint32_t)color; + + lcd_set_area(0, 0, lcd_ctl.width, lcd_ctl.height); + tft_fill_data(&data, LCD_X_MAX * LCD_Y_MAX / 2); +} + +void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t width, uint16_t color) +{ + uint32_t data_buf[640] = {0}; + uint32_t *p = data_buf; + uint32_t data = color; + uint32_t index = 0; + + data = (data << 16) | data; + for (index = 0; index < 160 * width; index++) + *p++ = data; + + lcd_set_area(x1, y1, x2, y1 + width - 1); + tft_write_word(data_buf, ((x2 - x1 + 1) * width + 1) / 2, 0); + lcd_set_area(x1, y2 - width + 1, x2, y2); + tft_write_word(data_buf, ((x2 - x1 + 1) * width + 1) / 2, 0); + lcd_set_area(x1, y1, x1 + width - 1, y2); + tft_write_word(data_buf, ((y2 - y1 + 1) * width + 1) / 2, 0); + lcd_set_area(x2 - width + 1, y1, x2, y2); + tft_write_word(data_buf, ((y2 - y1 + 1) * width + 1) / 2, 0); +} + +void lcd_draw_picture(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint32_t *ptr) +{ + lcd_set_area(x1, y1, x1 + width - 1, y1 + height - 1); + tft_write_word(ptr, width * height / 2, lcd_ctl.mode ? 2 : 0); +} + diff --git a/src/glyph_mapping/lcd.h b/src/glyph_mapping/lcd.h new file mode 100644 index 0000000..3721ba9 --- /dev/null +++ b/src/glyph_mapping/lcd.h @@ -0,0 +1,81 @@ +/* Copyright 2018 Canaan Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _LCD_H_ +#define _LCD_H_ + +#include + +/* clang-format off */ +#define LCD_X_MAX (240) +#define LCD_Y_MAX (320) + +#define BLACK 0x0000 +#define NAVY 0x000F +#define DARKGREEN 0x03E0 +#define DARKCYAN 0x03EF +#define MAROON 0x7800 +#define PURPLE 0x780F +#define OLIVE 0x7BE0 +#define LIGHTGREY 0xC618 +#define DARKGREY 0x7BEF +#define BLUE 0x001F +#define GREEN 0x07E0 +#define CYAN 0x07FF +#define RED 0xF800 +#define MAGENTA 0xF81F +#define YELLOW 0xFFE0 +#define WHITE 0xFFFF +#define ORANGE 0xFD20 +#define GREENYELLOW 0xAFE5 +#define PINK 0xF81F +#define USER_COLOR 0xAA55 +/* clang-format on */ + +typedef enum _lcd_dir +{ + DIR_XY_RLUD = 0x00, + DIR_YX_RLUD = 0x20, + DIR_XY_LRUD = 0x40, + DIR_YX_LRUD = 0x60, + DIR_XY_RLDU = 0x80, + DIR_YX_RLDU = 0xA0, + DIR_XY_LRDU = 0xC0, + DIR_YX_LRDU = 0xE0, + DIR_XY_MASK = 0x20, + DIR_MASK = 0xE0, +} lcd_dir_t; + +typedef struct _lcd_ctl +{ + uint8_t mode; + uint8_t dir; + uint16_t width; + uint16_t height; +} lcd_ctl_t; + +void lcd_polling_enable(void); +void lcd_interrupt_enable(void); +void lcd_init(void); +void lcd_clear(uint16_t color); +void lcd_set_direction(lcd_dir_t dir); +void lcd_set_area(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); +void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color); +void lcd_draw_string(uint16_t x, uint16_t y, char *str, uint16_t color); +void lcd_draw_picture(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint32_t *ptr); +void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t width, uint16_t color); +void lcd_ram_draw_string(char *str, uint32_t *ptr, uint16_t font_color, uint16_t bg_color); + +#endif + diff --git a/src/glyph_mapping/main.c b/src/glyph_mapping/main.c new file mode 100644 index 0000000..af8c405 --- /dev/null +++ b/src/glyph_mapping/main.c @@ -0,0 +1,378 @@ +/* Copyright 2019 W.J. van der Laan, based on original DVP sample + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "dvp.h" +#include "fpioa.h" +#include "lcd.h" +#include "ov5640.h" +#include "ov2640.h" +#include "plic.h" +#include "sysctl.h" +#include "uarths.h" +#include "nt35310.h" +#include "board_config.h" +#include "cp437_8x8.h" +#include "gpiohs.h" +#include "sleep.h" + +#define PIN_KEY0 16 +#define PIN_KEY1 15 +#define PIN_KEY2 17 +#define GPIO_KEY 4 + +#define DISP_WIDTH 320 +#define DISP_HEIGHT 240 + +uint32_t g_lcd_gram0[38400] __attribute__((aligned(64))); +uint32_t g_lcd_gram1[38400] __attribute__((aligned(64))); + +volatile uint8_t g_dvp_finish_flag; +volatile uint8_t g_ram_mux; +volatile int key_flag = 0; +volatile int key_state = 1; + +uint16_t inbuf[DISP_WIDTH*DISP_HEIGHT]; +uint16_t outbuf[DISP_WIDTH*DISP_HEIGHT]; + +static int on_irq_dvp(void *ctx) +{ + if (dvp_get_interrupt(DVP_STS_FRAME_FINISH)) + { + /* switch gram */ + dvp_set_display_addr(g_ram_mux ? (uint32_t)g_lcd_gram0 : (uint32_t)g_lcd_gram1); + + // dvp_clear_interrupt(DVP_STS_FRAME_FINISH); + dvp_clear_interrupt(DVP_STS_FRAME_START | DVP_STS_FRAME_FINISH); + g_dvp_finish_flag = 1; + } + else + { + if (g_dvp_finish_flag == 0) + dvp_start_convert(); + dvp_clear_interrupt(DVP_STS_FRAME_START); + } + + return 0; +} + +/** + * dvp_pclk io47 + * dvp_xclk io46 + * dvp_hsync io45 + * dvp_pwdn io44 + * dvp_vsync io43 + * dvp_rst io42 + * dvp_scl io41 + * dvp_sda io40 + * */ + +/** + * lcd_cs 36 + * lcd_rst 37 + * lcd_dc 38 + * lcd_wr 39 + * */ + +static void io_mux_init(void) +{ + +#if BOARD_LICHEEDAN + /* Init DVP IO map and function settings */ + fpioa_set_function(42, FUNC_CMOS_RST); + fpioa_set_function(44, FUNC_CMOS_PWDN); + fpioa_set_function(46, FUNC_CMOS_XCLK); + fpioa_set_function(43, FUNC_CMOS_VSYNC); + fpioa_set_function(45, FUNC_CMOS_HREF); + fpioa_set_function(47, FUNC_CMOS_PCLK); + fpioa_set_function(41, FUNC_SCCB_SCLK); + fpioa_set_function(40, FUNC_SCCB_SDA); + + /* Init SPI IO map and function settings */ + fpioa_set_function(37, FUNC_GPIOHS0 + RST_GPIONUM); + fpioa_set_function(38, FUNC_GPIOHS0 + DCX_GPIONUM); + fpioa_set_function(36, FUNC_SPI0_SS3); + fpioa_set_function(39, FUNC_SPI0_SCLK); + + sysctl_set_spi0_dvp_data(1); +#else + /* Init DVP IO map and function settings */ + fpioa_set_function(11, FUNC_CMOS_RST); + fpioa_set_function(13, FUNC_CMOS_PWDN); + fpioa_set_function(14, FUNC_CMOS_XCLK); + fpioa_set_function(12, FUNC_CMOS_VSYNC); + fpioa_set_function(17, FUNC_CMOS_HREF); + fpioa_set_function(15, FUNC_CMOS_PCLK); + fpioa_set_function(10, FUNC_SCCB_SCLK); + fpioa_set_function(9, FUNC_SCCB_SDA); + + /* Init SPI IO map and function settings */ + fpioa_set_function(8, FUNC_GPIOHS0 + DCX_GPIONUM); + fpioa_set_function(6, FUNC_SPI0_SS3); + fpioa_set_function(7, FUNC_SPI0_SCLK); + + sysctl_set_spi0_dvp_data(1); + +#endif +} + +static void io_set_power(void) +{ +#if BOARD_LICHEEDAN + /* Set dvp and spi pin to 1.8V */ + sysctl_set_power_mode(SYSCTL_POWER_BANK6, SYSCTL_POWER_V18); + sysctl_set_power_mode(SYSCTL_POWER_BANK7, SYSCTL_POWER_V18); +#else + /* Set dvp and spi pin to 1.8V */ + sysctl_set_power_mode(SYSCTL_POWER_BANK1, SYSCTL_POWER_V18); + sysctl_set_power_mode(SYSCTL_POWER_BANK2, SYSCTL_POWER_V18); +#endif +} + +/* helper functions */ +static inline uint8_t r_from_rgb565(uint16_t iv) +{ + return ((iv >> 11) & 0x1f) << 3; +} +static inline uint8_t g_from_rgb565(uint16_t iv) +{ + return ((iv >> 5) & 0x3f) << 2; +} +static inline uint8_t b_from_rgb565(uint16_t iv) +{ + return ((iv >> 0) & 0x1f) << 3; +} +static inline uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) +{ + return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); +} + +/** + * 2D convolution filter (box filter currently, would be more efficient with + * 2-pass but we want to compute some positions only) + */ +uint16_t filter(int32_t bx, int32_t by, int32_t ksize) +{ + uint32_t r = 0; + uint32_t g = 0; + uint32_t b = 0; + uint32_t n = 0; + int32_t c = ksize/2; + for (int32_t iy=0; iy= 0 && y >= 0 && x < DISP_WIDTH && y < DISP_HEIGHT) { + uint16_t iv = inbuf[y*DISP_WIDTH + x]; + r += r_from_rgb565(iv); + g += g_from_rgb565(iv); + b += b_from_rgb565(iv); + n += 1; + } + } + } + if (n) { + r /= n; + g /= n; + b /= n; + } + return rgb565(r, g, b); +} + +void irq_gpiohs2(void *gp) +{ + key_flag = 1; + key_state = gpiohs_get_pin(GPIO_KEY); + return; +} + +static const uint32_t MODE_CNT = 3; +int main(void) +{ + uint64_t core_id = current_coreid(); + + if (core_id == 0) + { + /* Set CPU and dvp clk */ + sysctl_pll_set_freq(SYSCTL_PLL0, 800000000UL); + sysctl_pll_set_freq(SYSCTL_PLL1, 300000000UL); + sysctl_pll_set_freq(SYSCTL_PLL2, 45158400UL); + + uarths_init(); + io_mux_init(); + io_set_power(); + plic_init(); + + /* LCD init */ + printf("LCD init\n"); + lcd_init(); +#if BOARD_LICHEEDAN +#if OV5640 + lcd_set_direction(DIR_YX_RLUD); +#else + lcd_set_direction(DIR_YX_RLDU); +#endif +#else +#if OV5640 + lcd_set_direction(DIR_YX_LRUD); +#else + lcd_set_direction(DIR_YX_LRDU); +#endif +#endif + + lcd_clear(BLACK); + + /* KEY init */ + fpioa_set_function(PIN_KEY0, FUNC_GPIOHS0 + GPIO_KEY); + gpiohs_set_drive_mode(GPIO_KEY, GPIO_DM_INPUT); + gpiohs_set_pin_edge(GPIO_KEY, GPIO_PE_BOTH); + gpiohs_set_irq(GPIO_KEY, 1, irq_gpiohs2); + + /* DVP init */ + printf("DVP init\n"); +#if OV5640 + dvp_init(16); + dvp_enable_burst(); + dvp_set_output_enable(0, 1); + dvp_set_output_enable(1, 1); + dvp_set_image_format(DVP_CFG_RGB_FORMAT); + dvp_set_image_size(320, 240); + ov5640_init(); +#else + dvp_init(8); + dvp_set_xclk_rate(24000000); + dvp_enable_burst(); + dvp_set_output_enable(0, 1); + dvp_set_output_enable(1, 1); + dvp_set_image_format(DVP_CFG_RGB_FORMAT); + dvp_set_image_size(320, 240); + ov2640_init(); +#endif + + dvp_set_ai_addr((uint32_t)0x40600000, (uint32_t)0x40612C00, (uint32_t)0x40625800); + dvp_set_display_addr((uint32_t)g_lcd_gram0); + dvp_config_interrupt(DVP_CFG_START_INT_ENABLE | DVP_CFG_FINISH_INT_ENABLE, 0); + dvp_disable_auto(); + + /* DVP interrupt config */ + printf("DVP interrupt config\r\n"); + plic_set_priority(IRQN_DVP_INTERRUPT, 1); + plic_irq_register(IRQN_DVP_INTERRUPT, on_irq_dvp, NULL); + plic_irq_enable(IRQN_DVP_INTERRUPT); + + /* enable global interrupt */ + sysctl_enable_irq(); + + /* system start */ + printf("system start\r\n"); + g_ram_mux = 0; + g_dvp_finish_flag = 0; + dvp_clear_interrupt(DVP_STS_FRAME_START | DVP_STS_FRAME_FINISH); + dvp_config_interrupt(DVP_CFG_START_INT_ENABLE | DVP_CFG_FINISH_INT_ENABLE, 1); + + uint32_t mode = 0; + while (1) + { + /* ai cal finish*/ + while (g_dvp_finish_flag == 0) + asm volatile ("wfi"); + g_dvp_finish_flag = 0; + + g_ram_mux ^= 0x01; + + /* word-swap input from camera */ + uint32_t *buf_32 = g_ram_mux ? g_lcd_gram0 : g_lcd_gram1; + for (size_t x=0; x> 16; + inbuf[x*2+1] = val & 0xffff; + } + + for (uint32_t cell_y=0; cell_y<30; ++cell_y) { + for (uint32_t cell_x=0; cell_x<40; ++cell_x) { + uint32_t by = cell_y * 8; + uint32_t bx = cell_x * 8; + + /* get average value */ + uint32_t r = 0; + uint32_t g = 0; + uint32_t b = 0; + for (uint32_t iy=0; iy<8; ++iy) { + for (uint32_t ix=0; ix<8; ++ix) { + uint16_t iv = inbuf[(by + iy)*DISP_WIDTH + (bx + ix)]; + r += r_from_rgb565(iv); + g += g_from_rgb565(iv); + b += b_from_rgb565(iv); + } + } + r /= 64; + g /= 64; + b /= 64; + uint16_t iv = rgb565(r, g, b); + uint8_t intensity = (r + g + b)/3; + + uint8_t ch = glyph_by_fill[intensity]; + const uint8_t *chdata = font[ch]; + + for (uint32_t iy=0; iy<8; ++iy) { + for (uint32_t ix=0; ix<8; ++ix) { + uint32_t y = by + iy; + uint32_t x = bx + ix; + uint16_t ov; + if (chdata[iy] & (1< + +/* clang-format off */ +#define NO_OPERATION 0x00 +#define SOFTWARE_RESET 0x01 +#define READ_ID 0x04 +#define READ_STATUS 0x09 +#define READ_POWER_MODE 0x0A +#define READ_MADCTL 0x0B +#define READ_PIXEL_FORMAT 0x0C +#define READ_IMAGE_FORMAT 0x0D +#define READ_SIGNAL_MODE 0x0E +#define READ_SELT_DIAG_RESULT 0x0F +#define SLEEP_ON 0x10 +#define SLEEP_OFF 0x11 +#define PARTIAL_DISPLAY_ON 0x12 +#define NORMAL_DISPLAY_ON 0x13 +#define INVERSION_DISPLAY_OFF 0x20 +#define INVERSION_DISPLAY_ON 0x21 +#define GAMMA_SET 0x26 +#define DISPLAY_OFF 0x28 +#define DISPLAY_ON 0x29 +#define HORIZONTAL_ADDRESS_SET 0x2A +#define VERTICAL_ADDRESS_SET 0x2B +#define MEMORY_WRITE 0x2C +#define COLOR_SET 0x2D +#define MEMORY_READ 0x2E +#define PARTIAL_AREA 0x30 +#define VERTICAL_SCROLL_DEFINE 0x33 +#define TEAR_EFFECT_LINE_OFF 0x34 +#define TEAR_EFFECT_LINE_ON 0x35 +#define MEMORY_ACCESS_CTL 0x36 +#define VERTICAL_SCROLL_S_ADD 0x37 +#define IDLE_MODE_OFF 0x38 +#define IDLE_MODE_ON 0x39 +#define PIXEL_FORMAT_SET 0x3A +#define WRITE_MEMORY_CONTINUE 0x3C +#define READ_MEMORY_CONTINUE 0x3E +#define SET_TEAR_SCANLINE 0x44 +#define GET_SCANLINE 0x45 +#define WRITE_BRIGHTNESS 0x51 +#define READ_BRIGHTNESS 0x52 +#define WRITE_CTRL_DISPLAY 0x53 +#define READ_CTRL_DISPLAY 0x54 +#define WRITE_BRIGHTNESS_CTL 0x55 +#define READ_BRIGHTNESS_CTL 0x56 +#define WRITE_MIN_BRIGHTNESS 0x5E +#define READ_MIN_BRIGHTNESS 0x5F +#define READ_ID1 0xDA +#define READ_ID2 0xDB +#define READ_ID3 0xDC +#define RGB_IF_SIGNAL_CTL 0xB0 +#define NORMAL_FRAME_CTL 0xB1 +#define IDLE_FRAME_CTL 0xB2 +#define PARTIAL_FRAME_CTL 0xB3 +#define INVERSION_CTL 0xB4 +#define BLANK_PORCH_CTL 0xB5 +#define DISPLAY_FUNCTION_CTL 0xB6 +#define ENTRY_MODE_SET 0xB7 +#define BACKLIGHT_CTL1 0xB8 +#define BACKLIGHT_CTL2 0xB9 +#define BACKLIGHT_CTL3 0xBA +#define BACKLIGHT_CTL4 0xBB +#define BACKLIGHT_CTL5 0xBC +#define BACKLIGHT_CTL7 0xBE +#define BACKLIGHT_CTL8 0xBF +#define POWER_CTL1 0xC0 +#define POWER_CTL2 0xC1 +#define VCOM_CTL1 0xC5 +#define VCOM_CTL2 0xC7 +#define NV_MEMORY_WRITE 0xD0 +#define NV_MEMORY_PROTECT_KEY 0xD1 +#define NV_MEMORY_STATUS_READ 0xD2 +#define READ_ID4 0xD3 +#define POSITIVE_GAMMA_CORRECT 0xE0 +#define NEGATIVE_GAMMA_CORRECT 0xE1 +#define DIGITAL_GAMMA_CTL1 0xE2 +#define DIGITAL_GAMMA_CTL2 0xE3 +#define INTERFACE_CTL 0xF6 + +#define DCX_GPIONUM (2) +#define RST_GPIONUM (3) + + +#define SPI_CHANNEL 0 +#define SPI_SLAVE_SELECT 3 +/* clang-format on */ + +void tft_hard_init(void); +void tft_write_command(uint8_t cmd); +void tft_write_byte(uint8_t *data_buf, uint32_t length); +void tft_write_half(uint16_t *data_buf, uint32_t length); +void tft_write_word(uint32_t *data_buf, uint32_t length, uint32_t flag); +void tft_fill_data(uint32_t *data_buf, uint32_t length); + +#endif diff --git a/src/glyph_mapping/ov2640.c b/src/glyph_mapping/ov2640.c new file mode 100644 index 0000000..f5f37cf --- /dev/null +++ b/src/glyph_mapping/ov2640.c @@ -0,0 +1,229 @@ +/* Copyright 2018 Canaan Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include "ov2640.h" +#include "dvp.h" +#include "plic.h" + +const uint8_t ov2640_config[][2]= +{ + {0xff, 0x01}, + {0x12, 0x80}, + {0xff, 0x00}, + {0x2c, 0xff}, + {0x2e, 0xdf}, + {0xff, 0x01}, + {0x3c, 0x32}, + {0x11, 0x00}, + {0x09, 0x02}, + {0x04, 0x88}, + {0x13, 0xe5}, + {0x14, 0x48}, + {0x2c, 0x0c}, + {0x33, 0x78}, + {0x3a, 0x33}, + {0x3b, 0xfb}, + {0x3e, 0x00}, + {0x43, 0x11}, + {0x16, 0x10}, + {0x39, 0x92}, + {0x35, 0xda}, + {0x22, 0x1a}, + {0x37, 0xc3}, + {0x23, 0x00}, + {0x34, 0xc0}, + {0x36, 0x1a}, + {0x06, 0x88}, + {0x07, 0xc0}, + {0x0d, 0x87}, + {0x0e, 0x41}, + {0x4c, 0x00}, + {0x48, 0x00}, + {0x5b, 0x00}, + {0x42, 0x03}, + {0x4a, 0x81}, + {0x21, 0x99}, + {0x24, 0x40}, + {0x25, 0x38}, + {0x26, 0x82}, + {0x5c, 0x00}, + {0x63, 0x00}, + {0x46, 0x22}, + {0x0c, 0x3c}, + {0x61, 0x70}, + {0x62, 0x80}, + {0x7c, 0x05}, + {0x20, 0x80}, + {0x28, 0x30}, + {0x6c, 0x00}, + {0x6d, 0x80}, + {0x6e, 0x00}, + {0x70, 0x02}, + {0x71, 0x94}, + {0x73, 0xc1}, + {0x3d, 0x34}, + {0x5a, 0x57}, + {0x12, 0x40}, + {0x17, 0x11}, + {0x18, 0x43}, + {0x19, 0x00}, + {0x1a, 0x4b}, + {0x32, 0x09}, + {0x37, 0xc0}, + {0x4f, 0xca}, + {0x50, 0xa8}, + {0x5a, 0x23}, + {0x6d, 0x00}, + {0x3d, 0x38}, + {0xff, 0x00}, + {0xe5, 0x7f}, + {0xf9, 0xc0}, + {0x41, 0x24}, + {0xe0, 0x14}, + {0x76, 0xff}, + {0x33, 0xa0}, + {0x42, 0x20}, + {0x43, 0x18}, + {0x4c, 0x00}, + {0x87, 0xd5}, + {0x88, 0x3f}, + {0xd7, 0x03}, + {0xd9, 0x10}, + {0xd3, 0x82}, + {0xc8, 0x08}, + {0xc9, 0x80}, + {0x7c, 0x00}, + {0x7d, 0x00}, + {0x7c, 0x03}, + {0x7d, 0x48}, + {0x7d, 0x48}, + {0x7c, 0x08}, + {0x7d, 0x20}, + {0x7d, 0x10}, + {0x7d, 0x0e}, + {0x90, 0x00}, + {0x91, 0x0e}, + {0x91, 0x1a}, + {0x91, 0x31}, + {0x91, 0x5a}, + {0x91, 0x69}, + {0x91, 0x75}, + {0x91, 0x7e}, + {0x91, 0x88}, + {0x91, 0x8f}, + {0x91, 0x96}, + {0x91, 0xa3}, + {0x91, 0xaf}, + {0x91, 0xc4}, + {0x91, 0xd7}, + {0x91, 0xe8}, + {0x91, 0x20}, + {0x92, 0x00}, + {0x93, 0x06}, + {0x93, 0xe3}, + {0x93, 0x05}, + {0x93, 0x05}, + {0x93, 0x00}, + {0x93, 0x04}, + {0x93, 0x00}, + {0x93, 0x00}, + {0x93, 0x00}, + {0x93, 0x00}, + {0x93, 0x00}, + {0x93, 0x00}, + {0x93, 0x00}, + {0x96, 0x00}, + {0x97, 0x08}, + {0x97, 0x19}, + {0x97, 0x02}, + {0x97, 0x0c}, + {0x97, 0x24}, + {0x97, 0x30}, + {0x97, 0x28}, + {0x97, 0x26}, + {0x97, 0x02}, + {0x97, 0x98}, + {0x97, 0x80}, + {0x97, 0x00}, + {0x97, 0x00}, + {0xc3, 0xed}, + {0xa4, 0x00}, + {0xa8, 0x00}, + {0xc5, 0x11}, + {0xc6, 0x51}, + {0xbf, 0x80}, + {0xc7, 0x10}, + {0xb6, 0x66}, + {0xb8, 0xa5}, + {0xb7, 0x64}, + {0xb9, 0x7c}, + {0xb3, 0xaf}, + {0xb4, 0x97}, + {0xb5, 0xff}, + {0xb0, 0xc5}, + {0xb1, 0x94}, + {0xb2, 0x0f}, + {0xc4, 0x5c}, + {0xc0, 0x64}, + {0xc1, 0x4b}, + {0x8c, 0x00}, + {0x86, 0x3d}, + {0x50, 0x00}, + {0x51, 0xc8}, + {0x52, 0x96}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x5a, 0xc8}, + {0x5b, 0x96}, + {0x5c, 0x00}, + {0xd3, 0x02}, + {0xc3, 0xed}, + {0x7f, 0x00}, + {0xda, 0x08}, + {0xe5, 0x1f}, + {0xe1, 0x67}, + {0xe0, 0x00}, + {0xdd, 0x7f}, + {0x05, 0x00}, + {0xff, 0x00}, + {0xe0, 0x04}, + {0x5a, 0x50}, + {0x5b, 0x3c}, + {0x5c, 0x00}, + {0xe0, 0x00}, + {0x00, 0x00} +}; + +int ov2640_init(void) +{ + uint16_t v_manuf_id; + uint16_t v_device_id; + ov2640_read_id(&v_manuf_id, &v_device_id); + printf("manuf_id:0x%04x,device_id:0x%04x\n", v_manuf_id, v_device_id); + uint16_t index = 0; + for (index = 0; ov2640_config[index][0]; index++) + dvp_sccb_send_data(OV2640_ADDR, ov2640_config[index][0], ov2640_config[index][1]); + return 0; +} + +int ov2640_read_id(uint16_t *manuf_id, uint16_t *device_id) +{ + dvp_sccb_send_data(OV2640_ADDR, 0xFF, 0x01); + *manuf_id = (dvp_sccb_receive_data(OV2640_ADDR, 0x1C) << 8) | dvp_sccb_receive_data(OV2640_ADDR, 0x1D); + *device_id = (dvp_sccb_receive_data(OV2640_ADDR, 0x0A) << 8) | dvp_sccb_receive_data(OV2640_ADDR, 0x0B); + return 0; +} + diff --git a/src/glyph_mapping/ov2640.h b/src/glyph_mapping/ov2640.h new file mode 100644 index 0000000..941142e --- /dev/null +++ b/src/glyph_mapping/ov2640.h @@ -0,0 +1,26 @@ +/* Copyright 2018 Canaan Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _OV2640_H +#define _OV2640_H + +#include + +#define OV2640_ADDR 0x60 + +int ov2640_init(void); +int ov2640_read_id(uint16_t *manuf_id, uint16_t *device_id); + +#endif /* _OV2640_H */ + diff --git a/src/glyph_mapping/ov5640.c b/src/glyph_mapping/ov5640.c new file mode 100644 index 0000000..7b5f070 --- /dev/null +++ b/src/glyph_mapping/ov5640.c @@ -0,0 +1,78 @@ +/* Copyright 2018 Canaan Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "ov5640.h" +#include "ov5640cfg.h" +#include "dvp.h" + +static void hal_delay(uint32_t delay) +{ + usleep(delay * 1000); +} + +static void ov5640_wr_reg(uint16_t reg,uint8_t data) +{ + dvp_sccb_send_data(OV5640_ADDR, reg, data); +} + +static uint8_t ov5640_rd_reg(uint16_t reg) +{ + return dvp_sccb_receive_data(OV5640_ADDR, reg); +} + +uint8_t ov5640_init(void) +{ + uint16_t i = 0; + uint16_t reg = 0; + + reg = ov5640_rd_reg(OV5640_CHIPIDH); + reg <<= 8; + reg |= ov5640_rd_reg(OV5640_CHIPIDL); + printf("ID: %X \r\n", reg); + if(reg != OV5640_ID) + { + printf("ID: %d \r\n", reg); + return 1; + } + + ov5640_wr_reg(0x3103,0X11); /*system clock from pad, bit[1]*/ + ov5640_wr_reg(0X3008,0X82); + hal_delay(10); + + for(i = 0; i + +#define OV5640_ID 0X5640 +#define OV5640_ADDR 0X78 +#define OV5640_CHIPIDH 0X300A +#define OV5640_CHIPIDL 0X300B + +#define XSIZE 320 +#define YSIZE 240 +#define LCD_GRAM_ADDRESS 0x60020000 + +#define QQVGA_160_120 0 +#define QCIF_176_144 1 +#define QVGA_320_240 2 +#define WQVGA_400_240 3 +#define CIF_352_288 4 + +#define jpeg_buf_size (30*1024) + +uint8_t ov5640_init(void); +void ov5640_flash_lamp(uint8_t sw); + +#endif + diff --git a/src/glyph_mapping/ov5640cfg.h b/src/glyph_mapping/ov5640cfg.h new file mode 100644 index 0000000..fd86ade --- /dev/null +++ b/src/glyph_mapping/ov5640cfg.h @@ -0,0 +1,278 @@ +/* Copyright 2018 Canaan Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _OV5640CFG_H +#define _OV5640CFG_H + +#include "ov5640.h" + +const uint16_t ov5640_init_reg_tbl[][2]= +{ + // 24MHz input clock, 24MHz PCLK + {0x3008, 0x42}, // software power down, bit[6] + {0x3103, 0x03}, // system clock from PLL, bit[1] + {0x3017, 0xff}, // FREX, Vsync, HREF, PCLK, D[9:6] output enable + {0x3018, 0xff}, // D[5:0], GPIO[1:0] output enable + {0x3034, 0x1a}, // MIPI 10-bit + {0x3035, 0x41},//0x41, // PLL + {0x3036, 0x40}, // PLL + {0x3037, 0x13},//0x13, // PLL root divider, bit[4], PLL pre-divider, bit[3:0] + {0x3108, 0x01}, // PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2], SCLK root divider, bit[1:0] + {0x3630, 0x36}, + {0x3631, 0x0e}, + {0x3632, 0xe2}, + {0x3633, 0x12}, + {0x3621, 0xe0}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3905, 0x02}, + {0x3906, 0x10}, + {0x3901, 0x0a}, + {0x3731, 0x12}, + {0x3600, 0x08}, // VCM control + {0x3601, 0x33}, // VCM control + {0x302d, 0x60}, // system control + {0x3620, 0x52}, + {0x371b, 0x20}, + {0x471c, 0x50}, + {0x3a13, 0x43}, // pre-gain = 1.047x + {0x3a18, 0x00}, // gain ceiling + {0x3a19, 0xf8}, // gain ceiling = 15.5x + {0x3635, 0x13}, + {0x3636, 0x03}, + {0x3634, 0x40}, + {0x3622, 0x01}, + {0x3c01, 0x34}, // Band auto, bit[7] + {0x3c04, 0x28}, // threshold low sum + {0x3c05, 0x98}, // threshold high sum + {0x3c06, 0x00}, // light meter 1 threshold[15:8] + {0x3c07, 0x07}, // light meter 1 threshold[7:0] + {0x3c08, 0x00}, // light meter 2 threshold[15:8] + {0x3c09, 0x1c}, // light meter 2 threshold[7:0] + {0x3c0a, 0x9c}, // sample number[15:8] + {0x3c0b, 0x40}, // sample number[7:0] + {0x3810, 0x00}, // Timing Hoffset[11:8] + {0x3811, 0x10}, // Timing Hoffset[7:0] + {0x3812, 0x00}, // Timing Voffset[10:8] + {0x3708, 0x64}, + {0x4001, 0x02}, // BLC start from line 2 + {0x4005, 0x1a}, // BLC always update + {0x3000, 0x00}, // enable blocks + {0x3004, 0xff}, // enable clocks + {0x300e, 0x58}, // MIPI power down, DVP enable + {0x302e, 0x00}, + {0x4300, 0x61}, + {0X501F, 0x01}, + {0x3820, 0x46}, // flip + {0x3821, 0x00}, // mirror + {0x3814, 0x71}, // timing X inc + {0x3815, 0x35}, // timing Y inc + {0x3800, 0x00}, // HS + {0x3801, 0x00}, // HS + {0x3802, 0x00}, // VS + {0x3803, 0x00}, // VS + {0x3804, 0x0a}, // HW (HE) + {0x3805, 0x3f}, // HW (HE) + {0x3806, 0x07}, // VH (VE) + {0x3807, 0x9f}, // VH (VE) + {0x3808, (320 >> 8)}, // DVPHO + {0x3809, (320 & 0xff)}, // DVPHO + {0x380a, (240 >> 8)}, // DVPVO + {0x380b, (240 & 0xff)}, // DVPVO + {0x380c, 0x07}, // HTS + {0x380d, 0x58}, // HTS + {0x380e, 0x01}, // VTS + {0x380f, 0xf0}, // VTS + {0x3810, 0x00}, // HTS + {0x3811, 0x08}, // HTS + {0x3812, 0x00}, // VTS + {0x3813, 0x02}, // VTS + {0x3618, 0x00}, + {0x3612, 0x29}, + {0x3709, 0x52}, + {0x370c, 0x03}, + {0x3a02, 0x02}, // 60Hz max exposure + {0x3a03, 0xe0}, // 60Hz max exposure + {0x3a14, 0x02}, // 50Hz max exposure + {0x3a15, 0xe0}, // 50Hz max exposure + {0x4004, 0x02}, // BLC line number + {0x3002, 0x1c}, // reset JFIFO, SFIFO, JPG + {0x3006, 0xc3}, // disable clock of JPEG2x, JPEG + {0x4713, 0x03}, // JPEG mode 3 + {0x4407, 0x04}, // Quantization scale + {0x460b, 0x37}, + {0x460c, 0x20}, + {0x4837, 0x16}, // MIPI global timing + {0x3824, 0x04}, // PCLK manual divider + {0x5001, 0xA3}, // SDE on, scale on, UV average off, color matrix on, AWB on + {0x3503, 0x00}, // AEC/AGC on + {0x440e, 0x00}, + {0x5000, 0xa7}, // Lenc on, raw gamma on, BPC on, WPC on, CIP on + {0x3a0f, 0x30}, // stable range in high + {0x3a10, 0x28}, // stable range in low + {0x3a1b, 0x30}, // stable range out high + {0x3a1e, 0x26}, // stable range out low + {0x3a11, 0x60}, // fast zone high + {0x3a1f, 0x14}, // fast zone low + {0x5800, 0x23}, + {0x5801, 0x14}, + {0x5802, 0x0f}, + {0x5803, 0x0f}, + {0x5804, 0x12}, + {0x5805, 0x26}, + {0x5806, 0x0c}, + {0x5807, 0x08}, + {0x5808, 0x05}, + {0x5809, 0x05}, + {0x580a, 0x08}, + {0x580b, 0x0d}, + {0x580c, 0x08}, + {0x580d, 0x03}, + {0x580e, 0x00}, + {0x580f, 0x00}, + {0x5810, 0x03}, + {0x5811, 0x09}, + {0x5812, 0x07}, + {0x5813, 0x03}, + {0x5814, 0x00}, + {0x5815, 0x01}, + {0x5816, 0x03}, + {0x5817, 0x08}, + {0x5818, 0x0d}, + {0x5819, 0x08}, + {0x581a, 0x05}, + {0x581b, 0x06}, + {0x581c, 0x08}, + {0x581d, 0x0e}, + {0x581e, 0x29}, + {0x581f, 0x17}, + {0x5820, 0x11}, + {0x5821, 0x11}, + {0x5822, 0x15}, + {0x5823, 0x28}, + {0x5824, 0x46}, + {0x5825, 0x26}, + {0x5826, 0x08}, + {0x5827, 0x26}, + {0x5828, 0x64}, + {0x5829, 0x26}, + {0x582a, 0x24}, + {0x582b, 0x22}, + {0x582c, 0x24}, + {0x582d, 0x24}, + {0x582e, 0x06}, + {0x582f, 0x22}, + {0x5830, 0x40}, + {0x5831, 0x42}, + {0x5832, 0x24}, + {0x5833, 0x26}, + {0x5834, 0x24}, + {0x5835, 0x22}, + {0x5836, 0x22}, + {0x5837, 0x26}, + {0x5838, 0x44}, + {0x5839, 0x24}, + {0x583a, 0x26}, + {0x583b, 0x28}, + {0x583c, 0x42}, + {0x583d, 0xce}, // lenc BR offset + {0x5180, 0xff}, // AWB B block + {0x5181, 0xf2}, // AWB control + {0x5182, 0x00}, // [7:4] max local counter, [3:0] max fast counter + {0x5183, 0x14}, // AWB advanced + {0x5184, 0x25}, + {0x5185, 0x24}, + {0x5186, 0x09}, + {0x5187, 0x09}, + {0x5188, 0x09}, + {0x5189, 0x75}, + {0x518a, 0x54}, + {0x518b, 0xe0}, + {0x518c, 0xb2}, + {0x518d, 0x42}, + {0x518e, 0x3d}, + {0x518f, 0x56}, + {0x5190, 0x46}, + {0x5191, 0xf8}, // AWB top limit + {0x5192, 0x04}, // AWB bottom limit + {0x5193, 0x70}, // red limit + {0x5194, 0xf0}, // green limit + {0x5195, 0xf0}, // blue limit + {0x5196, 0x03}, // AWB control + {0x5197, 0x01}, // local limit + {0x5198, 0x04}, + {0x5199, 0x12}, + {0x519a, 0x04}, + {0x519b, 0x00}, + {0x519c, 0x06}, + {0x519d, 0x82}, + {0x519e, 0x38}, // AWB control + {0x5480, 0x01}, // Gamma bias plus on, bit[0] + {0x5481, 0x08}, + {0x5482, 0x14}, + {0x5483, 0x28}, + {0x5484, 0x51}, + {0x5485, 0x65}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + {0x5488, 0x87}, + {0x5489, 0x91}, + {0x548a, 0x9a}, + {0x548b, 0xaa}, + {0x548c, 0xb8}, + {0x548d, 0xcd}, + {0x548e, 0xdd}, + {0x548f, 0xea}, + {0x5490, 0x1d}, + {0x5381, 0x1e}, // CMX1 for Y + {0x5382, 0x5b}, // CMX2 for Y + {0x5383, 0x08}, // CMX3 for Y + {0x5384, 0x0a}, // CMX4 for U + {0x5385, 0x7e}, // CMX5 for U + {0x5386, 0x88}, // CMX6 for U + {0x5387, 0x7c}, // CMX7 for V + {0x5388, 0x6c}, // CMX8 for V + {0x5389, 0x10}, // CMX9 for V + {0x538a, 0x01}, // sign[9] + {0x538b, 0x98}, // sign[8:1] + {0x5580, 0x06}, // saturation on, bit[1] + {0x5583, 0x40}, + {0x5584, 0x10}, + {0x5589, 0x10}, + {0x558a, 0x00}, + {0x558b, 0xf8}, + {0x501d, 0x40}, // enable manual offset of contrast + {0x5300, 0x08}, // CIP sharpen MT threshold 1 + {0x5301, 0x30}, // CIP sharpen MT threshold 2 + {0x5302, 0x10}, // CIP sharpen MT offset 1 + {0x5303, 0x00}, // CIP sharpen MT offset 2 + {0x5304, 0x08}, // CIP DNS threshold 1 + {0x5305, 0x30}, // CIP DNS threshold 2 + {0x5306, 0x08}, // CIP DNS offset 1 + {0x5307, 0x16}, // CIP DNS offset 2 + {0x5309, 0x08}, // CIP sharpen TH threshold 1 + {0x530a, 0x30}, // CIP sharpen TH threshold 2 + {0x530b, 0x04}, // CIP sharpen TH offset 1 + {0x530c, 0x06}, // CIP sharpen TH offset 2 + {0x5025, 0x00}, + {0x3008, 0x02}, // wake up from standby, bit[6] + {0x4740, 0X21}, //VSYNC active HIGH +}; + +#endif +