Monthly Archives: April 2021

ARM21: STM32-USB-CDC ir hardware handshake

Bežaidžiant su savadarbiu Atari kilo mintis iki galo iššifruoti kaip visdėlto persiduoda senoviniai RS232 būklės signalai į host kompiuterį. O pasirodo, čia didelė bėda. Daugelis interneto puslapių dažniausiai sako, kad tie visi RI, CTS, DSR, CD, DTR ir RTS jums nebus reikalingi “ir esamo straipsnio lygyje nebus padaryti”. Šūdeliai tie autoriai. Todėl, kiek pavargau ir po gabaliuką surinkau informaciją apie tai. Pakeliui STM32CubeMX gamintojai įpaišė eilinį sisteminį atnaujinimą ir su 1.8.0 versija viskas veikia, o su 1.8.3 kažkai biški neveikia ir biški veikia. Todėl pavyzdukinis softas kompiliuojasi su 1.8.0 firmwarės biblioteka.
Visi linijos kontrolės signalai iš įrenginio (device) į host eina per atskirą IN✻ endpointą (interrupt). Jis pagal nutylėjimą sukuriamas CDC pavyzdyje, bet nenaudojamas. Signalai iš host į įrenginį eina per standartinį komandinį (0 – nulinį) endpointą. Standartiniam kubo softe ten biški net padaryta.
Vienintelis signalas CTS niekaip negaminamas per USB, jo būklę nusprendžia host draiveris pagal esamą situaciją. Beja, net keli interneto šaltiniai rašo, kad windows standartiniai draiveriai to nemoka daryti ir iš viso ten bėda. Todėl visokie “hardwariniai” USB-COM adapteriai turi savo draiverius.

Biški prirašiau miglos? 🙂 Gerai- paprasčiau. RS232 būklė perduodama 7 bitais, per CDC status, per dedikuotą IN interrupt endpointą:

// 7 reserved 0.
// 6 bOverRun - received data has been discarded due to overrun in the device.
// 5 bParity - parity klaida, parity error
// 4 bFraming - framing error
// 3 BRingSignal =RI - ring signalas, vienas iš nepriklausomu RS232 signalų
// 2 bBreak - tai BREAK komandos statusas. Kai siunčiama BREAK komanda, RS232 siuntimas stabdomas, o duomenų linija užkeliama high.
// 1 bTxCarrier =DSR, data sender ready ar panašiai. Šitie du signalai užsikeldavo, kai modemas susijungdavo su kitu modemu.
// 0 bRxCarrier =DCD

Tokia informacija eina iš įrenginio. Tuo tarpu host gali siuntinėti daugiau komandų (nes CDC standartas tai ne tik COM portas). RS232 aišku naudoja greičio, stop bitų, parity valdymo signalus. Tai puikiausiai padaryta demonstraciniam kubo softe (komandos CDC_SET_LINE_CODING ir CDC_GET_LINE_CODING). Komanda sukurianti BREAK irgi lengvai randama (CDC_SEND_BREAK).
Lieka valdymo komanda CDC_SET_CONTROL_LINE_STATE, kurios paleisti aš ilgai negalėjau. Ten iš esmės tik du bitai iš 16:

//bitai: 15:2 - 0.
//bitas 1- carrier control. 0- deactivate carrier, 1- activate.
//bitas 0- DTR bukle, 0-not present, 1- present.

Tik kur tie bitai paslėpti? Pasirodo, kad USB CDC standarto kurėjai nutarė juos paslėpti kiek kitoje vietoje- wValue, o ne duomenų bloke. Kodėl? Tikriausiai, kad užsiknistum. Kubo kūrėjai irgi pasistengė viską paslėpti toje procedūroje kur viskas labai elegantiškai surašyta. Tačiau jei pakrutintume vienos struktūros prikabinta substruktūrą, tai joje ir rastume tą reikšmę. Aš tikrai nesuprantu struktūrų ir kaip iš jų traukiami duomenys, tai padariau taip, kaip nei vienam source kode nemačiau:

wValue=hUsbDeviceFS.request.wValue;

Ir čia randam tuos du bitus, kurie konkrečiam projekte nereikalingi.

Einam atgal prie būklės siuntimo per INT IN endpoitą. Kubo softo kūrėjai nenumatė duomenų perdavimą vartotojui per endpointus. Nu gal numatė, bet per žemesnio lygio procedūrą:
USBD_LL_Transmit(&hUsbDeviceFS, endpointo numeris, duomenų buferis, ilgis duomenų);
Alia, ką ten reikia siuntinėti nelabai ir rašo. Vargom vargom ir radom paketo aprašą:

