Tag Archives: ARM

ARM48: USB HID host

Jei naudojamas STM32 čipas palaiko USB HOST režimą, tai prijungti USB įrenginį labai paprasta. Mano hardwarė labai paprasta- čipas ir dvi USB jungtys. Viena jungtis suprogramuota kaip virtualus COM portas skirta peržiūrėti rezultatus, o kita USB jungtis tai USB host. Čia galima prijungti pelę ir klavietūrą. Kiti HID įrenginiai nepalaikomi.
Primenu, kad HID klavietūra palaiko iki 6 klavišų nuspaudimus (neįskaitant modifikatorius: shift, control, alt…) ir atskirai siunčiami klavišų paspaudimai ir atleidimai.
Kad viskas veiktu, tereikia savo programoje pasirašyti callbacką:

  1. void USBH_HID_EventCallback(USBH_HandleTypeDef *phost)
  2. {
  3. char txt_buf[100];
  4. char t[4];
  5.  
  6. strcpy(t,"[x]");
  7. if(USBH_HID_GetDeviceType(phost) == HID_MOUSE) // if the HID is Mouse
  8. {
  9. HID_MOUSE_Info_TypeDef *Mouse_Info;
  10. Mouse_Info = USBH_HID_GetMouseInfo(phost); // Get the info
  11. int8_t dX_Val = Mouse_Info->x; // get the delta x value (note unsigned - signed conversion)
  12. int8_t dY_Val = Mouse_Info->y; // get the delta y value
  13.  
  14. int len = sprintf (txt_buf, "dX=%d, dY=%d, Button1=%d, Button2=%d, Button3=%d\r\n", dX_Val, dY_Val, Mouse_Info->buttons[0],Mouse_Info->buttons[1], Mouse_Info->buttons[2]);
  15. user_usb_tx((uint8_t *) txt_buf,len);
  16. }
  17. if(USBH_HID_GetDeviceType(phost) == HID_KEYBOARD)
  18. {
  19.  
  20. HID_KEYBD_Info_TypeDef *Keyboard_Info;
  21. Keyboard_Info = USBH_HID_GetKeybdInfo(phost); // get the info
  22. txt_buf[0]=0;
  23. unsigned char i;
  24. for(i=0;i<6;i++)
  25. {
  26. if(Keyboard_Info->keys[i] != OLDKEYS[i] )
  27. {
  28. if (Keyboard_Info->keys[i]==0) {strcat(txt_buf,"UPKEY:"); hex8(txt_buf,OLDKEYS[i]);t[1]=Scan2Char(OLDKEYS[i]); strcat(txt_buf,t);}
  29. else if (OLDKEYS[i]==0) {strcat(txt_buf,"DOWNKEY:"); hex8(txt_buf,Keyboard_Info->keys[i]);t[1]=Scan2Char(Keyboard_Info->keys[i]); strcat(txt_buf,t);}
  30. else {strcat(txt_buf,"ERR:"); hex8(txt_buf,Keyboard_Info->keys[i]); strcat(txt_buf,"-");hex8(txt_buf,OLDKEYS[i]);}
  31. }
  32. OLDKEYS[i]=Keyboard_Info->keys[i];
  33. }
  34. strcat(txt_buf,"\r\n");
  35. user_print_usb( txt_buf);
  36. }
  37. }

Pačiam HID reporte visada transliuojami visi nuspausti klavišai, todėl, jei reikia susiderinti su PS2 ar senesniu standartu, reikia tikrinti, koks klavišas nuspaustas ir koks paleistas. Ta daro ciklas 24-33 eilutėse.
Rezultatas:


UPKEY:12[o]
DOWNKEY:13[p]
UPKEY:13[p]
DOWNKEY:2F[[]
UPKEY:2F[[]
DOWNKEY:30[]]
UPKEY:30[]]
DOWNKEY:31[\]
UPKEY:31[\]
DOWNKEY:04[q]
DOWNKEY:16[s]
DOWNKEY:07[d]
DOWNKEY:09[f]


DOWNKEY:2C[ ]
UPKEY:16[s]
UPKEY:07[d]
UPKEY:09[f]
UPKEY:04[q]
UPKEY:2C[ ]
DOWNKEY:0B[h]
UPKEY:0B[h]

Visas demo softo source code čia: STM32CubeMX USB host HID demo source code.

ARM47 ir PID dalis #4

O dabar pašnekėsim apie STM32F4 serijos variantą ir source code. Pirmiausia- kodėl 4 serija? Todėl, kad tokia PCB pasitaikė po ranka. Ir tikrai ne dėl kažkokio mistinio FPU ar net DSP. Ir dar neaišku, ar mano turimas MCU yra originalas, o ne koks nors permarkiruotas šlamštas. Šaltinis solidus, bet kodėl jie juos išmetė? Reikia surasti kur nors panaudotą procesorių ir palyginti.

