CAN magistralė: filtrai

Posted: 2015-11-30 in Darbeliai
Žymos:,

CAN logotipas | Darau, blėViena iš CAN savybių, kuri mane stačiai užvežė — automatinis žinučių filtravimas. STM32 „kauliukai“ tai daro patys, reikia tiktai juos tinkamai sukonfigūruoti. Teko šiek tiek palaužyti galvą, kol įkirtau kas ten kaip, bet paskui viskas pasidarė aišku.

Pradžiai reikia žinoti, kas yra filtruojama. O filtruojama yra ID A ir (jei naudojamas) ID B. Taip pat dar IDE ir RTR bitukai. Filtruojant ID A galima atsirinkti įrenginiui aktualius pranešimukus, o RTR labai svarbu, jei CAN tinkle naudojamos užklausų žinutės. Sakykim, koks nors vienas tinklo mazgas gali užklausti visų įrenginių, kaip jiems sekasi ir laukti atsakymo. Įrenginiai gavę užklausas puls į jas atsakinėti. Tačiau jų atsakymus „matys“ ir kiti tinklo mazgai, kuriems ta informacija galbūt nesvarbi. Todėl galime nufiltruoti visas tam tikro tipo žinutes, jei jos yra atsakymai, o priimti tik užklausas. Smagumas, ką?

STM32 yra 14 filtrų vienai CAN magistralei („naglesni“ procesoriukai turi po dvi CAN magistrales, tuo tarpu maniškiai — tik vieną). Be to, kiekvienas iš tų filtrų gali būti viengubas 32 arba dvigubas 16 bitų.

Dar filtrai gali būti šablono (mask) arba sąrašo (list) tipo.

Smulkiau apie filtrus galite paskaityti RM0008 dokumente 24.7.4 skyriuje.

Pasižiūrėkime, kaip čia visa tai veikia. STM32 Standard Peripherals bibliotekoje yra struktūra filtrams aprašyti:

typedef struct
{
  uint16_t CAN_FilterIdHigh;
  uint16_t CAN_FilterIdLow;
  uint16_t CAN_FilterMaskIdHigh;
  uint16_t CAN_FilterMaskIdLow;
  uint16_t CAN_FilterFIFOAssignment;
  uint8_t CAN_FilterNumber;
  uint8_t CAN_FilterMode;
  uint8_t CAN_FilterScale;
  FunctionalState CAN_FilterActivation;
} CAN_FilterInitTypeDef;

Maždaug taip. Pradėkime nuo apačios.

  • CAN_FilterScale nusako, ar filtras bus 16 ar 32 bitų. Tam yra dvi konstantos CAN_FilterScale_16bit ir CAN_FilterScale_32bit.
  • CAN_FilterMode nusako darbo režimą: CAN_FilterMode_IdMask (šablonas) ar CAN_FilterMode_IdList (sąrašas).
  • CAN_FilterNumber — filtro numeriukas, nuo 0 iki 13.
  • CAN_FilterFIFOAssignment — gali būti CAN_Filter_FIFO0 arba CAN_Filter_FIFO1, priklauso, kuriam FIFO buferiui priskirsim.

Na, o dabar truputuką pasiaiškinsim apie filtravimo būdus. Pirmiausia, reikia žinoti, kas yra filtruojama. Kaip jau minėjau, tai ID A, ID B, IDE ir RTR bitukai. Jei filtras yra 32 bitų, tai jį sudaro va toks bitukų kratinys: ID A[10:0], ID B[17:0], IDE ir RTR (ir dar vienas tuščias nulinis bitukas gale). Jei pamenate iš mano įžangos apie CAN magistralę, tai ID A yra 11 bitų ilgio, o ID B — 18.

16 bitų filtras filtruoja mažiau duomenų: ID A[10:0], RTR, IDE ir ID B[17:15]. Taigi ID B tik tris bitukus.

Na, o dabar reikia išsiaiškinti, kas yra su šabloniniu arba sąrašiniu filtravimu. Trumpai drūtai, yr taip. Sakykim, turim filtruojamą pranešimuką WTF. Šablono režime tikrinama šitaip:

WTF & FILTER_MASK == FILTER_ID

T.y. WTF bitukai logiškai sudauginami su FilterMask ir žiūrima, ar gaunasi FilterId. Jei filtras 16 bitų, tai Low Mask lyginama su Low Id, o High Mask su High Id.

Na, o jeigu filtro režimas yra sąrašinis, tai tikrinamos dvi sąlygos:

WTF == FILTER_MASK
WTF == FILTER_ID

Jei sekėte mintį, tai galite suprasti, jog antruoju atveju ir 16 bitų režimu galima patikrinti keturis WTF vienu filtru.