buf[0]=0b10100001; //request type (0xA1). Nu tokis skaičius rodo, kad męs norim kažką pranešti CDC.
buf[1]=0x20; //notification SERIAL STATE, o čia kad pranešam COM porto būseną.
buf[2]=0; //wValue
buf[3]=0; //wValue
buf[4]=0; //wIndex - interface number, 16 bit, LSB
buf[5]=0; //wIndex - nes gali būti keli COM portai ant to pačio USB. MSB.
buf[6]=2; // Čia pranešimo ilgis. Ne paketo ilgis, o "svarbių" duomenų. LSB.
buf[7]=0; // Čia irgi ilgis. Viso du baitai. MSB.
buf[8]=status; // Šitie svarbūs. LSB pirmiau
buf[9]=0; // o šitie visada nuliai.

Šitą masyvą išsiunčiam:

USBD_LL_Transmit(&hUsbDeviceFS, 0x82, buf,10);

..ir? Nifiga neveikia. Tiksliau veikia dalinai. Kodėl? Ogi todėl, kad kubo softo kūrėjai nusprendė, kad “control endpointui” užtenka aštuonių (8) baitų. O šio paketo ilgis- 10. USB Shark ir parodo, kad eina 8 baitai mūsų, ir du randominiai. Todėl einam į “usbd_cdc.h” failą ir taisom:

#define CDC_CMD_PACKET_SIZE 10U /* Control Endpoint Packet size */

Va dabar, jau kažkas pradeda veikti. Paleidžiam softą skirta Atari diskų emuliacijai ir … neveikia. Be “flow control” veikia, su bet kokiu “flow control” trukinėja ryšis. Kol kas nežinau kur bėda. Vienas svarbus momentas, kad USB labai jau “asinchroniškai” nusiskaito būklę. Kartais labai vėluoja. Nes principas toks- “ei! Kompiuteriau, pasikeitė laidų būklė. O kompas po kažkiek laiko- gerai, gerai, jau supratau”. Ar tai MS draiverio bėda, ar mano, aš dar nežinau.

Nu ir pats pilnas softas:
ATARI 1088XEL SMD on board USB firmware for STM32F103, full source code and compiled hex and binary.

Ir kam buvo neįdomu ar nesupratot, va viena iliustracija:
Atari disk emulator with STM32F103
Tikrai veikia.

✻) USB endpointų kryptis visada rašoma iš kompiuterio, hosto pusės. Visi signalai iš kompo yra “OUT”, visi signalai į kompą yra “IN”. Ir nesvarbu kas iššaukia perdavimą. Taip padaryta todėl, kad USB yra vienkryptis (pagal hierarchiją) protokolas. Tuo tarpu senovinis COM yra lygiavertis. Kartais susibalamutinasi protas, kai duomenys iš MCU eina į IN endpointą, o gaunami per OUT. Pas COM dažnai susipainioja laidai, nes TX sujungtas su RX. Pas kokį modemą RX pavadintas TX ir laide sujungta RX-RX. Taip darosi painiava.

HDMI (DVI) Testeris arba mano pirma ketursluoksnė

Buvo nuo senų laikų prikaupto Cikloninio šroto. Viena bėda- BGA korpusas. Tačiau laikai keičiasi, ketursluoksnės PCB jau pigios ir iš garažo buvo ištrauktos mikroshemos. Plokštė gavosi su viena rimta klaida, bet šiaip viskas veikia. Reikia pagirti Alteros mikroschemos tvirtumą. Ji buvo lituota fabrike, poto aš nulupau. Poto bandžiau reballinti, poto dar bandžiau, poto dar bandžiau. Poto prilitavau, bet ne iki galo. Poto dar kaitinau ir tapšnojau, kad rutuliukai susijungtu su PCB. Ir mikroschema išlaikė šiuos išbandymus.
Šis ciklonas turi PLL ir turi LVDS išėjimus. Kaip tik išbandyti HDMI (DVI) interfeisui:
HDMI test generator on single FPGA chip
Čia mano kiniško monitoriaus ekrano nuotrauka. Signalas eina tiesiai per HDMI iš FPGA. Matosi viena problema- eilutėje su vertikaliais brūkšneliais, pats kairinis turi būti irgi dryžuotas. Dabar neaišku, ar čia kaltas mano projektas arba visdėlto kiniškas upscaller. Mano “generatorius” išduoda VGA (640×480@60Hz, 25MHz pixel clock) vaizdą, o kiniškas žaislinis monitorius lygtai FullHD.
Continue reading →