Source code yra sugeneruotas su CubeMX programa, kompiliuota su gcc. Ryšiai su kubiko paprogramėm tik per callback. Ir tai tik naudojam USB biblioteką (virtualus COM portas) ir taimerių pertraukimus. ADC skaitom blokavimo režime.

Pats PID skaičiavimas, float variantas visiškai toks pats kaip ir teoriniam variante:

float pid_generic(float measured, float setpoint, float amplif)
{
float output;
float deritative;
float error;
float proportional;
 
error = (setpoint - measured)/10;
proportional = error;
 
integral=integral + error * pid_dt;
deritative = (error - old_error) / pid_dt;
old_error = error;
 
output = (PID_KP * proportional + PID_KI * integral + PID_KD * deritative) * amplif;
 
return output;
}

Atsirado papildomas parametras “amplif” – tai bendro rezultato daugiklis, kaip ir sustiprinimas (ar susilpninimas). Teoriškai tą patį galima atlikti su pagrindiniais parametrais (P, I, D), bet taip lengviau priderinti prie “krosnelės” galingumo: kaitinimas dirba su integer skaičiais, ir PID rezultatas apsiapvalina. Taip prarandam “jautrumą”. Ir dar temperatūros parodymus pasmulkinam- tik dėl koeficientų.

uint32_t CalcTemp(void)
{
uint32_t a;
a=median_filter(adc_read_blocking(ADC_CHANNEL_4));
a=median_filter(adc_read_blocking(ADC_CHANNEL_4));
//a=median_filter(adc_read_blocking(ADC_CHANNEL_4));
// 786 - max t, 0.62V ->1655
// 3529 - kambario t, 2.83V -> 283
return (4096-a)/2; //12 bitų max apverčiam ir pašalinam LSB.
}
 

Dėl ADC blogumo, matuojam kelis kartus, invertuojam (dėl schemotechnikos išėjimas mažėja didėjant temperatūrai) ir pašalinam mažiausią bitą, nes jis rodo kvailystes. Funkcija “median filter” vogta iš interneto. Tai funkcija, kuri teoriškai turi išfiltruoti sporadiškus nukrypimus: jei eina 5, 6, 4, 3, 100 – tai tas šimtas kaip ir ne į temą. Kiek veikia per daug netikrinau. Manau, reikia dar padidinti buferį.

  1. readtemp=CalcTemp();
  2. pwm=PID(readtemp, SETTEMP);
  3. SetPWM(pwm);

O čia pats pagrindinis ciklas, kuris kartojamas sistemingai: 1 – nuskaitom, 2 – paskaičiuojam. 3 – valdom kaitinimą.

Visas likęs source kodas aptarnauja kontrolerio valdymą per terminalą (galima keisti parametrus) ir duomenų išmetimą analizei. Tai tik eksperimentinis variantas- darbiniam trūksta dar visokių apsaugų, normalaus valdymo ir indikacijos. Ir aišku, išėjimas čia PWM, kad tinkama mažam rezistoriui, bet ne tikram šildytuvui. Dar nesugalvojau, kaip padaryti korektiškai proporcinį valdymą realiai rėlei ar 50Hz simistoriniam reguliatoriui. Jei užteks kantrybės, tema bus vystoma.

Pats pilnas STM32F446 PID controller source code skirtas gcc ir kartu CubeMX projekto failas (versija užrakinta posto datai, galimi patobulinimai). Prie papildų- median filter, ftoa ir usb paprogramės. Visa mano kūryba “USER” aplanke.

PID, dalis #3, FLOAT ir STM32F4

Dabar, apjungiant senesnius straipsnius [0, 1, 2], sumontavau kažką panašaus į PID termokontrolerį.

PID controller STM32F4xx

Ant lentos surinkta mano universali White Pill plokštė, tik su STM32F446 procesorium (toks biški itartinas tas procesoriukas, kažko man taimeriai neteisingai veikia). Dešinėje- mano Pt100 termorezistoriaus stiprintuvas. Geltona plokštė, tai kiek pasvilęs RGB LED “stiprintuvas”- galvaniškai atrišti trys galingi MOSFET valdomi per optoporas. Optoporų rezistorių teko sumažinti, kad nuo 3V procesoriuko pilnai valdytusi. O vienas “RGB LED stiprintuvo” kanalų paprasčiausiai valdo vielinį rezistorių kairėje, į kurį įkištas Pt100 jutiklis- tai mikro krosnelė, kurioje bandysim stabilizuoti temperatūrą.

