1. septembra, 2017

Programiranje s HAL knjižnicami (3) – CAN vodilo

Revija logo se - Programiranje s HAL knjižnicami (3) - CAN vodilo

Avtor: dr. Matej Kseneman
E-pošta: matej.kseneman@gmail.com
2017_255_38

V predhodnem članku, objavljenim v reviji Svet Elektronike, smo zasnovali osnovno razširitev razvojne plošče, v kateri smo dodali monokromatski LCD zaslon, ki je s procesorjem povezan preko vodila SPI. Prav tako ste lahko spoznali z delovanje notranje RTC ure, ki pa je nismo uporabili le za štetje časa, ampak smo ji dodali tudi tako imenovano prekinitev bujenja procesorja (wake-up ISR). Ta je potrebna, kajti med izvajanjem aplikacije smo procesor prestavili v stanje nižje porabe energije. S programerskega stališča je predstavljen tudi stražni mehanizem, brez katerega skoraj ne smemo načrtovati naprave, kajti kaj hitro se pripreti, da se ujamemo v neskončni zanki iz katere ni povratka (beri nedelujoča naprava).

V tokratni izdaji revije Svet Elektronike bomo implementirali komunikacijski vmesnik CAN [1]. Razvojna plošča STM32F4-Disco0 je opremljena z dvema sprejemno-oddajnima enotama (CAN1 in CAN2), ki jih lahko uporabimo za komunikacijo po CAN vodilu. V demonstracijske namene bomo uporabili dve neodvisni plošči, med katerima bomo pošiljali komande, ki so jasno definirane v lastnem protokolu pošiljanja sporočil. Pri medsebojnem pošiljanju se bo druga plošča na sprejete komande tudi odzvala. V kolikor nimate dveh razvojnih plošč, je enak efekt mogoče doseči le z eno, a je potrebno uporabiti obe CAN vodili razvojne plošče (nekoliko bo potrebno prilagoditi CubeMX in dodati CAN2 v projekt).

 

Strojna oprema

CAN vmesnik

CAN (ang. Controller Area Network) v svetu avtomobilizma predstavlja standard, ki omogoča centralni enoti (ECU) in posameznim modulom medsebojno komunikacijo brez prisotnosti gostiteljskega računalnika. Če si lahko zamislite, kako bi izgledali avtomobili, če bi za vsak mikrokontroler, senzor, aktuator, itd. morali ožičiti sistem, ta seveda ne bi bil poceni, pa tudi praktičen ne. Zato je podjetje Bosch že davnega leta 1986 prestavilo prvotno različico protokola [1], s katero so uspeli vzpostaviti medsebojno komunikacijo z zgolj dvema žicama.

255 38 01 300x157 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 1: CAN_H in CAN_L diferenčni izhod

Demo plošča ima prisotna kanala CAN_RX in CAN_TX, ki ju na prvi pogled lahko celo zamenjamo z navadnim UART vodilom. Vendar, demo plošča žal nima sprejemno-oddajne enote CAN vmesnika – inženirji so želeli poceniti razvojno okolje, a CAN niti ni tako pogosto uporabljeno vodilo med entuziasti. Na fizičnem nivoju je CAN vodilo definirano kot diferenčno vodilo (torej je mogoče z njim doseči kar dobre dolžine – cca. 40 m pri 1 Mb/s), ki mora biti na koncih vodila zaključeno s terminatorskim uporom vrednosti 120 Ω. Na diferencialni strani sta liniji označeni kot CAN_L (low) in CAN_H (high), kjer obe liniji prenašata identične podatke, le njuni amplitudi sta nasprotno predznačeni. Če si zamislimo pulz, ki gre na CAN_H liniji iz 2,5 V na 3,75 V, potem se mora na CAN_L ta napetost odšteti, se pravi, dobimo skok iz 2,5 V na 1,25 V. S takšnim prenosom dosežemo večjo odpornost na šum in s tem manjšo verjetnost uničenih podatkov. Slika 1 prikazuje diferenčni izhod CAN vodila, kjer imajo dominantni biti, katerih diferenca znaša – v tem primeru) vrednost 2,5 V – vrednost 0, medtem ko diferenca 0 V predstavlja recesiven bit, torej logično vrednost 1.

