Меню

Фильтры для can шин

CAN-шина и stm32 — часть вторая — фильтры и два CAN

Первая часть закончилась на том, что я обещал рассказать про настройку фильтров и работу двух CAN-интерфейсов на одном камне. Начнём с фильтров.

Фильтры

Как уже говорилось ранее, у каждого CAN-интерфейса есть 14 фильтров. Фильтры так же называются фильтрами-банками или просто банками. Каждый фильтр имеет порядковый номер (счёт идёт он нуля), и представляет из себя два 32-х битных регистра (далее буду называть их «регистры фильтра», первый и второй). Каждый фильтр имеет свои «регистры фильтра».

В двух словах, фильтр работает так: в первый «регистр фильтра» мы прописываем некий идентификатор пакета, а во второй некую маску, в результате чего CAN будет принимать только некоторый диапазон кадров, а все остальные отбрасывать. Это первый способ. Второй способ: в один или оба «регистра фильтра» мы прописываем конкретные идентификаторы кадров, которые хотим получать — всё остальное будет отброшено.

Фильтр нельзя настроить как исключающий, то есть если нужно принимать все кадры кроме какого-то одного, например 0x0296, придётся настроить два фильтра, так чтобы они принимали кадры с 0x0000 по 0x0295, и с 0x0297 по 0x07FF.

Для гибкой настройки фильтрации есть четыре варианта использования «регистров фильтра» для каждого фильтра. Картинка из референс мануала иллюстрирует это…


Красными полосками я разделил варианты использования «регистров фильтра».

Первый и второй варианты подходят для фильтрации как стандартных (11 бит), так и расширенных (29 бит) идентификаторов, а третий и четвёртый только для стандартных. Теперь давайте рассмотрим всё это подробно.

Первый вариант — 32-Bit Filter — Identifier Mask

Заранее предупреждаю — будет сложно, так что приготовьтесь

В первый «регистр фильтра» (ID на картинке ) записываться идентификатор кадра, а во второй (Mask на картинке ) маска, которая будет накладываться на этот идентификатор и сравниваться с этим идентификатором. Выглядит запутано, но не пугайтесь, ниже всё разъяснится. Таким образом фильтр будет содержать определённый диапазон идентификаторов, которые будут приняты (не отброшены).

Поскольку наш CAN-интерфейс принимает все кадры, а потом уже аппаратно отбрасывает те, которые не соответствуют настройкам фильтра, то далее по тексту под словом «принимать» я буду подразумевать, что кадр не был отброшен.

Для примера будем настраивать фильтр №0.

Указываем режим работы фильтра…

Режим идентификатора и маски. Обращаю ваше внимание на этот параметр, если записать сюда другой макрос (CAN_FILTERMODE_IDLIST), тогда фильтр будет работать по другому. Поскольку эти макросы схожие, будьте внимательны.

Указываем масштаб (размерность) фильтра…

32 бита, говорит о том, что фильтроваться могут либо стандартные (11 бит) идентификаторы, либо расширенные (29 бит).

Далее, в прошлой части мы писали такой код…

Это два «регистра фильтра», каждый из которых условно разделён на старшую и младшую часть. Везде записаны нули чтоб принимать все кадры.

Сейчас мы настроим фильтр для работы с идентификаторами стандартных кадров, то есть с 11-битными идентификаторами.

Не смотря на то, что размерность фильтра у нас указана 32 бита (CAN_FILTERSCALE_32BIT), мы можем настраивать фильтр для работы и со стандартными (11 бит), и с расширенными (29 бит) идентификаторами. Если бы мы указали размерность фильтра 16 бит (CAN_FILTERSCALE_16BIT), тогда фильтр работал бы только со стандартными кадрами, об этом речь пойдёт ниже.

Предположим что мы хотим принимать идентификаторы с 0x0100 по 0x0107, тогда в старшую часть первого «регистра фильтра» записываем начальный идентификатор, со сдвигом…

В младшую часть пишем нули.

Сдвиг делается потому, что идентификатор у нас 11-ти битный, и согласно рисунку для него выделена старшая часть, старшей части «регистра фильтра». Такое вот получилось «масло масляное».


Mapping показывает что здесь должен лежать стандартный идентификатор — STDID — восемь бит в первом октете (слева) и три во втором.


В бинарном формате число 0x0100 выглядит так — 0000000100000000, а поскольку количество стандартных идентификаторов не может превышать 0x07FF (0000011111111111), то первые пять нулей никому не нужны, поэтому «выкидываются на мороз».