Šiame eksperimente naudojamas source code su “float” kintamaisiais. Nenorėjau iškarto eksperimentuoti su integer matematika, nes dar nežinojau savo “krosnelės” parametrų.

PID įėjimas- ADC, išėjimas PWM. Panaudojau “prabangų”, 16 bitų PWM, nes norėjau platesnio reguliavimo. O su ADC išėjo biški problematiškai- STM32F446 ADC biški gaidiškas (net jo datašytas tai rašo). Nėra jokio tikslumo ir rezultatai vaikšto. Todėl panaudojau matematinį “filtrą” ir kelis nuskaitymus iš karto. Temperatūra matuojama “papūgomis”1 – 1000 vienetų tai kažkur 40℃, o apie 280 tai kažkur kambario. Užduotis- palaikyti 1000 vienetų temperatūrą viduje rezistoriaus.

Pagrindinė bėda- tiek rezistorius, tiek Pt100 žiauriai lėtai veikia. Todėl teko gana ilgai žaisti su koeficientais, ir dar iki optimalumo jie neprivesti.

PID chart

Mėlynas grafikas- rezistoriaus temperatūra. Raudonas grafikas PWM užpildymas. Matosi periodiniai triukšmai tiek temperatūros nuskaityme, tiek išeinančiam signale. Reikia rimtesnio skaitmeninio filtro. Dar kiek palaikius, paskutinius 400+ skaitymų temperatūros vidurkis buvo lygiai 1000 papūgų. Tačiau signalas nesusistabilizavo. Gal per maža PWM reikšmė:

PID PWM

Vos 2% iš viso. Gal vėliau pabandysiu su aukštesne temperatūra. (PWM signalas iš MOSFETO- žemas lygis reiškia, kad rezistorius prijungtas prie žemės ir kaista). Beja, gal ir siauresnis signalas per siauras šiems kiniškiems mosfetams. Pastebėjau, kad prie mažesnių signalų, PWM dingsta iš oscilografo ekrano.

Pratesimas ir veikiantis source code sekančiam straipsnyje.

  1. Tai santykinis matvimo vienetas iš seno multifilmo, kai matavo smauglio ilgį. ↩︎

ARM46: WS2812 RBG LED juostelė kitaip

Prieš septynius metus rašiau apie WS2812 RGB LED juostelę ir kaip ją valdyti su AVR. Naujam projektui kilo mintis panaudoti minėtus diodus pašvietimui. Tingėjau rašyti savo programą LED valdymui, tai pažiūrėjau internetuose kaip rekomenduoja STM32 procikui. Ir pirmieji pavyzdukai buvo su taimeriu, PWM ir pertraukimais. Kažkaip griozdiškai viskas atrodė ir dar mano projekte sunaudoti visi PWM signalai.

RGB LED W2812
(fotkė vogta iš interneto, vėliau pakeisiu)

O gal panaudoti tokius:
LEDas
Turiu visą špūlę tik nežinau kas tai.

Kiek paguglinau giliau ir radau elegantišką sprendimą su SPI. Netgi galimas DMA variantas. Man tereikia valdyti tik 6 diodus, tai DMA nenaudosim. Tiesa, po šio sprendimo lieka SPI CLK koja nepanaudojama. O ir pats SPI sunaudojamas. Principas tame, kad reikiamus signalo intervalus suformuojam su keliais bitais baite. Poto užtenka buferį išpumpuoti per SPI ir WS2812 pilnai viską supranta.
Metodas pilnai vogtas iš interneto, todėl va kopyraitas: “© 2008 – 2022 Martin van der Werff · Groningen, The Netherlands.”. Originalaus puslapio adresas: https://www.newinnovations.nl/post/controlling-ws2812-and-ws2812b-using-only-stm32-spi/.

Failiukai, jei nesinori pačiam spausdinti arba jei originalus puslapis dings: WS2812 SPI gcc C.

ARM45: CAN bus

Iš archyvo ištraukiau “motininę PCB” su nesupjaustytom schemutėm. Tai husqvarnos išmanios žoliapjovės “loop sensor” plokštė. Dabar tas “loop” visiškai nedomina. Prilituojam USB lizdą ir bandom suprogramuoti CAN protokolo siuntėją ir gavėją.
husqvarna loop sensor can usb board

PCB numatytas maitinimas iš CAN jungties, todėl norint maitinti iš USB, reikia pajugti papildomą laidelį. Visą kitą paliekam kaip buvo. Iš aiškių elementų: Texas Instruments TCAN330 čipas ir SPI EEPROM, toliau komparatorius, analoginis multipleksorius ir keletas tranzistorių. Loop ryšiui- ritė laido juodame korpuse.