Da dosežemo na demo plošči želeno pretvorbo v diferenčno vodilo, potrebujemo CAN sprejemno-oddajno enoto, ki jih je na trgu ogromno. Sam se posegel po razvojnemu modulu, ki se ga da naročiti preko eBay portala [2]. Slika 2 prikazuje razvojni modul sprejemno-oddajne enote, na katero je nameščen čip SN65HVD230, ki deluje pri 3,3 V in ima prisotno ESD zaščito.

Najenostavnejše je CAN vodilo z le dvema vozliščema (Slika 3), kakršno bo nastalo v okviru tega demonstracijskega projekta. Če povzamemo elemente, imamo dva CAN vozlišča, ki vsebujeta CAN krmilnik in sprejemno-oddajno enoto, dve liniji (CAN_H in CAN_L) in dva terminatorja vodila (R).

CAN krmilnik skrbi za pravilno CAN komunikacijo, torej to ni zgolj UART vodilo, kajti na strojnem nivoju je podprto celotno delovanje CAN protokola (npr. sprejem ACK paketov, BUS-OFF stanje, ipd.). CAN krmilnik torej sprejme podatke od procesne enote, jih procesira in preda sprejemno-oddajni napravi (SON), prav tako deluje tudi v nasprotni smeri, kjer procesira podatke, ki jih sprejme od SON ter jih preda procesni enoti. Sprejemno-oddajna naprava skrbi le za pretvorbo podatkov v električne signale, ki jih pošlje prek vodila, prav tako pa sprejete električne signale pretvori v podatke. Zelo zanimiva je vloga končnih uporov, ki imajo tipično vrednost 120 Ω, kajti skrbijo za to, da ne prihaja do odbojev sporočil, kar se lahko eventualno manifestira kot eho signal.

255 38 02 208x300 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 2: Sprejemno-oddajni CAN vmesnik

 

Struktura sporočil

Ker je teorija okoli CAN protokola zelo obširna, bomo v tem članku predstavili le osnove, ki jih moramo razumeti pri prenosu podatkov med CAN vozlišči. Obstajata dva tipa sporočil, in sicer standardno CAN sporočilo (Standard CAN – 2.0A), ki uporablja 11 bitni identifikator naprave in razširjeno CAN sporočilo (Extended CAN – 2.0B), ki omogoča 29 bitne identifikatorje, kjer prvih 11 bitov predstavlja osnovo, nadaljnjih 18 pa razširitev. Slika 4 prikazuje strukturo CAN sporočila, ki pa je enaka tako za standardno kot razširjeno različico.

 

Elementi sporočila so:

  • SF (Start Field): z dominantnim bitom označuje začetek sporočila (glej Slika 1).
  • Message Identifier: definira nivo prioritete podatkovnega protokola. V primeru, ko želita dve napravi istočasno oddati sporočilo, bo prevladovalo sporočilo naprave, ki ima višjo prioriteto, kjer velja, nižja kot je vrednost ID-ja, višja je prioriteta (več dominantnih bitov!).
  • Control: nosi informacijo o številu podatkov Data dela. Na ta način lahko sprejemnik ugotovi, ali je sprejel celotno sporočilo.
  • Data: Predstavlja podatke, ki se prenašajo v sporočilu.
  • CRC: Del namenjen preverjanju integritete vsebine. Vsebuje 15 bitov in en recesiven bit.
  • ACK: Ta del sporočila je zastavljen na poseben način, saj ta del potrdijo sprejemniki! Tako lahko oddajna naprava »ve«, ali je bilo sporočilo sprejeto ali je morebiti prišlo do napake.
  • EF (End Field): označuje konec prenosa sporočila.

 