В целом, все эти картинки с распределением ноликов и единичек вам не так уж и нужны, однако для общего развития и упрощения понимания будут полезны.

Переходим к старшей части второго «регистра фильтра». Сюда мы записываем (опять же со сдвигом) число 0x07F8

Читайте также:  Срок службы шин белшина

Вот это поворот, скажет неискушенный читатель, наверное автор что-то перепутал. Но нет, всё правильно, и сейчас будем с этим разбираться.

В младшую часть второго «регистра фильтра» мы опять записали нолик, а число 0x07F8 (0000011111111000) легло у нас в старшую часть второго «регистра фильтра» вот так…


Первые (слева) пять ноликов опять же выкинуты, по описанным выше причинам.

Таким образом, число 0x07F8 у нас является маской. Немного забегая вперёд, скажу что это число нужно подбирать самостоятельно, экспериментальным путём. Теперь давайте разбираться как это работает.

Теперь смотрите что получается. Когда прилетает какой-то идентификатор, он попадает в систему фильтрации, и происходит следующее: между прилетевшим идентификатором и маской выполняется побитовая операция «И» (она же AND, она же &), и далее этот результат сравнивается с тем, что записано в «первом регистре» фильтра, в нашем случае это число 0x0100. Если результат совпадает, тогда идентификатор (кадр с этим идентификатором) принимается, если нет — отбрасывается.

Чтоб внести ясность можно описать этот механизм простой программой…

ID — это то, что мы записали в первый «регистр фильтра».
Mask — маска, записанная во второй «регистр фильтра».

Цикл for имитирует прилетающие идентификаторы, от 0 до 0x07FF (0x0800 — 1, максимальное кол-во стандартных идентификаторов) . В условии if на прилетевший идентификатор накладывается маска, и если полученный результат равен 0x0100, прилетевший идентификатор будет принят CAN’ом (выведен на печать).

Результатом работы программы будет вот такой вывод…


Идентификаторы с 0x0100 по 0x0107, которые мы планировали принимать.

По ссылке можно качнуть программу, которая выводит значения в HEX и бинарном формате…


С её помощью можно методом «тыка» подбирать маску. Программа написана для Линукса, но должна скомпилиться и для Windows. Либо просто перенесите её на stm.

Если мы изменим в маске последнюю цифру с 0x07F8 на 0x07FC, то наш CAN будет принимать идентификаторы с 0x0100 по 0x0103…

Совсем не обязательно настраивать фильтр так, чтобы принимаемые идентификаторы были последовательны в порядке возрастания, можно делать как угодно, всё зависит от ID и Mask. Например если в ID записать 0x0200, а маску 0x03FD, тогда будут приниматься идентификаторы 0x0200, 0x0202, 0x0600 и 0x0602…

Чтобы понять как подбирать маску взгляните на последнюю картинку. Если бит в маске равен единице значит и соответствующий бит в прилетевшем идентификаторе тоже должен быть равен единице. Если же бит в маске равен нулю, тогда не важно чему равен соответствующий бит в прилетевшем идентификаторе.

Первый бит в маске (слева направо) равен нулю, соответственно не важно чему равен первый бит у идентификаторов, которые будут приняты, может быть ноль или единица. Второй бит в маске равен единице, поэтому принимаются только те идентификаторы, у которых второй бит равен единице. Десятый бит в маске равен нулю, значит не важно чему равен этот бит у идентификаторов, которые будут приняты.

Для закрепления понимания давайте сделаем маску, у которой все биты кроме трёх последних будут равны единице…


Вуаля. Теперь мы принимаем идентификаторы в диапазоне с 0x0200 по 0x0207. Обратите внимание, что маска такая же как и для идентификаторов с 0x0100 по 0x0107. Это ничего не значит, просто к слову.

В общем думаю более менее всё понятно. Берёте какой-нибудь идентификатор, придумываете маску, скармливаете это программе и смотрите какие идентификаторы будут приняты.

Теперь давайте мысленно вернёмся к началу статьи, вспомним что мы хотели принимать идентификаторы с 0x0100 по 0x0107, и закончим настройку фильтра №0.

В результате у нас получится следующий код…