husqvarna loop sensor can usb board
Perdaryta plokštė jau su ISP jungtimi ir sujungta į grupę per CAN laidus (laidinio telefono kabelis).

USB CAN schema STM32F
Procesoriukas (STM32F302CCT6) turi “hardwarinį” CAN, tai dar ir panaudojus CubeMX sukurtą skeletą darbo nelieka daug. Ryšiui su išoriniu pasauliu nutariau panaudoti USB CDC (serial) įrenginį. Kad viskas veiktu, procesoriaus CLK turi būti kiek aukštesnis nei pagal nutylėjimą sugeneruoja kubikas. Aš pasirinkau 72MHz, maksimumą. Dar, kad nesipjautų pertraukimai, CAN RX1 kiek pažeminau prioritetuose iki “1”.

Apie softą:
CAN siuntimui naudojam HAL paprogramę HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox). TxHeaderyje nurodom kam siunčia ir kokio tipo paketas. TxData- tai max 8 baitai duomenų. TxMailbox- nežinau kas tai, teoriškai “pašto dėžutė” kurioje kabo paketas, kol nėra pristatytas. Kaip jis veikia nežinau ir nesvarbu.

CAN paketo priėmimas kiek įdomesnis. MCU turi kažkiek “hardwarinio” spartinimo, todėl paketų filtrą ir priėmimą galima automatizuoti. Pats priėmimas geriausiai veikia per pertraukimą- gaunam paketą, jį priima ir pakelia vėliavėlę. O toliau jau softas rūpinasi kas bus toliau.
Todėl pirma reikia sukonfiguruoti paketų filtrą:

void can_filter(void) //CAN filtas- tai hardwarinis (softwarinis) paketu filtras, kad duomenys eitu tik reikalingi.
{
CAN_FilterTypeDef canfilterconfig;

canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
canfilterconfig.FilterBank = 10; // which filter bank to use from the assigned ones??? Kodėl?
//canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; //Ziureti kuris yra. Šits kažko neveikė
canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO1; //O šitas jau veikia.
canfilterconfig.FilterIdHigh = CAN_ID_RX << 5;
canfilterconfig.FilterIdLow = 0;
canfilterconfig.FilterMaskIdHigh = CAN_ID_RX << 5;
canfilterconfig.FilterMaskIdLow = 0x0000;
canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;
canfilterconfig.SlaveStartFilterBank = 0; // how many filters to assign to the CAN1 (master can)
// doesn't matter in single can controllers
HAL_CAN_ConfigFilter(&hcan, &canfilterconfig);
}

Čia yra niuansų- CAN_FILTER_FIFO0 ir FIFO1.

Ir kitas niuansas RX pertraukimo Call-back:

void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) //RX interrupto callbackas. Gali buti "Fifo0", zr. filtrą ir galimus int.
{
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO1, &RxHeader, RxData);
CAN_MSG_R=1;
}

Matot “CAN_RX_FIFO1” ir “HAL_CAN_RxFifo1MsgPendingCallback”? Fifo0 ar Fifo1 nėra taip paprastai pasirenkamas, o nuo ko priklauso aš pilnai ir nesupratau. Kažkodėl FIFO0 neveikė. Gal buvo per vėlus vakaras ar panašiai. Bet su FIFO1 viskas veikia.

Kitas momentas- pirma sukonfiguruoji, poto paleidi. Jei reikia perkonfiguruoti- pirma stabdom, poto perkonfiguruojam ir poto paleidžiam:

can_filter(); //pirma filtras, poto start
HAL_CAN_Start(&hcan );
HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO1_MSG_PENDING);

Visas veikiantis demo source kodas: STM32F302 USB CAN writer reader source code.
Ten dar yra windows console programėlė filtro konfiguracijos testavimui (su source code, ne mano).

ARM44: Netikras flešiukas

Norim imituoti USB Flash diskelį? Jokių problemų su STM32F103.
STM CubeMX programoje pasirenkam turimą procesoriuką, įjungiam USB DeviceFS, middleware sekcijoje pasirenkam USB-DEVICE-Mass Storage Class. Sugeneruojam kodą.

Eima į aplanką “\USB_DEVICE\App” ir redaguojam failą “usbd_storage_if.c”:


#define STORAGE_LUN_NBR 1
#define STORAGE_BLK_NBR 0x28000 / 0x200 //Čia mūsų disko dydis padalintas iš sektorių dydžio. Tai mažas diskelis.
#define STORAGE_BLK_SIZ 0x200 //Čia sektoriaus dydis, 512 baitų