Programski del

Programski del, bomo razdelili na dva dela, in sicer bomo v prvem delu predstavili, kako pravilno nastaviti CAN, ki bo deloval v eho načinu (v literaturi se uporablja izraz Loopback), kajti preden bomo povezali dve plošči, se želimo prepričati, da je z našo izvorno kodo vse v redu. Ko prestanemo ta del, bomo uporabili normalno delovanje CAN vodila ter med ploščama prenašali komande.

 

Grafična konfiguracija periferije

Predlagam vam, da uporabite CubeMX [3] datoteko iz predhodnega članka, kajti uporabite lahko trik, in sicer s klikom na gumb Save as izberete novo ime (npr. CanBus) in že imate skoraj vse potrebne nastavitve. Za potrebe prenosa sporočil omogočimo USART1 (asinhroni način), ter izberemo CAN1. Privzeta pina (PA11 in PA12) sta žal povezana na USB OTG (CN5 priključek), zato moramo najti druge, nam ustreznejše, pine. Da ni potrebno iskati kam so ti pini povezani, si lahko pomagamo s samim orodjem. S kombinacijo Ctrl + klik se nam obarvajo preostali pini, na katerih je mogoče uporabiti omenjeno alternativno funkcijo. Sam sem izbral PD0 in PD1. Slika 5 prikazuje celotno nastavitev pinov, ki so potrebni za CAN aplikacijo.

255 38 03 300x135 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 3: CAN vodilo z dvema vozliščema

Nastavitev USART1 je dokaj trivialna, saj nastavimo baudrate na 115200 in v DMA zavihku definiramo podatkovne toke za RX in TX liniji. Težje je pravilno nastaviti CAN nastavitve, saj je potrebno nekaj vedeti o sinhronizaciji in vzorčenju. Pri CAN vodilu je pomembno kdaj in kako se vzorčijo podatki. CAN vodili sta spojeni z APB1, ki ima PCLK1 hitrost 42 MHz (lahko ugotovimo iz konfiguracije ure v CubeMX). Hitrost samega vodila pa se izračuna kot PCLK1 hitrost deljena z vsoto faktorjev vzorčenja pomnoženo z delilnikom. Če izpišemo nastavljene vrednosti:

Prescaler = 2;

SJW = CAN_SJW_1TQ;

BS1 = CAN_BS1_14TQ;

BS2 = CAN_BS2_6TQ;

 

Lahko zapišemo enačbo, po kateri izračunamo hitrost CAN vodila, in sicer 42 MHz / (2 * (1 + 14 + 6)) = 42 / (2 * 21) = 1 MBit/s. Slika 6 podaja nastavitve CAN vodila, kjer je potrebno pod Operating Mode za prvi del aplikacije izbrati Loopback.

Najprej moramo implementirati gonilnik CAN vodila, in sicer je na CAN vodilu potrebno nastaviti filtre. Kot smo že opisali v teoriji, ima vsaka naprava na CAN vodilu svoj unikaten ID. Nižjo kot ima številko, višjo prioriteto ima pri istočasnem pošiljanju sporočil. Prav s filtri lahko dosežemo, da sprejemamo samo sporočila od naprave, ki je za nas zanimiva, preostale pakete pa zavržemo. To za nas stori že strojni krmilnik, tako da na programskem nivoju se niti ne prožijo prekinitve.

255 38 04 300x38 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 4: Struktura CAN podatkovnega paketa

Pri STM32 mikrokontrolerjih je mogoče promet filtrirati z masko, s 16 bitnimi ID-ji ali z 32 bitnimi ID-ji (malenkost več rezerviranega prostora, s katerimi opišemo identifikator). Mi bomo uporabili standardno metodo, in sicer bomo filtrirali s 16-bitno ID listo. Prav tako je pomembno, da se nastavi ID naprave. Izsek 1 podaja izvorno kodo definiranja filtrov, kjer statična funkcija SetTxMessageIds nastavi ID naprave, medtem ko se v funkciji CAN_SetUpBusFilter nastavijo filtri RX linije, in sicer tako, da se napolni HAL struktura s podatki, zajetimi v strukturi vhodnega argumenta. Naj omenim, da v kolikor ne želimo imeti filtriranja, enostavno postavimo vse vrednosti na 0, glej program 1.