Pasigaminau kodą, su kuriuo vienas procesoriukas siunčia tris CAN žinutes. Pirma žinutė yra duomenų su ID A 1 ir ID B 2. Antroji — ID A 2 ir ID B 2, bet su RTR bitu (užklausos žinutė). Trečioji — tokia pati, kaip antroji, tačiau be RTR bito, t.y. duomenų žinutė:

	tx_msg.ExtId = SET_ID_A(1) | 2;
	tx_msg.IDE = CAN_ID_EXT;
	tx_msg.RTR = CAN_RTR_DATA;
	tx_msg.DLC = 4;
	tx_msg.Data[0] = 1;
	tx_msg.Data[1] = 2;
	tx_msg.Data[2] = 3;
	tx_msg.Data[3] = 4;

	mbox = CAN_Transmit(CAN1, &tx_msg);
	while(CAN_TransmitStatus(CAN1, mbox) == CAN_TxStatus_Pending);

	tx_msg.ExtId = SET_ID_A(2) | 2;
	tx_msg.IDE = CAN_ID_EXT;
	tx_msg.RTR = CAN_RTR_REMOTE;
	tx_msg.DLC = 0;

	mbox = CAN_Transmit(CAN1, &tx_msg);
	while(CAN_TransmitStatus(CAN1, mbox) == CAN_TxStatus_Pending);

	tx_msg.ExtId = SET_ID_A(2) | 2;
	tx_msg.IDE = CAN_ID_EXT;
	tx_msg.RTR = CAN_RTR_DATA;
	tx_msg.DLC = 1;
	tx_msg.Data[0] = 58;

	mbox = CAN_Transmit(CAN1, &tx_msg);
	while(CAN_TransmitStatus(CAN1, mbox) == CAN_TxStatus_Pending);

Dabar norėčiau, kad antrasis įrenginys gautų tiktai pirmą ir antrą žinutę. T.y. jo turėtų nedominti duomenų žinutė su ID A 2. Kaip pasidaryti tokius filtrus? Aš naudosiu šabloninius 32 bitų filtrus.

Kaip sakiau, mane pradžiai domina tik ID A. Pirmosios žinutės ID A yra vienetukas. Tai reiškia, kad atlikus loginę daugybą su šablonu man tas vienetukas turi gautis. 32 bitų filtrui ID A yra nuo 31 iki 20 bito (skaičiuojant nuo nulio). Taigi filtro šabloną ir filtro ID, skirtus ID A, reikia pastumti per 21 bituką į kairę. Sukonstravau va kažką tokio:

	CAN_FilterInitTypeDef  can_filter_init;
	uint32_t filter_mask = 0x7FF << 21;
	uint32_t filter_id   = 0x01  << 21;
	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 = (uint16_t) ((uint32_t) filter_id >> 16);
	can_filter_init.CAN_FilterIdLow = 0x0000;
	can_filter_init.CAN_FilterMaskIdHigh = (uint16_t) ((uint32_t) filter_mask >> 16);
	can_filter_init.CAN_FilterMaskIdLow = 0x0000;
	can_filter_init.CAN_FilterFIFOAssignment = 0;
	can_filter_init.CAN_FilterActivation = ENABLE;
	CAN_FilterInit(&can_filter_init);

0x7FF yra 211-1. Na, ID A dydis. Aš visus bitukus dauginu logiškai iš vienetukų ir tikiuosi gauti vienetuką. Kadangi šitas reikalas 32 bitui telpa viršutinėje skaičiuko dalyje, tai užpildžiau tik High Mask ir High ID. Pabandžiau, suveikė. Antrasis įrenginys „pamatė“ tik pirmąją žinutę, antrosios ir trečiosios — nebe.

Konstruojam filtrą žinutei su ID A 2 ir RTR bituku:

	filter_mask = 0x7FF << 21 | 0x02;
	filter_id   = 0x02  << 21 | 0x02;
	can_filter_init.CAN_FilterNumber = 1;
	can_filter_init.CAN_FilterMode = CAN_FilterMode_IdMask;
	can_filter_init.CAN_FilterScale = CAN_FilterScale_32bit;
	can_filter_init.CAN_FilterIdHigh = (uint16_t) ((uint32_t) filter_id >> 16);
	can_filter_init.CAN_FilterIdLow = (uint16_t) filter_id & 0xFFFF;
	can_filter_init.CAN_FilterMaskIdHigh = (uint16_t) ((uint32_t) filter_mask >> 16);
	can_filter_init.CAN_FilterMaskIdLow = (uint16_t) filter_mask &0xFFFF;
	can_filter_init.CAN_FilterFIFOAssignment = 0;
	can_filter_init.CAN_FilterActivation = ENABLE;
	CAN_FilterInit(&can_filter_init);

Kaip minėjau, 32 bitų filtro pradžioje yra tuščias bitukas. Todėl RTR bitukas yra antroje pozicijoje (taigi iš ten ir 0x02). Užkroviau firmwarę — veikia. Antrasis įrenginukas reaguoja tik į pirmą ir antrą žinutę, trečiąją ignoruoja. Smagums, gėris ir grožis 🙂

Advertisements

Parašykite komentarą

Įveskite savo duomenis žemiau arba prisijunkite per socialinį tinklą:

WordPress.com Logo

Jūs komentuojate naudodamiesi savo WordPress.com paskyra. Atsijungti / Keisti )

Twitter picture

Jūs komentuojate naudodamiesi savo Twitter paskyra. Atsijungti / Keisti )

Facebook photo

Jūs komentuojate naudodamiesi savo Facebook paskyra. Atsijungti / Keisti )

Google+ photo

Jūs komentuojate naudodamiesi savo Google+ paskyra. Atsijungti / Keisti )

Connecting to %s