ARM:0010 STM32CubeMX- USB 2 COM dongle

Taigis. Karantinas ir po gana daug dienų degradacijos nutariau dar kiek pasinagrinėti tą MX kubą ir pagaminti USB į serial adapterį. Taip jau gavosi, kad prigriebiau kažkada tokių PCB su STM32F102 procesoriuku. Kas tai buvo originaliai aš nežinau. Tačiau čia yra “košerinis” USB ir RS485/422 išvadas. Todėl eksperimentams kaip tik:

STM32F102 as USB to UART/COM dongle
Prigriebiau tik tiek. O buvo dešimtim. Tokiom nesulaužytom plokštėm. Per vidurį išvesti programavimo ir kitokie signalai. Ant popieriaus parašytas pinoutas. Todėl ten pajungiam STLink programatorių. O su raudonu ir žaliu laideliu uždarom serial porto kilpą. Ten diferencialinis signalas, todėl du laidai- pliusinis ir minusinis.

Taigis pradedam…

Pasileidžiam STM32CubeMX programą ir pasirenkam turimą procesorių: STM32F102C8T. Ir pradedam:

STM32F102 as USB2COM dongle USART port serial USB
Naudosim UART. Mano išvadas prijungtas prie USART2. Todėl jį ir pasirenkam. Dvi kojelės pažaliavo.

STM32F102 as USB2COM dongle USART port serial USB
Naudosim USB, devaisą. (kito varianto kaip ir nėra). Dar dvi kojos.

STM32F102 as USB2COM dongle USART port serial USB
Mano turimoje PCB yra RS485 draiveris. Jo darbas valdomas dviem kojom. Todėl jungiam du GPIO. Ir iškarto įjungiam tą mikroschemą- viena koja turi būti žemo lygio, kita- aukšto. Dar dvi kojos.

STM32F102 as USB2COM dongle USART port serial USB
Kad lengviau programuoti, įjungiam serial debug. (Aš pirmam straipsnyje pamiršau tai paminėti. To nepadarius, kad programuoti, reikia spausti RESET ir poto paleidus STlink, atleisti. Nepatogu.). Dar dvi kojos.

STM32F102 as USB2COM dongle USART port serial USB
Pasirenkam CLK šaltinį- 8MHz kvarciuką. Dar dvi kojos. Beja, raudonas rutuliukas prie clock configuration ispėja, kad ten viskas blogai.

STM32F102 as USB2COM dongle USART port serial USB
Kadangi nieko nesuprantam apie USB, tai paprašom kubiko sugeneruoti visą USB kodą. Pasirenkam CDC klasę- tai virtualus COM portas.

STM32F102 as USB2COM dongle USART port serial USB
Aš norių programą padaryti tokią, kad viskas veiktu per pertraukimus. Todėl čia pasirinkau, kad USART2 generuotu pertraukimus. Nesu tikras, bet manau, kad taip reikia. Iš esmės su tuom kubiku gana sunku suprasti kaip veikia. Užsienio forumuose arba nagrinėjami kokie sudėtingi reikalai, arba jei klausiama kas nors žalio, dažniausiai arogantiškai pasiūloma skaityti fucking manualą. Arba atsakoma ne į temą, ar siūloma daryti viską kitaip. Dokumentacija nėra labai suprantama, o aš mėgstu daryti iš veikiančių pavyzdukų. Todėl ir rašau šitą straipsniuką- jei pačiam reikės prisiminti kaip tai daroma, pasiskaitysiu. Brain dump.

STM32F102 as USB2COM dongle USART port serial USB
Paspaudus clock configuration, programa pasisiūlo viską sutvarkyti pati. Taigi ir sutinkam. Svarbu, kad 8MHz tai pagal default pas šį procesoriuką.

STM32F102 as USB2COM dongle USART port serial USB
Visaip pamirguliavus programa išduoda savo clock configuration variantą. Manau ta animacija tik dėl grožio. 🙂