Последний параметр просто включает фильтр, а вот про предпоследний нужно сказать пару слов. Как вы помните у CAN’а есть два приёмных буфера CAN_RX_FIFO0 и CAN_RX_FIFO1. В данном случае мы указали CAN_RX_FIFO0, это означает что все принятые кадры прошедшие через фильтр №0 попадут CAN_RX_FIFO0. Отсюда вытекает, что настроив несколько фильтров, мы можем указать в какой именно буфер будут сваливаться принятые кадры. Теперь давайте настроим ещё один фильтр.

Читайте также:  Датчики давления в шинах рав 4 где находится

Возьмём последний пример и будем принимать идентификаторы с 0x0200 по 0x0207 через фильтр №1…

Указываем только номер фильтра и прописываем значения в «регистры фильтра». Все остальные настройки будут автоматически взяты из настроек фильтра №0.

Таким образом инициализация CAN’а и фильтров будет выглядеть так…

Если мы хотим помещать кадры прошедшие через фильтр №1 в буфер CAN_RX_FIFO1, тогда это нужно указать. И вообще, если работаете с разными буферами, то не лишним будет указывать это везде чтоб не запутаться, хуже не будет.

Таким образом кадры с 0x0100 по 0x0107 будут падать в буфер CAN_RX_FIFO0, а кадры с 0x0200 по 0x0207 в буфер CAN_RX_FIFO1.

Далее если есть необходимость вы можете настроить ещё сколько нужно фильтров — максимальный №13.

Режим фильтра (CAN_FILTERMODE_IDMASK) и размерность (CAN_FILTERSCALE_32BIT) тоже можно настраивать для каждого фильтра отдельно, но об этом позже.

Теперь рассмотрим фильтрование 29-ти битных идентификаторов. Вы же помните, что мы всё ещё разбираем первый вариант использования «регистров фильтра», который подразумевает фильтрование 11-ти и 29-ти битных идентификаторов

Поскольку настройки режима и размерности фильтра здесь те же самые, что и в предыдущих примерах, мы настроим только номер фильтра и «регистры фильтра», ну и укажем буфер CAN_RX_FIFO1 (можно CAN_RX_FIFO0, как хотите). Разумеется, если вы настраиваете только один фильтр, тогда нужно прописать все значения. При этом совсем не важно какой номер фильтра вы укажите, можно любой с 0 до 13.

Настроим фильтр №2, а принимать будем кадры с идентификаторами в диапазоне от 0x00010000 до 0x00010007

Здесь у нас «регистры фильтра» используются полностью, тоже делается сдвиг, чтоб биты встали в нужные места, и ещё в младшие части добавляется | 0x04.

ID и Mask лягут в области STID и EXID…


… а в бит IDE будет записана единичка с помощью | 0x04 (00000100). Это сообщит системе, что фильтр настроен на работу с расширенным идентификатором. То есть всегда, когда настраиваем фильтр на работу с расширенным кадром, добавляем | 0x04.

Бит RTR не трогаем, однако если захотим настроить фильтр на работу с Remote Frame, тогда в него надо записать единицу. Это касается и стандартных, и расширенных кадров.

Операции с ID и маской здесь точно такие же как и в предыдущем примере, и вот такая же программка, только для 29-ти битных идентификаторов…

Дальше вы уже знаете что с этим делать.

Вы может настроить несколько фильтров для расширенных кадров, и совмещать их со стандартными, как мы это только что сделали. Сейчас наш CAN принимает стандартные идентификаторы с 0x0100 по 0x0107 и с 0x0200 по 0x0207, складывая их в CAN_RX_FIFO0, и расширенные с 0x00010000 по 0x00010007, складывая их в CAN_RX_FIFO1.

На этом первый вариант закончен, самое трудное позади, дальше будет легко.

Второй вариант — 32-Bit Filters — Identifier List

Здесь всё просто, фильтр можно настроить на приём только одного или двух конкретных идентификатора, они просто записываются в первый и второй «регистры фильтра», так же со сдвигом. Единственное важное отличие от предыдущего варианта, это режим фильтра, тут он будет CAN_FILTERMODE_IDLIST.

Фильтр №3 будет принимать два стандартных идентификатора — 0x06D9 и 0x04C6…

А фильтр №4 будет принимать два расширенных идентификатора — 0x000006D9 и 0x000004C6…

Если нужно принимать только один идентификатор, тогда во второй «регистр фильтра» нужно прописать то же, что и в первом…

Получится что CAN будет два раза фильтровать одно и то же, но другого варианта видимо нет, так как если записать туда просто нули, будет приниматься идентификатор 0x0000.