Na strani gonilnika nam manjkata še funkciji pošiljanja in sprejemanja, kjer prva kratko malo prekopira vhodne podatke v CAN TX strukturo (maksimalna velikost je 8 zlogov), nastavi dolžino sporočila ter pošlje sporočilo na linijo. Malenkost drugače pa je s sprejemom, ki se vrši v prekinitveni rutini. Ta se sproži, v kolikor smo sprejeli sporočilo, ki ima ID nastavljen tako, da ga filter prepušča. Pri pisanju gonilnika smo si dodali funkcijo povratnega klica, ki jo bomo poklicali znotraj ISR ter ji predali podatke. Pomembno pa je, da se ob vsaki CAN_RX prekinitvi, le-ta ponovno omogoči. Izsek 2 podaja izvorno kodo pošiljanja in sprejemanja CAN sporočil, glej program 2.

 

Implementacija aplikacije

255 38 05 300x292 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 5: Nastavitev CAN in Debug pinov v CubeMX

Sedaj, ko imamo spisan CAN gonilnik, se lahko dvignemo en nivo višje, in sicer na aplikacijski nivo. Prvi del aplikacije si zamislimo kot pošiljanje kratkega niza znakov, ki ga lahko zapišemo v CAN sporočilo. To zna prikazati vsak razhroščevalnik in že na daleč lahko potrdimo pravilno delovanje. Kasneje bomo te nize zamenjali s komandami, ki se bodo prenašale. Zamislil sem si preprosti niz znakov, ki ga lahko zapišem kot *CMD_UP, tako da tudi te komande lahko spremljam v dnevniških izpisih tekom delovanja aplikacije.

Prvi korak pri implementaciji CAN aplikacije je definiranje vrednosti ID-jev naprave in filtrov, ki jih posredujemo gonilniku, da ta pravilno nastavi registre. Izsek 3 podaja izvorno kodo, s katero definiramo ID-je, medtem ko v nadaljevanju nastavi funkcije povratnih klicev. Ker sem želel napraviti kar se da nastavljiv projekt, sem s preprocesorskimi direktivami določil, katere funkcije se bodo klicale. Tako lahko za prvi del aplikacije med globalnimi nastavitvami nastavite direktivo LOOPBACK_TEST na vrednost 1, glej program 3.

Manjkata nam torej še dve funkciji, in sicer funkcija, ki bo kreirala vsebino sporočila in ga poslala s pomočjo gonilnika in funkcija, ki se bo izvedla, ko bo sporočilo sprejeto. Ta bo seveda morala s temi podatki nekaj storiti, kjer bomo v Loopback načinu izpisali to sporočilo, medtem ko bomo pri kompletni progi sprejeto komando tudi obdelali. Izsek 4 povzema funkcijo kreiranja CAN sporočil, kjer lahko vidimo, da bomo v prvem načinu poslali neko testno sporočilo, medtem ko v normalnem režimu delovanja CAN vodila pošljemo komando iz globalnega polja definiranih komand, glej program 4.

255 38 06 247x300 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 6: Nastavitev CAN vodila