STM32F102 as USB2COM dongle USART port serial USB
Užpildom projekto pavadinimą, pasirenkam toolchain ir spaudžiam generuoti kodą ir galim uždaryti programą.

Dabar prasideda baisioji dalis. Einam į “src” folderį, pasileidžiam teksto redaktorių ir verkiam. Tiksliau rašom oldskūlinį kodą. “main.c” faile… nerašom nieko. Viskas gi groja per pertraukimus, o kojeles sukonfiguravom su kubiku. Todėl einam į pertraukimų failą “stm32f1xx_it.c”.

Apsirašom savo milžinišką, vieno baito, cirkuliarinį buferį (!) 🙂

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t baitinisbuferis;
/* USER CODE END PV */

Scrolinam žemyn, kol randam USART pertraukimą:

/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */

/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
HAL_UART_Receive_IT(&huart2, &baitinisbuferis,1); //vieno baito buferis, uzsakom interupta. Nes uzsakymas vienkartinis, sunaudojus, reikia vel uzsakyti.
/* USER CODE END USART2_IRQn 1 */
}

Čia vienas fintas. HAL_UART_Receive_IT iš esmės tik užsako pas HAL’ą- tipo kai gausi iš UARTo vieną baitą, pranešk. Tačiau, kai HALas praneša, jis pats naujo pranešimo nebeužsako. Todėl atlikus globalų pertraukimą, reikia užsakyti naujo baito pristatymą. Aišku galima tą padaryti baito apdorimo vietoje, bet tada reikia pirmą užsakymą kažkur kitur paleisti. Ir dar nežinau, ar užsakymas neanuliuojamas jei įvyksta kokia klaida. Žodžiu taip veikia.

O toliau totali mistika. Užsakyto baito analizės programa vadinasi “HAL_UART_RxCpltCallback” (ir kas sugalvojo) ir jos paprasčiausiai nėra. Ją reikia parašyti:

/* USER CODE BEGIN 1 */
/* programulka kuri paleidziama kai musu uzsakytas baitas atvyksta.
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART2)
{
CDC_Transmit_FS(&baitinisbuferis,1); // ka gavo, ta ir issiunciam
}
}
/* USER CODE END 1 */

Aišku, kompileris pyks dėl “CDC_Transmit…” funkcijos. Aš nemoku savo gcc išaiškinti visos programos struktūros. Kompiliuojasi ir taip, bet kad neužknistu pranešimai, parašom:

/* USER CODE BEGIN Includes */
#include "usbd_cdc_if.h"
/* USER CODE END Includes */

Visas šitas tekstas daro labai paprasta darbą: jei per UARTą gaunam baitą, tai HALas tą baitą priima, poto iškarto perduoda į USB CDC buferį ir laukia naujo baito. Ši programos dalis atlieką COM->USB funkciją. Dar reikia aprašyti USB->COM funkciją. O tai žymiai paprasčiau. Susirandam “usbd_cdc_if.c” failą. Čia jau USB priėmimo ir kitos funkcijos. Beja, čia jau mūsų naudota siuntimo funkcija parašyta.

Pirmiausia, kad kompileris netriestu, reikia parašyti, kad męs naudojam globalinį kintamąjį iš kitos vietos:

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
extern UART_HandleTypeDef huart2; //va cia tikrai nezinau kaip reikia daryti, kad imtu globalini huart2
/* USER CODE END PV */

Dabar prasideda nedokumentuota mistika skirta… Windows operacinei sistemai ir Putty programai. Gal kitos OS ir programos nėra tokios priekabios, bet be šito reikalo, paprasčiausiai atsiras virtualus COM portas, bet bus Putty klaida, kad negali konfiguruoti ir rašyti į tą portą. Tačiau device manageryje tas portas bus!
Pasirodo, Putty programa pasitikrina porto veikimą sukonfiguruodama tą portą ir poto nusiskaito konfiguraciją. Mums reikia suorganizuoti tokių užklausų apdorojimą ir teisingų atsakymų formavimą. Netgi jei jums nereikia konfiguruoti COM porto.