Третий вариант — 16-Bit Filters — Identifier Mask

Читайте также:  Как узнать есть can шина в автомобиле или нет


Этот вариант, как и первый, работает с маской, но может фильтровать только стандартные (11 бит) кадры. Достоинство этого варианта перед первым в том, что один фильтр можно настроить на два диапазона идентификаторов. Это достигается за счёт использования «регистров фильтра» полностью, а не только старшие части.

Будем принимать кадры в диапазонах с 0x0320 по 0x0323, и с 0x0480 по 0x0487.

Режим фильтра на работу с маской…

Важное отличие от предыдущих примеров — размерность фильтра указываем 16 бит…

В старшую и младшую часть первого «регистра фильтра» записываем начальные ID первого и второго диапазона…

А в старшую и младшую часть второго «регистра фильтра» записываем соответствующие маски…

Если нужно принимать только один диапазон, тогда вместо второго идентификатора и маски нужно прописать то же что и для первого…

Опять же, получится что CAN будет выполнять лишнюю работу, но если записать туда нули, тогда будут приниматься вообще все стандартные кадры.

Четвёртый вариант — 16-Bit Filters — Identifier List

Этот вариант похож на второй, только опять же, мы можем фильтровать не два конкретных идентификатора, четыре. И как вы уже наверно догадались, они могут быть только стандартными.

Будем принимать идентификаторы 0x0690, 0x0693, 0x0696 и 0x0699.

Настраиваем фильтр №6, и меняем режим на CAN_FILTERMODE_IDLIST…

Опять же, если вместо какого-то идентификатора вписать 0x0000, то будет приниматься кадр 0x0000.

С последним вариантом, равно как и с разбором фильтров, покончено.

В довершение осталось сказать что фильтр настроенный как CAN_FILTERSCALE_32BIT имеет более высокий приоритет чем CAN_FILTERSCALE_16BIT.

То есть если мы настроим два фильтра вот так…

… все кадры будут складываться в буфер 1.

Так же и номера фильтров имеют приоритет. Чем меньше номер фильтра, тем выше у него приоритет. В примере ниже, все кадры будут складываться опять же в буфер 1.

А если поменять номера фильтров, то в буфер 0. Всё.

Два CAN’а на одном камне

Единственной особенностью CAN2 является то, что он не может работать при отключённом CAN1, во всём остальном настройки CAN2 такие же как у CAN1, кроме номеров фильтров. Чтоб CAN2 заработал, достаточно просто активировать CAN1 и настроить у него хотя бы один фильтр.

То есть инициализация обоих CAN’ов будет такая…

Для CAN1 фильтры с 0 по 13, для CAN2 фильтры с 14 по 27. У CAN2 есть дополнительный параметр — sFilterConfig.SlaveStartFilterBank = 14 , сообщающий системе с какого номера начинаются фильтры CAN2. Добавляются фильтры так же как описано выше.

Разумеется CAN’ы можно настраивать на разные скорости. Собственно не знаю что ещё сказать.

Не смотря на то, что в мануалах CAN2 называют Slave, это полностью самостоятельный интерфейс, просто он не может работать без включённого CAN1, так как именно CAN1 включает всю систему буферов, фильтров, почтовых ящиков, и т.д. Видимо это какая-то особенность проектирования камня. Наверно сначала не планировали два CAN’а делать, а потом передумали и приладили

Если CAN1 не используется и к нему не подключён трансивер, тогда ножку RX нужно подтянуть к плюсу, можно внутреннюю подтяжку пользовать.

Если CAN1 не используется, тогда его запускать не нужно.

CAN_IT_RX_FIFO0_MSG_PENDING — это может быть CAN_IT_RX_FIFO1_MSG_PENDING, зависит от того какой буфер вы указали в настройках фильтра (CAN_RX_FIFO0 или CAN_RX_FIFO1).

Колбек для приёма, если у обоих CAN’ов задействован буфер CAN_RX_FIFO0…

Если для обоих CAN’ов задействован буфер CAN_RX_FIFO1…

Обратите внимание, что в названии функции . RxFifo0… поменялось на . RxFifo1.

Если для разных CAN’ов настроены разные буферы…

Если CAN1 не используется, тогда для него ничего писать не нужно.

Статью писал долго, с перерывами, так что если что упустил/забыл — пишите в чат.

Это всё, всем спасибо

Источник

Adblock
detector