Na sprejemni strani v prvem delu aplikacije preprosto izpišemo tekst, ki smo ga sprejeli v CAN sporočilu – v eho načinu je to vse na eni demo plošči. Do sedaj nismo še nič povedali o tem, a vendar smo v projektu uporabili USART, ki smo ga namenili uporabi prenosa DEBUG sporočil (zaradi omejenosti s prostorom se ne bom spuščal v detajle). Deluje identično slavni printf() funkciji, le da smo tukaj uporabili informativnejši izpis s časom, imenom funkcije, tipom sporočila, itd., kar je marsikdo izmed vas morebiti že srečal pri uporabi knjižnice log4net, log4cpp, log4j… Če uporabite še program MobaXterm, potem so napake in opozorila izjemno hitro vidna.
Da pridemo do teh sporočil, je potrebno povezati USB2TTL na demo ploščo, kjer se (za tiste, ki ste pozabili) RX ene strani poveže na TX druge in obratno. RX se na plošči nahaja na pinu PB6, medtem ko je TX na PB7. Osebni računalnik mora razpoznati USB pretvornik kot nov serijski vmesnik, katerega odpremo s programom MobaXterm, kjer nastavimo hitrost na 115200. Izsek 5 podaja izpis sprejetega sporočila na serijski vmesnik, kjer smo uporabili klic direktive LOG_INFO, kjer ta deluje z variabilnimi argumenti, glej program 5.

255 38 07 300x205 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 7: Povezava dveh razvojnih plošč s CAN vodilom

Na tem mestu lahko že periodično pošiljamo sporočila, ter jih izpisujemo na zaslon. Izsek 6 povzema le pošiljanje CAN sporočil, ki se ciklično izvaja v sekundnem intervalu (uporabljena je kar RTC prekinitev). Poskusite se malce poigrati s sprejemnimi filtri in ID-jem naprave in videli boste, kako se sporočila prične zavračati, v kolikor niso zajeti v listi dovoljenih ID-jev, glej program 6.

Drugače moramo postopati pri drugem delu aplikacije, kjer smo si zamislili, da pritisk uporabniškega gumba na ločeni razvojni plošči pošlje komando prek CAN vodila, ki bo ob pravilnem interpretiranju komande na prvi demo plošči povzroči premik označenega elementa v meniju. Izsek 7 podaja izvorno kodo, kjer prek vhodnega argumenta prenese sprejeto CAN sporočilo ter jih primerja s podprtimi komandami. V kolikor je komanda veljavna, jo izvedemo, v kolikor je ne razpoznamo, to javimo kot napako, le-ta pa se izpiše v dnevniških izpisih delovanja aplikacije, glej program 7.

 

Delovanje programa

255 38 08 300x225 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 8: Časovno porazdeljeni biti CAN sporočila

Kot smo že uvodoma nakazali, bo ciljna aplikacija delovala na dveh ločenih razvojnih ploščah, ki bosta uporabljali identično periferijo. Tako sem v projektu ločil določene segmente izvorne kode, ki se bodo vključili glede na nastavitev, in sicer ali imamo primarno CAN napravo ali ne. Primarna naprava je v tem primeru tista, ki ima povezan LCD zaslon. V kolikor boste programirali primarno napravo, je potrebno v projektu nastaviti preprocesorsko direktivo PRIMARY_CAN_DEV na vrednost 1, v nasprotnem primeru pa na vrednost nič.

V sklopu projekta sem izbral dve komandi (Izsek 8), a jih lahko uporabnik poljubno razširi. Gre za smiselne komande, ki se bodo pravilno prikazale tudi v dnevniških zapisih delovanja naprave, in sicer ob vsaki sprejeti komandi, kajti komando prikažemo kot niz znakov.

 

Izsek 8: Skupek možnih komand

static const CanCmdMsg_t CanCommands[] = {

    // # {00 - 09}

    {cmd_MoveUp,    "*CMD_UP"},

    {cmd_MoveDown,  "*CMD_DN"},

};

 