Beja, išnaša: Daug kas (ir internete) nesupranta kam reikalingas virtualaus porto greitis, bitai, parity ir panašiai. Dažniausiai to ir nereikia, nes duomenys neina per tikrą serial portą, o paprasčiausiai COM portas yra kaip abstrakcija palengvinanti programos rašymo darbus. Tačiau, jei yra fizinis COM portas, šie konfiguraciniai skaičiai naudojami. Naudojant virtualaus porto CDC komandą, procesoriukas turi sukonfiguruoti realų USARTą norimiems parametrams. Mano pavyzdyje aš norėjau keisti tik perdavimo greitį.

Todėl darom taip:
Susikuriam buferį kuriame laikysim porto parametrus:

/* USER CODE BEGIN PRIVATE_VARIABLES */
//cia kaip ir buferis, nes windows nori ne tik konfiguruoti porta, bet ir paziureti ar jam pavyko.
USBD_CDC_LineCodingTypeDef linecoding=
{ 115200, /*greitis */
0, /*stop bitai */
0, /*parity */
8 /* bitai */
};

Skrolinam žemyn iki konfiguracinių komandų apdorojimo funkcijos:

static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
{
/* USER CODE BEGIN 5 */
switch(cmd)
{
... ...
case CDC_SET_LINE_CODING:
//cia kompas siuncia UARTo konfiguracija.
linecoding.bitrate= (uint32_t) ((pbuf[0]) | (pbuf[1]< <8) | (pbuf[2]<<16) | (pbuf[3]<<24)); linecoding.format =pbuf[4]; linecoding.paritytype =pbuf[5]; linecoding.datatype=pbuf[6]; // Realaus COM porto perkonfiguravimas: HAL_UART_DeInit(&huart2); //atjungiam huart2.Init.BaudRate=linecoding.bitrate; HAL_UART_Init(&huart2); //ijungiam per naujo break; case CDC_GET_LINE_CODING: //o cia windows ir putty nori nusiskaityti parametrus ir suzinoti ar sukonfiguravosi. pbuf[0]=(uint8_t) (linecoding.bitrate); pbuf[1]=(uint8_t) (linecoding.bitrate >>8);
pbuf[2]=(uint8_t) (linecoding.bitrate >>16);
pbuf[3]=(uint8_t) (linecoding.bitrate >>24);
pbuf[4]=linecoding.format;
pbuf[5]=linecoding.paritytype;
pbuf[6]=linecoding.datatype;

break;
... ...

Dabar putty tikrai nepyks ir veiks. Pakeliui dar galima pakeisti perdavimo/gavimo greitį.

Nu ir liko visai nedaug. Skrolinam žemyn iki tos vietos, kur yra duomenų gavimas iš USB:

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
HAL_UART_Transmit_IT(&huart2,Buf,sizeof(Buf)); //ka gavom per USB, ta dedam i UARTa.
return (USBD_OK);
/* USER CODE END 6 */
}

Čia paprasčiausiai įterpiam duomenų išsiuntimą per HALo UART pertraukimo procedūrą.

Nepatikėsit. Viskas! Jei kas neveikia, va jums:
Visas source code ir kiti reikalingi ir nereikalingi failas susikurti USB 2 COM dongle and STM32F10x MCU. Taip pat ir STM32CubeMX failas.

Suprogramuojam PCB ir per putty matom rašomų raidžių “echo”, o su oscilografu galima pamatyti, kaip keičiasi perdavimo greitis.

DĖMESIO! Tai tik demo programos versija. Ji netikrina nei UART nei USB būklės, todėl prie normalių darbo sąlygų, galimas nekorektiškas programos veikimas.

2 replies on “ARM:0010 STM32CubeMX- USB 2 COM dongle”

Leave a Reply

Your email address will not be published. Required fields are marked *