From abfad9db516823ee638f26952b970ce53802ef34 Mon Sep 17 00:00:00 2001 From: Sol-X Date: Tue, 22 Jul 2014 09:25:29 -0700 Subject: [PATCH] Pongsat update --- Anarean AIR.spin | 1 + CPC1822.spin | 1 + Clock.spin | Bin 0 -> 43054 bytes DeathStar.spin | Bin 0 -> 9540 bytes FME.spin | Bin 0 -> 57700 bytes GDB Hello World.spin | Bin 0 -> 7716 bytes GDB SSW Hello World.spin | Bin 0 -> 8210 bytes GDB-API-V0.1.0.spin | Bin 0 -> 125872 bytes GDB-SerialMirror.spin | Bin 0 -> 36292 bytes I2C_ROMEngine.spin | 1 + L298MotorPwm.spin | 142 ++++++++++++++++++++++++++++++++++ L298SetMotor.spin | 136 ++++++++++++++++++++++++++++++++ MCP3208.spin | Bin 0 -> 17774 bytes MPL115A1.spin | Bin 0 -> 35064 bytes NLAST4051.spin | Bin 0 -> 23334 bytes Parallax Serial Terminal.spin | Bin 0 -> 38490 bytes PongSat-API-V0.1.0.spin | Bin 0 -> 23630 bytes SD_Card_Micro_01.spin | Bin 0 -> 25722 bytes Timing.spin | 57 ++++++++++++++ UAHLunarWormbotTest.spin | 92 ++++++++++++++++++++++ _README_.txt | Bin 0 -> 1136 bytes fsrw.spin | 1 + safe_spi.spin | 1 + 23 files changed, 432 insertions(+) create mode 100644 Anarean AIR.spin create mode 100644 CPC1822.spin create mode 100644 Clock.spin create mode 100644 DeathStar.spin create mode 100644 FME.spin create mode 100644 GDB Hello World.spin create mode 100644 GDB SSW Hello World.spin create mode 100644 GDB-API-V0.1.0.spin create mode 100644 GDB-SerialMirror.spin create mode 100644 I2C_ROMEngine.spin create mode 100644 L298MotorPwm.spin create mode 100644 L298SetMotor.spin create mode 100644 MCP3208.spin create mode 100644 MPL115A1.spin create mode 100644 NLAST4051.spin create mode 100644 Parallax Serial Terminal.spin create mode 100644 PongSat-API-V0.1.0.spin create mode 100644 SD_Card_Micro_01.spin create mode 100644 Timing.spin create mode 100644 UAHLunarWormbotTest.spin create mode 100644 _README_.txt create mode 100644 fsrw.spin create mode 100644 safe_spi.spin diff --git a/Anarean AIR.spin b/Anarean AIR.spin new file mode 100644 index 0000000..413ac7c --- /dev/null +++ b/Anarean AIR.spin @@ -0,0 +1 @@ +PUB Initialize | OK 'Initializes the Non range exteneded Anarean AIR Rx/Tx module \ No newline at end of file diff --git a/CPC1822.spin b/CPC1822.spin new file mode 100644 index 0000000..b69874a --- /dev/null +++ b/CPC1822.spin @@ -0,0 +1 @@ +PUB Initialize | OK 'Initializes the solar tracking system \ No newline at end of file diff --git a/Clock.spin b/Clock.spin new file mode 100644 index 0000000000000000000000000000000000000000..a6f36f177f63f6d04c9fdb803cf3cf4fd68d80e7 GIT binary patch literal 43054 zcmeI5TXS7UR>ybck%B57=79$u4knJ2OypRyJ$8mnD90l)CgTg1GD(J_rW9LoOngZs zIgXh?6;#3J;7jnm@(F;G9=tM^Su(vd7Xu3ggEm)_m$*6Y@n{eS=a$L5dCpEZBd zymP2SZ%&$P&BNwNbGP}rnKa)u%gs6an~WUqOxv6^SDUBJZu6DBz1{rM-uU}wtNGH7 zNpqvww0pbFgXVFnDc)SPR^PTKE6sU3eqm>~%Nv^p%R@W+zIoT~UAMRI8axxj?`U}P z_HBdciDCGlxo2?hSgm&x=D_->zmAY1XZtT|3Y9 zJhD>M|DM&cZNJ}IOsFl;1A3FO}LK+0%(#@9eb=MY*?QrS&w|zhlpK%bSlI zAiX<8mVj2{K2UG+va2A%B|+t&8pEBdN>wPp7v>T%P7BPb;4I+ zJ}Gbk;p0*t^*<>*hFW(EO?g*v+_QS%9M9n7gHmt(3~2$o_B$D-_2zk@*;l20TK*R+ zFKnT%xWRv?75F1@4xRY9)hsp8;j!`LiJbwxWb%0NIaeqQM(YMG@SbTl3ans{B#4H$ zns4msw!!Z7K4Z_muzU2$XLkOj{X#F~3V9Ozy89@-7cHU{mJV^;G0FIy^|^KXyVcyZ zmVQ&NHtgz7^Z6laT$;*RXq6?a^%KMSUV(heekZ1FmaVs}G?&YN*9Y`Auzz3@+26y^ zR_F`**TiUrB%p1)2SO?M{h48L(|Z3W#sN6OUvu`Sdw~DH+TpagR=CX-8sSsZ60Ws> zRqli>xX@hL$2LUS({Q$6*jzR4ZvH!$Jpfq9rKaBeciC<`QlXfxLkxM z@OF_RH1)Nj!yejO*fMD>^uO9dv!MgG4I+6O@{rvAVO@5u4)@?>1)%!vBCA)eH?NxH zubSMh7avX9unWtAe?v^~C58jqAH16W*sLdm4s2E>mqzFB)g%X}(?fitIkyJHaS zPUlR+G1+xu`kJWcw)lE*UAmB8w}E5o z*b}#F!OIVu53Tfy{fVn<uI!v{c(@f`(g0l8`GNP73h=rF54xK7Omud>3#xF+x=3!Bts`& zE}M;vT2A9x%}e37Z2k|N)YVFLb+Nj-Qe7=uZt^pG@3=S%tbf{@i+Hznx4!=KvzhXl zm-+td^OK|iynMs>6498q&fR=)9X+*vI?T(30ZBPnCTminTr!Kw31hjo%%Wq@r6(qq z1&qAf!D{a-UCU~w!|6SDUalAINp99NJ#Z`2hwG2*xw0LH<)v$;RpZW8;|;p@mc;|t zEIQDgMbkuYHJ3^R9#nSPN{9G6H!L!nn74eT^lmz1DuA@)I^!(!jBJ{59}O&L$xj!PfH~h%YCx!^0mp=$Zk{q=k}-C zv&Dx#NC17Fksrq2&;(>m$SKq@=p&<|Mqn7jV^nEeg8;$&4q*MumSvP=e$cvGU%y#t z;LOV!wuRymMgc+jsW&HL4r=#hEZ&S&=JPTxt|_OFX(`@!x=^Zm+lt>{$r4|y`|DUn9OQwKd(C87kP>K`UeN4l=`J|vX0ZvbTs2`8IP0SPak9~fxIUpn|aJjV4 z(75prcY{d~P z@O`})V@qhf`DmeW${|rVEM6nuO6I^<3%dOX?CLd!Uef0|4)GDH_?nPdmFSxG@Vd3e z%KODoR2Z_Pc#xI6$|)-rba-l-YwT8g6ZGv{FfZsG7qEDB>_t09c1glb+lSu^xLlv7DM(TvJ|ykylBdI+lLp2u@~K}#;a&AoR6=H zz36gnr?sA{D@I2^gw-zyC;(pUC z@kP#|zDO$(vI8%UBfbzHPsUorJb>egFEZEWZzXD8ypAuh7mNzj8lXGg7IZs~Gq)G~ zN?+Zv6+ymIu)TK$dnJBuwrt44kH=<_Qd$Fogdl1i~Y@{Pr*;S zF);s5`}M%f-7-UTKYj!GhSe-73Rya1SuOVZc{U1&>Q;z~ou0kUuXAPPD817C9`m*AkM zxc);~(V%sc@yu6bLZ8fGVnXLOzwLQ3Kc#D@&2eblZk-#pL)q$duS!-zuNhx%ndivf zXID*zwX5Z8p2$M^cpQC2z50pu$A7jS#a?IhA$CjC+<;x{TkHT7J!{AMFMUuug=Jgf z+LSl%+Z)|kK{3mq%flCar++Vwy3f`-an#S6vw3~|$;$shXy-F6wUYt7u8{6q_Wxr$$3n33{uA@bm+btyl~^tQ zn72BQ-=7BW^8EZZ)rNV;obTwB?f2T(r&s&TgU4-bhpsafB-gi%ipxcAV`tgx=5OQq z$3C55$$f@vo>AW3@zqCFeXl>}*W?wjebB+ldf$p}sVoc2sUK^nhjAS|w$m(gi~>VJ z;rq(6rZYz&Z0?5~EwL%R6y!7T!soSkZc;>?%C(k@qgN z`A)`e#bD?=+Sq{=ba(9f-pTjIc$>?e{CURyLND&p!fs#TDm(Na1bfFSQz5hyJ4^X~ zL&Oy8CcAkL>pm?tzIxkxfwExCdQ{IKvZ8?-+q;wMkRDDUlNHGCJUzhs6keX75T`CA7j|Dv*R#!^j_AI z?PpnHbz{vT>8W8`;aa;b(Xp2ov<+Uc^CVh4A7j{2ejK~~dDq`pWZ4;;im3jYQQg;_ z=`CdvHNQ67f9(vsWba~!#Oc~TJBDM}vGe8_cCLHJB(vVX-E$G>1?|@@FG%apzbLly zqP6Ms#f~~!Glre-BuW`{Yu)eAu;QIl>Tthfv^iXj^LlF@8h2J$G8M>O<4K`YzbIZE z;~LpN@94G-^`ohOQ1&9sG|&5)z(O=mOWveVW;=Z?|_pGu)V) z0<^T`_Fy<~<2iTs2WCfV_2YOK{}DU1Z3lUKzJH;`TkA0OTR8ijmBR-jqD%V^YqEEel37Q9~8Ry-wZVl{e-I^s-9&wu!lAXNLP8pBrcYHZVx)b}2 z*1T*Po=NjRMO)I(&`x~yiD;M+I?1;4*11;y4)btWcRH5*BEK0s<`ZlikC^+940Y)n z(w60P+)Joop8aYVJNK=J`d@h08N1nAK8cd^)}1E-^XzS8-d!*`noV~O`@OTbd=e#x zu^ZE!iU9j`=SjdkdmF>pjp)&~(9__C89jc$PpF0*oW5vt{X1(gF>8Dl8MXlP%FyD;zui<`VKK6CHyJn-tpU-_{ zxXshkYN>|c%J(DJ-yVsK{epaDFdkam^>sts)red?BPP9T_Z0<@zwmsk?>eZ+B%&6) zC%nq}eZ4wk+2)wwCEipX-@k>8=cLT2z9-JSNPIrcgUU7bMdE>f9fO10SQO6puERTn zLgb0!3vTfx^EMu5Cd`JoXLG@zuSOx9!ox8zqb_*E_Y1aoirzl_a7c(nCRTU7qm+*p z`1l9&(eU{74OySlN{qvdvO3c>1W~4I8^gYlQ0S#xh0id~7&**HlVBhs4hbZZCZ-A5 z11D$)ufLq~x(x@{XC_^0yO-}$!?^APtBGi-OFMnp94S}NfQpP@D^3O?Ja^}y*S&5f z)HkjdIs?5%tQ2d}|Jq+gRutp89;bzB9HV#69nrJv%tT!e7(z34>Y}Vi$@C(hAMbC# zNA1e(Sml-!g>01QNt%HFA(v2v(J6LQ(VRz*(y~V44YDFoh1!wHFtpiH@PkLQ^RRp?F^U^^BGFaXxaNI|j!XRggb=5%W#dFQ96yK=GXd zoI9?0z_6{Y`ChQ++=lQ0FIl^XMaIasARFjkAVDv7ZRy8$A4zc!Kzsnwpi~>0phKU9 zK+@5&_oq(R>`rL>E#a%UcjuO!u{ZxV^riBZ0%kNZ_S(OP z3Tt72;tPWn+F?sGuFebeBN{?udvGIbN|(G#d$~30)YnVMwCRd3UicL@=586~A~*cp zVD_;u?_Khtq+w|p_;<@2tU*Hq!G(Sr^KF@l%{Jvr>SHv{n1+M|b=^zGf=DJa)^1^f z;wzTZUWGMn_OK`>S25M;o)zniilaS!QtL`bxSE3TG_ydG?aH-}mRUdq20? zy4Q@JcWeynrbTDJFVWjoYww!fxnajuyZ>ujQBDa(b2m#F)j-Kl?LEry9dKVa;`4u@ z8Cs%mFE#f4bWg?CZe`f#eLh5^dAD=W`i9`j_lG0G?#@K=V#$tg+jnI`6Cr=?o$t_= z=rQRCdY;ea=vst;p*;g_XtyKexBblf9?ztc+a=aZk3whle^aF1dCB#xymqig!Xkou!V?X&s&nOf?x zuRZ;vd-`oD;W|svb<;7EB02C_o`~zVw#9j`qvN}Mcy=#9tFX~4%3bjPUYQevmD;rE zfhV3@3F{W#UAJ}gX2vdjLgAz+WOzo-Y#AZ?8PwA3TZa&j77TLcL=l72v$kBaC3UVb zuxx9~j1_hTUlcr)iL2XD?4wN0j=>~518tPC(u^BoBytS0kg`T-e!e^;-(0Id9D2uE zF$NZcd|j-iCvJaeEj%#FXbc`-o7k89j4}^IqSTiox*D^9Xw!TeB!KLN;zn$eJQ#5D zh?u(CHdXqgrc>9?8HyEy3&cfj4#7n*BUcU;Xg_2!$8+>7bXZUh+?`jf`n_K$MGchm zcy!UOh}X%3(9VYaDOTZqon>vG*CS_YdPb0WJG21(1U0gt?5tq)SrovgI9R?5(c5Et z^P#;rG?#?E7-%)OoV5=8Bj5IqC8GML=HKiXDhq_cv0~nr^AowhzehjyfZK%%k}yevq~q)I7H`N~ zgIkpHu>m;OCynwwoS)Dd%?Z69O+B>?amLUg;d{&f{$m{f@#BB(9sfC(`?=L2XmXkV z{?V_F=j@zO=A6-U7Fftba7eN^BR8SD7xvCB?wwuQJL5~|DtCGBjNFm#y}x%h8yrrN z@!yRiKgi0WWop((dP2q>Ip!U-vvitkRL7dS6cY^PkdS%LBIUQRD4v@_d(n!q&GvX;==KDQNQThW;rJNM4LH`P`Fy~u7SPf8AkED;p&{uB_)iiOt1&a+pF zaznaLe$_|y)N{4(N;fx;y~1|NZehvP+dU=(!*&FpqsXo;yI$&Zc-_`xE#3Cw&1Wuo zS>_i8#!ll|aSLBVIbP($*Dz`I2yoLD*d7$m%tz6xEnC6@E9Uqd#%om$8vg~pifif# zWX$Pv8&lcs)4|~p)Z=wf+o1|S49Sq?my~53u}V87mm^;J+Zv_$uJ97L+WBqz`{8{C z`hc>xFOR2y2hiqprexNWSzip55s%MxA{_VUj_z9wwVdxGW%BP{9giaXiSQ|7~(jZPy5>a%>qxoh+u{*-*3&|?wVV$reD zZvESFb+>;dF{U#?JL}bUyx(dm!1igx`ej zcx-tId@Jk>(SyH*CkEWoFL^#kWOp-;eYp@?buZ6p;H?j3L-4481y19QI7c*VWys^| z^Z&Z@OPs4+pAHIyZxa4Hl9I7>Piebkp0w{>)*Q7|=O?+A*Y`68W;q|C-YoAPt;|og RW$+&RbE^K7>BBC;{||6@jIaOz literal 0 HcmV?d00001 diff --git a/DeathStar.spin b/DeathStar.spin new file mode 100644 index 0000000000000000000000000000000000000000..f181ee120e967553bb4ab4097ce42945af959987 GIT binary patch literal 9540 zcmdU!Sx;L>6vsz-tkm~DUPYoN8-y%P+o%r#vd}o#0+KYUs>pyLGAvEnb>xb$m zX#4-2y;JToI-cW4meJ0M=5E**o~^JceSnLT;^sB=cf_L^ zHE_di^n~kuVc!uAE!{m1A2q*cT!U|ugR!^5FSbTCRKuK}kJrj*m$eUkY#J9cq1w>h zhOl$;a`}Ufxn43G#QzuN!)FxuYj&xzG7tS7+bKXwQC!vt04| zfCEK+Sdlf@G7iF5Y*r?XNUbm29nGHJ)W}uYjq(cH+Mz9(Ws=A{+o2(;A|Y&il+l{* zdup6|p3=%3?%S-M5H^LC^*Z+Ns}8gC4>e{(D|gJ6HFWpEtla(quD140^$h=b&wPjb zmj!8CP0!qS!jV1Ag_=}CWwd3A;6uyiMSi$Nh5t~3w}6Myzkw8c1~=A?J9u+HrIBmg zw=`xPo0sCfe3ooZl%ol|#!;f3ElG7#&(JP(5HE~IMxTMcp*3(3&PH$7Pkddo;OVC9 zEVLwjPKL))w@g}i$y?g7t$(~yM-srVwshy3n`*|FxW3%d#WxYNy3a>y7s*?tyhXM| zP8-6zC&ZlTEG!7`WAja%=ylT_qEIXql=(^Dv}?|=+ehpGE1;j7rrGEn_QoShRw5$9 z>ZSJjcug;(-4o6|%LPtz0yr-zjYWMwI}OKAXCQl6rK@-mI6pXeK=Y-#AqemaLEM3@C!}gG6VSmg1wwMEc4vIMU z=0vb|_B|OZ`r9!3N!DAHjxEbVFX?(-x>hrL?OCz=d5?w}8zyc}6nPgBUl}|BG*ns*F_l{xf^LXuYP=b zBckzFIG>r3a}Nh+=e+vy(SM^86H(SJ3(Vw?s4CFk8T5At{k=hdf6#w7=pPJvuRYYm z4}<#|TaopA#qn_7Rt`Jf%+Y2+Kfk}!;`LE8r_qdk4 zo){p`sVH}%z0^}GQoA<8s}Jc6ALa9&$yPsA?2(0^^qaA@s@j{XFHdeCtX|W)E0P!g z>F5RFOrPd6Udbmc9n7yN%IT<)-FbZNRRgljRePGr!ttJ(as4UULWb(>J`h(h-;}BWm;oNSTgAxvV+%MRSc)qI#wt6 zsQ9%j`iRttZ5yfv3brYhyx}iNAw11U=4`;i5_}Z_nMccr8yFrb6Nqe30qw&wPi1eA>iu^t$=zH43By$(jL0si)lTf?~TCj#%5N+;SbzO%srzOQV@QRh+If<;$UCx*pLM1y!bkG;^enzWIc!0^cB@SJN# zCA>b)2OZ^8;m%|8KJH^jTO*>4{AE@p@u0}Oet?P7)g4I9JzV|;EVm#h$%wt!henlT zNj~6Los!>l@06ucb~@Uhmq*9wJ*6Ryo~A@WLly{Yu&m#jbffGYobPe3A_wLz6Ii_9 VT=0A)@_?S$Xh(Dte;lt*{{!sqSDF9- literal 0 HcmV?d00001 diff --git a/FME.spin b/FME.spin new file mode 100644 index 0000000000000000000000000000000000000000..4d2c682c8a9b16b798cb0841435ba93de2af1466 GIT binary patch literal 57700 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 literal 0 HcmV?d00001 diff --git a/GDB Hello World.spin b/GDB Hello World.spin new file mode 100644 index 0000000000000000000000000000000000000000..f8ef6bd7c5ea59f171ca9f10ff3d4010748150d8 GIT binary patch literal 7716 zcmdU!S#uLd5Xa{^RrwAZ9*{XKTjmT^d64A;Qy3`AKzK^Yl8hbe!ji#;;;WPVf89Im zffQ86KwMkW&aQfnsD^PENuR+_(xWWxZa9#fN!XW6$C7QL@kpa#c&+bN z=Ejijk~&D7y}RDzfEm zi#MJq%lcu@{2b$;Eh%41?j=Rsv+!J%got#{cTD;_dIlYca*vDq8eRNVmiFQ~T;z(^ zQwtR10n|M-3u0F=D^Wxc2NT5w_=t5QVn&n%O~_7=7)u{(4l@r7aXZ3VP~_XIWZA-X z#jR&OGJ*CP_D4O6HRGmeylQ|LQGRLx6)~~c0#T?XJy8-#BYiu9$MpxIA1LZTmMwAi zYLZ_8PnD(J*NQD!2+5cYPOhsaW6`+2$LYwTl?-$`bUZaaZ}qMv`sjgYdZHS_yCoWs z8WcR#H|y@&Q{PsC$|a>54dsucJ2BAY-uuWxiTbS*8}5g&5Vv0*_XV4SPZ#0pXG)j6|wIUB3yr@lo+-trL{ z^6!4^IB&1Z;Nisl`!Rg9J9$EOR20{A*?aO3nb**GAWzYUf~VvsapAGiOy76RpZL0M zv)kcFA-Y<)t8Zpd1Gm-rzt^2#?yJj-Z{Yuz zaXZTKfeOrlLrvow`iwN@>8>Ho_lyab=~Q_!5E>K9Z^R#&?nu^UyW%~w;i9_tMPU(L zpJRBeUU{oKmD~QI@Ox8s@wIE_i^uZc%k<5%y#e2Lt-Ao-+_G@plAq~0;#;S#hZn-d z9bw_7;bTVM5bchxb9iYEKaWvP7#B;a;T@@j4Yn-{PdJ)j+0f4l_Jv7(JDA zkL6!Buk<@JlO?MDaoBBdrL0#L{H}MwwQ0( zZm%vcZs}KQlq2kyJr6RG#j*0n-l2@D-aWuMYJ;|sI?EY$iC5D1i?RR`EXrof!u6Uy z%lb9ixRS;+I(VN`ln-nn4vUxpf3N)X8stM>D)(T;c5Ye6MtoB97xjr%+-o>EMrEnW z(#4*mDqX#zL-V(&(0xf-+zYo%hh6EmtlSeB8?xw7)JHj8(PLFq+u}m@6N?)f z#WR4u*2f3nA^N&s@b67M=VOAok7QY*I`8?4p5j$ES+kwamNNM6ON?iudx)OQIZRpzg$pAKaBE1GFb(HaZcVfqR`TQNNI=0-Q^1$nv$>rYk zZm}HOacAwjDLQ66;vIJ@XSw+#j1t{YjCG}dM{+Esk?j&)oc8}EQIsRn+z{@9Q|MKc zBcpt5aq76_xK^Z$YkkW-fb>J+B z_2_xgpHGx|TIT0bBacC^v=Vs09xCqQI0f4@9=;R4s$Yn6P|Ejt^z%b;KhPVp+-nKD z>J#<;yajRMc^qf4?5eL7r1X`%lgvDS&-@;HnR#a1oibyVzhm6g>})Thm&cTA0=m(2 zc~$Cswk_2qdz+|7`H^-blJXv&Zo4~EED>X3`i$FCW_%CZ<)bM^5HU6kzs(gDD+RCD~)PR)i^ZD`XF6d7K1$ zPEkW+rf~-Nbl6{bDV{rNkGG(H?xDIaI?3N{uf}e3SNj8~K=tYWL0wHa3!(4hJcE9&_{2HV zTa%8yDeemJJ6+dUXiO9^kMk|=Tk3)Mr-&}Sr)aq%y^Fs7x+d(l`7ejlKvwDL9y_Dh zF;NR#GCyJV{Yup&nCP2*%RsU)_EQ~B@u*8@S`U4}|Gw5g*5#)`-Bbhba^vilMLpGw z%F;TgMqlz-RbC;TOJ1?n(Q0Vt4{lrkCyf))tbWB<@qN=%%Sd$FlNOvrumgi;PwTUv UgGXjP>{89?K1#0 zo&{DH*>qIfnq)E$XyOYqnyH!I+X@ccL#d+<*!j~>(a9?^!A0pmb3=j@wAa{ z>-VQLm8R1#YB?TYM*GBU&^FxX(y`g1t!J!geApN4m}5~_UB6emMzw7SIXt0lC}z@q zQ+N0Ukw**Eu_Fj~O(XAfsCHYPeKkEUH>#-Zx4>a#2AeS-Xd9xl!sFVv9AF6lLACZgvho*pQZ6(tA=Kw~}5cli-nV`Ic$_yY68Jyxjfbky@Lu*mhZ z5`K?DiI>Gii_s2TQIpd!Cc-DY1LVt1r88I{8f>FpN9reVM`ucVTkNJD*-;>wD zEDMtC*`zoFmKxP|Q!_S&5SlR>o}5!o2GN+`{j_i1N(6d8)_81kj`Xal{$YU?-H{EE z-Bcf#8W!BqH}kI9UDsx!mupHk+LKj^%s8s;6U7odo}*Mx%hGmEPwJ_rSR~7F_e9TM z=zdo1nj~WPfv&zZc&xab)LN;NwzaBt$;6+!dWx@fr1Rvd9WL+Rq3yT8xae4pvbyjf zMh*>^Ovqu?ysp{M9nl2Sd48j!p=xOgM{LMN#Rh_Sz(FBD<12W*XXhO6(P*riox0`~ zMdX7SiqF1myNK7fVByT{`!RjAU*d%5sK~FWve#uHBCoD?PnMz%g-?l3{K9>sF?`-K zdt&Pi8@-Ws)$VGnvoY2oM|(ZyGjE!;-^!n0;SJ-h-_&n9J zdY=#%QS~{8NA@bB?!6rQr{eE<;o@^on=KY)zgNR2Q}zUYTeIo{c5`Hrx*$7KbHr2c zT}v;;i>u{te_ef7bDwC{Z_2`AGrZa>eBvZ_dhaAH_IVxk-jyzyev!#8fJ{m3Oxjaop`-pp z4o}PRmhbjzvf_f?lB1kq&(=KXL=;EnjoM+9RlRyZa;%2cM(;dk*d>k)pHBz@G?)-( zQ{we$eWvs_Fs>B#SLE1#ukGix1ZA&|V}&b2%d&qZ>+G6^=;t_&zR#@M9+P(N5kBto zYpZEKBHS@&U$-TjQPl*3nY?8^{b_yB#A`jb2z87{x$`44dG+u4bD;O^AIYb>+&j zcluF*MW3^k)rxfTN*7O{%7Q^*s2#J^ieh(JtDm#F2?lth_e3ea#pO0XJ5S8w8R9$y z#)dN#J=l2;zji#zK1;rVo630vM7A*U1if934PuM(Ypc9sm5h%uU5L;i>x;2m1y4__`{x z2HpiDhd*!!Clf2Igx!MgtvH9AkQQW!mN0f6hh2I?hA4MdC(;6Mw-(a);N3Io_gpyn z+!f89pBB1o&g8H$HMPfWE8Ww5F?*obGw~|vmiY^V=?cJo%Nx; zOUbz=nExyT2jgIM&aGmXjKn}R(<(i(awOSR;qS04s9pEUW%*32n)Se}goh&w#%d33 z>if#@_gGz=!8!AWwcYZ~0&_=4osFE$J~ijMtl9{iievJ)+NDRcQDt>niJ! z_gs`++}E;U1yt@Cb6w{*mzK{`A9|ikbGuZn{+k`{JmLgrTo%1;F;L{-s(G5*kBHyU zsjl}sPg*XQ)xXg<)Q!yf42hUD;X_+Cfwnv8HsUtZx zW7pHI~QRYRk^+J;uXDlS>l?Bin8SCZTj_T&a)yPRE$XY(+upR)PR zk=i#?jBr}TEN!cek@kb;h4FIM|4|;XDj&E#O1l+Kj&CtngaW$pc->zq1h((p_du);G7W` zMPS6=v~*!t=lR5=+*g8^9Y|NN9$O{kLi4jxI_kGil{u*qe;w}4;eX$bp~X8+4UeQ5ryqRh=eA_d1FP%zZ5*eWj9<_Ssj0}d9aXnur#^o9m2kGg zKX;t{MlO%+7FiGPam{?k!YEQb*_A@-rY~hv@^Apa1SrwVxcgWN~z5=gQ zVsulhaOwDB^NK4vD`&=HFEFi`r?aX(u?Cv@o(cV_M01Z1Z}A=BvUEc$R=Fb&I?;!E bjeE{EJBC1x9=H(Q!8}AKY%oyoeIfP_aOon{ literal 0 HcmV?d00001 diff --git a/GDB-API-V0.1.0.spin b/GDB-API-V0.1.0.spin new file mode 100644 index 0000000000000000000000000000000000000000..3fe07a041d59bd9d527b4fc96ed69f184cc4a327 GIT binary patch literal 125872 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 YHr=22?mwSX>-fKntz&G^f3v^;2Ok~W)Bpeg literal 0 HcmV?d00001 diff --git a/GDB-SerialMirror.spin b/GDB-SerialMirror.spin new file mode 100644 index 0000000000000000000000000000000000000000..f65e0a87f0170dd504e318a98d48389e40c99100 GIT binary patch literal 36292 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 EAMCB_A^-pY literal 0 HcmV?d00001 diff --git a/I2C_ROMEngine.spin b/I2C_ROMEngine.spin new file mode 100644 index 0000000..9254e58 --- /dev/null +++ b/I2C_ROMEngine.spin @@ -0,0 +1 @@ +{{ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // I2C Electrically Erasable Programmable Read Only Memory Engine // // Author: Kwabena W. Agyeman // Updated: 7/27/2010 // Designed For: P8X32A // Version: 1.1 // // Copyright (c) 2010 Kwabena W. Agyeman // See end of file for terms of use. // // Update History: // // v1.0 - Original release - 8/8/2009. // v1.1 - Added variable pin support - 7/27/2010. // // For each included copy of this object only one spin interpreter should access it at a time. // // Nyamekye, /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // I2C Circuit: // // 3.3V // | // R 10KOHM // | // Data Pin Number --- EEPROM SDA Pin. // // 3.3V // | // R 10KOHM // | // Clock Pin Number --- EEPROM SCL Pin. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }} PUB readByte(EEPROMaddress) '' 18 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Reads a byte from the EEPROM. It is recommended to read the byte on a byte boundary. '' // '' // Returns the byte on success and false on failure. Could return a byte of value 0. '' // '' // EEPROMaddress - Starting byte address of the data to access. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// result &= readPage(EEPROMaddress, @result, 1) PUB writeByte(EEPROMaddress, value) '' 19 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Writes a byte to the EEPROM. It is recommended to write the byte on a byte boundary. '' // '' // Returns true on success and false on failure. '' // '' // EEPROMaddress - Starting byte address of the data to access. '' // Value - Value to write to the EEPROM. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// return writePage(EEPROMaddress, @value, 1) PUB readWord(EEPROMaddress) '' 18 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Reads a word from the EEPROM. It is recommended to read the word on a word boundary. '' // '' // Returns the word on success and false on failure. Could return a word of value 0. '' // '' // EEPROMaddress - Starting byte address of the data to access. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// result &= readPage(EEPROMaddress, @result, 2) PUB writeWord(EEPROMaddress, value) '' 19 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Writes a word to the EEPROM. It is recommended to write the word on a word boundary. '' // '' // Returns true on success and false on failure. '' // '' // EEPROMaddress - Starting byte address of the data to access. '' // Value - Value to write to the EEPROM. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// return writePage(EEPROMaddress, @value, 2) PUB readLong(EEPROMaddress) '' 18 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Reads a long from the EEPROM. It is recommended to read the long on a long boundary. '' // '' // Returns the long on success and false on failure. Could return a long of value 0. '' // '' // EEPROMaddress - Starting byte address of the data to access. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// result &= readPage(EEPROMaddress, @result, 4) PUB writeLong(EEPROMaddress, value) '' 19 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Writes a long to the EEPROM. It is recommended to write the long on a long boundary. '' // '' // Returns true on success and false on failure. '' // '' // EEPROMaddress - Starting byte address of the data to access. '' // Value - Value to write to the EEPROM. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// return writePage(EEPROMaddress, @value, 4) PUB readPage(EEPROMaddress, RAMaddress, byteCount) '' 14 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Reads bytes from the EEPROM. Uses a 19 bit addressing scheme which can acess multiple EERPOMs on a bus. '' // '' // This rountine can only read from the EEPROM selected. Any reads past that EEPROM will warp arround. '' // '' // It is recommended to use the byte/word/long read rountines used on byte/word/long boundaries respectively. '' // '' // Returns true on success and false on failure. '' // '' // EEPROMaddress - Starting byte address of the data to access. '' // RAMaddress - Starting byte address of the data to write to. '' // ByteCount - Number of bytes to read. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// result := eepromPoll(EEPROMaddress) if(result) stopDataTransfer startDataTransfer result and= transmitPacket(constant((10 << 4) | 1) | ((EEPROMaddress >> 15) & $E)) repeat ((byteCount <# (65_536 - (RAMaddress #> 0))) #> 0) byte[RAMaddress++] := receivePacket(--byteCount) stopDataTransfer clearLock PUB writePage(EEPROMaddress, RAMaddress, byteCount) '' 14 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Writes bytes to the EEPROM. Uses a 19 bit addressing scheme which can access multiple EERPOMs on a bus. '' // '' // This rountine can only write to the page of the EEPROM selected. Any writes past that page will warp arround. '' // '' // It is recommended to use the byte/word/long write rountines used on byte/word/long boundaries respectively. '' // '' // Returns true on success and false on failure. '' // '' // EEPROMaddress - Starting byte address of the data to access. '' // RAMaddress - Starting byte address of the data to read from. '' // ByteCount - Number of bytes to write. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// result := eepromPoll(EEPROMaddress) if(result) repeat ((byteCount <# (65_536 - (RAMaddress #> 0))) #> 0) result and= transmitPacket(byte[RAMaddress++]) stopDataTransfer clearLock PUB ROMEngineStart(dataPinNumber, clockPinNumber, lockNumberToUse) '' 9 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Checks out a lock for the driver and changes the I2C Circuit pins. '' // '' // Returns true on success and false on failure. '' // '' // DataPinNumber - Pin to use to drive the SDA data line circuit. '' // ClockPinNumber - Pin to use to drive the SCL clock line circuit. '' // LockNumberToUse - Lock number to use if sharing the I2C bus (0 - 7). -1 to request a new lock number. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ROMEngineStop dataPin := ((dataPinNumber <# 31) #> 0) clockPin := ((clockPinNumber <# 31) #> 0) if((dataPin <> clockPin) and (chipver == 1)) lockNumber := lockNumberToUse if(lockNumberToUse == -1) lockNumber := locknew result or= ++lockNumber PUB ROMEngineStop '' 3 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Returns the lock used by the driver. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(lockNumber) lockret(-1 + lockNumber~) PRI eepromPoll(EEPROMaddress) ' 8 Stack Longs setLock startDataTransfer result := cnt repeat until(transmitPacket(constant(10 << 4) | ((EEPROMaddress >> 15) & $E))) stopDataTransfer startDataTransfer if((||(cnt - result)) > (clkfreq / constant(1_000 / 5))) return false result := transmitPacket(EEPROMaddress >> 8) result and= transmitPacket(EEPROMaddress) PRI transmitPacket(value) ' 4 Stack Longs value := ((!value) >< 8) repeat 8 dira[dataPin] := value dira[clockPin] := false dira[clockPin] := true value >>= 1 dira[dataPin] := false dira[clockPin] := false result := not(ina[dataPin]) dira[clockPin] := true dira[dataPin] := true PRI receivePacket(aknowledge) ' 4 Stack Longs dira[dataPin] := false repeat 8 result <<= 1 dira[clockPin] := false result |= ina[dataPin] dira[clockPin] := true dira[dataPin] := (not(not(aknowledge))) dira[clockPin] := false dira[clockPin] := true dira[dataPin] := true PRI startDataTransfer ' 3 Stack Longs outa[dataPin] := false outa[clockPin] := false dira[dataPin] := true dira[clockPin] := true PRI stopDataTransfer ' 3 Stack Longs dira[clockPin] := false dira[dataPin] := false PRI setLock ' 3 Stack Longs if(lockNumber) repeat while(lockset(lockNumber - 1)) PRI clearLock ' 3 Stack Longs if(lockNumber) lockclr(lockNumber - 1) DAT ' //////////////////////Variable Array///////////////////////////////////////////////////////////////////////////////////////// dataPin byte 29 ' Default data pin. clockPin byte 28 ' Default clock pin. lockNumber byte 00 ' Driver lock number. ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {{ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TERMS OF USE: MIT License /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the // Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }} \ No newline at end of file diff --git a/L298MotorPwm.spin b/L298MotorPwm.spin new file mode 100644 index 0000000..27a2f92 --- /dev/null +++ b/L298MotorPwm.spin @@ -0,0 +1,142 @@ +{{ + L298MotorsPwm.spin + Tom Doyle + 25 Feb 2007 + + Starts a cog to maintain a PWM signal to the L298 chip + The control pins on the L298 are controlled by the forward and reverse procedures + Speed is controlled by the update procedure + + In normal use it is not necessary to call any of these procedures directly as + they are called by the SetMotor.spin object +}} + +CON + + _clkmode = xtal1 + pll16x + _xinfreq = 5_000_000 + + +VAR + + long duty, period, pPin, dirM, I1pin, I2Pin ' par access + + byte cog + +PUB start(EnPin, In1Pin, In2Pin, pulsesPerCycle) : success + + ' EnPin - L298 Enable Pin + ' In1Pin - L298 Input 1 Pin + ' In2Pin - L298 Input 2 Pin + ' pulsesPerCycle - pulses per PWM cycle = clkfreq/pwmfreq + + pPin := EnPin + I1pin := In1Pin + I2pin := In2Pin + + duty := 0 + period := pulsesPerCycle + + reverse ' initialize dirM + success := cog := cognew(@entry, @duty) + + +PUB stop +{{ set esc PWM pin to off + stop cog }} + + waitpeq(0, |< pPin, 0) + dira[pPin] := 0 + if cog > 0 + cogstop(cog) + +PUB forward + + dirM := !0 + +Pub reverse + + dirM := 0 + + +PUB update(dutyPercent) + + duty := period * dutyPercent / 100 + + +DAT + +entry movi ctra,#%00100_000 + movd ctra,#0 + + mov addr, par + add addr, #8 ' L298 Enable (PWM) pin number + rdword _Enpin, addr ' stored in _EnPin + movs ctra,_Enpin + + mov temp, #1 + shl temp,_Enpin ' L298 Enable (PWM) pin + or dira, temp ' make an output + + mov addr, par + add addr, #16 ' L298 In1 pin + rdlong _In1Pin, addr ' stored in _In1Pin + mov temp, #1 + shl temp,_In1pin ' L298 In1 pin + or dira, temp ' make an output + + mov addr, par + add addr, #20 ' L298 In2 pin + rdlong _In2Pin, addr ' stored in _In2Pin + mov temp, #1 + shl temp,_In2pin ' L298 In2 pin + or dira, temp ' make an output + + + mov frqa,#1 + + mov addr, par + add addr, #4 ' pulses per pwm cycle + rdlong _cntadd, addr + mov cntacc,cnt + add cntacc,_cntadd + +:loop waitcnt cntacc,_cntadd + + mov tempDir, outa + + mov addr, par + add addr, #12 ' dirM + rdlong _dirM, addr ' store in _dirM + + mov tempDir, outa + + mov temp, #1 + shl temp,_In1Pin ' L298 In1 + test _dirM, 1 WZ ' check if direction = 1 + muxz tempDir, temp ' set Input 1 + + mov temp, #1 + shl temp,_In2Pin ' L298 In2 + test _dirM, 1 WZ ' check if direction = 1 + muxnz tempDir, temp ' set Input 2 + + mov outa, tempDir + + rdlong _duty,par + mov temp, par + add temp, #1 + rdlong _duty, temp + neg phsa,_duty + jmp #:loop + +_dirM res 1 ' motor direction 0 or 1 +_In1Pin res 1 ' L298 Input 1 Pin +_In2Pin res 1 ' L298 Input 2 Pin +tempDir res 1 ' temp direction +cntacc res 1 +_duty res 1 +_cntadd res 1 +_Enpin res 1 ' L298 Enable Pin +addr res 1 +temp res 1 \ No newline at end of file diff --git a/L298SetMotor.spin b/L298SetMotor.spin new file mode 100644 index 0000000..386441a --- /dev/null +++ b/L298SetMotor.spin @@ -0,0 +1,136 @@ +{{ + L298SetMotor.spin + Tom Doyle + 25 Feb 2007 + + Control of a DC motor with the L298 motor control chip. + The L298 chip does not use the traditional PWM, Direction and Brake control lines. + It uses what it refers to as the Enable line for PWM input and two lines refered to + as InX and InY for direction, coast and brake functions. The chip contains two controllers + and is readily available in a kit (L298 Compact Motor Driver) for less + than $20. The only deal better than this I have found is the Propeller Proto Board. + + The init procedure initializes local varibles and starts a PWM loop in a new cog + + The setMotor procedure sets the duty cycle (0%-100%), direction and delay per % change in + duty cycle. The delay per % change in duty cycle allows the motor speed to ramp up and down in + a smooth manner. The amount of delay to use will depend on the characteristics of the motor + and the load on the motor. This procedure is operating open loop which means that if the delay + per % change in duty cycle is not long enough you can over run the motor. The procedure keeps + track of the direction of the motor and will ramp the speed to 0 before changing direction. + The setMotor procedure is run in a new cog which waits for any previous operation to quit before + starting the new one. The setMotor cog is released after the new motor setting has been reached. + The pwm loop will run constantly in its own cog to keep the motor running at the set duty cycle. + +}} + +CON + + _clkmode = xtal1 + pll16x ' use crystal x 16 (5 x 16 = 80 Mhz) + _xinfreq = 5_000_000 + + _lcdPin = 0 ' Parallax 4x20 LCD Serial Line + + ' ESC Values + _escForward = 1 ' forward direction + _escReverse = 0 ' reverse direction + + +OBJ + + pwm[2] : "L298MotorPwm" ' Motor PWM object + + +VAR + + long Stack[50] + + WORD dutyCycleOld ' previous duty cycle value for ramp calculations + WORD directionOld ' previous directon value for reverse control + BYTE motorCog ' cog ID for setMotor + WORD escFreq ' ESC Frequency + BYTE pwmIndex ' pwm.spin index + + +PUB init(EnPin, In1, In2, Freq, pIndex) | pulsesPerCycle + +{{ + initialize electronic speed control + start pwm object in a new cog +}} + + escFreq := Freq + pwmIndex := pIndex + dutyCycleOld := 0 + directionOld := _escForward + pulsesPerCycle := clkfreq / escFreq + pwm[pwmIndex].start(EnPin, In1, In2, pulsesPerCycle ) + pwm[pwmIndex].reverse + motorCog := 0 + + +PUB setMotor(duty, Direction, delayPerStep) : success + + ' return value is cog 0-8 where 0 is no cog + + REPEAT + WHILE motorCog > 0 ' wait for end of previous operation + + success := (motorCog := cognew(csetMotor(duty, Direction, delayPerStep, @dutyCycleOld, @directionOld), @Stack) + 1) + + +PRI csetMotor(duty, Direction, delayPerStep, adrDCold, adrDirOld) | tempFreq, tempDuty, lDutyCycleOld, lDirectionOld + + + lDutyCycleOld := word[adrDCold] + lDirectionOld := word[adrDirOld] + + + IF (Direction <> lDirectionOld) AND (lDutyCycleOld > 0) ' stop motor before reversing + + REPEAT + + lDutyCycleOld := lDutyCycleOld - 1 + + tempFreq := escFreq + tempDuty := lDutyCycleOld + + pwm[pwmIndex].update(tempDuty) + + waitcnt(((CLKFREQ/1000) * delayPerStep) + cnt) + + WHILE lDutyCycleOld > 0 + + if Direction == _escForward + pwm[pwmIndex].forward + + if Direction == _escReverse + pwm[pwmIndex].reverse + + lDirectionOld := Direction + + REPEAT + tempFreq := 1000 + + IF lDutyCycleOld < duty + lDutyCycleOld := lDutyCycleOld + 1 + + IF lDutyCycleOld > duty + lDutyCycleOld := lDutyCycleOld - 1 + + tempDuty := lDutyCycleOld + + pwm[pwmIndex].update(tempDuty) + + waitcnt(((CLKFREQ/1000) * delayPerStep) + cnt) + + WHILE lDutyCycleOld <> duty + + lDutyCycleOld := duty + + word[adrDCold] := lDutyCycleOld + word[adrDirold] := lDirectionOld + + cogstop(motorCog~ - 1) ' stop cog and update cog variable + + \ No newline at end of file diff --git a/MCP3208.spin b/MCP3208.spin new file mode 100644 index 0000000000000000000000000000000000000000..30e500403ce126892cc02d58f58d24fd26223b70 GIT binary patch literal 17774 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 u{j~l{6D{+P)GZ`?*4TGIY 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 literal 0 HcmV?d00001 diff --git a/Parallax Serial Terminal.spin b/Parallax Serial Terminal.spin new file mode 100644 index 0000000000000000000000000000000000000000..3ecffe5097dde7d595af1841652ce028f9f4b14f GIT binary patch literal 38490 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 literal 0 HcmV?d00001 diff --git a/SD_Card_Micro_01.spin b/SD_Card_Micro_01.spin new file mode 100644 index 0000000000000000000000000000000000000000..c9d5bf6d4f61bc6104c2db58992693d61f341ae3 GIT binary patch literal 25722 zcmeI5TW?&~d4@M~S+qbedeO@X0Yh?P)0UM4t&tX>s1p;3B1Fn=fC2_}R!vDPDO*m{ z{IT{=;rj5+ zusQ4xN5f;iyF0uX9_jsSz5jf;GOQ2B!=bLdYOU$9se6aR@73nT@N9TKycuT0W%c=3 zuV1u2SM=_d?myA}nZ|#j-pA_YF?RL&iLP_?v0h&fPxbeyMmuQNpX$nyuDsEwqv54` zrpe{Ty;*DhU3dJq{YLFK)r-&JHry|3XGi*aEomM} zsxQ@JQ}3SWJ%|Ad*QD<+oAg%e{Fd5BJD_!}AGjIorL=KZb3YbVK&+(;6FvPAp}L#^R$|K_|9%puaz;U-_XzX?Q_%`?528 zh|e_B6mQpc_0@1mt!8s8G>u5~iRO6Jq=^Q9sw-&xxvm`R-Jxjd@#U)zHRrKfAXDf> z1EK#|*IqV$X;asc_e+g6Q$M^%+Amr?8pR*JQ9GzMuJ+|^UJu{v)4KX%eehyxxO&uR zfTowC8SjbB1OaaMQlFXI^zsg=ff3JPt>OC^ai$URZ1JQ}&&akd%WtOp_QU4&PtBfahP0naJqN18!pgtt{iDXmjfU^%jcOv0BNpw}mZY@S z_`|b5n@i!9Uhz6lM8$Wa^Nn6D!Cl=!8ngz>U?n*K*WR`(_!&@gsMjE8zv-&%VG$h_ z_3byn+>ypMclKtC6m%zHF^+mMc=mOaceHp(}aZhODdYyG(Kiq2!f&WZ4_ zXc4_4d0Vd$L)?EPxqUK6DcXQm_(CK6{dY;}MSJ&njsp-qC0*u?MOWVCY$Ql~+O!ob zg{HTSK9FqGVT0)QrN)Zh&^B~=EHrNVY_H}Q?NQWq`2LoD6>~KP9i_-nwHRtticYPL~@Zo(FNawzxBwp z@?EF{(P613 zz7Ut7k1UF4$HUVm>*L@AC@J_6um+US-iAkE4HlIW+UT*E9jj7UNhV z5%(C`p5ZL);gtmO3Ll3}(aK}n*HnBalW=Btpq3yovYM6@aR%Q))S1e7#@5c<s_Z((gcZuI{6e#xUVPxQ5n z!`+66RL%CwQ4b4>N+H5tJ@eH(Dj28Gu=gm|P@WHgQ7q*vlw&%<#(fYO8;!~j2Z7X{v zK9^_ic}e76j!cW0WAOst<9d-JDB3iAaKL-~w>3&khhF4CJ{8SdW`!Cw6bq)$r{amM z4BW?8IfGn8*S;k0(Hxuc*x$d?Tjm?D414NAtw#^)BKLTv%{dOYHG1+wXE;dP-1ADV zNc@ZT9?sDW9nP5+=|@f=7YR5jIu=Hb;L0*6saB*eiH$=SAJ* zGb=b}0Fj<`@lw3J5-p6A%*zOHoPMKz%tJ3rr1z)rhy6QiK50a+l^7)!4)T^)A)A_o z+%d5{c~p2$6{n9)wjkC^<`Bv)b>f-xN@jBoY(Geh>@n;$TVw-2wTG~X>mZHnk`-L^ zoYk1UkHX-u$^sQPvZMdOU;guXKrsIyDA4LDFcdbm%FWdgn z=c;0}kHin0@XMUk?60?1X#YifR}|7>D%$U~dE6d9{y?(^Z^=!Oxb2ZVA2ggE{fq0( z)|-_(IU+iDJ&kGtbHzI1MS>M92gHI<lk@s)QoKn7Odu|$$T zmhQ(j3?hg%|J{5%atd=tl+AuPGBGMwO9hXxFIi;wxBRSk;Y;W5`(0%1d6qHV+eR?D zEUDW~4n%T%Uj2B7e@8px?B(Ck4!vtZ$n9d)8t-A(4C8mhNBsQlN8_u1-L4KF75)AF zTgpou$7jP&KKidec?|yk_n&^Ek$iu;?^S92YFl|t_x-u<(t-yKCkN@rr8!N(j&wHNGr7Sk+IMu4V7lk7H0a2_gAH8t5WaSczsm_d5A)>mYnw2Y4&27eg(7Aau;?YXeM z=NkV-+f8K#XOGkhSPk!LEwMF^Y^7b^jUyYq7%`F=)4^I2T3B;clp60XxSru!jsJ%F zW$BWimIR$K5(!xA`fLYzMQE|-=-1B~`KI{0uhU=ab3V7#tKXAl6xv!J)N#gMXc>95 zwpWTL${5JDtRlH}c_ea^KF`SxC;11GO>DPkCA0MD4=_-f$5=;uqK0|x;}>U`l64<- z7f~~oy4T%>vGVHU)t@tA>nQXrYxF$xq7O?1&b)S9Tyq`^3#@%W_p+A2Kh-1b3;BAz zhwf}+8MDvB%)ypWmHLYjMXrSu7WWK{nVt@G2;GN`X6AOC!}VJ1bzB7lIjQCLXG5sM zKyzlT{27a#)^g>L#sc&%%jM*{0rUj7rW7v$&hcTX7%{)EStewpUQb58JtD9va#H&R zvK4Z*-{~q9KG$?JXON$|8rmVB^C=o=h_AF<`zjKvjPgNf0ll+Yjt9s|-QsdtKBhG1 z;#kSRJ5rC2@tl^iXMk&Cp+9BBEUAK$kN3MRXcip@}51^>u?Rc#dU-0bQ(^0QKFIQISx`-{y$nGn+)>Uth zp#d(dwo{%NT%|%;SO5HXjO+8jLGH0+0%kxIxy_nB9ZSY<_3nK0fgH|+jw?jbH=`oE zI`2M?qaZhH*z{SB0aM_OTyX2t7_o`?KxVP0T(0b)17|6Hw$p1VQZ4)SBiAx$4PfuQg#{ehEQvX5kEenwHSnsE3_pS`m$~*E7 zb|YP5@6QxiC)trk7gT*g5z?qwp3|T`CM$MhO*!8jE#Sq;_u&pYur{I??|NC>(_Hi~ znP)B|5In=@BEmBd(!xT5Z9XG6Te>}dZ&}HSEp2>tcCI}ib5ZeQUdG6{u^HFRz%eU5 zd>C07qxi%mlH;B&)tm;?_8zPxvad@O@QR*C_@+Id@OgX2=WJdVT+}#@!pu6QLv-MI zKAF!$EC&zXRb$36>UiFWJuo~Yr)YmRfIarTjLMJoZuO6S8+tWfKv_}9NREzu4iU84 z=XlmSyNf2!)p*2{WuIAslwQ}K1q9nRtnb2QGDw-=n_P3hRUe8(y&GFL65w4Pj z;SFma!x>uTk7Q-N?Pav_x}~SbPht3!gYyvj_#IV#yiuPeMK@fx|LS83ahd&zf7QKY zHtW(*^4o93J*QByS3jY0Sy{{Hjb_)tpq@$^uhim}u@1g(YaCDtC;UZPj!EJ8^sGA3 zIz4`&ahwq&Z}3VEPcA_gO72RJ(*GsV$IdnMI4&ecmj12NRkHfEdM@tdthVd(-LV&7 zEnL#~*$Jw)oFsrtQ2ig`j@++yNHSAACUN9QnVerOH+<(+X=hnK>3z{xC@0fo9xMeK zZ4J)roL8d#aDq?DJ6mK6SDwP2hRFKZVuxIyf(S)@0-5L89TD9V4pOdO6kqB{ zD>Aq<*I@W{W&>U;H^@C})v<_KEgR169m&&?iTCIxYL&dsCzG?uCI~-UYy9f(MKitw zA6?lzoIDo~$dbFZRMsqBcVqJvc_Rhy2HEoCTUjge>})DcW%u42LQ>3}YA87#E8xuO zU7XB}q{nlwjtCc(^vQZjEP{Wu?X}!&__b;(+ma{M*nfWK{d#Pj{(jk424{sqYhv}x zXtGYSQc1>xvL+*XQ~Nj@HWraI-3xSI*0|Q$MRJeuJhf_0u7J+ToH9?ian8A%37)qK z<@}pWfy{qhGyVVkM5cM|b98cjp8;OX^VW2S=MH?reO>Rc7p#sPeN7tYJscq0d-G9{ z;c_ppO?t`Z92$3wH89?5$15eQc9nk0@_uYN!qH}152c=Ienab;^hZt6d4AS(se+`H zpK_iaAo{{@E?4>LbU4T_dabSSfxX*X+H$;87wuv} zwM!EIlGkAmy=2Jck<pLHIu1^%|B89%L*9Er@gKY5d9Poj%e zcH-<{thdsTM~@fqigz6Q5_heaX`X$u+EM&sQnt-e1xzlmh$YS;fe-spulp|^x5LN9 zs9oI)J~;D$SFPu@3-h(BP%B(42>-`kn==(Z6~ndai5|!Y`dkmoL{9IMo7K4wS%B?7 z)Q6tlV?b}Xz@G0G0Hj=hDClET<$Qh~7s7rkeBxm9Rk4p#uHhPeEFCY8^6zu#*T zTQ9rt{%!~Ss_SpjRyY7{P#Kw!nILa#6RMp@LreAq7PZMNNM*cU%YH*NyVcrwFB$IZ ze0%Yb>y}9eVx!pR`tBOGn8l} z6#1K&(B8>;Jo&BjrM-6=O&s*dwK5v7 z*Iv4nQz6;f8AG56=Ig5aASOisCf)kUVX*SZ;qoo zvtbjbqnldr*;;!|n_VF9JzdNOm=oOZ2t|K+EB_5)hm2tL>N{0WXCbFcV)aPaLrytW z-hDo;Pir4^_WAGXjA_*!__{Bv=vRfY>-gbVW<^+A-iI|i>!=TFc2?>3)gRXEu^lYr z!3r>!P0kmO8p zoWYYXdUc)?HNjl%U5xk7#x;UMW?y@xCPS`X>z}M8Jqzm@ulP>qnk?7*6j17MGnZF$ ziAYu9?rY{%AMpy7)~-zoL5pL3}_lKNkV2#BqE9r~;pv%2?TC9AH zQ?ke1=*T@Brqbx0x~j}%earqFPl)byazMP@^oH(V6|TQA`{^GbL9QJY0!veSCcbg z>~2}Jd^?+47M}X=yZlV^c-I-k9=xN%-!xJy7t4K7dQdv9ix@kWz!*uzF?C~COd^-X zagjCcsZDbp%zUPngVbCFJk~hSU@q%hc#VOKPE7~is18MM$eIkZ>U_uT+UDcsgBKIe(=>1n4uJ?+T%)pMo)F8r_b>AJ37*Y!2E*qz^Z9q{Ys z({5{o2hF#648A>|GXlR;TgK*_?6=#e->N;&wxd^`&ERSL8yb^Q`KEp5_P5<{wK-`= z56);lXy-Gx_4GU6Nay+dd@eqfe)v~Yx~I|b{^oE@6(f5-D7&jq_ci|QIn5gyd#jBH z{kEo?ZRG9NkDmDk{tdm~QNR0|iEsaZpniPf>I1#*hzftJKl~#>=;eO^Y-^1B?e&&M zg$Cq>cDUzuNQkH2xPz9U(EooRd~Gx-ApQHD1UVhLBib4NmU_{fHg`k;yutxb##n0b z%l9DkTv=Dhd_!a0ZQ9~_)VunH-qIFNM 381 ' Calculate 10 us time unit + waitcnt(clkcycles + cnt) ' Wait for designated time + + +PUB pause1ms(period) + +'' Pause execution for period (in units of 1 ms). + + clkcycles := ((clkfreq / _1ms * period) - 4296) #> 381 ' Calculate 1 ms time unit + waitcnt(clkcycles + cnt) ' Wait for designated time + + +PUB pause1s(period) + +'' Pause execution for period (in units of 1 sec). + + clkcycles := ((clkfreq / _1s * period) - 4296) #> 381 ' Calculate 1 s time unit + waitcnt(clkcycles + cnt) ' Wait for designated time + + +PUB marksync10us(period) + + delay := (clkfreq / _10us * period) #> 381 ' Calculate 10 us time unit + syncpoint := cnt + + +PUB waitsync + + waitcnt(syncpoint += delay) + + \ No newline at end of file diff --git a/UAHLunarWormbotTest.spin b/UAHLunarWormbotTest.spin new file mode 100644 index 0000000..34bb1b0 --- /dev/null +++ b/UAHLunarWormbotTest.spin @@ -0,0 +1,92 @@ +{Object_Title_and_Purpose} + + +CON 'Global Constants + +'---Useful constants--- +HIGH = 1 +LOW = 0 +OUTPUT = 1 +INPUT = 0 +AVERAGE = 1 +CURRENT = 0 +FORWARD = 1 +REVERSE = 0 +INFINITY = 1 +ENABLE = 1 +DISABLE = 0 + +HIGH_SPEED_IO0 = 0 +HIGH_SPEED_IO1 = 1 +HIGH_SPEED_IO2 = 2 +HIGH_SPEED_IO3 = 3 +HIGH_SPEED_IO4 = 4 +HIGH_SPEED_IO5 = 5 + + +VAR 'Global variables + +long ExampleVariable + +OBJ 'Additional files you would like imported / included + +'Sol-X API that controls all the GDB hardware function +GDB : "GDB-API-V0.1.0" + +'Used to control clock timing functions +TIMING : "Clock" + +PUB Main 'First method called, like in JAVA + +'' Action: Initializes all the GDB hardware and firmware +'' Parameters: None +'' Results: Prepares the GDB for user interaction +''+Reads/Uses: None +'' +Writes: None +'' Local Vars: None +'' Calls: GDB.Initialize( ), GDB.SendText( ), TIMING.PauseSec( ) +'' GDB.GetNumber( ), and GDB.SetMotorShield( ) functions +'' URL: http://www.solarsystemexpress.com/store.html + +GDB.Initialize + +repeat 'Infinte Loop + + repeat + GDB.SendText(STRING("TYPE 1 and hit enter to extend L-16 actuators.")) + TIMING.PauseSec(5) 'Pause 5 seconds before outputing promote again + until(GDB.GetNumber) + + GDB.SetMotorShield(HIGH_SPEED_IO0, HIGH_SPEED_IO1, HIGH_SPEED_IO2, FORWARD) + GDB.SetMotorShield(HIGH_SPEED_IO3, HIGH_SPEED_IO4, HIGH_SPEED_IO5, FORWARD) + + repeat + GDB.SendText(STRING("TYPE 2 and hit enter to contract L-16 actuators.")) + TIMING.PauseSec(5) 'Pause 5 seconds before outputing promote again + until(GDB.GetNumber) + + GDB.SetMotorShield(HIGH_SPEED_IO0, HIGH_SPEED_IO1, HIGH_SPEED_IO2, REVERSE) + GDB.SetMotorShield(HIGH_SPEED_IO3, HIGH_SPEED_IO4, HIGH_SPEED_IO5, REVERSE) + +{{ See GDB-API-V.0.1 +PUB SetMotorShield(EnablePin, Input1Pin, Input2Pin, Direction) + + Action: More complex function to control actuator movement + Parameters: EnablePin - GDB GPIO pin connected to the "Enable Motor X Connector" on Seeed Studio Motor Driver + Input1Pin - GDB GPIO pin connected to the J7.1 (Channel A) or J7.5 (Channel B) on Seeed Studio Motor Driver + Input2Pin - GDB GPIO pin connected to the J7.4 (Channel A) or J7.6 (Channel B) on Seeed Studio Motor Driver + Direction - Sets direction of actuator movement using contsant FORWARD or REVERSE + Results: Changes the direction of current flow in the Seeed Studio Motor Driver ++Reads/Uses: None + +Writes: None + Local Vars: None + Calls: FullH_BridgeSubroutine( ) + URL: www.seeedstudio.com/wiki/Motor_Shield_V1.0 +}} + +PRI private_method_name + + +DAT +name byte "string_data",0 + \ No newline at end of file diff --git a/_README_.txt b/_README_.txt new file mode 100644 index 0000000000000000000000000000000000000000..928dc1357a0c55bfc45c0f78dd47737e73042f86 GIT binary patch literal 1136 zcmb`G&r1SP5XZlG47zpfumnOuEUlKMr=%_gf~3(=mQ~3}S60jV=c~^fU2`?mK!)A- z=Es}wyzk8W{!)IIvadbW6)NF-MM8bZ!;&c0P#v>%&^+DfSv_p2_XTgHF?tn$WZrjd zF5@mMRNiHD-zstio2Mf+bgLE>x_h+Q)u=SGJG6ABfQU3lw$?VMr#YUeysZKM5o@SV zo#MOKo7p@SR5nY&@&))pyL*6MQ4y)=0&z9g=4h@uCr#Ll;W9=0i{IZE$6`VqJRZf+sU0FQy%_6yZR{g&rwT@yDJy|dV!F;*uWY7^`T@@O Bhb{mB literal 0 HcmV?d00001 diff --git a/fsrw.spin b/fsrw.spin new file mode 100644 index 0000000..49c1dd7 --- /dev/null +++ b/fsrw.spin @@ -0,0 +1 @@ +{{ ' fsrw 2.6 Copyright 2009 Tomas Rokicki and Jonathan Dummer ' ' See end of file for terms of use. ' ' This object provides FAT16/32 file read/write access on a block device. ' Only one file open at a time. Open modes are 'r' (read), 'a' (append), ' 'w' (write), and 'd' (delete). Only the root directory is supported. ' No long filenames are supported. We also support traversing the ' root directory. ' ' In general, negative return values are errors; positive return ' values are success. Other than -1 on popen when the file does not ' exist, all negative return values will be "aborted" rather than ' returned. ' ' Changes: ' v1.1 28 December 2006 Fixed offset for ctime ' v1.2 29 December 2006 Made default block driver be fast one ' v1.3 6 January 2007 Added some docs, and a faster asm ' v1.4 4 February 2007 Rearranged vars to save memory; ' eliminated need for adjacent pins; ' reduced idle current consumption; added ' sample code with abort code data ' v1.5 7 April 2007 Fixed problem when directory is larger ' than a cluster. ' v1.6 23 September 2008 Fixed a bug found when mixing pputc ' with pwrite. Also made the assembly ' routines a bit more cautious. ' v2.1 12 July 2009 FAT32, SDHC, multiblock, bug fixes ' v2.4 26 September 2009 Added seek support. Added clustersize. ' v2.4a 6 October 2009 modified setdate to explicitly set year/month/etc. ' v2.5 13 November 2009 fixed a bug on releasing the pins, added a "release" pass through function ' v2.6 11 December 2009: faster transfer hub <=> cog, safe_spi.spin uses 1/2 speed reads, is default }} ' ' Constants describing FAT volumes. ' con SECTORSIZE = 512 'Original Value 'SECTORSIZE = 2048 SECTORSHIFT = 9 DIRSIZE = 32 DIRSHIFT = 5 ' ' The object that provides the block-level access. ' obj sdspi: "safe_spi" 'sdspi: "mb_rawb_spi" var ' ' ' Variables concerning the open file. ' long fclust ' the current cluster number long filesize ' the total current size of the file long floc ' the seek position of the file long frem ' how many bytes remain in this cluster from this file long bufat ' where in the buffer our current character is long bufend ' the last valid character (read) or free position (write) long direntry ' the byte address of the directory entry (if open for write) long writelink ' the byte offset of the disk location to store a new cluster long fatptr ' the byte address of the most recently written fat entry long firstcluster ' the first cluster of this file ' ' Variables used when mounting to describe the FAT layout of the card ' (moved to the end of the file in the Spin version). ' ' ' Variables controlling the caching. ' ' ' Buffering: two sector buffers. These two buffers must be longword ' aligned! To ensure this, make sure they are the first byte variables ' defined in this object. ' byte buf[SECTORSIZE] ' main data buffer pub release ' ' This is just a pass-through function to allow the block layer ' to tristate the I/O pins to the card. ' sdspi.release pri writeblock2(n, b) ' ' On metadata writes, if we are updating the FAT region, also update ' the second FAT region. ' sdspi.writeblock(n, b) if (n => fat1) if (n < fat1 + sectorsperfat) sdspi.writeblock(n+sectorsperfat, b) pri flushifdirty ' ' If the metadata block is dirty, write it out. ' if (dirty) writeblock2(lastread, @buf2) dirty := 0 pri readblockc(n) ' ' Read a block into the metadata buffer, if that block is not already ' there. ' if (n <> lastread) flushifdirty sdspi.readblock(n, @buf2) lastread := n pri brword(b) ' ' Read a byte-reversed word from a (possibly odd) address. ' return (byte[b]) + ((byte[b][1]) << 8) pri brlong(b) ' ' Read a byte-reversed long from a (possibly odd) address. ' return brword(b) + (brword(b+2) << 16) pri brclust(b) ' ' Read a cluster entry. ' if (filesystem == 1) return brword(b) else return brlong(b) pri brwword(w, v) ' ' Write a byte-reversed word to a (possibly odd) address, and ' mark the metadata buffer as dirty. ' byte[w++] := v byte[w] := v >> 8 dirty := 1 pri brwlong(w, v) ' ' Write a byte-reversed long to a (possibly odd) address, and ' mark the metadata buffer as dirty. ' brwword(w, v) brwword(w+2, v >> 16) pri brwclust(w, v) ' ' Write a cluster entry. if (filesystem == 1) brwword(w, v) else brwlong(w, v) ' ' This may do more complicated stuff later. ' pub unmount pclose sdspi.stop pri getfstype : r if (brlong(@buf+$36) == constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)) and buf[$3a]=="6") return 1 if (brlong(@buf+$52) == constant("F" + ("A" << 8) + ("T" << 16) + ("3" << 24)) and buf[$56]=="2") return 2 ' return r (default return) pub mount_explicit(DO, CLK, DI, CS) : r | start, sectorspercluster, reserved, rootentries, sectors {{ ' Mount a volume. The address passed in is passed along to the block ' layer; see the currently used block layer for documentation. If the ' volume mounts, a 0 is returned, else abort is called. }} if (pdate == 0) pdate := constant(((2009-1980) << 25) + (1 << 21) + (27 << 16) + (7 << 11)) unmount sdspi.start_explicit(DO, CLK, DI, CS) lastread := -1 dirty := 0 sdspi.readblock(0, @buf) if (getfstype > 0) start := 0 else start := brlong(@buf+$1c6) sdspi.readblock(start, @buf) filesystem := getfstype if (filesystem == 0) abort(-20) ' not a fat16 or fat32 volume if (brword(@buf+$0b) <> SECTORSIZE) abort(-21) ' bad bytes per sector sectorspercluster := buf[$0d] if (sectorspercluster & (sectorspercluster - 1)) abort(-22) ' bad sectors per cluster clustershift := 0 repeat while (sectorspercluster > 1) clustershift++ sectorspercluster >>= 1 sectorspercluster := 1 << clustershift clustersize := SECTORSIZE << clustershift reserved := brword(@buf+$0e) if (buf[$10] <> 2) abort(-23) ' not two FATs sectors := brword(@buf+$13) if (sectors == 0) sectors := brlong(@buf+$20) fat1 := start + reserved if (filesystem == 2) rootentries := 16 << clustershift sectorsperfat := brlong(@buf+$24) dataregion := (fat1 + 2 * sectorsperfat) - 2 * sectorspercluster rootdir := (dataregion + (brword(@buf+$2c) << clustershift)) << SECTORSHIFT rootdirend := rootdir + (rootentries << DIRSHIFT) endofchain := $ffffff0 else rootentries := brword(@buf+$11) sectorsperfat := brword(@buf+$16) rootdir := (fat1 + 2 * sectorsperfat) << SECTORSHIFT rootdirend := rootdir + (rootentries << DIRSHIFT) dataregion := 1 + ((rootdirend - 1) >> SECTORSHIFT) - 2 * sectorspercluster endofchain := $fff0 if (brword(@buf+$1fe) <> $aa55) abort(-24) ' bad FAT signature totclusters := ((sectors - dataregion + start) >> clustershift) ' return r (default return) ' ' For compatibility, a single pin. ' pub mount(basepin) : r | start, sectorspercluster, reserved, rootentries, sectors return mount_explicit(basepin, basepin+1, basepin+2, basepin+3) pri readbytec(byteloc) ' ' Read a byte address from the disk through the metadata buffer and ' return a pointer to that location. ' readblockc(byteloc >> SECTORSHIFT) return @buf2 + (byteloc & constant(SECTORSIZE - 1)) pri readfat(clust) ' ' Read a fat location and return a pointer to the location of that ' entry. ' fatptr := (fat1 << SECTORSHIFT) + (clust << filesystem) return readbytec(fatptr) pri followchain : r ' ' Follow the fat chain and update the writelink. ' r := brclust(readfat(fclust)) writelink := fatptr ' return r (default return) pri nextcluster : r ' ' Read the next cluster and return it. Set up writelink to ' point to the cluster we just read, for later updating. If the ' cluster number is bad, return a negative number. ' r := followchain if (r < 2 or r => totclusters) abort(-9) ' bad cluster value ' return r (default return) pri freeclusters(clust) | bp ' ' Free an entire cluster chain. Used by remove and by overwrite. ' Assumes the pointer has already been cleared/set to end of chain. ' repeat while (clust < endofchain) if (clust < 2) abort(-26) ' bad cluster number") bp := readfat(clust) clust := brclust(bp) brwclust(bp, 0) flushifdirty pri datablock ' ' Calculate the block address of the current data location. ' return (fclust << clustershift) + dataregion + ((floc >> SECTORSHIFT) & ((1 << clustershift) - 1)) pri uc(c) ' ' Compute the upper case version of a character. ' if ("a" =< c and c =< "z") return c - 32 return c pri pflushbuf(rcnt, metadata) : r | cluststart, newcluster, count, i ' ' Flush the current buffer, if we are open for write. This may ' allocate a new cluster if needed. If metadata is true, the ' metadata is written through to disk including any FAT cluster ' allocations and also the file size in the directory entry. ' if (direntry == 0) abort(-27) ' not open for writing if (rcnt > 0) ' must *not* allocate cluster if flushing an empty buffer if (frem < SECTORSIZE) ' find a new clustercould be anywhere! If possible, stay on the ' same page used for the last cluster. newcluster := -1 cluststart := fclust & (!((SECTORSIZE >> filesystem) - 1)) count := 2 repeat readfat(cluststart) repeat i from 0 to SECTORSIZE - 1<> filesystem) if (newcluster => totclusters) newcluster := -1 quit if (newcluster > 1) brwclust(@buf2+i, endofchain+$f) if (writelink == 0) brwword(readbytec(direntry)+$1a, newcluster) writelink := (direntry&(SECTORSIZE-filesystem)) brwlong(@buf2+writelink+$1c, floc+bufat) if (filesystem == 2) brwword(@buf2+writelink+$14, newcluster>>16) else brwclust(readbytec(writelink), newcluster) writelink := fatptr + i fclust := newcluster frem := clustersize quit else cluststart += (SECTORSIZE >> filesystem) if (cluststart => totclusters) cluststart := 0 count-- if (rcnt < 0) rcnt := -5 ' No space left on device quit if (frem => SECTORSIZE) sdspi.writeblock(datablock, @buf) if (rcnt == SECTORSIZE) ' full buffer, clear it floc += rcnt frem -= rcnt bufat := 0 bufend := rcnt if (rcnt < 0 or metadata) ' update metadata even if error readblockc(direntry >> SECTORSHIFT) ' flushes unwritten FAT too brwlong(@buf2+(direntry & (SECTORSIZE-filesystem))+$1c, floc+bufat) flushifdirty if (rcnt < 0) abort(rcnt) return rcnt pub pflush {{ ' Call flush with the current data buffer location, and the flush ' metadata flag set. }} return pflushbuf(bufat, 1) pri pfillbuf : r ' ' Get some data into an empty buffer. If no more data is available, ' return -1. Otherwise return the number of bytes read into the ' buffer. ' if (floc => filesize) return -1 if (frem == 0) fclust := nextcluster frem := (clustersize) <# (filesize - floc) sdspi.readblock(datablock, @buf) r := SECTORSIZE if (floc + r => filesize) r := filesize - floc floc += r frem -= r bufat := 0 bufend := r ' return r (default return) pub pclose : r {{ ' Flush and close the currently open file if any. Also reset the ' pointers to valid values. If there is no error, 0 will be returned. }} if (direntry) r := pflush bufat := 0 bufend := 0 filesize := 0 floc := 0 frem := 0 writelink := 0 direntry := 0 fclust := 0 firstcluster := 0 sdspi.release ' return r (default return) pub setdate(year, month, day, hour, minute, second) {{ ' Set the current date and time, as a long, in the format ' required by FAT16. Various limits are not checked. }} pdate := ((year-1980) << 25) + (month << 21) + (day << 16) pdate += (hour << 11) + (minute << 5) + (second >> 1) pub popen(s, mode) : r | i, sentinel, dirptr, freeentry {{ ' Close any currently open file, and open a new one with the given ' file name and mode. Mode can be "r" "w" "a" or "d" (delete). ' If the file is opened successfully, 0 will be returned. If the ' file did not exist, and the mode was not "w" or "a", -1 will be ' returned. Otherwise abort will be called with a negative error ' code. }} pclose i := 0 repeat while (i<8 and byte[s] and byte[s] <> ".") padname[i++] := uc(byte[s++]) repeat while (i<8) padname[i++] := " " repeat while (byte[s] and byte[s] <> ".") s++ if (byte[s] == ".") s++ repeat while (i<11 and byte[s]) padname[i++] := uc(byte[s++]) repeat while (i < 11) padname[i++] := " " sentinel := 0 freeentry := 0 repeat dirptr from rootdir to rootdirend - DIRSIZE step DIRSIZE s := readbytec(dirptr) if (freeentry == 0 and (byte[s] == 0 or byte[s] == $e5)) freeentry := dirptr if (byte[s] == 0) sentinel := dirptr quit repeat i from 0 to 10 if (padname[i] <> byte[s][i]) quit if (i == 11 and 0 == (byte[s][$0b] & $18)) ' this always returns fclust := brword(s+$1a) if (filesystem == 2) fclust += brword(s+$14) << 16 firstcluster := fclust filesize := brlong(s+$1c) if (mode == "r") frem := (clustersize) <# (filesize) return 0 if (byte[s][11] & $d9) abort(-6) ' no permission to write if (mode == "d") brwword(s, $e5) if (fclust) freeclusters(fclust) flushifdirty return 0 if (mode == "w") brwword(s+$1a, 0) brwword(s+$14, 0) brwlong(s+$1c, 0) writelink := 0 direntry := dirptr if (fclust) freeclusters(fclust) bufend := SECTORSIZE fclust := 0 filesize := 0 frem := 0 return 0 elseif (mode == "a") ' this code will eventually be moved to seek frem := filesize freeentry := clustersize if (fclust => endofchain) fclust := 0 repeat while (frem > freeentry) if (fclust < 2) abort(-7) ' eof repeat while following chain fclust := nextcluster frem -= freeentry floc := filesize & constant(!(SECTORSIZE - 1)) bufend := SECTORSIZE bufat := frem & constant(SECTORSIZE - 1) writelink := 0 direntry := dirptr if (bufat) sdspi.readblock(datablock, @buf) frem := freeentry - (floc & (freeentry - 1)) else if (fclust < 2 or frem == freeentry) frem := 0 else frem := freeentry - (floc & (freeentry - 1)) if (fclust => 2) followchain return 0 else abort(-3) ' bad argument if (mode <> "w" and mode <> "a") return -1 ' not found direntry := freeentry if (direntry == 0) abort(-2) ' no empty directory entry ' write (or new append): create valid directory entry s := readbytec(direntry) bytefill(s, 0, DIRSIZE) bytemove(s, @padname, 11) brwword(s+$1a, 0) brwword(s+$14, 0) i := pdate brwlong(s+$e, i) ' write create time and date brwlong(s+$16, i) ' write last modified date and time if (direntry == sentinel and direntry + DIRSIZE < rootdirend) brwword(readbytec(direntry+DIRSIZE), 0) flushifdirty writelink := 0 fclust := 0 bufend := SECTORSIZE ' return r (default return) pub get_filesize return filesize pub pread(ubuf, count) : r | t {{ ' Read count bytes into the buffer ubuf. Returns the number of bytes ' successfully read, or a negative number if there is an error. ' The buffer may be as large as you want. }} repeat while (count > 0) if (bufat => bufend) t := pfillbuf if (t =< 0) if (r > 0) ' parens below prevent this from being optimized out return (r) return t t := (bufend - bufat) <# (count) if ((t | (ubuf) | bufat) & 3) bytemove(ubuf, @buf+bufat, t) else longmove(ubuf, @buf+bufat, t>>2) bufat += t r += t ubuf += t count -= t ' return r (default return) pub pgetc | t {{ ' Read and return a single character. If the end of file is ' reached, -1 will be returned. If an error occurs, a negative ' number will be returned. }} if (bufat => bufend) t := pfillbuf if (t =< 0) return -1 return (buf[bufat++]) pub pwrite(ubuf, count) : r | t {{ ' Write count bytes from the buffer ubuf. Returns the number of bytes ' successfully written, or a negative number if there is an error. ' The buffer may be as large as you want. }} repeat while (count > 0) if (bufat => bufend) pflushbuf(bufat, 0) t := (bufend - bufat) <# (count) if ((t | (ubuf) | bufat) & 3) bytemove(@buf+bufat, ubuf, t) else longmove(@buf+bufat, ubuf, t>>2) r += t bufat += t ubuf += t count -= t ' return r (default return) {{ ' Write a null-terminated string to the file. }} pub pputs(b) return pwrite(b, strsize(b)) pub pputc(c) : r {{ ' Write a single character into the file open for write. Returns ' 0 if successful, or a negative number if some error occurred. }} if (bufat == SECTORSIZE) if (pflushbuf(SECTORSIZE, 0) < 0) return -1 buf[bufat++] := c ' return r (default return) {{ ' Seek. Right now will only seek within the current cluster. ' Added for PrEdit so he can debug; do not use with files larger ' than one cluster (and make that cluster size 32K please.) ' ' Returns -1 on failure. Make sure to check this return code! ' ' We only support reads right now (but writes won"t be too hard to ' add). }} pub seek(pos) | delta if (direntry or pos < 0 or pos > filesize) return -1 delta := (floc - bufend) & - clustersize if (pos < delta) fclust := firstcluster frem := (clustersize) <# (filesize) floc := 0 bufat := 0 bufend := 0 delta := 0 repeat while (pos => delta + clustersize) fclust := nextcluster floc += clustersize delta += clustersize frem := (clustersize) <# (filesize - floc) bufat := 0 bufend := 0 if (bufend == 0 or pos < floc - bufend or pos => floc - bufend + SECTORSIZE) ' must change buffer delta := floc + frem floc := pos & - SECTORSIZE frem := delta - floc pfillbuf bufat := pos & (SECTORSIZE - 1) return 0 pub tell return floc + bufat - bufend pub opendir | off {{ ' Close the currently open file, and set up the read buffer for ' calls to nextfile. }} pclose off := rootdir - (dataregion << SECTORSHIFT) fclust := off >> (clustershift + SECTORSHIFT) floc := off - (fclust << (clustershift + SECTORSHIFT)) frem := rootdirend - rootdir filesize := floc + frem return 0 pub nextfile(fbuf) | i, t, at, lns {{ ' Find the next file in the root directory and extract its ' (8.3) name into fbuf. Fbuf must be sized to hold at least ' 13 characters (8 + 1 + 3 + 1). If there is no next file, ' -1 will be returned. If there is, 0 will be returned. }} repeat if (bufat => bufend) t := pfillbuf if (t < 0) return t if (((floc >> SECTORSHIFT) & ((1 << clustershift) - 1)) == 0) fclust++ at := @buf + bufat if (byte[at] == 0) return -1 bufat += DIRSIZE if (byte[at] <> $e5 and (byte[at][$0b] & $18) == 0) lns := fbuf repeat i from 0 to 10 byte[fbuf] := byte[at][i] fbuf++ if (byte[at][i] <> " ") lns := fbuf if (i == 7 or i == 10) fbuf := lns if (i == 7) byte[fbuf] := "." fbuf++ byte[fbuf] := 0 return 0 {{ ' Utility routines; may be removed. }} pub getclustersize return clustersize pub getclustercount return totclusters {{ ' Permission is hereby granted, free of charge, to any person obtaining ' a copy of this software and associated documentation files ' (the "Software"), to deal in the Software without restriction, ' including without limitation the rights to use, copy, modify, merge, ' publish, distribute, sublicense, and/or sell copies of the Software, ' and to permit persons to whom the Software is furnished to do so, ' subject to the following conditions: ' ' The above copyright notice and this permission notice shall be included ' in all copies or substantial portions of the Software. ' ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ' MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ' IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ' CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ' TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ' SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. }} DAT filesystem long 0 ' 0 = unmounted, 1 = fat16, 2 = fat32 rootdir long 0 ' the byte address of the start of the root directory rootdirend long 0 ' the byte immediately following the root directory. dataregion long 0 ' the start of the data region, offset by two sectors clustershift long 0 ' log base 2 of blocks per cluster clustersize long 0 ' total size of cluster in bytes fat1 long 0 ' the block address of the fat1 space totclusters long 0 ' how many clusters in the volume sectorsperfat long 0 ' how many sectors per fat endofchain long 0 ' end of chain marker (with a 0 at the end) pdate long 0 ' current date lastread long 0 ' the block address of the buf2 contents dirty long 0 ' nonzero if buf2 is dirty buf2 byte 0[SECTORSIZE] ' main metadata buffer padname byte 0[11] ' filename buffer \ No newline at end of file diff --git a/safe_spi.spin b/safe_spi.spin new file mode 100644 index 0000000..005c7a0 --- /dev/null +++ b/safe_spi.spin @@ -0,0 +1 @@ +{{ SPI interface routines for SD & SDHC & MMC cards Jonathan "lonesock" Dummer version 0.3.0 2009 July 19 Using multiblock SPI mode exclusively. This is the "SAFE" version...uses * 1 instruction per bit writes * 2 instructions per bit reads For the fsrw project: fsrw.sf.net }} CON ' possible card types type_MMC = 1 type_SD = 2 type_SDHC = 3 ' Error codes ERR_CARD_NOT_RESET = -1 ERR_3v3_NOT_SUPPORTED = -2 ERR_OCR_FAILED = -3 ERR_BLOCK_NOT_LONG_ALIGNED = -4 '... ' These errors are for the assembly engine...they are negated inside, and need to be <= 511 ERR_ASM_NO_READ_TOKEN = 100 ERR_ASM_BLOCK_NOT_WRITTEN = 101 ' NOTE: errors -128 to -255 are reserved for reporting R1 response errors '... ERR_SPI_ENGINE_NOT_RUNNING = -999 ERR_CARD_BUSY_TIMEOUT = -1000 ' SDHC/SD/MMC command set for SPI CMD0 = $40+0 ' GO_IDLE_STATE CMD1 = $40+1 ' SEND_OP_COND (MMC) ACMD41 = $C0+41 ' SEND_OP_COND (SDC) CMD8 = $40+8 ' SEND_IF_COND CMD9 = $40+9 ' SEND_CSD CMD10 = $40+10 ' SEND_CID CMD12 = $40+12 ' STOP_TRANSMISSION CMD13 = $40+13 ' SEND_STATUS ACMD13 = $C0+13 ' SD_STATUS (SDC) CMD16 = $40+16 ' SET_BLOCKLEN CMD17 = $40+17 ' READ_SINGLE_BLOCK CMD18 = $40+18 ' READ_MULTIPLE_BLOCK CMD23 = $40+23 ' SET_BLOCK_COUNT (MMC) ACMD23 = $C0+23 ' SET_WR_BLK_ERASE_COUNT (SDC) CMD24 = $40+24 ' WRITE_BLOCK CMD25 = $40+25 ' WRITE_MULTIPLE_BLOCK CMD55 = $40+55 ' APP_CMD CMD58 = $40+58 ' READ_OCR CMD59 = $40+59 ' CRC_ON_OFF ' buffer size for my debug cmd log 'LOG_SIZE = 256<<1 { VAR long SPI_engine_cog ' these are used for interfacing with the assembly engine | temporary initialization usage long SPI_command ' "t", "r", "w", 0 =>done, <0 => error | pin mask long SPI_block_index ' which 512-byte block to read/write | cnt at init long SPI_buffer_address ' where to get/put the data in Hub RAM | unused '} DAT '' I'm placing these variables in a DAT section to make this driver a singleton. '' If for some reason you really need more than one driver (e.g. if you have more '' than a single SD socket), move these back into VAR. SPI_engine_cog long 0 ' these are used for interfacing with the assembly engine | temporary initialization usage SPI_command long 0 ' "t", "r", "w", 0 =>done, <0 => error | unused SPI_block_index long 0 ' which 512-byte block to read/write | cnt at init SPI_buffer_address long 0 ' where to get/put the data in Hub RAM | unused { VAR ' for debug ONLY byte log_cmd_resp[LOG_SIZE+1] PUB get_log_pointer return @log_cmd_resp '} PUB start( basepin ) {{ This is a compatibility wrapper, and requires that the pins be both consecutive, and in the order DO CLK DI CS. }} return start_explicit( basepin, basepin+1, basepin+2, basepin+3 ) PUB readblock( block_index, buffer_address ) if SPI_engine_cog == 0 abort ERR_SPI_ENGINE_NOT_RUNNING if (buffer_address & 3) abort ERR_BLOCK_NOT_LONG_ALIGNED SPI_block_index := block_index SPI_buffer_address := buffer_address SPI_command := "r" repeat while SPI_command == "r" if SPI_command < 0 abort SPI_command PUB writeblock( block_index, buffer_address ) if SPI_engine_cog == 0 abort ERR_SPI_ENGINE_NOT_RUNNING if (buffer_address & 3) abort ERR_BLOCK_NOT_LONG_ALIGNED SPI_block_index := block_index SPI_buffer_address := buffer_address SPI_command := "w" repeat while SPI_command == "w" if SPI_command < 0 abort SPI_command PUB get_seconds if SPI_engine_cog == 0 abort ERR_SPI_ENGINE_NOT_RUNNING SPI_command := "t" repeat while SPI_command == "t" ' secods are in SPI_block_index, remainder is in SPI_buffer_address return SPI_block_index PUB get_milliseconds : ms if SPI_engine_cog == 0 abort ERR_SPI_ENGINE_NOT_RUNNING SPI_command := "t" repeat while SPI_command == "t" ' secods are in SPI_block_index, remainder is in SPI_buffer_address ms := SPI_block_index * 1000 ms += SPI_buffer_address * 1000 / clkfreq PUB start_explicit( DO, CLK, DI, CS ) : card_type | tmp, i {{ Do all of the card initialization in SPIN, then hand off the pin information to the assembly cog for hot SPI block R/W action! }} ' Start from scratch stop ' clear my log buffer { bytefill( @log_cmd_resp, 0, LOG_SIZE+1 ) dbg_ptr := @log_cmd_resp dbg_end := dbg_ptr + LOG_SIZE '} ' wait ~4 milliseconds waitcnt( 500 + (clkfreq>>8) + cnt ) ' (start with cog variables, _BEFORE_ loading the cog) pinDO := DO maskDO := |< DO pinCLK := CLK pinDI := DI maskDI := |< DI maskCS := |< CS adrShift := 9 ' block = 512 * index, and 512 = 1<<9 ' pass the output pin mask via the command register maskAll := maskCS | (| 74 clocks outa |= maskAll repeat 4096 outa[CLK]~~ outa[CLK]~ ' time-hack SPI_block_index := cnt ' reset the card tmp~ repeat i from 0 to 9 if tmp <> 1 tmp := send_cmd_slow( CMD0, 0, $95 ) if (tmp & 4) ' the card said CMD0 ("go idle") was invalid, so we're possibly stuck in read or write mode if i & 1 ' exit multiblock read mode repeat 4 read_32_slow ' these extra clocks are required for some MMC cards send_slow( $FD, 8 ) ' stop token read_32_slow repeat while read_slow <> $FF else ' exit multiblock read mode send_cmd_slow( CMD12, 0, $61 ) if tmp <> 1 ' the reset command failed! crash( ERR_CARD_NOT_RESET ) ' Is this a SD type 2 card? if send_cmd_slow( CMD8, $1AA, $87 ) == 1 ' Type2 SD, check to see if it's a SDHC card tmp := read_32_slow ' check the supported voltage if (tmp & $1FF) <> $1AA crash( ERR_3v3_NOT_SUPPORTED ) ' try to initialize the type 2 card with the High Capacity bit repeat while send_cmd_slow( ACMD41, |<30, $77 ) ' the card is initialized, let's read back the High Capacity bit if send_cmd_slow( CMD58, 0, $FD ) <> 0 crash( ERR_OCR_FAILED ) ' get back the data tmp := read_32_slow ' check the bit if tmp & |<30 card_type := type_SDHC adrShift := 0 else card_type := type_SD else ' Either a type 1 SD card, or it's MMC, try SD 1st if send_cmd_slow( ACMD41, 0, $E5 ) < 2 ' this is a type 1 SD card (1 means busy, 0 means done initializing) card_type := type_SD repeat while send_cmd_slow( ACMD41, 0, $E5 ) else ' mark that it's MMC, and try to initialize card_type := type_MMC repeat while send_cmd_slow( CMD1, 0, $F9 ) ' some SD or MMC cards may have the wrong block size, set it here send_cmd_slow( CMD16, 512, $15 ) ' card is mounted, make sure the CRC is turned off send_cmd_slow( CMD59, 0, $91 ) ' check the status 'send_cmd_slow( CMD13, 0, $0D ) ' done with the SPI bus for now outa |= maskCS ' set my counter modes for super fast SPI operation ' writing: NCO single-ended mode, output on DI writeMode := (%00100 << 26) | (DI << 0) ' reading 'readMode := (%11000 << 26) | (DO << 0) | (CLK << 9) ' clock 'clockLineMode := (%00110 << 26) | (CLK << 0) ' DUTY, 25% duty cycle ' clock clockLineMode := (%00100 << 26) | (CLK << 0) ' NCO, 50% duty cycle ' how many bytes (8 clocks, >>3) fit into 1/2 of a second (>>1), 4 clocks per instruction (>>2)? N_in8_500ms := clkfreq >> constant(1+2+3) ' how long should we wait before auto-exiting any multiblock mode? idle_limit := 125 ' ms, NEVER make this > 1000 idle_limit := clkfreq / (1000 / idle_limit) ' convert to counts ' Hand off control to the assembly engine's cog bufAdr := @SPI_buffer_address sdAdr := @SPI_block_index SPI_command := 0 ' just make sure it's not 1 ' start my driver cog and wait till I hear back that it's done SPI_engine_cog := cognew( @SPI_engine_entry, @SPI_command ) + 1 if( SPI_engine_cog == 0 ) crash( ERR_SPI_ENGINE_NOT_RUNNING ) repeat while SPI_command <> -1 ' and we no longer need to control any pins from here dira &= !maskAll ' the return variable is card_type PUB release {{ I do not want to abort if the cog is not running, as this is called from stop, which is called from start/ [8^) }} if SPI_engine_cog SPI_command := "z" repeat while SPI_command == "z" PUB stop {{ kill the assembly driver cog. }} release if SPI_engine_cog cogstop( SPI_engine_cog~ - 1 ) PRI crash( abort_code ) {{ In case of Bad Things(TM) happening, exit as gracefully as possible. }} ' and we no longer need to control any pins from here dira &= !maskAll ' and report our error abort abort_code PRI send_cmd_slow( cmd, val, crc ) : reply | time_stamp {{ Send down a command and return the reply. Note: slow is an understatement! Note: this uses the assembly DAT variables for pin IDs, which means that if you run this multiple times (say for multiple SD cards), these values will change for each one. But this is OK as all of these functions will be called during the initialization only, before the PASM engine is running. }} ' if this is an application specific command, handle it if (cmd & $80) ' ACMD is the command sequense of CMD55-CMD cmd &= $7F reply := send_cmd_slow( CMD55, 0, $65 ) if (reply > 1) return reply ' the CS line needs to go low during this operation outa |= maskCS outa &= !maskCS ' give the card a few cocks to finish whatever it was doing read_32_slow ' send the command byte send_slow( cmd, 8 ) ' send the value long send_slow( val, 32 ) ' send the CRC byte send_slow( crc, 8 ) ' is this a CMD12?, if so, stuff byte if cmd == CMD12 read_slow ' read back the response (spec declares 1-8 reads max for SD, MMC is 0-8) time_stamp := 9 repeat reply := read_slow while( reply & $80 ) and ( time_stamp-- ) ' done, and 'reply' is already pre-loaded { if dbg_ptr < (dbg_end-1) byte[dbg_ptr++] := cmd byte[dbg_ptr++] := reply if (cmd&63) == 13 ' get the second byte byte[dbg_ptr++] := cmd byte[dbg_ptr++] := read_slow '} PRI send_slow( value, bits_to_send ) value ><= bits_to_send repeat bits_to_send outa[pinCLK]~ outa[pinDI] := value value >>= 1 outa[pinCLK]~~ PRI read_32_slow : r repeat 4 r <<= 8 r |= read_slow PRI read_slow : r {{ Read back 8 bits from the card }} ' we need the DI line high so a read can occur outa[pinDI]~~ ' get 8 bits (remember, r is initialized to 0 by SPIN) repeat 8 outa[pinCLK]~ outa[pinCLK]~~ r += r + ina[pinDO] ' error check if( (cnt - SPI_block_index) > (clkfreq << 2) ) crash( ERR_CARD_BUSY_TIMEOUT ) DAT {{ This is the assembly engine for doing fast block reads and writes. This is *ALL* it does! }} ORG 0 SPI_engine_entry ' Counter A drives data out mov ctra,writeMode ' Counter B will always drive my clock line mov ctrb,clockLineMode ' set our output pins to match the pin mask mov dira,maskAll ' handshake that we now control the pins neg user_request,#1 wrlong user_request,par ' start my seconds' counter here mov last_time,cnt waiting_for_command ' update my seconds counter, but also track the idle ' time so we can to release the card after timeout. call #handle_time ' read the command, and make sure it's from the user (> 0) rdlong user_request,par cmps user_request,#0 wz,wc if_be jmp #waiting_for_command ' handle our card based commands cmp user_request,#"r" wz if_z jmp #read_ahead cmp user_request,#"w" wz if_z jmp #write_behind cmp user_request,#"z" wz if_z jmp #release_card ' time requests are handled differently cmp user_request,#"t" wz ' time if_z wrlong seconds,sdAdr ' seconds goes into the SD index register if_z wrlong dtime,bufAdr ' the remainder goes into the buffer address register ' in all other cases, clear the user's request mov user_request,#0 wrlong user_request,par jmp #waiting_for_command release_card mov user_cmd,#"z" ' request a release neg lastIndexPlus,#1 ' reset the last block index neg user_idx,#1 ' and make this match it call #handle_command mov user_request,user_cmd wrlong user_request,par jmp #waiting_for_command read_ahead rdlong user_idx,sdAdr ' if the correct block is not already loaded, load it mov tmp1,user_idx add tmp1,#1 cmp tmp1,lastIndexPlus wz if_z cmp lastCommand,#"r" wz if_z jmp #:get_on_with_it mov user_cmd,#"r" call #handle_command :get_on_with_it ' copy the data up into Hub RAM movi transfer_long,#%000010_000 'set to wrlong call #hub_cog_transfer ' signify that the data is ready, Spin can continue mov user_request,user_cmd wrlong user_request,par ' request the next block mov user_cmd,#"r" add user_idx,#1 call #handle_command ' done jmp #waiting_for_command write_behind rdlong user_idx,sdAdr ' copy data in from Hub RAM movi transfer_long,#%000010_001 'set to rdlong call #hub_cog_transfer ' signify that we have the data, Spin can continue mov user_request,user_cmd wrlong user_request,par ' write out the block mov user_cmd,#"w" call #handle_command ' done jmp #waiting_for_command {{ Set user_cmd and user_idx before calling this }} handle_command ' Can we stay in the old mode? (address = old_address+1) && (old mode == new_mode) cmp lastIndexPlus,user_idx wz if_z cmp user_cmd,lastCommand wz if_z jmp #:execute_block_command ' we fell through, must exit the old mode! (except if the old mode was "release") cmp lastCommand,#"w" wz if_z call #stop_mb_write cmp lastCommand,#"r" wz if_z call #stop_mb_read ' and start up the new mode! cmp user_cmd,#"w" wz if_z call #start_mb_write cmp user_cmd,#"r" wz if_z call #start_mb_read cmp user_cmd,#"z" wz if_z call #release_DO :execute_block_command ' track the (new) last index and command mov lastIndexPlus,user_idx add lastIndexPlus,#1 mov lastCommand,user_cmd ' do the block read or write or terminate! cmp user_cmd,#"w" wz if_z call #write_single_block cmp user_cmd,#"r" wz if_z call #read_single_block cmp user_cmd,#"z" wz if_z mov user_cmd,#0 ' done handle_command_ret ret {=== these PASM functions get me in and out of multiblock mode ===} release_DO ' we're already out of multiblock mode, so ' deselect the card and send out some clocks or outa,maskCS call #in8 call #in8 ' if you are using pull-up resistors, and need all ' lines tristated, then uncomment the following line. ' for Cluso99 'mov dira,#0 release_DO_ret ret start_mb_read movi block_cmd,#CMD18<<1 call #send_SPI_command_fast start_mb_read_ret ret stop_mb_read movi block_cmd,#CMD12<<1 call #send_SPI_command_fast call #busy_fast stop_mb_read_ret ret start_mb_write movi block_cmd,#CMD25<<1 call #send_SPI_command_fast start_mb_write_ret ret stop_mb_write call #busy_fast ' only some cards need these extra clocks mov tmp1,#16 :loopity call #in8 djnz tmp1,#:loopity ' done with hack movi phsa,#$FD<<1 call #out8 call #in8 ' stuff byte call #busy_fast stop_mb_write_ret ret send_SPI_command_fast ' make sure we have control of the output lines mov dira,maskAll ' make sure the CS line transitions low or outa,maskCS andn outa,maskCS ' 8 clocks call #in8 ' send the data mov phsa,block_cmd ' do which ever block command this is (already in the top 8 bits) call #out8 ' write the byte mov phsa,user_idx ' read in the desired block index shl phsa,adrShift ' this will multiply by 512 (bytes/sector) for MMC and SD call #out8 ' move out the 1st MSB ' rol phsa,#1 call #out8 ' move out the 1st MSB ' rol phsa,#1 call #out8 ' move out the 1st MSB ' rol phsa,#1 call #out8 ' move out the 1st MSB ' ' bogus CRC value call #in8 ' in8 looks like out8 with $FF ' CMD12 requires a stuff byte shr block_cmd,#24 cmp block_cmd,#CMD12 wz if_z call #in8 ' 8 clocks ' get the response mov tmp1,#9 :cmd_response call #in8 test readback,#$80 wc,wz if_c djnz tmp1,#:cmd_response if_nz neg user_cmd,readback ' done send_SPI_command_fast_ret ret busy_fast mov tmp1,N_in8_500ms :still_busy call #in8 cmp readback,#$FF wz if_nz djnz tmp1,#:still_busy busy_fast_ret ret out8 andn outa,maskDI 'movi phsb,#%11_0000000 mov phsb,#0 movi frqb,#%01_0000000 rol phsa,#1 rol phsa,#1 rol phsa,#1 rol phsa,#1 rol phsa,#1 rol phsa,#1 rol phsa,#1 mov frqb,#0 ' don't shift out the final bit...already sent, but be aware ' of this when sending consecutive bytes (send_cmd, for e.g.) out8_ret ret { in8 or outa,maskDI mov ctra,readMode ' Start my clock mov frqa,#1<<7 mov phsa,#0 movi phsb,#%11_0000000 movi frqb,#%01_0000000 ' keep reading in my value, one bit at a time! (Kuneko - "Wh) shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 mov frqb,#0 ' stop the clock mov readback,phsa mov frqa,#0 mov ctra,writeMode in8_ret ret } in8 neg phsa,#1' DI high mov readback,#0 ' set up my clock, and start it movi phsb,#%011_000000 movi frqb,#%001_000000 ' keep reading in my value test maskDO,ina wc rcl readback,#1 test maskDO,ina wc rcl readback,#1 test maskDO,ina wc rcl readback,#1 test maskDO,ina wc rcl readback,#1 test maskDO,ina wc rcl readback,#1 test maskDO,ina wc rcl readback,#1 test maskDO,ina wc rcl readback,#1 test maskDO,ina wc mov frqb,#0 ' stop the clock rcl readback,#1 mov phsa,#0 'DI low in8_ret ret ' this is called more frequently than 1 Hz, and ' is only called when the user command is 0. handle_time mov tmp1,cnt ' get the current timestamp add idle_time,tmp1 ' add the current time to my idle time counter sub idle_time,last_time ' subtract the last time from my idle counter (hence delta) add dtime,tmp1 ' add to my accumulator, sub dtime,last_time ' and subtract the old (adding delta) mov last_time,tmp1 ' update my "last timestamp" rdlong tmp1,#0 ' what is the clock frequency? cmpsub dtime,tmp1 wc ' if I have more than a second in my accumulator addx seconds,#0 ' then add it to "seconds" ' this part is to auto-release the card after a timeout cmp idle_time,idle_limit wz,wc if_b jmp #handle_time_ret ' don't clear if we haven't hit the limit mov user_cmd,#"z" ' we can't overdo it, the command handler makes sure neg lastIndexPlus,#1 ' reset the last block index neg user_idx,#1 ' and make this match it call #handle_command ' release the card, but don't mess with the user's request register handle_time_ret ret hub_cog_transfer ' setup for all 4 passes mov ctrb,clockXferMode mov frqb,#1 rdlong buf_ptr,bufAdr mov ops_left,#4 movd transfer_long,#speed_buf four_transfer_passes ' sync to the Hub RAM access rdlong tmp1,tmp1 ' how many long to move on this pass? (512 bytes / 4)longs / 4 passes mov tmp1,#(512 / 4 / 4) ' get my starting address right (phsb is incremented 1 per clock, so 16 each Hub access) mov phsb,buf_ptr ' write the longs, stride 4...low 2 bits of phsb are ignored transfer_long rdlong 0-0,phsb add transfer_long,incDest4 djnz tmp1,#transfer_long ' go back to where I started, but advanced 1 long sub transfer_long,decDestNminus1 ' offset my Hub pointer by one long per pass add buf_ptr,#4 ' do all 4 passes djnz ops_left,#four_transfer_passes ' restore the counter mode mov frqb,#0 mov phsb,#0 mov ctrb,clockLineMode hub_cog_transfer_ret ret read_single_block ' where am I sending the data? movd :store_read_long,#speed_buf mov ops_left,#128 ' wait until the card is ready mov tmp1,N_in8_500ms :get_resp call #in8 cmp readback,#$FE wz if_nz djnz tmp1,#:get_resp if_nz neg user_cmd,#ERR_ASM_NO_READ_TOKEN if_nz jmp #read_single_block_ret ' set DI high neg phsa,#1 ' read the data mov ops_left,#128 :read_loop mov tmp1,#4 movi phsb,#%011_000000 :in_byte ' Start my clock movi frqb,#%001_000000 ' keep reading in my value, BACKWARDS! (Brilliant idea by Tom Rokicki!) test maskDO,ina wc rcl readback,#8 test maskDO,ina wc muxc readback,#2 test maskDO,ina wc muxc readback,#4 test maskDO,ina wc muxc readback,#8 test maskDO,ina wc muxc readback,#16 test maskDO,ina wc muxc readback,#32 test maskDO,ina wc muxc readback,#64 test maskDO,ina wc mov frqb,#0 ' stop the clock muxc readback,#128 ' go back for more djnz tmp1,#:in_byte ' make it...NOT backwards [8^) rev readback,#0 :store_read_long mov 0-0,readback ' due to some counter weirdness, we need this mov add :store_read_long,const512 djnz ops_left,#:read_loop ' set DI low mov phsa,#0 ' now read 2 trailing bytes (CRC) call #in8 ' out8 is 2x faster than in8 call #in8 ' and I'm not using the CRC anyway ' give an extra 8 clocks in case we pause for a long time call #in8 ' in8 looks like out8($FF) ' all done successfully mov idle_time,#0 mov user_cmd,#0 read_single_block_ret ret write_single_block ' where am I getting the data? (all 512 bytes / 128 longs of it?) movs :write_loop,#speed_buf ' read in 512 bytes (128 longs) from Hub RAM and write it to the card mov ops_left,#128 ' just hold your horses call #busy_fast ' $FC for multiblock, $FE for single block movi phsa,#$FC<<1 call #out8 mov phsb,#0 ' make sure my clock accumulator is right 'movi phsb,#%11_0000000 :write_loop ' read 4 bytes mov phsa,speed_buf add :write_loop,#1 ' a long in LE order is DCBA rol phsa,#24 ' move A7 into position, so I can do the swizzled version movi frqb,#%010000000 ' start the clock (remember A7 is already in place) rol phsa,#1 ' A7 is going out, at the end of this instr, A6 is in place rol phsa,#1 ' A5 rol phsa,#1 ' A4 rol phsa,#1 ' A3 rol phsa,#1 ' A2 rol phsa,#1 ' A1 rol phsa,#1 ' A0 rol phsa,#17 ' B7 rol phsa,#1 ' B6 rol phsa,#1 ' B5 rol phsa,#1 ' B4 rol phsa,#1 ' B3 rol phsa,#1 ' B2 rol phsa,#1 ' B1 rol phsa,#1 ' B0 rol phsa,#17 ' C7 rol phsa,#1 ' C6 rol phsa,#1 ' C5 rol phsa,#1 ' C4 rol phsa,#1 ' C3 rol phsa,#1 ' C2 rol phsa,#1 ' C1 rol phsa,#1 ' C0 rol phsa,#17 ' D7 rol phsa,#1 ' D6 rol phsa,#1 ' D5 rol phsa,#1 ' D4 rol phsa,#1 ' D3 rol phsa,#1 ' D2 rol phsa,#1 ' D1 rol phsa,#1 ' D0 will be in place _after_ this instruction mov frqb,#0 ' shuts the clock off, _after_ this instruction djnz ops_left,#:write_loop ' write out my two (bogus, using $FF) CRC bytes call #in8 call #in8 ' now read response (I need this response, so can't spoof using out8) call #in8 and readback,#$1F cmp readback,#5 wz if_z mov user_cmd,#0 ' great if_nz neg user_cmd,#ERR_ASM_BLOCK_NOT_WRITTEN ' oops ' send out another 8 clocks call #in8 ' all done mov idle_time,#0 write_single_block_ret ret {=== Assembly Interface Variables ===} pinDO long 0 ' pin is controlled by a counter pinCLK long 0 ' pin is controlled by a counter pinDI long 0 ' pin is controlled by a counter maskDO long 0 ' mask for reading the DO line from the card maskDI long 0 ' mask for setting the pin high while reading maskCS long 0 ' mask = (1<