Mano šviesų tinkle prasidėjo keisti gliukai. Iš pradžių galvojau, kad mano nauji firmwarių eksperimentai kalti. Paskui pasirodė, kad nebuvau įdėjęs į firmwares apsaugų, jei CAN žinutės išsiuntimas užvėluoja. Paskui dar pasirodė, kad kai kurie tinklo mazgai iš viso „išsijungia“ (bus-off būsena). Atradau, kad galima liepti valdikliui iš bus-off išlipti pačiam. Bet kodėl tai iš viso prasidėjo? Juk pusę metų buvo viskas gerai. Ir štai, prieš kelias dienas mano šviesų tinklas iš viso dingo iš eterio. Gal susiję su pražaibavusia audra, o gal ir ne.
Pasirodė, kad greičiausiai nusvilo Avietės Canberry skydelis 😦 Pabandžiau ir tą pačią Avietę, ir atsarginę su skydeliu, pabandžiau sujungti ir į kitą tinklą (o gal kiti mazgai durniuoja, ką aš žinau). Visgi kaltė liko Avietės pusėje, įvyko ten turbūt mikroschemoj gedimas.
Skydelį turiu tik vieną — jie ganėtinai brangūs. AliExpress galima nusipirkti paprastesnių ir visai pigių adapterių, tik jie ant Avietės neužsideda, reikia laidukais prijungti, kvarcą pakeisti ir dar ten kitokių hakų padaryti. Nepatogums biškį. Na, bet vis tiek užsisakiau. Biškį liūdesys, aišku, kad negaliu šviesų tam kartui telefonu valdyt.
Kilo mintis: o gal pasidaryt per USART adapterį į CAN? Spausdintų sau žinutes arba priimtų nurodymus, kokias išsiųst? Na, bet reikėtų kodyti ir firmwarę, ir Avietės serviso pusę.
Tuo pačiu truputuką pasibrauzijau aplink, kas gero yra apskritai CAN adapterių pasaulyje. Gal koks CAN-USB yra? Ir netyčia užsiroviau ant Lawicel CAN232 adapterio. Pasidarė juokinga: tas adapteris įgyvendina būtent mano sugalvotą principą: verčia CAN žinutes į apsibrėžtą ASCII HEX tekstą ir siunčia jį per USART (tiksliau, netgi RS232) į kompiuterį. Taip pat per USART priima tekstą ir jį paverčia CAN žinute. Yra ten ir daugiau navarotų, bet šitas pagrindinis. O Linux kernelis turi modulį šitam reikalui ir gali sukurti ant jo virtualų CAN tinklo adapterį. Va čia tai bajeris.
Pasiieškojęs radau ne tik Arduino, bet ir tiesiogiai STM32F103 procesoriukams sukurtos progaminės įrangos, įgyvendinančios Lawicel protokolą (dar vadinamą slcan). Protokolas yra atviras. Patys Lawicel netgi negamina jokių draiverių: štai jums mūsų divaisas, o komandų parsinimą ir gamybą pasidarykit patys, kaip jums patogiau.
Kol laukiu MCP2515 adapterių iš kinų, pagalvojau, kad turėti atsarginį variantą per USART gal ir visai neblogai. Tik tiek, kad internete gulinčių adapterių kodas nelabai patiko. Kai kas lengvai pataisoma (pvz. žiedinio buferio rodyklės postūmis per dalybą — net purto pamačius), kai kas kiek sudėtingiau (uber greitas bitukų vertimas į hex tekstą), tad nusprendžiau remdamasis esamu kodu pasirašyti savo variantą. Be abejo, išnaudoti DMA, net ir žinučių kopijavimui į laikiną žiedinį buferį.
CAN adapterio idėjos:
- Kodas turi būti labiau greitas, nei kompaktiškas: 1 Mb/s tinkle tai svarbu.
- 20 kB RAM: maksimaliai išnaudoti lookup lentukes visoms konvertavimo operacijoms.
- Naudoti DMA spausdinimui į USART, atlaisvinti procesorių.
- Naudoti DMA žinučių kopijavimui į žiedinį buferį apdorojimui.
- Automatinis atsistatymas bus-off atveju (ABOM).
- Jei paveš, naudoti nestandartinį USART baud rate — ~1 Mb/s.
- Įgyvendinti tik reikalingą Lawicel protokolo dalį.
Visas Lawicel protokolas aprašytas šioje nuorodoje į PDF failą.
Truputukas teorijos apie žiedinius buferius
Tai yra tiesiog masyvai, kuriuose įrašai įdedami vienas po kito. Kai pasiekiama masyvo pabaiga, jis pradedamas pildyti nuo pradžių. Kodijant paprastuoju būdu reikia patikrinti ar jau pasiekta ta pabaiga ir einamojį indeksą „nunulinti“. Bet yra ir kitų metodų. Jeigu žiedinio buferio narių skaičius yra dvejeto laipsnis (2, 4, 16, 32, 64 etc.), tai indeksą galima nesukant galvos tiesiog didinti vienetu ir imti jo liekaną nuo buferio dydžio. Čia navarotas tame, kad jei indeksas po padidinimo tampa didesnis už buferio dydį, liekana „nusinulina“. Sakykim, 64 elementų buferiui tai bus šitaip:
i = (i+1) % 64;
Kai i pasieks 64, jis dėl liekanos taps vėl nuliuku.
Bet taip yra baisiai blogai. Nes liekana (mod operacija) yra vis tiek dalyba. O dalyba procesoriuose yra viena iš lėčiausių operacijų, paprastai įgyvendinama programiškai. Mikrovaldikliuose ypač neparanki. Man keista, bet programuotojai dažnai patingi apsirašyti antrą konstantą, kuri yra vienetu mažesnė už žiedinio buferio talpą. Nes tada galima išsisukti be dalybos, su and operacija, kuri yra vieno procesoriaus takto greičio. Ir tada mūsų indeksas bus skaičiuojamas šitaip:
i = (i+1) & 63;
Nes štai čia yra ta pati mod operacija, tik kiek kitaip. Tik nepamirškite: buferio elementų kiekis turi būti dvejeto laipsnio! Beje, šią matematiką galima pritaikyti gausybei ir kitų atvejų, kad ir laikmačio persivertimui įvertinti, skaičiuoti periodus ir panašiai — jei tik dvejeto laipsnis tinka.
Kitas labai galingas navarotas su dvejeto laipsniu yra neapdorotų elementų skaičiaus radimas. Žiediniame buferyje minimaliai reikalingi du indeksai: paskutinio įrašyto elemento ir paskutinio apdoroto. Kai šie indeksai lygūs, buferis laikomas tuščiu. Kai įrašyto elemento indeksas didesnis už paskutinio apdoroto — buferyje kažkas yra. Deja, šito skirtumo apskaičiavimas pasidaro problema, kai buferio indeksas „persiverčia“ ir buferis vėl pradeda būti pildomas nuo pradžios, o tuo tarpu jo tuštinimas dar „nepersivertė“. Šioje vietoje galima sužaisti su tuo, kaip kompiuteryje atvaizduojami neigiami skaičiai. Svarbu, kad indeksų tipai būtų unsigned ir tada neapdorotų elementų skaičius bus toks:
(write_pos - read_pos) & 63
Vėlgi and operacija, „nuvalanti“ nereikalingus bitukus ir netgi neigiamą rezultatą paverčianti reikalingu skaičiumi.
Na, tai šitą matematiką aš naudosiu. Ji efektyvi ir greita.
Spartus konvertavimas į HEX ir atgal
Lawicel protokolas CAN žinutes verčia šešioliktainiu tekstu. Tai labai gerai, nes šešioliktainė sistema yra visiškai tiesiogiai atvaizduojama į dvejetainę ir atvirkščiai.
Tad, sakykim, naudoti kokį nors sprintf verčiant skaičiukus į šešioliktainius yra visiškai bereikalingas procesoriaus apkrovimas. Visos printf šeimos funkcijos yra „sunkios“ ir net geriausi kompiliatoriaus optimizavimai čia nepagelbės. Kadangi atminties yra daug, tai pats greičiausias tiesioginio atvaizdavimo metodas, be abejo, yra lookup lentukės: masyvai, kurių indeksas yra konvertuojamas skaičius, o reikšmė — atvaizdavimo simbolis. Versti skaičiuką į šešioliktainį galima su šešiolikos narių lentele:
char bin2hex[16] = { '0', '1', '2',... 'E', 'F' }; ... str++ = bin2hex[byte << 4]; str++ = bin2hex[byte & 15];
Tikiuosi, čia aišku, kame reikalas. Imamas baitas, kurį reikia atvaizduoti, kaip HEX eilutę, su nuliuku priekyje, jei skaičius mažesnis už 16. Na, “%02X“ printf analogas.
Tačiau yra įmanomas ir dar greitesnis variantas. Galime naudoti ne 16 baitų lookup lentukę pusbaičiui, o dvi 256 baitų lentukes visam baitui. Aišku, čia jau biškį „pereboras“, bet tokį variantą galima turėti galvoje, jei prireiktų mirtinai optimizuoti ir sutaupyti dar porą procesoriaus taktų. O taupant atmintį galima panaudoti paprastą sudėtį ASCII simbolių generavimui, bet reikia papildomo tikrinimo, nes ‘A’ neina iš karto po ‘9’, yra skylė. Todėl pagal lentukę visgi greičiau. Vertimas atgal kiek problematiškesnis, bet galima pasidaryti tiesiog didesnį masyvą su septyniais tarpiniais tuščiais nariais (nes tiek yra kitų simbolių tarp ‘9’ ir ‘A’):
uint32_t hex2bin[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Čia simboliai, esantys tarpe tarp '9' ir 'A' 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
Ir tada šešioliktainis skaičiukas konvertuojamas iš teksto tiesiog atėmus ‘0’ ASCII kodą.
Buferizavimas
Galvojau kurį laiką, kiek ir kokių buferių reikės, bei kaip jie „santykiaus“. Kad tiek CAN, tiek USART magistralės būtų kuo daugiau laiko laisvesnės, mintis gavosi maždaug tokia:
- CAN žinučių buferis, 64 vienetams. Ateina žinutė ir DMA ją nukopijuoja į einamąjį buferio elementą. Pertraukime duodama komanda, o kai DMA atlieka savo darbą — įvyksta kitas pertraukimas.
- HEX formato žinučių buferis. Atskira paprogramė žiūri, kada CAN buferyje atsiranda nauja žinutė. Tada ją performatuoja ir padeda į HEX buferį.
- Kita paprogramė pasižiūri, kada HEX buferyje atsiranda tekstas, tada jį paima ir per USART išspausdina. Tiksliau, duoda komandą kitam DMA kanalui, kad spausdintų ir toliau dinderį muša.
Nuskaitymui panašiai, bet paprasčiau:
- Vienas baitų buferis nuskaitymui, kas per USART ateina, iki ‘\r’ simbolio. Jei buferis persipildo, tai jį išvalo, cypteli ‘\a’ simboliuku.
- Radus ‘\r’ užsukamas parseris, kuris analizuoja, kas čia atėjo. Tiesa, ‘\n’ irgi priima, kaip komandos pabaigą, dėl visa ko.
- Jei parseryje randama žinutė, ji, baigus parsinimą padedama į CAN FIFO išsiuntimui. Tiesa, FIFO rimtai nenaudoju, tik vieną mailbox’ą. Kada nors paspartinsiu iki visų trijų mailbox’ų naudojimo.
- Parseriui baigus darbą nuskaitymo buferis išvalomas ir vėl laukiama, kas įbyrės į vidų.
USART greitis
Mano CAN magistralė sukasi maksimaliu 1 Mb/s greičiu. USART, tuo tarpu, nėra toks jau spartus, nors sako, kad FT232RL mikroschema iki 4,5 Mb/s pavaro. Nemačiau tokių greičių 🙂
Maksimali sparta, per kurią mano kompiuteris ir STM32F103 procesoriukas per FT232RL mikroschemą sugebėjo susikalbėti, gavosi 460800 bodų. Teoriškai įtraukus CTS, RTS ir CK kojytes, t.y. vietoj paprasto RX/TX UART’o padarius tikrą USARTą sparta tikrai turėtų pakilti, bet kadangi CAN adapteriui naudojau vieną atlikusią plokštę (ir ne visai tam skirtą), tai šių kojyčių neišbandžiau. Lieka ateičiai.
Įgyvendinimas
Taigi per kelis vakarus susikurpiau firmwarę, kuri įgyvendina dalį Lawicel protokolo:
- Priima ir siunčia CAN žinutes, CAN2A ir CAN2B
- Parodo versijos numerį
- Parodo serijinį numerį
- Prideda kas 60 s persiverčiantį milisekundinį gautos žinutės timestamp’ą (jei įjungta)
Ko nedaro:
- Negalima pakeisti nei UART, nei CAN spartos
- Negalima įjungti POLL režimo
- Negalima išjungti/įjungti CAN: magistralė įjungta tiesiog visada
- Negalima įjungti filtrų
- Negalima nuskaityti klaidų statuso
Nu ir dar ten kelių fyčių nėra, nes man nereikia. Bet šiaip man šis „projektas“ patiko, aš jį vėliau kiek patobulinsiu.
Kodijimas su CMSIS
Aš vis stengiuosi vengti HAL ar Standard Peripherals bibliotekų, kurios turėtų „palengvinti“ programavimą su STM32 mikrovaldikliais. Kiek išeina, naudoju CMSIS. Kaip turbūt jau žinote, ten toks beveik asembleris.
Aš šiaip tikrai neteigiu, kad visada reikia lįsti į registrus ir asemblerius. Gyvenimas trumpas, kodyti reikia greičiau, procesoriai yra spartūs. Bet aš pats mėgstu daugiau pakrapštyti galvą (ir nosį) bei pasigilinti, kaip kas kur veikia. O kai rašau kodą, kuris yra beveik „produkcinis“, norisi jį padaryti kompaktiškesnį.
Turėjau tokį CAN magistralės inicializavimo „utėlyną“ su Standard Peripherals:
void init_can_utils(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); GPIO_InitTypeDef gpio_init; /* GPIO clock enable */ AFIO_ENABLE; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_CAN, ENABLE); gpio_init.GPIO_Pin = GPIO_Pin_CAN_RX; gpio_init.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIO_CAN, &gpio_init); /* Configure CAN pin: TX */ gpio_init.GPIO_Pin = GPIO_Pin_CAN_TX; gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; gpio_init.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_CAN, &gpio_init); GPIO_PinRemapConfig(GPIO_Remapping_CAN , ENABLE); /* CANx Periph clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE); /* CAN register init */ CAN_DeInit(CAN); CAN_StructInit(&can_init); /* CAN cell init */ can_init.CAN_TTCM = DISABLE; can_init.CAN_ABOM = ENABLE; can_init.CAN_AWUM = DISABLE; can_init.CAN_NART = DISABLE; can_init.CAN_RFLM = DISABLE; can_init.CAN_TXFP = DISABLE; can_init.CAN_Mode = CAN_Mode_Normal; /* CAN Baudrate = 1MBps*/ can_init.CAN_SJW = CAN_SJW_1tq; can_init.CAN_BS1 = CAN_BS1_3tq; can_init.CAN_BS2 = CAN_BS2_5tq; #if CAN_BAUDRATE == 1000 /* 1MBps */ can_init.CAN_Prescaler =4; #elif CAN_BAUDRATE == 500 /* 500KBps */ can_init.CAN_Prescaler =8; #elif CAN_BAUDRATE == 250 /* 250KBps */ can_init.CAN_Prescaler =16; #elif CAN_BAUDRATE == 125 /* 125KBps */ can_init.CAN_Prescaler =32; #elif CAN_BAUDRATE == 100 /* 100KBps */ can_init.CAN_Prescaler =40; #elif CAN_BAUDRATE == 50 /* 50KBps */ can_init.CAN_Prescaler =80; #elif CAN_BAUDRATE == 20 /* 20KBps */ can_init.CAN_Prescaler =200; #elif CAN_BAUDRATE == 10 /* 10KBps */ can_init.CAN_Prescaler =400; #else #error "Please select first the CAN Baudrate in Private defines in main.c " #endif /* CAN_BAUDRATE == 1000 */ CAN_Init(CAN, &can_init); /* CAN filter init */ #ifdef CUSTOM_FILTER init_custom_filters(); #else can_filter_init.CAN_FilterNumber = 0; can_filter_init.CAN_FilterMode = CAN_FilterMode_IdMask; can_filter_init.CAN_FilterScale = CAN_FilterScale_32bit; can_filter_init.CAN_FilterIdHigh = 0x0000; can_filter_init.CAN_FilterIdLow = 0x0000; can_filter_init.CAN_FilterMaskIdHigh = 0x0000; can_filter_init.CAN_FilterMaskIdLow = 0x0000; can_filter_init.CAN_FilterFIFOAssignment = 0; can_filter_init.CAN_FilterActivation = ENABLE; CAN_FilterInit(&can_filter_init); #endif CAN_ITConfig(CAN, CAN_IT_FMP0, ENABLE); }
Ir užsimaniau jį pilnai perrašyti naudodamasis vien CMSIS. Nelabai žinau, kam to reikėjo, bet norėjosi ir viskas. Persiskaičiau daug tuzinų kartų reikiamas RM0008 dalis. Ir po vieną kabaliuką pradėjau keisti, palaipsniui. Na, kad būtų aišku, kur klaida, jei pradeda neveikti — o tų klaidų būna, kaip kitaip. Ir po vieną dalį keisdamas vis žiūrėjau, kaip mažėja firmwarės apimtys. Žemiau išrašas, kas buvo pakeista ir kiek tai sumažino firmwarę nuo prieš tai buvusios:
arm-none-eabi-size --format=berkeley "ARM_F103_SLCAN.elf" ---- NVIC config ---- 6723 176 412 7311 1c8f ARM_F103_SLCAN.elf ---- GPIO ---- 6263 176 412 6851 1ac3 ARM_F103_SLCAN.elf ---- Remap CAN pins ---- 5763 176 412 6351 18cf ARM_F103_SLCAN.elf ---- Init CAN ---- 5095 176 412 5683 1633 ARM_F103_SLCAN.elf ---- Enable CAN interrupt ---- 4947 176 412 5535 159f ARM_F103_SLCAN.elf ---- Init CAN filter ---- 4679 176 412 5267 1493 ARM_F103_SLCAN.elf Finished building: ARM_F103_SLCAN.siz
Neišsisaugojau tik pilno dydžio, kur ir NVIC buvo inicializuojamas. Bet kokiu atveju, firmwarė sumažėjo dviem kilobaitais. O kodas gavosi gerokai mažiau skaitomas ir suvokiamas, bet va toks:
int open_can() { // Configure NVIC for CAN SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup_1; NVIC->IP[CAN1_RX_IRQ_CH] = 0x00; NVIC->ISER[CAN1_RX_IRQ_CH >> 0x05] = (uint32_t) 0x01 << (CAN1_RX_IRQ_CH & (uint8_t)0x1F); // Configure IOs for CAN RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // Enable Alternative IO RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; // Enable CAN clock RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // Enable GPIOB pins for CAN1 GPIOB->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_MODE8 | GPIO_CRH_CNF9 | GPIO_CRH_MODE9); // Reset pin8 and 9 GPIOB->CRH |= GPIO_CRH_CNF8_1; // CNF b10: input pull up/down, mode 00: input GPIOB->BSRR |= GPIO_BSRR_BS8; // Pull up GPIOB->CRH |= GPIO_CRH_CNF9_1| GPIO_CRH_MODE9; // CNF b10: alternative push-pull, mode 11: 50 MHz output AFIO->MAPR &= ~(AFIO_MAPR_CAN_REMAP); AFIO->MAPR |= AFIO_MAPR_CAN_REMAP_1; // b10, CAN_RX PB8, CAN_TX PB9. // Configure CAN peripheral CAN1->MCR = CAN_MCR_RESET; CAN1->MCR &= ~CAN_MCR_SLEEP; // Exit from sleep CAN1->MCR |= CAN_MCR_INRQ; // Request initialisation uint32_t timeout = INAK_TIMEOUT; while (((CAN1->MSR & CAN_MSR_INAK) == 0) && (--timeout)); if (timeout == 0) { CAN1->MCR &= ~(uint32_t)CAN_MCR_INRQ;// Leave initialisation return -1; // Initialisation failed } CAN1->MCR |= CAN_CFG_FLAGS; CAN1->BTR = cfg_can_mode | cfg_can_sjw_quanta | cfg_can_ts1_quanta | cfg_can_ts2_quanta | cfg_can_prescaler; CAN1->MCR &= ~(uint32_t)CAN_MCR_INRQ;// Leave initialisation timeout = INAK_TIMEOUT; while (((CAN1->MSR & CAN_MSR_INAK) == 0) && (--timeout)); if (timeout == 0) { return -2; // Initialisation finalisation failed } // Enable filter 0 to pass through all messages CAN1->FMR |= CAN_FMR_FINIT; // Enter initialisation CAN1->FA1R &= ~(uint32_t)CAN_FA1R_FACT0; // Deactivate filter CAN1->FS1R |= CAN_FS1R_FSC0; // Scale 32 bit CAN1->sFilterRegister[0].FR1 = 0x00; // No filter CAN1->sFilterRegister[0].FR2 = 0x00; CAN1->FM1R &= ~(uint32_t)CAN_FM1R_FBM0; // Mask mode CAN1->FFA1R &= (uint32_t)CAN_FFA1R_FFA0; // FIFO0 assignment CAN1->FA1R |= CAN_FA1R_FACT0; // Activate filter CAN1->FMR &= ~CAN_FMR_FINIT; // Exit initialisation // Enable CAN interrupt CAN1->IER |= CAN_IER_FMPIE0; // Configure DMA NVIC NVIC->IP[CAN_RX_DMA_IRQ] = 0x00; NVIC->ISER[CAN_RX_DMA_IRQ >> 0x05] = (uint32_t) 0x01 << (CAN_RX_DMA_IRQ & (uint8_t)0x1F); // Configure DMA for Mem2mem RCC->AHBENR |= CAN_RX_DMA_CLK; CAN_RX_DMA_CH->CCR = 0; // Mem2mem, High priority, Increment memory source and destination buffer, transfer complete interrupt enabled CAN_RX_DMA_CH->CCR = (DMA_CCR1_MEM2MEM | DMA_CCR1_PL_1 | DMA_CCR1_MINC | DMA_CCR1_PINC | DMA_CCR1_TCIE); return 0; }
Ne visai ta vieta, kur būtina baisiai taupyti, bet vis tiek smagu. Išmetus (arba, tiksliau, atjungus) visą debuginimą firmwarė sulindo į nepilnus 4 kB. printf tipo debugai yra labai labai ėdrūs atminčiai.
Iš Standard Peripherals pasilikau CAN_Receive ir CAN_Transmit paprogrames, jos parašytos patogiai ir nėra per daug apkrautos, o visokie assert per kompiliatorių išsijungia.
Surinkimas ir paleidimas
Aš panaudojau vieną iš savo „išmanaus“ apšvietimo plokščių, kurios daugiau niekam nenaudojamos. Bet jei norėtumėte surinkti šitą daiktą patys, jums reikės:
- STM32F103C6 procesoriuko devboardo. Mano mėgstamiausias — Blue Pill.
- SN65HVD230 CAN transiverio, yra papigiai AliExpresse break-out-boardų.
- FT232RL mikroschema pagrįsto USB-UART adapterio. Jungiamas prie USART1.
- Laidukų, be abejo, breadboardo ar panašiai.
Pastaba: dėl mano poreikių CAN-RX ir CAN-TX yra perkelti (remapinti) ant PB8 ir PB9.
Surinkus, įrašius firmwarę ir prijungus prie kompiuterio su Linux reikia sukurti tinklo adapterį. Tam paleidžiamas slcand demonas, o paskui pakuriamas ir pats adapteris (interfeisas):
sudo slcand -S 460800 -t sw /dev/ttyUSB0 can0 sudo ip link set up can0
Nu ir viskas, galima su candump can0 stebėti, kas CAN magistralėje vyksta, o su cansend patiems siuntinėti žinutes.
Kadangi Canberry nuduso, tai kol kas prijungiau šitą reikalą bile kaip:
Bet veikia. Tiesa, jeigu pabyra į tinklą labai daug žinučių, nespėja jų išspausdinti į USART1: persipildo buferis. Bet čia retas atvejis, pasireiškiantis, kai aš naudoju tokį durną savo išmislą: CAN terminalą. To nepaveža. Pradžiai bandysiu didinti buferį, o ir gal terminalą paoptimizuoti, nes ans dabar vieną simbolį viena žinute ir siunčia, o didelius gabalus galėtų ir visais 8 baitais išsiųst.
Pabaigai šito daikto kodas Githube: https://github.com/darauble/lawicel-slcan
RingBuffer vertimas į LT ganėtinai keistai skamba 🙂 kaip koks žiedinis raumuo 😀
Mano supratimu, trūksta dviejų žvaigždučių pirmame kodo gabaliuke skyrelyje „Spartus konvertavimas į HEX ir atgal“ — abu kartus prieš str.
Taip, trūksta, pataisysiu prie progos.