kiek žemiau ieškom skaitymo iš disko procedūros:

int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
memcpy(buf, disk + blk_addr * STORAGE_BLK_SIZ, blk_len * STORAGE_BLK_SIZ);
return (USBD_OK);
/* USER CODE END 6 */
}

Tai netikras diskas, todėl skaitysim iš MCU flash atminties, o rašymus į diską ignoruosim. Šioje vietoje paprasčiausiai “memcopy” iš flaš atminties į skaitymo buferį. Atmintis adresuojama baitais, o procedūra užklausinėja sektoriais (blokais). Todėl reikia dauginti iš bloko dydžio.

Toliau reikia sugeneruoti 8″ disketės image. Kodėl? Todėl, kad ji maža ir jos imidžo generatorių galima lengvai rasti internete. Kadangi nemoku su linkeriu prijungti tiesiogiai binarinių failų, tai su savo programa skirta šriftams sugeneruojam C kodą su imidžu.
Diskas tikrai netilps į mažo MCU atmintį, tai reikia kiek pataisyti:


const unsigned char disk[163840]={
0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x57, 0x49, 0x4E, // 00
0x34, 0x2E, 0x31, 0x00, 0x02, 0x01, 0x01, 0x00, // 01
0x02, 0x40, 0x00, 0x40, 0x01, 0xFE, 0x01, 0x00, // 02
...

Sukompiliuojam, supumpuojam į procesoriuką ir:

usb flash disk on STM32F103
Gaunam “Read Only” USB diską. Visus rašymus diskas ignoruoja, todėl Windows per daug nepyksta, nes jai vaidenasi, kad viskas OK. Netgi matom, kad “System Volume Information” susikūrė.

Visas source code (su įžymiuoju penis.jpg):STM32F103 source code for USB mass storage (flash disk)

Kam visą tai naudingą? Ogi, galima prijungti SD diskelį ar SPI flash mikroschemą. Kokius nors duomenis loginti per fatfs į mikroschemą, o kai prisijungiam prie kompo per USB, visi duomenys matosi kaip failai.
O jei mokate gerai programuoti, galima “on the fly” generuoti FAT failų sistemą ir skaityti ar rašyti informaciją. Nereikia jokio softo hoste- visi duomenys matosi kaip virtualūs failai.

ARM43: Parduoto gyvenimo laikrodis

Bevaikščiojant po …lobyną, pamačiau nerusišką matuoklį. Kodėl jis “nerusiškas”? Ogi todėl, kad jis elegantiškas, gražus, matuoja velniai žino ką ir yra ne rūsiškas. 🙂

Skalė matuoja kažką nuo nulio iki dėvynių. Benešant iš sandėlio kilo mintis. Besidalinant mintimis su žmonėm vienas netgi iškarto pasakė- padarai, perku už 100€. Bet ir pačiam mintis patiko.
9h laikrodis
Tai parduoto gyvenimo laikrodis. Kiekvieną rytą, darbo metu jis rodo nuo 9 ir po truputi mažėja iki nulio. Lygiai penktą valandą po pietų, matuoklio rodyklė priartėja prie nulio ir daugiau nekruta iki kito ryto. Nuotraukoje jis toks nučiupinėtas, nes ką tik pagamintas ir nenuvalytas.
Continue reading →

ARM42: 4 lempų NIXIE laikrodis

Tai keturių lempų NIXIE laikrodžio “skeletas”. Laikrodis naudoja hardwarinį RTC. PWM šviesumo valdymas, trys skaitmeniniai mygtukai, vienas analoginis (potenciometras, fotorezistorius), USB-COM jungtis laikrodžio nustatymui (sinchronizavimui). Maitinimas 9-15V. Dinaminė indikacija, vienas dešifratorius (155id1), lempas junginėja MPSA42/MPSA92 tranzistoriai. Šis modelis dabar naudoja apie 2W galios. (kogero LEDai daug naudoja 🙂 )

4 nixie clock STM32F103 RTC
Dizainas bus kuriamas kitų žmonių. Čia tik sumestos detalės, kad patikrinti elektrinę dalį ir ištestuoti programinę įrangą. Jei dizaino autoriai pasidalins galutiniu produktu, nuotraukas įdėsim.

Programos skeletas- tai minimumas programos kuris jau veikia, tačiau nepriprogramuota visokių “custom” dalykėlių kurių tikriausiai reikės galutiniam projektui.

STM32F103 RTC NIXIE clock source code, ir STM32CubeMX projektas. Kompiliuojasi su ARM gcc t.y. visiškai nuo platformos nepriklausoma C kalba.