Na primarni napravi ustvarimo za vsako komando določeno akcijo, in sicer sem kreiral tabelo kazalcev funkcij LCD zaslona, ki upravljajo s premikanjem menijskega pravokotnika. Ker sem želel predstaviti sila poenostavljeno zadevo, je mogoče s pritiskom na uporabniško tipko poslati le eno komando, a se z nekaj dela da razširiti tudi te komande (morda se spomnite IR diode in daljinskega upravljalnika?). Slika 7 prikazuje povezavo med razvojnima ploščama, kjer pritisk uporabniškega gumba razvojne plošče na levi povzroči prenos komande prek CAN protokola na drugo ploščo, na kateri se označeni del menija premakne za eno mesto navzdol. Pri pošiljanju sporočil po CAN vodilu, si lahko pomagamo z digitalnim analizatorjem, ki zna dekodirati CAN protokol. Na ta način dobimo bolj informativen vpogled v to, kaj se pošilja po CAN vodilu. Sam sem napravil posnetek takšnega sporočila, ki prenese komando premakni izbiro menija navzdol. Slika 8 podaja časovni diagram posameznih bitov (bralec se lahko vrne na teorijo – Slika 4), medtem ko Slika 9 s pomočjo programa Saleae Logic [4] prikazuje izpisano vsebino sporočila, iz katere lahko razberemo, da je to sporočilo uspešno sprejeto na eni izmed naprav.

Bralec lahko s spleta prenese CubeMX AC6 projekt [5], ki ga po potrebi prilagodi svojim potrebam. V kolikor imate še več razvojnih plošč, lahko poskusite s povezovanjem le-teh na CAN vodilo, dodati morate le ID te naprave in že lahko prenesete dodatno komando. Video, ki nazorno prikaže delovanje CAN vodila, je dostopen na [6].

 

Zaključek

Članek predstavlja osnove delovanja CAN vodila, ki smo ga implementirali s pomočjo HAL knjižnic, kjer si pomagamo z namenskim CubeMX orodjem. CAN vodilo je namenjeno hitremu prenosu sporočil, a če želimo pri tem biti zanesljivi, moramo zmanjšati dolžino sporočil.

255 38 09 300x19 - Programiranje s HAL knjižnicami (3) - CAN vodilo

Slika 9: CAN ID in njegova vsebina

Tako v članku spoznamo, da smo pri CAN sporočilih omejeni z osmimi zlogi koristne vsebine. S programerskega stališča spoznamo, da je mogoče uporabiti CAN vodilo v eho načinu, kar nam izjemno olajša delo pri odpravljanju hroščev izvorne kode, preden se poslužujemo dejanskega prenosa med napravami. Bežno članek povzame tudi delovanje USART linije, ki jo uporabimo za pošiljanje diagnostičnih sporočil – te nato na PC strani zajemamo kot dnevniške izpise.

Marsikdo izmed vas je morebiti razočaran nad hitrostjo delovanja openSTM razhroščevalnika v AC6 orodju. A obstaja manjši trik, kako uporabiti Segger J-Link [7] orodje, s katerim dosežemo hitrejše in zanesljivejše razhroščevnanje izvorne kode. To bomo predstavili na preprostem AD merilniku napetosti, katerega vrednost bomo izpisali na zaslonu.

Literatura

  • [1] Wikipedia, „CAN bus,“ 4 May 2017. [Elektronski].
    • Available: https://en.wikipedia.org/wiki/CAN_bus. [Poskus dostopa 8 May 2017].
  • [2] eBay, „SN65HVD230 CAN Board Network Transceiver Evaluation Development Module,“ 21 April 2017. [Elektronski].
    • Available: http://www.ebay.com/itm/SN65HVD230-CAN-Board-Network-Transceiver-Evaluation-Development-Module-new-/252436745316?hash=item3ac6670c64:g:Z7cAAOSwepJXbQj6. [Poskus dostopa 8 May 2017].
  • [3] ST, „STM32CubeMX tool,“ ST, 10 March 2017. [Elektronski].
    • Available: http://www.st.com/stm32cube. [Poskus dostopa 16 March 2017].
  • [4] saleae, „Home page,“ saleae, [Elektronski].
    • Available: https://www.saleae.com/. [Poskus dostopa 13 May 2017].
  • [5] M. Kseneman, „CanBus project,“ 11 May 2017. [Elektronski].
    • Available: https://goo.gl/ED2Asw. [Poskus dostopa 11 May 2017].
  • [6] M. Kseneman, „CanBus video,“ 11 May 2017. [Elektronski].
    • Available: https://goo.gl/ehbiQI. [Poskus dostopa 11 May 2017].
  • [7] Segger, „The Embedded Experts,“ 2017. [Elektronski].
    • Available: https://www.segger.com/index.html. [Poskus dostopa 11 May 2017].
