From 33497f79b58b447187ba57167a2fa933ef12609d Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Wed, 26 Mar 2025 17:56:34 +0300 Subject: [PATCH] SPI1/2 works --- F1:F103/BISS_C_encoders/99-myHW.rules | 4 + F1:F103/BISS_C_encoders/encoders.bin | Bin 6968 -> 11520 bytes F1:F103/BISS_C_encoders/encoders.files | 4 + F1:F103/BISS_C_encoders/flash.c | 196 +++++++++++++++++++++++++ F1:F103/BISS_C_encoders/flash.h | 46 ++++++ F1:F103/BISS_C_encoders/hardware.c | 8 +- F1:F103/BISS_C_encoders/main.c | 10 ++ F1:F103/BISS_C_encoders/openocd.cfg | 94 +++++++++++- F1:F103/BISS_C_encoders/openocd.cfg_ | 4 + F1:F103/BISS_C_encoders/proto.c | 181 ++++++++++++++++++++--- F1:F103/BISS_C_encoders/spi.c | 174 ++++++++++++++++++++++ F1:F103/BISS_C_encoders/spi.h | 22 +++ F1:F103/BISS_C_encoders/strfunc.c | 9 +- F1:F103/BISS_C_encoders/strfunc.h | 2 +- F1:F103/BISS_C_encoders/usb_descr.c | 36 ++++- F1:F103/BISS_C_encoders/usb_descr.h | 1 + F1:F103/BISS_C_encoders/usb_lib.c | 1 + F1:F103/BISS_C_encoders/version.inc | 4 +- 18 files changed, 763 insertions(+), 33 deletions(-) create mode 100644 F1:F103/BISS_C_encoders/99-myHW.rules create mode 100644 F1:F103/BISS_C_encoders/flash.c create mode 100644 F1:F103/BISS_C_encoders/flash.h create mode 100644 F1:F103/BISS_C_encoders/openocd.cfg_ create mode 100644 F1:F103/BISS_C_encoders/spi.c create mode 100644 F1:F103/BISS_C_encoders/spi.h diff --git a/F1:F103/BISS_C_encoders/99-myHW.rules b/F1:F103/BISS_C_encoders/99-myHW.rules new file mode 100644 index 0000000..983dee6 --- /dev/null +++ b/F1:F103/BISS_C_encoders/99-myHW.rules @@ -0,0 +1,4 @@ +ACTION=="add", DRIVERS=="usb", ENV{USB_IDS}="%s{idVendor}:%s{idProduct}" +ACTION=="add", ENV{USB_IDS}=="067b:2303", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty" +ACTION=="add", ENV{USB_IDS}=="0483:5740", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty" + diff --git a/F1:F103/BISS_C_encoders/encoders.bin b/F1:F103/BISS_C_encoders/encoders.bin index a55eb97e60c296c6cda236d2877a3ed20277c213..380605620cc3eaca61b686a2e2c0bee5612a8910 100755 GIT binary patch delta 7089 zcmcIo4^&fUmjAw&1OkEtK?#4f9|R?WfB096Rw{-N2nmLYwia!D0fI>@kOr92PR}N- z%q*?lt@@mvj_vMf|IBJ_djz4QQyJ{mo-(a%lU1i!b!D{E*rnZlwp3n1AbY=;M{VuQ zIlJfVzH@GV-@D)a?!Di=_xtm5Y=x)ZjcO6e%tFffK&iJ7y5*rVlqj3xtBRkFxmCxl z?`xAHW1fhh`zJ=H$p2pHtw#Rm@9zJ5WBMBl8q2Dee#uuR_Eay})3;C8a=z4zNU0I? zKcC9BUlx?Rnbqe7 zL~z?tXGV|i9Uu6kN-?MEQ>qrR46hYrqZU3~lw{Vh$wsQ;wkcxA{0hl{dTQ_*Ul~#U z#fdmmeD%B6QKM|^3C&BBG7V$i9HU`>=^vd4-v-4(UV&OU1|)}e9MB19qkN>ae09pb z4r+!2=4B6BRBYI6xh&Y9*ep77A#_YY2=ku^k+_@* z-6Qfp3DF3glDNa6yG8D)5RKt+i8~mo5xK`hW|JDG^M>G#DCrFu#f~>ZRbs~hX~3(Y z$Q(OECv^2^z;OOi-96{gQpbe>-THrP%_udcIp##k&aF0RWgTYtr=OYHPGL5n9RWLVsmT0a*>$52JIh6*JtE{>@$)N#LNmfYH z-(y05FAH1t$67#J*{SQONj~;ucK_tW4R@U%-~OiVxc-C>k*j{R@HsykqIpA*tkaN| z5uj0^Yd|!AAwZf_M3^!S-d1t=ssH*2a#=>xsMH{(Fi>JDF{jxzc4pN+UF9WQq+#t@ zw;OLaD;>>Cud$bQG!0^^5~IJs}UmYrX-gdMiOPXm=*sE5!kT5MWkyvV8@i!Eh5 zMrtd*ZjUPvm{}^%i2c22*}1#e5&KDySM>mQi>L1wx(wflUB%0h#%Ha#$THU%<_ljW z3K*gw45n74nyOe6`>%eafx^z$u;Gc}>0`&;8v7AHB1y0|dne*9R>wbs!Ace^56cUt z_qYRiVa#q*nthL~;@mFo6MR2@gj>oi-Hfus6Aw=D3&RtRFjzE4XF94MG(A}L(7Ja8 z)yaEUkD#gD%ONDQ%=uoQxr-!WwwM>)qpr zyHMUoUfRcqeFvfMt_ubqCd?rO+Z)pU!K=(-NcA#V*v7VPriSpgk6>R%+vY>>Fw!J~eS$T3Nr z^x!f7Z6Ze(lpmrS-Ce;9@qJbBuwQ-<@!NyH_NR)Rf}lI190&XTb48X`8k8MO5Z_7g zUts+p6;uUb%2cCh6e)2T`WlU{NyVrbDx#l3gyyme+O_8q+&1mzl(7jN``Mr9ulSnU+M0;|L$tSWHLFLx6stcNAwz%HON zEmFRo10jR5c(y#S|)04`K10 zZbGVoCy(G=D|_P%_z*E5esZc?7wyH*hbLx-B9hDz)u4<_LQOdb6ox0c-SQ+i=C28* z;slNfwDc!Kk7t?&gv?EyTE)kl@l6j}87OV-<>Ifysa+Q4F~eUe`_dA_3b3uEN>$1S9?` zA#>cM2LiE2noB!m;bBgoPu?RhIv7S1I02{kji<*&Se>DL!@`B@B7d!l*H& z?4^u(0#0WNkLnmfwuKSo88>_-`duLRre6ULQ)j8uO!04FrtiQ^>8?#@`Z=6ybhgTY z$jZJ$NH(c^5T7TxvRp_MqnB4HsNjl#__b1pMhrXRzYU{IIiVXb%2WW_0HXk{VY!&_ z&p|mgHgk}2$%DM`fU|(V z5>y_iwM55W3?2!3__7$~pfG^=l+bXmtV>}adGe9mQI&w#ki13bOY;K0=&s)#N7?hM z7OcatDvRA{F~s~2L2?uqtCrmG(R~i_2g8Hcgff~T#Q$ulbosYFq(K6{6Y*n{vaV=Q zduilLKfd#}yiZ4Fdj5Sfx(hA$dHw!q(@KME?38S?eYEfSO1W0%Rha4oqVoBL!EuNI z4R#1{39R0u8Bw*2yJTRlNp1j{TH|q?nb$L?S_3zoba{GQo+oib$spnjgS5mEzhat} zFuYIC5^>pd#LMX^2FA&&U+ANEBp9_3Mh%U_&h-jLgr;>Atmziu+|m(YKnA*Us3J;Q2!hpj1| zVispVO%i*a>RUTLYF*3AaiV9hhq)ey=T#qKwJfcovl|e936AoaU>+WHR)XdY+MI-S zr`cgPU)1cTs`J?%M>MGJ5mo!3Fsb+SAh|{{4ws=nqft7W5X^)gD&-4s9y;%=R1KsU z z)9qlcdvwrQ$9&gaxYL>>3M7GmLKH{_2WFh1j!-A4aeN*@MbbSC%<~Q6mjU9Nry@nv zFr^k#^nm2lGH`0H3GV@?NYYcOQStPSOcITA%x>8zVUwOpkCLCmFW{n`I?UZTkN3r! z_URZeToqsojAVPIq$*|#k#8YFG_Ok}=hpn40?}i$3{Vd|tj!*A4kWFU8;Z8fI<7W2 z2jjs>mE>gdRJ`b%Y6|Mf4Fwe^slFIOnxc(ZqpU(>r{uEJTGqkR!AJwYmHVY)+5!f4 zLEAYXH!KtteI;Jk5q~0tm&ivlO}329_d|rdAT#)%effs)#5P#d6W$C{dQW}S{IltR zo1?PMDmAIq?zF1)!opf=@GDkre2AqLeo9ifmwvxwh9{iUnB>`I<$0`gP$il?DoN}J zjrb$0;x&o$Sm+Ey=W%e7rV8al?UW#~j;Pb8fxj2%2#|6h&Xnf1Cr0UqJX!T@482i8 z8VJFe(bb#SL?_C`u!R*)%d2Tsz`dA-vkWn1G!hwr$^j@Md!k%FL~o9g2D#!Kywyf@ z+zmPy(4qRPI`p8!jdJx~da*^>&mM`tE}ymA5M|hJs5e-65*wRSvp_M5i(lzGP{9cJ zaF!QtG;3-zQe%FT*E4i)fs9X(JwKFRkm!ublk*AqnW1}&@{1A{qwAQFIj{I|af$ptwE1{7zI$sl8O{81U zWTtU%&R#!zX*yE20PO&>rgO6s3bDYgO(R38bGXDF7T;}Hi?u&%1RX4ea?CvcYh%qS)>030tN<$nh{n?Ys0dK|Fnh5jsj7`Z!Ca6TE;ypSdv+)ew>|PQW@VBJP3$<;Fw*d8%IpM z=>x002@T;pLq3pgqVmzz3S&}JlI=`WT~l(C>V%RFPi%!c=KAOAR`dtpBV-6cHtDxa z2b43#&%tD@ezy)?4;LXHJsT1QY}&1p3D^?wsRlXKyll5|opD1fjBIf7azpum@V6V<1PJ#%&aURWmbYCFRdlpBonXOh-Zf zO6YmS3hQ6oZ@%Xjh*lgewzf|AIKL@hPy+JnA=xt}PnxV9)|>r>R3B6TtPQCgM5hai ziYVXdC?Jn)qP}JRP-#(dq_Z&M_mBIi!I|lT0vZZ<=xvbR<`8|PD<_wnYR^EqkTakR zpe;c33epF37KnR3m3PB|C&rv6&@K-D|Nq$yIqruO%LDs4mzKz#Rjc!ET1}SOP%COg zZKw&^kPB@^n{IZ}78h2}rR8$-;SQvw;}e`OEvfW`3=u@D?FY{Z`A>Pnioe3+GSidP M!_hzODFbc)4V|g6`~Uy| delta 2550 zcmb_ceN0=|6+iDi+q^m?IKg}wlU!^PJPgP{$kIR*$0aYAX9^9BWKC&IVmh;igc73B zBocIE73(+LREyfE($;EP*HnTA(q;{UiDXKvL^B%A@=;VYX)+nKd)vr(1fJQsj-ySX z`Dfabemdu!^E)5++>g#DKiSj))j+N$f%qBX(gT18KE`2SW+s2eEsuFH$HV?_tcb>T zEGzn<`F~IPuwoClcl`g0xdy5fIEttiy`t~sCb8l%B}sU>q0wIx{KcKFg2!e@gc`r= zLb<jKi=>8M?$MT z`0n%yfqF`=SIu^|0^#2W<(&vPcHF8Qd$xV_{0Hseyc%LYKI(e|efC=yL%CjDhw@wm z`6!zsSRduq$j4#E<)YkU$j!es5wbrzlp#)EMMjpc}HL^lFl5Z$Mi!Hj5`xLBD#X_n~!l6G%(f6!8VRmxIP^5ZF$ z=E*v>$i!MzwC_Y)p3#6b;1Z@{>`%1GU}t}=DsBU3yf=K4ac^o39YLmC)3!th_A6~F z+%jas&H{+jsUQXsZ=l|dXc^Q?Kki%IzoI=}8a0hj z2@7PBjTA@5nmLJ$(AxN%jvs2^m<<^-IZUt_1H~a8wS8#4jaH`TMXdsQ1i}tGo4h&* z%GkXBtkABL+)m+!N#(Y=oeS5wl66k!4MY1k=QAa1IjpxA-^jvZl?}^YalS_>{^rRx(9JQsqL-g$a>hxdWy{qOjg^E3RH`(F=csHZb zkrWsADgAp&O+wGYY?zaVrj(s*6JDYeERu-3d*S$tIv&no}0(l{BBB1xc56bgku>hsCg17Gn$= zGVR*N_)}^_}oQ$F}>8tB4fpk)*Swy3v`p!w&g!oAU*w?2Op93 zQu0Ya~ZiSo(Hmdh{x0Ez^QC@FU@rgV;<~-_F4<;URVH!ZbDzCSGj{Yv=@lf z7+ji@7JlkU9d=1~#)z~uH?hDu?vCNDbbTF;tJF<*3Hs@u=pE<@MLl3=K2g=VCc
    }0I?T5O5CK- zRal(oD#|M?1~_oIxw+v`;0Sr~#v8}DKwf?C?U?ljlHzJQfX{Zk{(1~OxB{@gLn^lew3$BF5!0uCCf8{l0*4r%szvX z$Nxo?0!k~!Fg~hO$W3#Yg+@382jDOSpb=VOAMAnUw(mo4PS9$`>zRshAzBdQm}>?B zsMD?-#n6L^M}U6gOr{5OQ=hoZt`lqKzrM@-x7dW`wsGCJ*y!(a6H~_x%RO$a@Ne>< B3B&*Z diff --git a/F1:F103/BISS_C_encoders/encoders.files b/F1:F103/BISS_C_encoders/encoders.files index 9bc5db9..42d3791 100644 --- a/F1:F103/BISS_C_encoders/encoders.files +++ b/F1:F103/BISS_C_encoders/encoders.files @@ -1,3 +1,5 @@ +flash.c +flash.h hardware.c hardware.h main.c @@ -5,6 +7,8 @@ proto.c proto.h ringbuffer.c ringbuffer.h +spi.c +spi.h strfunc.c strfunc.h usart.c diff --git a/F1:F103/BISS_C_encoders/flash.c b/F1:F103/BISS_C_encoders/flash.c new file mode 100644 index 0000000..5a64626 --- /dev/null +++ b/F1:F103/BISS_C_encoders/flash.c @@ -0,0 +1,196 @@ +/* + * This file is part of the encoders project. + * Copyright 2025 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "stm32f1.h" + +#include "flash.h" +#include "strfunc.h" +#include "usb_dev.h" // printout +#include // memcpy + +extern const uint32_t __varsstart, _BLOCKSIZE; +static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE; +static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here + +#define USERCONF_INITIALIZER { \ + .userconf_sz = sizeof(user_conf) \ + } + +static int write2flash(const void*, const void*, uint32_t); +const user_conf *Flash_Data = (const user_conf *)(&__varsstart); +user_conf the_conf = USERCONF_INITIALIZER; + +int currentconfidx = -1; // index of current configuration + +/** + * @brief binarySearch - binary search in flash for last non-empty cell + * any struct searched should have its sizeof() @ the first field!!! + * @param l - left index + * @param r - right index (should be @1 less than last index!) + * @param start - starting address + * @param stor_size - size of structure to search + * @return index of non-empty cell or -1 + */ +static int binarySearch(int r, const uint8_t *start, int stor_size){ + int l = 0; + while(r >= l){ + int mid = l + (r - l) / 2; +#ifdef EBUG + CMDWR("mid/l/r="); + CMDWR(u2str(mid)); CMDWR("/"); + CMDWR(u2str(l)); CMDWR("/"); + CMDWR(u2str(r)); CMDWR("/"); + CMDn(); +#endif + const uint8_t *s = start + mid * stor_size; + if(*((const uint16_t*)s) == stor_size){ + if(*((const uint16_t*)(s + stor_size)) == 0xffff){ // next is free + return mid; + }else{ // element is to the right + l = mid + 1; + } + }else{ // element is to the left + r = mid - 1; + } + } + return -1; // not found +} + +/** + * @brief flashstorage_init - initialization of user conf storage + * run in once @ start + */ +void flashstorage_init(){ + if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ + uint32_t flsz = FLASH_SIZE * 1024; // size in bytes + flsz -= (uint32_t)(&__varsstart) - FLASH_BASE; + maxCnum = flsz / sizeof(user_conf); + } +#ifdef EBUG + CMDWR("INIT\n"); +#endif + // -1 if there's no data at all & flash is clear; maxnum-1 if flash is full + currentconfidx = binarySearch((int)maxCnum-2, (const uint8_t*)Flash_Data, sizeof(user_conf)); + if(currentconfidx > -1){ + memcpy(&the_conf, &Flash_Data[currentconfidx], sizeof(user_conf)); + } +#ifdef EBUG + CMDWR("currentconfidx="); CMDWR(i2str(currentconfidx)); CMDn(); +#endif +} + +// store new configuration +// @return 0 if all OK +int store_userconf(){ + // maxnum - 3 means that there always should be at least one empty record after last data + // for binarySearch() checking that there's nothing more after it! + if(currentconfidx > (int)maxCnum - 3){ // there's no more place + currentconfidx = 0; + if(erase_storage(-1)) return 1; + }else ++currentconfidx; // take next data position (0 - within first run after firmware flashing) + return write2flash((const void*)&Flash_Data[currentconfidx], &the_conf, sizeof(the_conf)); +} + +static int write2flash(const void *start, const void *wrdata, uint32_t stor_size){ + int ret = 0; + if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + while (FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; // clear all flags + FLASH->CR |= FLASH_CR_PG; + const uint16_t *data = (const uint16_t*) wrdata; + volatile uint16_t *address = (volatile uint16_t*) start; + uint32_t i, count = (stor_size + 1) / 2; + for(i = 0; i < count; ++i){ + *(volatile uint16_t*)(address + i) = data[i]; + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + if(*(volatile uint16_t*)(address + i) != data[i]){ + CMDWR("DON'T MATCH!\n"); + ret = 1; + break; + } + if(FLASH->SR & FLASH_SR_PGERR){ + CMDWR("Prog err\n"); + ret = 1; // program error - meet not 0xffff + break; + } + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; + } + FLASH->CR &= ~(FLASH_CR_PG); + return ret; +} + +// erase Nth page of flash storage (flash should be prepared!) +static int erase_pageN(int N){ + int ret = 0; +#ifdef EBUG + CMDWR("Erase page #"); CMDWR(u2str(N)); CMDn(); +#endif + FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize; + FLASH->CR |= FLASH_CR_STRT; + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP; + if(FLASH->SR & FLASH_SR_WRPRTERR){ /* Check Write protection error */ + ret = 1; + FLASH->SR = FLASH_SR_WRPRTERR; /* Clear the flag by software by writing it at 1*/ + } + return ret; +} + +// erase full storage (npage < 0) or its nth page; @return 0 if all OK +int erase_storage(int npage){ + int ret = 0; + uint32_t end = 1, start = 0, flsz = 0; + if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ + flsz = FLASH_SIZE * 1024; // size in bytes + flsz -= (uint32_t)Flash_Data - FLASH_BASE; + } + end = flsz / FLASH_blocksize; +#ifdef EBUG + CMDWR("FLASH_SIZE="); CMDWR(u2str(FLASH_SIZE)); + CMDWR("\nflsz="); CMDWR(u2str(flsz)); + CMDWR("\nend="); CMDWR(u2str(end)); + CMDWR("\ncurrentconfidx="); CMDWR(u2str(currentconfidx)); + CMDWR("\nmaxCnum="); CMDWR(u2str(maxCnum)); + CMDn(); +#endif + if(end == 0 || end >= FLASH_SIZE) return 1; + if(npage > -1){ // erase only one page + if((uint32_t)npage >= end) return 1; + start = npage; + end = start + 1; + } + if((FLASH->CR & FLASH_CR_LOCK) != 0){ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; + FLASH->CR |= FLASH_CR_PER; + for(uint32_t i = start; i < end; ++i){ + if(erase_pageN(i)){ + ret = 1; + break; + } + } + FLASH->CR &= ~FLASH_CR_PER; + return ret; +} + diff --git a/F1:F103/BISS_C_encoders/flash.h b/F1:F103/BISS_C_encoders/flash.h new file mode 100644 index 0000000..fb7c002 --- /dev/null +++ b/F1:F103/BISS_C_encoders/flash.h @@ -0,0 +1,46 @@ +/* + * This file is part of the encoders project. + * Copyright 2025 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include "usb_descr.h" + +#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0) +#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG) + +// maximal size (in letters) of iInterface for settings +#define MAX_IINTERFACE_SZ (16) + +/* + * struct to save user configurations + */ +typedef struct __attribute__((packed, aligned(4))){ + uint16_t userconf_sz; // "magick number" + uint16_t iInterface[bTotNumEndpoints][MAX_IINTERFACE_SZ]; // hryunikod! + uint8_t iIlengths[bTotNumEndpoints]; +} user_conf; + +extern user_conf the_conf; +extern int currentconfidx; + +void flashstorage_init(); +int store_userconf(); +int erase_storage(int npage); + + diff --git a/F1:F103/BISS_C_encoders/hardware.c b/F1:F103/BISS_C_encoders/hardware.c index f28ac75..f24d2d1 100644 --- a/F1:F103/BISS_C_encoders/hardware.c +++ b/F1:F103/BISS_C_encoders/hardware.c @@ -16,21 +16,27 @@ */ #include "hardware.h" +#include "spi.h" static inline void gpio_setup(){ // Enable clocks to the GPIO subsystems (PB for ADC), turn on AFIO clocking to disable SWD/JTAG - RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN; + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN; // turn off SWJ/JTAG // AFIO->MAPR = AFIO_MAPR_SWJ_CFG_DISABLE; AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // for PA15 // Set led as opendrain output GPIOC->CRH |= CRH(13, CNF_ODOUTPUT|MODE_SLOW); + GPIOA->CRL = CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT); // USB pullup (PA15) - pushpull output GPIOA->CRH = CRH(15, CNF_PPOUTPUT|MODE_SLOW); + GPIOB->CRH = CRH(13, CNF_AFPP|MODE_FAST) | CRH(14, CNF_FLINPUT); } void hw_setup(){ gpio_setup(); + // setup both SPI channels + spi_setup(1); + spi_setup(2); } #ifndef EBUG diff --git a/F1:F103/BISS_C_encoders/main.c b/F1:F103/BISS_C_encoders/main.c index 4087e90..33299cd 100644 --- a/F1:F103/BISS_C_encoders/main.c +++ b/F1:F103/BISS_C_encoders/main.c @@ -16,8 +16,10 @@ * along with this program. If not, see . */ +#include "flash.h" #include "hardware.h" #include "proto.h" +#include "spi.h" #include "strfunc.h" #include "usart.h" #include "usb_dev.h" @@ -32,7 +34,9 @@ void sys_tick_handler(void){ int main(){ char inbuff[RBINSZ]; uint32_t lastT = 0, lastS = 0; + uint8_t encbuf[ENCODER_BUFSZ]; StartHSE(); + flashstorage_init(); hw_setup(); USBPU_OFF(); SysTick_Config(72000); @@ -79,6 +83,9 @@ int main(){ CMDWR(inbuff); CMDWR("'\n"); } + if(spi_read_enc(0, encbuf)){ // send encoder data + hexdump(I_X, encbuf, ENCODER_BUFSZ); + } } if(CDCready[I_Y]){ int l = USB_receivestr(I_Y, inbuff, RBINSZ); @@ -88,6 +95,9 @@ int main(){ CMDWR(inbuff); CMDWR("'\n"); } + if(spi_read_enc(1, encbuf)){ // send encoder data + hexdump(I_Y, encbuf, ENCODER_BUFSZ); + } } } return 0; diff --git a/F1:F103/BISS_C_encoders/openocd.cfg b/F1:F103/BISS_C_encoders/openocd.cfg index bdab2a1..a43a499 100644 --- a/F1:F103/BISS_C_encoders/openocd.cfg +++ b/F1:F103/BISS_C_encoders/openocd.cfg @@ -1,4 +1,94 @@ set FLASH_SIZE 0x10000 -source [find interface/stlink-v2-1.cfg] -source [find target/stm32f1x.cfg] +source [find interface/stlink.cfg] + + +# script for stm32f1x family + +# +# stm32 devices support both JTAG and SWD transports. +# +source [find target/swj-dp.tcl] +source [find mem_helper.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME stm32f1x +} + +set _ENDIAN little + +# Work-area is a space in RAM used for flash programming +# By default use 4kB (as found on some STM32F100s) +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x1000 +} + +# Allow overriding the Flash bank size +if { [info exists FLASH_SIZE] } { + set _FLASH_SIZE $FLASH_SIZE +} else { + # autodetect size + set _FLASH_SIZE 0 +} + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + if { [using_jtag] } { + # See STM Document RM0008 Section 26.6.3 + set _CPUTAPID 0x2ba01477 + } { + # this is the SW-DP tap id not the jtag tap id + set _CPUTAPID 0x2ba01477 + } +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +if {[using_jtag]} { + jtag newtap $_CHIPNAME bs -irlen 5 +} + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +# flash size will be probed +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME stm32f1x 0x08000000 $_FLASH_SIZE 0 0 $_TARGETNAME + +# JTAG speed should be <= F_CPU/6. F_CPU after reset is 8MHz, so use F_JTAG = 1MHz +adapter_khz 1000 + +adapter_nsrst_delay 100 +if {[using_jtag]} { + jtag_ntrst_delay 100 +} + +reset_config srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +$_TARGETNAME configure -event examine-end { + # DBGMCU_CR |= DBG_WWDG_STOP | DBG_IWDG_STOP | + # DBG_STANDBY | DBG_STOP | DBG_SLEEP + mmw 0xE0042004 0x00000307 0 +} + +$_TARGETNAME configure -event trace-config { + # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync + # change this value accordingly to configure trace pins + # assignment + mmw 0xE0042004 0x00000020 0 +} diff --git a/F1:F103/BISS_C_encoders/openocd.cfg_ b/F1:F103/BISS_C_encoders/openocd.cfg_ new file mode 100644 index 0000000..7533dd1 --- /dev/null +++ b/F1:F103/BISS_C_encoders/openocd.cfg_ @@ -0,0 +1,4 @@ +set FLASH_SIZE 0x10000 + +source [find interface/stlink.cfg] +source [find target/stm32f1x.cfg] diff --git a/F1:F103/BISS_C_encoders/proto.c b/F1:F103/BISS_C_encoders/proto.c index 28a772f..659e88b 100644 --- a/F1:F103/BISS_C_encoders/proto.c +++ b/F1:F103/BISS_C_encoders/proto.c @@ -18,20 +18,13 @@ #include +#include "flash.h" #include "proto.h" +#include "spi.h" #include "strfunc.h" #include "usb_dev.h" #include "version.inc" -// commands indexes -typedef enum{ - C_dummy, - C_help, - C_sendX, - C_sendY, - C_AMOUNT -} cmd_e; - // error codes typedef enum { ERR_OK, // no errors @@ -50,6 +43,27 @@ static const char* const errors[ERR_AMOUNT] = { [ERR_SILENCE] = "", }; +// commands indexes +typedef enum{ + C_dummy, + C_help, + C_sendX, + C_sendY, + C_setiface1, + C_setiface2, + C_setiface3, + C_dumpconf, + C_erasestorage, + C_storeconf, + C_reboot, + C_fin, + C_encstart, + C_spistat, + C_spiinit, + C_spideinit, + C_AMOUNT +} cmd_e; + // command handler (idx - index of command in list, par - all after equal sign in "cmd = par") typedef errcode_e (*handler_t)(cmd_e idx, char *par); @@ -65,6 +79,7 @@ static const funcdescr_t commands[C_AMOUNT]; CMDWR(i2str(ival)); CMDn(); return ERR_SILENCE; }while(0) static errcode_e help(cmd_e idx, char* par); +static errcode_e dumpconf(cmd_e idx, char *par); static errcode_e dummy(cmd_e idx, char *par){ static int32_t val = -111; @@ -98,13 +113,132 @@ static errcode_e sendenc(cmd_e idx, char *par){ return ERR_OK; } +static errcode_e setiface(cmd_e idx, char *par){ + if(idx < C_setiface1 || idx >= C_setiface1 + bTotNumEndpoints) return ERR_BADCMD; + idx -= C_setiface1; // now it is an index of iIlengths + if(par && *par){ + int l = strlen(par); + DBGs(i2str(l)); + if(l > MAX_IINTERFACE_SZ) return ERR_BADPAR; // too long + the_conf.iIlengths[idx] = (uint8_t) l * 2; + char *ptr = (char*)the_conf.iInterface[idx]; + for(int i = 0; i < l; ++i){ // make fucking hryunicode + *ptr++ = *par++; + *ptr++ = 0; + } + } + // getter + int l = the_conf.iIlengths[idx] / 2; + char *ptr = (char*)the_conf.iInterface[idx]; + CMDWR(commands[idx + C_setiface1].cmd); + CMDWR("="); + for(int i = 0; i < l; ++i){ + USB_putbyte(I_CMD, *ptr); + ptr += 2; + } + CMDn(); + return ERR_SILENCE; +} + +static errcode_e erasestor(cmd_e _U_ idx, char *par){ + int32_t npage = -1; + if(par){ + if(par == getint(par, &npage)) return ERR_BADPAR; + } + if(erase_storage(npage)) return ERR_FAIL; + return ERR_OK; +} + +static errcode_e storeconf(_U_ cmd_e idx, _U_ char *par){ + if(store_userconf()) return ERR_FAIL; + return ERR_OK; +} + +static errcode_e reboot(_U_ cmd_e idx, _U_ char *par){ + NVIC_SystemReset(); + return ERR_OK; // never reached +} + +static errcode_e fini(_U_ cmd_e idx, _U_ char *par){ + flashstorage_init(); + return ERR_OK; // never reached +} + +static errcode_e encstart(_U_ cmd_e idx, _U_ char *par){ + if(!spi_start_enc(0)) return ERR_FAIL; + if(!spi_start_enc(1)) return ERR_FAIL; + return ERR_OK; +} + +static errcode_e spistat(_U_ cmd_e idx, _U_ char *par){ + for(int i = 1; i < 3; ++i){ + USB_sendstr(I_CMD, "SPI"); + USB_putbyte(I_CMD, '0' + i); + USB_sendstr(I_CMD, ": "); + switch(spi_status[i]){ + case SPI_NOTREADY: + USB_sendstr(I_CMD, "not ready"); + break; + case SPI_BUSY: + USB_sendstr(I_CMD, "busy"); + break; + case SPI_READY: + USB_sendstr(I_CMD, "ready"); + break; + } + newline(I_CMD); + } + return ERR_SILENCE; +} + +static errcode_e spiinit(_U_ cmd_e idx, _U_ char *par){ + for(int i = 1; i < 3; ++i){ + USB_sendstr(I_CMD, "Init SPI"); + USB_putbyte(I_CMD, '0' + i); + newline(I_CMD); + spi_setup(i); + } + return ERR_SILENCE; +} + +static errcode_e spideinit(_U_ cmd_e idx, _U_ char *par){ + for(int i = 1; i < 3; ++i){ + USB_sendstr(I_CMD, "DEinit SPI"); + USB_putbyte(I_CMD, '0' + i); + newline(I_CMD); + spi_deinit(i); + } + return ERR_SILENCE; +} + // text commands static const funcdescr_t commands[C_AMOUNT] = { [C_dummy] = {"dummy", dummy}, [C_help] = {"help", help}, [C_sendX] = {"sendx", sendenc}, [C_sendY] = {"sendy", sendenc}, - }; + [C_setiface1] = {"setiface1", setiface}, + [C_setiface2] = {"setiface2", setiface}, + [C_setiface3] = {"setiface3", setiface}, + [C_dumpconf] = {"dumpconf", dumpconf}, + [C_erasestorage] = {"erasestorage", erasestor}, + [C_storeconf] = {"storeconf", storeconf}, + [C_reboot] = {"reboot", reboot}, + [C_fin] = {"fin", fini}, + [C_encstart] = {"readenc", encstart}, + [C_spistat] = {"spistat", spistat}, + [C_spiinit] = {"spiinit", spiinit}, + [C_spideinit] = {"spideinit", spideinit}, +}; + +static errcode_e dumpconf(cmd_e _U_ idx, char _U_ *par){ + CMDWR("userconf_sz="); CMDWR(u2str(the_conf.userconf_sz)); + CMDWR("\ncurrentconfidx="); CMDWR(i2str(currentconfidx)); + CMDn(); + for(int i = 0; i < bTotNumEndpoints; ++i) + setiface(C_setiface1 + i, NULL); + return ERR_SILENCE; +} typedef struct{ int idx; // command index (if < 0 - just display `help` as grouping header) @@ -113,18 +247,31 @@ typedef struct{ // SHOUL be sorted and grouped static const help_t helpmessages[] = { + {-1, "Configuration"}, + {C_dumpconf, "dump current configuration"}, + {C_erasestorage, "erase full storage or current page (=n)"}, + {C_setiface1, "set name of first (command) interface"}, + {C_setiface2, "set name of second (axis X) interface"}, + {C_setiface3, "set name of third (axis Y) interface"}, + {C_storeconf, "store configuration in flash memory"}, {-1, "Different commands"}, {C_dummy, "dummy integer setter/getter"}, + {C_encstart, "start reading encoders"}, {C_help, "show this help"}, - {-1, "Debug commands"}, - {C_sendX, "send text string to X encoder"}, - {C_sendY, "send text string to Y encoder"}, + {C_reboot, "reboot MCU"}, + {C_spideinit, "deinit SPI"}, + {C_spiinit, "init SPI"}, + {C_spistat, "get status of both SPI interfaces"}, + {-1, "Debug"}, + {C_sendX, "send text string to X encoder's terminal"}, + {C_sendY, "send text string to Y encoder's terminal"}, + {C_fin, "reinit flash"}, {-1, NULL}, }; static errcode_e help(_U_ cmd_e idx, _U_ char* par){ - CMDWRn("https://github.com/eddyem/stm32samples/tree/master/F1:F103/ build #" BUILD_NUMBER " @ " BUILD_DATE); - CMDWRn("commands format: command[=setter]\\n"); + CMDWRn("https://github.com/eddyem/stm32samples/tree/master/F1:F103/BISS_C_encoders build #" BUILD_NUMBER " @ " BUILD_DATE); + CMDWRn("\ncommands format: 'command[=setter]\\n'"); const help_t *c = helpmessages; while(c->help){ if(c->idx < 0){ // header @@ -139,7 +286,7 @@ static errcode_e help(_U_ cmd_e idx, _U_ char* par){ CMDn(); ++c; } - return ERR_OK; + return ERR_SILENCE; } // *cmd is "command" for commands/getters or "parameter=value" for setters @@ -149,7 +296,7 @@ void parse_cmd(char *cmd){ char *cmdstart = omit_spaces(cmd), *parstart = NULL; if(!cmdstart) goto retn; char *ptr = cmdstart; - while(*ptr > '@') ++ptr; + while(*ptr > ' ' && *ptr != '=') ++ptr; if(*ptr && *ptr <= ' '){ // there's spaces after command *ptr++ = 0; ptr = omit_spaces(ptr); diff --git a/F1:F103/BISS_C_encoders/spi.c b/F1:F103/BISS_C_encoders/spi.c new file mode 100644 index 0000000..4c83255 --- /dev/null +++ b/F1:F103/BISS_C_encoders/spi.c @@ -0,0 +1,174 @@ +/* + * This file is part of the encoders project. + * Copyright 2025 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "spi.h" +#include // memcpy +#include "usb_dev.h" +#ifdef EBUG +#include "strfunc.h" +#endif + +#define CHKIDX(idx) do{if(idx == 0 || idx > AMOUNT_OF_SPI) return;}while(0) +#define CHKIDXR(idx) do{if(idx == 0 || idx > AMOUNT_OF_SPI) return 0;}while(0) + +spiStatus spi_status[AMOUNT_OF_SPI+1] = {0, SPI_NOTREADY, SPI_NOTREADY}; +static volatile SPI_TypeDef* const SPIs[AMOUNT_OF_SPI+1] = {NULL, SPI1, SPI2}; +static volatile DMA_Channel_TypeDef * const DMAs[AMOUNT_OF_SPI+1] = {NULL, DMA1_Channel2, DMA1_Channel4}; +#define WAITX(x) do{volatile uint32_t wctr = 0; while((x) && (++wctr < 360000)) IWDG->KR = IWDG_REFRESH; if(wctr==360000){ DBG("timeout"); return 0;}}while(0) + +static uint8_t encoderbuf[AMOUNT_OF_SPI][ENCODER_BUFSZ] = {0}; +static uint8_t freshdata[AMOUNT_OF_SPI] = {0}; + +// init SPI to work RX-only with DMA +// SPI1 (PA5/PA6): DMA1_Channel2 +// SPI2 (PB13/PB14): DMA1_Channel4 +void spi_setup(uint8_t idx){ + CHKIDX(idx); + volatile SPI_TypeDef *SPI = SPIs[idx]; + SPI->CR1 = 0; // clear EN + SPI->CR2 = 0; + RCC->AHBENR |= RCC_AHBENR_DMA1EN; + volatile DMA_Channel_TypeDef *DMA = DMAs[idx]; + if(idx == 1){ // PA5/PA6; 72MHz + RCC->APB2RSTR = RCC_APB2RSTR_SPI1RST; + RCC->APB2RSTR = 0; // clear reset + GPIOA->CRL = (GPIOA->CRL & ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6)) + | CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT); + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; + SPI->CR1 = SPI_CR1_BR_0 | SPI_CR1_BR_2; // Fpclk/64 + NVIC_EnableIRQ(DMA1_Channel2_IRQn); // enable Rx interrupt + }else if(idx == 2){ // PB12..PB15; 36MHz + RCC->APB1RSTR = RCC_APB1RSTR_SPI2RST; + RCC->APB1RSTR = 0; + GPIOB->CRH = (GPIOB->CRH & ~(GPIO_CRH_CNF13 | GPIO_CRH_CNF14)) + | CRH(13, CNF_AFPP|MODE_FAST) | CRH(14, CNF_FLINPUT); + RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; + SPI->CR1 = SPI_CR1_BR_2; // Fpclk/32 + NVIC_EnableIRQ(DMA1_Channel4_IRQn); + }else return; // err + // Baudrate = 0b110 - fpclk/128 + SPI->CR1 |= SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_RXONLY; + SPI->CR2 = SPI_CR2_RXDMAEN; + DMA->CPAR = (uint32_t)&(SPI->DR); + DMA->CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_TEIE; // mem inc, hw->mem, Rx complete and error interrupt + spi_status[idx] = SPI_READY; + DBG("SPI works"); +} + +void spi_onoff(uint8_t idx, uint8_t on){ + CHKIDX(idx); + volatile SPI_TypeDef *SPI = SPIs[idx]; + if(on){ + DBGs(u2str(idx)); + DBG("turn on SPI"); + SPI->CR1 |= SPI_CR1_SPE; + spi_status[idx] = SPI_BUSY; + }else{ + SPI->CR1 &= ~SPI_CR1_SPE; + spi_status[idx] = SPI_READY; + } +} + +// turn off given SPI channel and release GPIO +void spi_deinit(uint8_t idx){ + CHKIDX(idx); + DBG("deinit SPI"); + volatile SPI_TypeDef *SPI = SPIs[idx]; + SPI->CR1 = 0; + SPI->CR2 = 0; + if(idx == 1){ + RCC->APB2ENR &= ~RCC_APB2ENR_SPI1EN; + GPIOA->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6); + }else if(idx == 2){ + RCC->APB1ENR &= ~RCC_APB1ENR_SPI2EN; + GPIOB->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_CNF14); + } + spi_status[idx] = SPI_NOTREADY; +} + +int spi_waitbsy(uint8_t idx){ + CHKIDXR(idx); + DBGs(u2str(idx)); + DBG("wait busy"); + WAITX(SPIs[idx]->SR & SPI_SR_BSY); + return 1; +} + +// just copy last read encoder value into `buf` +// @return TRUE if got fresh data +int spi_read_enc(uint8_t encno, uint8_t buf[8]){ + if(encno > 1 || !freshdata[encno]) return FALSE; + DBGs(u2str(encno)); DBG("Read encoder data"); + memcpy(buf, encoderbuf[encno], ENCODER_BUFSZ); + freshdata[encno] = 0; // clear fresh status + return TRUE; +} + +// start encoder reading over DMA +// @return FALSE if SPI is busy +// here `encodernum` is 0 (SPI1) or 1 (SPI2), not 1/2 as SPI index! +int spi_start_enc(int encodernum){ + int spiidx = encodernum + 1; + DBG("start enc"); + if(spiidx < 1 || spiidx > AMOUNT_OF_SPI) return FALSE; + if(spi_status[spiidx] != SPI_READY) return FALSE; + if(!spi_waitbsy(spiidx)) return FALSE; + if(SPI1->CR1 & SPI_CR1_SPE) DBG("spi1 works!"); + if(SPI2->CR1 & SPI_CR1_SPE) DBG("spi2 works!"); + volatile DMA_Channel_TypeDef *DMA = DMAs[spiidx]; + DMA->CMAR = (uint32_t) encoderbuf[encodernum]; + DMA->CNDTR = ENCODER_BUFSZ; + DBG("turn on spi"); + spi_onoff(spiidx, 1); + DMA->CCR |= DMA_CCR_EN; + return TRUE; +} + +// SSI got fresh data +void dma1_channel2_isr(){ + if(DMA1->ISR & DMA_ISR_TEIF2){ + DMA1->IFCR = DMA_IFCR_CTEIF2; + } + if(DMA1->ISR & DMA_ISR_TCIF2){ + //uint32_t ctr = TIM2->CNT; + DMA1->IFCR = DMA_IFCR_CTCIF2; + freshdata[0] = 1; + //encoderbuf[5] = (ctr >> 16) & 0xff; + //encoderbuf[6] = (ctr >> 8 ) & 0xff; + //encoderbuf[7] = (ctr >> 0 ) & 0xff; + } + spi_onoff(1, 0); + // turn off DMA + DMA1_Channel2->CCR &= ~DMA_CCR_EN; +} + +void dma1_channel4_isr(){ + if(DMA1->ISR & DMA_ISR_TEIF4){ + DMA1->IFCR = DMA_IFCR_CTEIF4; + } + if(DMA1->ISR & DMA_ISR_TCIF4){ + DMA1->IFCR = DMA_IFCR_CTCIF4; + freshdata[1] = 1; + } + spi_onoff(2, 0); + // turn off DMA + DMA1_Channel4->CCR &= ~DMA_CCR_EN; +} + diff --git a/F1:F103/BISS_C_encoders/spi.h b/F1:F103/BISS_C_encoders/spi.h new file mode 100644 index 0000000..333af93 --- /dev/null +++ b/F1:F103/BISS_C_encoders/spi.h @@ -0,0 +1,22 @@ + +#pragma once +#include + +#define AMOUNT_OF_SPI (2) + +#define ENCODER_BUFSZ (8) + +typedef enum{ + SPI_NOTREADY, + SPI_READY, + SPI_BUSY +} spiStatus; + +extern spiStatus spi_status[AMOUNT_OF_SPI+1]; + +void spi_onoff(uint8_t idx, uint8_t on); +void spi_deinit(uint8_t idx); +void spi_setup(uint8_t idx); +int spi_waitbsy(uint8_t idx); +int spi_start_enc(int encodernum); +int spi_read_enc(uint8_t encno, uint8_t buf[8]); diff --git a/F1:F103/BISS_C_encoders/strfunc.c b/F1:F103/BISS_C_encoders/strfunc.c index 594a2cb..20850e6 100644 --- a/F1:F103/BISS_C_encoders/strfunc.c +++ b/F1:F103/BISS_C_encoders/strfunc.c @@ -18,14 +18,15 @@ #include #include +#include "usb_dev.h" /** * @brief hexdump - dump hex array by 16 bytes in string - * @param sendfun - function to send data + * @param ifno - number of interface * @param arr - array to dump * @param len - length of `arr` */ -void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){ +void hexdump(int ifno, uint8_t *arr, uint16_t len){ char buf[52], *bptr = buf; for(uint16_t l = 0; l < len; ++l, ++arr){ for(int16_t j = 1; j > -1; --j){ @@ -36,14 +37,14 @@ void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){ if(l % 16 == 15){ *bptr++ = '\n'; *bptr = 0; - sendfun(buf); + USB_sendstr(ifno, buf); bptr = buf; }else *bptr++ = ' '; } if(bptr != buf){ *bptr++ = '\n'; *bptr = 0; - sendfun(buf); + USB_sendstr(ifno, buf); } } diff --git a/F1:F103/BISS_C_encoders/strfunc.h b/F1:F103/BISS_C_encoders/strfunc.h index a31e0bd..44637a2 100644 --- a/F1:F103/BISS_C_encoders/strfunc.h +++ b/F1:F103/BISS_C_encoders/strfunc.h @@ -20,7 +20,7 @@ #include -void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len); +void hexdump(int ifno, uint8_t *arr, uint16_t len); char *u2str(uint32_t val); char *i2str(int32_t i); char *uhex2str(uint32_t val); diff --git a/F1:F103/BISS_C_encoders/usb_descr.c b/F1:F103/BISS_C_encoders/usb_descr.c index ad67b50..6237867 100644 --- a/F1:F103/BISS_C_encoders/usb_descr.c +++ b/F1:F103/BISS_C_encoders/usb_descr.c @@ -15,6 +15,9 @@ * along with this program. If not, see . */ +#include + +#include "flash.h" #include "usb_descr.h" #undef DBG @@ -166,18 +169,28 @@ _USB_LANG_ID_(LD, LANG_US); _USB_STRING_(SD, u"0.0.1"); _USB_STRING_(MD, u"eddy@sao.ru"); _USB_STRING_(PD, u"USB BISS-C encoders controller"); -_USB_STRING_(ID1, u"encoder_cmd"); -_USB_STRING_(ID2, u"encoder_X"); -_USB_STRING_(ID3, u"encoder_Y"); + +// iInterface will change on initialisation by config +#define _USB_IIDESCR_(str) {sizeof(str), 0x03, str} +typedef struct{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString[MAX_IINTERFACE_SZ]; +}iidescr_t; +static iidescr_t iids[bTotNumEndpoints] = { + _USB_IIDESCR_(u"encoder_cmd"), + _USB_IIDESCR_(u"encoder_X"), + _USB_IIDESCR_(u"encoder_Y"), +}; static const void* const StringDescriptor[iDESCR_AMOUNT] = { [iLANGUAGE_DESCR] = &LD, [iMANUFACTURER_DESCR] = &MD, [iPRODUCT_DESCR] = &PD, [iSERIAL_DESCR] = &SD, - [iINTERFACE_DESCR1] = &ID1, - [iINTERFACE_DESCR2] = &ID2, - [iINTERFACE_DESCR3] = &ID3, + [iINTERFACE_DESCR1] = &iids[0], + [iINTERFACE_DESCR2] = &iids[1], + [iINTERFACE_DESCR3] = &iids[2], }; static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){ @@ -246,3 +259,14 @@ void get_descriptor(config_pack_t *pack){ break; } } + +// change values of iInterface by content of global config +void setup_interfaces(){ + for(int i = 0; i < bTotNumEndpoints; ++i){ + if(the_conf.iIlengths[i]){ + iids[i].bLength = the_conf.iIlengths[i]; + memcpy(iids[i].bString, the_conf.iInterface[i], the_conf.iIlengths[i]); + } + iids[i].bDescriptorType = 0x03; + } +} diff --git a/F1:F103/BISS_C_encoders/usb_descr.h b/F1:F103/BISS_C_encoders/usb_descr.h index f364a50..b2778f5 100644 --- a/F1:F103/BISS_C_encoders/usb_descr.h +++ b/F1:F103/BISS_C_encoders/usb_descr.h @@ -62,3 +62,4 @@ enum{ }; void get_descriptor(config_pack_t *pack); +void setup_interfaces(); diff --git a/F1:F103/BISS_C_encoders/usb_lib.c b/F1:F103/BISS_C_encoders/usb_lib.c index 7c6d6b5..2779402 100644 --- a/F1:F103/BISS_C_encoders/usb_lib.c +++ b/F1:F103/BISS_C_encoders/usb_lib.c @@ -401,6 +401,7 @@ void USB_setup(){ USB->BCDR |= USB_BCDR_DPPU; NVIC_EnableIRQ(USB_IRQn); #endif + setup_interfaces(); } diff --git a/F1:F103/BISS_C_encoders/version.inc b/F1:F103/BISS_C_encoders/version.inc index 6b6881f..35655c8 100644 --- a/F1:F103/BISS_C_encoders/version.inc +++ b/F1:F103/BISS_C_encoders/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "19" -#define BUILD_DATE "2025-03-25" +#define BUILD_NUMBER "63" +#define BUILD_DATE "2025-03-26"