www.svet-el.si
     program 1           

Izsek 1: Nastavitev CAN filtrov

static void SetTxMessageIds(CanTxMsgTypeDef* pTxMsg, uint32_t stdId, uint32_t extId)

{

  // Configure TX message details

  pTxMsg->StdId = stdId;      // Standard ID of this device

  pTxMsg->ExtId = extId;      // Extended ID

  pTxMsg->RTR = CAN_RTR_DATA; // Remote transmission request:data

  pTxMsg->IDE = CAN_ID_STD;   // Identifier type: standard

}

// Set-up CAN bus with message structures and filtering

HAL_StatusTypeDef CAN_SetUpBusFilter(const CanFilter_t* filter)

{

  // Sanity check

  if (!filter) { return HAL_ERROR; }

  // Store pointers to message structures

  (CAN_BUS_PTR)->pTxMsg = &txMessage;

  (CAN_BUS_PTR)->pRxMsg = &rxMessage;

  // Set up TX message

  SetTxMessageIds(&txMessage, filter->stdId, filter->extId);

  // Create CAN filtering

  CAN_FilterConfTypeDef canFilter = {0};

  canFilter.FilterMode = CAN_FILTERMODE_IDLIST;

  canFilter.FilterScale = CAN_FILTERSCALE_16BIT;

  canFilter.FilterFIFOAssignment = CAN_FILTER_FIFO0;

  canFilter.FilterActivation = ENABLE;

  canFilter.FilterNumber = 0;

  // In case size is zero no filtering will be performed

  if (filter->filterSize)

  {

    uint32_t* pCanFilter = (uint32_t*)&canFilter;

    for (uint32_t i = 0; i < filter->filterSize; ++i)

    {

      pCanFilter[i] = filter->filterIds[i] << 5;

    }

  }

  // Initialize CAN filter

  HAL_StatusTypeDef canStatus = HAL_CAN_ConfigFilter(CAN_BUS_PTR, &canFilter);

  if (canStatus != HAL_OK)

  {

    // Unable to initialize CAN filter

    LOG_ERROR("Unable to initialize CAN filter!");

    return canStatus;

  }

  // Enable CAN receive interrupt

  return EnableRxInterrupt();

}

 

     program 2           

Izsek 2: Pošiljanje in sprejemanje CAN sporočil

// This function will send CAN message through CAN bus.

HAL_StatusTypeDef CAN_SendMessage(const uint8_t* const data, uint8_t length)

{

  if (!data || (length > CAN_MAX_DATA_LENGTH))

  {

    LOG_ERROR("Invalid input arguments!");

    return HAL_ERROR;

  }

  // Specify the data length

  (CAN_BUS_PTR)->pTxMsg->DLC = length;

  // Copy data and transmit them over CAN2

  memcpy((CAN_BUS_PTR)->pTxMsg->Data, data, length);

  return HAL_CAN_Transmit_IT(CAN_BUS_PTR);

}

// This function is a callback for the CAN receive interrupt routine

void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)

{

  if (!hcan)

  {

    LOG_ERROR("Null pointer passed!");

    return;

  }

  // Callback function with RX message contents

  if (pRxFunc)

  {

    CanRxMsgTypeDef* rxMsg = hcan->pRxMsg;

    pRxFunc(rxMsg->Data, rxMsg->DLC);

  }

  // Re-enable the receive interrupt

  EnableRxInterrupt();

}

 

     program 3           

Izsek 3: Posredovanje vrednosti IDjev in nastavljanje funkcij povratnih klicev

// Set-up the CAN bus filtering with the desired CAN filter structure.

ErrorCode CAN_InitBusFilters()

{

  // Initialize CAN configuration

  CanFilter_t canFilter = {

      DEV_CAN_STD_ID,

      DEV_CAN_EXT_ID,

      {CAN_FIRST_FILTER, CAN_SECOND_FILTER, CAN_THIRD_FILTER, CAN_FOURTH_FILTER},

      MAX_FILTERS_SIZE

  };

  // CAN - Set up filters

  if (CAN_SetUpBusFilter(&canFilter))

  {

    // CAN HAL error occurred

    return CanFilterHalError;

  }

#if LOOPBACK_TEST

  // CAN - Set message callback function

  CAN_SetRxCallback(&CAN_MsgParserCallBack);

#else

  // CAN - Set command callback function

  CAN_SetRxCallback(&CAN_CmdParserCallBack);

#endif

  return NoError;

}

 

     program 4           

Izsek 4: Kreiranje CAN sporočil

// Send dummy CAN message

ErrorCode CAN_SendCommand()

{

  HAL_StatusTypeDef status;

  uint32_t retryCounter = 0;

  do

  {

#if LOOPBACK_TEST

    status = CAN_SendMessage(dummy, DUMMY_SIZE);

#else

# if PRIMARY_CAN_DEV

    status = CAN_SendMessage((const uint8_t*)CanCommands[0].cmd, CAN_MAX_DATA_LENGTH);

# else

    status = CAN_SendMessage((const uint8_t*)CanCommands[1].cmd, CAN_MAX_DATA_LENGTH);

# endif

#endif

  } while ((status != HAL_OK) && (++retryCounter <= CAN_TX_MAX_RETRIES));

  // Check status and return

  HAL_Delay(1);

  return (status == HAL_OK) ? NoError : UnknownError;

}

 

     program 5           

Izsek 5: Izpis sprejetih CAN sporočil

static ErrorCode CAN_MsgParserCallBack(const uint8_t* buffer, const uint8_t size)

{

  // Sanity check

  if (!buffer)

  {

    return NullPointer;

  }

  // Debug print message

  if (size != DUMMY_SIZE) { LOG_DEBUG_HEX("Received CAN payload: ", buffer, size); }

  else { LOG_INFO("Received CAN message: %s", buffer); }

  return NoError;

}

     program 6           

Izsek 6: Periodično pošiljanje sporočil iz glavne zanke

  // Enable CAN bus

  ErrorCode status = CAN_InitBusFilters();

  if (status) { LOG_ERROR("Unable to set CAN filters!"); }

#if USE_LCD_DISPLAY

  // Set LCD font

  ConfigureLCD();

#endif

  // Enter endless loop

  while (true)

  {

    if (SendCanMessage)

    {

      // Dummy message send once per second

       LOG_INFO("Sending CAN message...");

       status = CAN_SendCommand();

       if (status) { LOG_ERROR("Unable to send CAN message!"); }

       SendCanMessage = DISABLE;

    }

  }
     program 7           

Izsek 7: Kreiranje CAN sporočil

static ErrorCode CAN_CmdParserCallBack(const uint8_t* canCmd, const uint8_t size)

{

#if PRIMARY_CAN_DEV

  // Prevent weird bugs

  assert_param(CAN_CMD_COUNT == LCD_FN_COUNT);

#endif

  // Sanity check

  if (!canCmd) { return NullPointer; }

  if (size != MAX_CMD_SIZE) { return InvalidInputArg; }

  // Try to parse input command

  for (uint8_t i = 0; i < CAN_CMD_COUNT; ++i)

  {

    if (!memcmp(CanCommands[i].cmd, canCmd, size))

    {

      LOG_INFO("Received CAN command: %s", canCmd);

#if PRIMARY_CAN_DEV

      return LcdFunctions[i]();

#else

      return NoError;

#endif

    }

  }

  // Command not found

  return ERROR_TRACE(CanUnknownCmd);

}
Tags: