- трохи теорії
- Відправлення повідомлень
- прийом повідомлень
- Переривання по отриманню повідомлень
- фільтрація ідентифікаторів
- масштабованість
- Filter Math Index - індексування відповідностей фільтрів
- Правила призначення пріоритету для фільтрів
- зберігання повідомлень
- Обробка помилок
- Реалізація
- Стандартний розмір кадру повідомлення CAN (11 біт)
- Розширений розмір кадру повідомлення CAN (29 біт)
Мікроконтролери серії STM32 обробляють вхідні повідомлення на рівні заліза. І тільки ті повідомлення, які пройшли фільтр, потрапляють в поштові ящику. Це дозволяє зменшити навантаження на процесор і не відволікати його на зайву обробку переривань.
Спробуємо розібратися, як це відбувається і взагалі, що ж таке поштові скриньки.
трохи теорії
Периферія Basic Extended CAN (bxCAN) - це інтерфейс мережі CAN. Підтримує протоколи CAN версії 2.0A і B.
Він був розроблений для управління великою кількістю вхідних повідомлень з максимальною ефективністю і з мінімальним завантаженням процесора. Він також відповідає за пріоритетні вимоги для передачі повідомлень.
У сучасних системах, число CAN - вузлів мережі зростає і часто кілька мереж пов'язані один з одним через шлюзи. Таким чином, збільшується і кількість повідомлень в цих системах. До того ж до систем реєстрації додатків додаються і повідомлення мережевого управління та діагностики.
Все вищесказане призводить до того, що для обробки Потрібно знати все більше процесорного часу і тому розробники ST Microelectronix реалізували обробку повідомлень на рівні периферії, звільнивши процесорний час для інших, більш важливих, завдань. Модуль bxCan дозволяє отримувати і відправляти повідомлення повністю автономно.
Ось так виглядає схема роботи bxCan:
Мал. 1. Схема роботи bxCan
Відправлення повідомлень
При передачі повідомлень використовуються три поштові скриньки. Планувальник передачі сам вирішує, яке повідомлення повинні бути передано першим. Залежно він налаштувань bxCan це може бути організовано за форматом FIFO (First Input First Output), або, виходячи з пріоритету повідомлень - повідомлення з більш високим пріоритетом відправляється раніше, незалежно від того, в якому поштовому ящику воно зберігається.
Для того щоб передати повідомлення в шину, додаток повинен вибрати один з трьох поштових скриньок.
Після приміщення повідомлення в поштову скриньку, програма більше не зможе змінювати його, а сам поштову скриньку входить в режим очікування найвищого пріоритету для передачі повідомлення в шину. Як тільки він його отримає, bxCan переходить в режим очікування, коли CAN шина стане неактивною, і після цього спробує виконати його передачу.
Після того, як повідомлення буде передано, поштовий ящик звільниться і буде готовий для прийому чергового повідомлення з коду програми. bxCan в разі успішної передачі повідомлення, встановить біти RQCP і TXOK в регістрі CAN_TSR.
Якщо передача не вдалася, то причина вказується бітом ALST в регістрі CAN_TSR в разі програшу арбітражу і / або бітом TERR - в разі виявлення помилки передачі.
прийом повідомлень
Для прийому повідомлень CAN використовуються два буфера FIFO, в кожному з яких по три поштові скриньки. Для того, щоб знизити навантаження на мікроконтролер, спростити узгодженість програмного забезпечення та гарантувати максимальну безпеку даних, буфер FIFO управляється повністю апаратними засобами. Додаток отримує доступ до повідомлень, збережених в FIFO, через один з трьох вхідних поштових скриньок буфера.
Перед приміщенням повідомлення в поштову скриньку, воно повинно пройти перевірку. Повідомлення вважається таким, що перевірку, якщо воно було отримано відповідно до вимог протоколу CAN (в повідомленні присутні всі біти, аж до останнього біта закінчення передачі пакета) і він пройшов через фільтрацію ідентифікатора повідомлення.
Ось як виглядає приблизна схема руху повідомлень в буфері FIFO:
Мал. 2. Схема обробки вхідного повідомлення
Починаючи з моменту, коли буфер повідомлень порожній, при отриманні першого валидного повідомлення воно зберігається в FIFO, у якого встановлюється статус Pending-1. "Залізо" сигналізує про це, встановлюючи біти FMP [1: 0] регістра CAN_RFR в значення 01b. Після цього повідомлення в поштовій скриньці FIFO є для програми.
Програмне забезпечення зчитує вміст поштової скриньки і звільняє його установкою біта RFOM в регістрі CAN_RFR. FIFO знову стає порожнім. Але якщо в цей же час, поки програма зчитує повідомлення, надходить наступне, то воно буде збережено в FIFO і йому (буферу) буде присвоєно статус Pending_2 (установка бітів FMP [1: 0] регістра CAN_RFR в значення 10b). А після отримання програмою першого повідомлення для буфера буде встановлено статус Pending_1.
Якщо програма не звільняє поштову скриньку, то наступні валідниє повідомлення, після першого, будуть збережені в FIFO, якому буде присвоєно статус Pending_2, а потім Pending_3 (установка бітів FMP [1: 0] регістра CAN_RFR в значення 11b).
При заповненні всіх трьох поштових скриньок, програмне забезпечення повинно звільнити вихідний поштову скриньку шляхом установки біта RFOM. Це потрібно для того, щоб звільнити поштову скриньку для зберігання наступного валидного повідомлення. Якщо цього не зробити, то наступні повідомлення будуть втрачені.
Після того, як буфер FIFO знаходиться в статусі Pending_3 (тобто всі три поштові скриньки будуть заповнені), наступний валідний прийом повідомлення призведе до перевантаження (OverRun) і повідомлення буде втрачено.
bxCan сигналізує про стан перевантаження отримання повідомлень в буфер FIFO шляхом установки біта FOVR регістра CAN_RFR. Яке повідомлення при цьому втрачається, залежить від конфігурації FIFO:
• Якщо функція блокування FIFO відключена (встановлений біт RFLM в регістрі CAN_MCR), останнім прийняте повідомлення буде перезаписано новим надійшли, які пройшли перевірку, сполученням;
• Якщо функція блокування FIFO включена (скинуто біт RFLM в регістрі CAN_MCR), знову прийняті повідомлення будуть ігноровані. Таким чином, програмне забезпечення буде бачити в поштовій скриньці найстаріші повідомлення, всі наступні будуть загублені.
Переривання по отриманню повідомлень
Як же програма дізнається про те, що прийшло нове повідомлення?
Після того, як повідомлення було збережено в FIFO біти FMP [1: 0] оновлюються і генерується запит переривання (якщо встановлений біт FFIE в регістрі CAN_IER). Коли заповнюються всі три поштові скриньки, встановлюється біт FULL в регістрі CAN_IER.
Необхідно звернути увагу на те, що при перевантаженні біт FOVR встановлюється і генерується переривання тільки в тому випадку, якщо встановлений біт FOVIE регістра CAN_IER. В іншому випадку все нові повідомлення при перевантаженні будуть повністю ігноруватися і не буде викликатися переривань по їх прийому, поки не звільниться хоча б один поштову скриньку.
фільтрація ідентифікаторів
Ми розібралися, як обробляються повідомлення з CAN шини. Однією з умов приймання повідомлень і передачі його в поштову скриньку служить проходження ідентифікатора повідомлення через фільтр.
У протоколі CAN ідентифікатор повідомлення не пов'язаний з адресою вузла, а пов'язаний з вмістом повідомлення. Отже, передавач передає своє повідомлення всім одержувачам, а вже при прийомі повідомлення сам приймальний вузол вирішує (в залежності від значення ідентифікатора) потребує програмне забезпечення в цьому повідомленні чи ні. Якщо повідомлення потрібно, то воно копіюється в SRAM. Якщо ж повідомлення не потрібно, то програма може навіть і не дізнатися про те, що воно взагалі приходило.
Для того, щоб виконати цю вимогу, контролер bxCan забезпечує 28 настроюються і масштабованих банків фільтрів для додатка (по 14 на кожне пристрої CAN в мікроконтролері). Налаштування цих фільтрів в додатку дозволяє відправляти в поштову скриньку тільки ті повідомлення, які він може обробляти. Решта повідомлення будуть відкинуті. Ця апаратна фільтрація економить ресурси процесора, які в іншому випадку були б необхідні для виконання фільтрації за допомогою програмного забезпечення. Кожен банк фільтрів складається з 32-бітових регістрів CAN FxR0 і CAN FxR1.
масштабованість
Для того, щоб оптимізувати і адаптувати фільтри до потреб додатків, кожен банк фільтрів можна масштабувати незалежно один від одного. Залежно від фільтра масштабу банк фільтра надає:
• Один 32-бітний фільтр для бітів STDID [10: 0], EXTID [17: 0], IDE і RTR;
• Два 16-бітних фільтра для бітів STDID [10: 0], RTR, IDE і EXTID [17:15].
Біт IDE (Identifier Extension Bit) означає, що фільтр призначений для розширеного кадру повідомлення, а біт RTR (Remote Transmission Request) - вказує на тип повідомлення "Remote".
Крім того, фільтри можуть бути налаштовані в режимі маски або в режимі списку ідентифікаторів:
• У режимі маски регістри ідентифікатора асоційовані з регістрами маски, яка визначає, які біти ідентифікатора повинні відповідати, а на які фільтр не буде звертати увагу.
• У режимі списку ідентифікаторів, регістри маски використовуються в якості регістрів ідентифікаторів. Таким чином, замість того щоб призначити один ідентифікатор і маску, вказуються два ідентифікатора, відбувається подвоєння (x4 для 16-ти бітних ідентифікаторів) кількості одиночних ідентифікаторів. Всі біти вхідного ідентифікатора повинні відповідати зазначеним бітам в регістрах фільтрів.
Трохи зрозуміліше стане, якщо ми звернемо увагу на малюнок:
Мал. 3. Налаштування фільтрації
Filter Math Index - індексування відповідностей фільтрів
Після того, як повідомлення було отримано в FIFO, воно стає доступним для обробки. Як правило, дані програми копіюються в осередку SRAM. Щоб скопіювати дані в потрібне місце, додаток повинен ідентифікувати дані за допомогою ідентифікатора. Щоб уникнути цього, а також, щоб полегшити доступ до комірки пам'яті SRAM, контролер може забезпечити індексування збіги фільтра.
Цей індекс зберігається в поштовій скриньці разом з повідомленням відповідно до пріоритетних правилами фільтра. Таким чином, кожне отримане повідомлення має відповідний індекс відповідності фільтру.
Filter Match Index може бути використаний двома способами:
• Порівняння індексу фільтра зі списком очікуваних значень.
• Використовувати Filter Index Match як індекс на масив, щоб отримати доступ до потрібної комірки пам'яті даних.
Для фільтрів без маски, контролеру більше немає необхідності порівнювати ідентифікатор. Якщо фільтр використовує маску, то програмне забезпечення зменшує кількість порівнянь, виробляючи порівняння тільки маскованих бітів. Значення індексу номера фільтра не бере до уваги стан активації банків фільтрів. Крім того, використовуються дві незалежні схеми нумерації, по одному для кожного FIFO.
Правила призначення пріоритету для фільтрів
Залежно від комбінацій налаштованих фільтрів, може вийти так, що ідентифікатор повідомлення успішно проходить через кілька фільтрів. У цьому випадку значення FMI зберігатися в приймальному поштовій скриньці і вибирається відповідно до наступних правил пріоритету:
• 32-бітний фільтр має пріоритет над 16-ти бітовим фільтром;
• Для фільтрів однакового масштабу, пріоритет режиму списку ідентифікаторів дається вище, ніж режим маски ідентифікатора;
• Для фільтрів однакового масштабу і режиму, пріоритет задається номером фільтра (чим менше число, тим вище пріоритет).
Розглянемо механізм фільтрації на прикладі:
Приклад для 3-х банків фільтрів в режимі списку 32-х бітних ідентифікаторів і банку фільтрів в режимі 32-х бітного ідентифікатора маски
Мал. 4.Прімер апаратної фільтрації повідомлень bxCan
Наведений вище приклад показує принцип фільтрації повідомлень bxCan. При отриманні повідомлення, ідентифікатор спочатку порівнюється з фільтрами, налаштованими в режимі списку ідентифікаторів. Якщо є збіг, то повідомлення буде збережено у відповідному буфері FIFO і індекс фільтра відповідності буде збережений в FMI (Filter Index Match). Як показано в прикладі, ідентифікатор збігається з ідентифікатором №2, таким чином, вміст повідомлення і FMI 2 зберігаються в буфері FIFO.
Якщо збігів, не знайдено, то потім ідентифікатор маски порівнюється з фільтрами, сконфігурованими в режимі маски.
Ну а якщо ідентифікатор не відповідає жодному з ідентифікаторів, налаштованих в фільтрах, то повідомлення відкидається апаратними засобами bxCan, не виказуючи його для отримання програмним забезпеченням.
зберігання повідомлень
Інтерфейс взаємодії між програмним забезпеченням і апаратними засобами для повідомлень CAN реалізуються за допомогою поштових скриньок. Поштова скринька містить всю інформацію, пов'язану з повідомленням: ідентифікатор, дані, контроль, стан і мітку часу.
передача повідомлень
Програмне забезпечення поміщає повідомлення, що підлягають передачі, в порожній поштову скриньку для відправки повідомлень. Статус передачі вказується за допомогою апаратних засобів в регістрі CAN_TSR.
Таб. 1. Структура вихідного повідомлення Зсув відносно базового адреси повідомлення
(В байтах) Найменування регістра 0 CAN_TIxR 4 CAN_TDTxR 8 CAN_TDLxR 12 CAN_TDHxR
отримання повідомлень
Коли повідомлення отримано, воно стає доступним для програмного забезпечення в вихідному поштовій скриньці FIFO. Після того, як програма обробила повідомлення, воно повинно звільнити вихідний поштову скриньку FIFO за допомогою біта RFOM в регістрі CAN_RFR. Це необхідно для того, щоб було доступно наступне поштове повідомлення і для того, щоб звільнити поштову скриньку для прийому чергового повідомлення шини.
FMI зберігається в поле MFMI регістра CAN_RDTxR. 16 бітове значення штампа часу зберігаються в поле TIME [15: 0] регістра CAN_RDTxR.
Таб. 2. Структура вхідного повідомлення Зсув відносно базового адреси повідомлення
(В байтах) Найменування регістра 0 CAN_RIxR 4 CAN_RDTxR 8 CAN_RDLxR 12 CAN_RDHxR
Обробка помилок
Управління помилками, як описано в протоколі CAN, обробляються повністю апаратними засобами за допомогою лічильника помилок передачі (Transmit Error Counter) (значення TEC регістра CAN_ESR) і лічильника помилок прийому (Receive Error Counter) (значення REC регістра CAN_ESR), в яких відображається зменшення або збільшення кількості помилок з відповідно з поточним станом.
Для контролю стану шини і визначення стабільності мережі програмне забезпечення може переглянути значення обох лічильників.
Крім того, апаратні засоби можуть надати детальну інформацію про поточний стан в регістрі CAN_ESR. За допомогою регістра CAN_IER, програмне забезпечення може налаштувати генерацію переривань для оперативного виявлення і виправлення помилок.
Скидання стану Bus-Off
Пристрій переходить в стан Bus-Off, коли лічильник помилок передачі (TEC) перевищує значення 255 - це стан позначається бітом BOFF в регістрі CAN_ESR. У стані Bus-Off, bxCan більше не може як передавати повідомлення, так і приймати їх. Залежно від біта ABOM в регістрі CAN_MCR, bxCan самостійно позбавиться від режиму Bus-Off, або буде чекати, поки програмне забезпечення сама не скине цей стан. Але в обох випадках bxCan повинен чекати, принаймні, для послідовності відновлення, зазначеного в стандарті CAN (128 входжень 11-ти послідовних рецесивних біт на вході RX контролера).
Мал. 5. Схема обробки помилок bxCan
Якщо біт ABOM встановлений, то bxCan автоматично почне відновлення послідовності після того, як він увійшов в режим Bus-Off.
Якщо біт ABOM скинутий, програмне забезпечення повинно самостійно ініціювати відновлення, запитуючи bxCan для входу і виходу з режиму ініціалізації. Іншими словами - необхідно перезапустити і заново форматувати bxCan "вручну".
Примітка: В режимі ініціалізації bxCAN не контролює сигнал CANRX, тому він не може виконати послідовність відновлення. Для відновлення bxCAN повинен перебувати в нормальному режимі роботи.
Реалізація
З теорією покінчено, тепер приступаємо до реалізації.
Почнемо зі стандартного, коли наша програма буде обробляти всі повідомлення:
Лістинг №1. Фільтрація відключена - прийом всіх повідомлень // CAN filter init CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_FilterInitStructure.CAN_FilterNumber = 0; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // Режим роботи фільтра CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // Розрядність (масштабування) CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; // Старша частина фільтра CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; // Молодша частина фільтра CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; // Старша частина маски CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; // Молодша частина маски CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; // Номер буфера FIFO (у нас їх всього два) CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // Активність фільтра CAN_FilterInit (& CAN_FilterInitStructure);
При такій настройці всі повідомлення будуть проходити через апаратний фільтр і будуть поміщені у вхідний буфер FIFO (в даному прикладі в буфер FIFO0).
Стандартний розмір кадру повідомлення CAN (11 біт)
А тепер розглянемо приклад, коли нам потрібно відфільтрувати повідомлення для стандартного пакета з ідентифікаторами в діапазоні 0x07E0 - 0x07EF
Лістинг №2. Фільтрація включена - прийом стандартних повідомлень в діапазоні 0x07E0 - 0x07EF // CAN filter init CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_FilterInitStructure.CAN_FilterNumber = 0; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // Режим роботи фільтра CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // Розрядність (масштабування) CAN_FilterInitStructure.CAN_FilterIdHigh = 0x07E0 << 5; // Старша частина фільтра CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; // Молодша частина фільтра CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x07E0 << 5; // Старша частина маски CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; // Молодша частина маски CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; // Номер буфера FIFO (у нас їх всього два) CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // Активність фільтра CAN_FilterInit (& CAN_FilterInitStructure);
У старшій частині фільтра ми вказуємо ідентифікатор повідомлення, з яким будемо порівнювати маску, а в старшій частині маски - побітно маска для порівняння з ідентифікатором.
Зверніть увагу, що ідентифікатор для стандартного повідомлення розміщується в старшій частині фільтра зі зміщенням вліво на 5 біт. (Див. Рис.3)
Нуль в масці означає, що може бути будь-яке значення, одиниця - точна відповідність фільтру. В даному випадку через фільтр будуть проходити всі повідомлення, починаючи з 0x07E0 по 0x7EF. Якщо в якості маски ми вкажемо 0x07EF, то в фільтр буде потрапляти тільки повідомлення з ідентифікатором 0x07E0. Нагадаю, що максимальний номер повідомлення для стандартного кадру дорівнює 0x07EF (Обмеження протоколу CAN).
А що робити, якщо нам необхідна фільтрація декількох діапазонів?
Лістинг №3. Фільтрація включена - прийом стандартних повідомлень в діапазоні 0x00D0 - 0x00DF CAN_FilterInitStructure.CAN_FilterNumber = 1; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterIdHigh = 0x00D0 << 5; // Старша частина фільтра CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0F0 << 5; // Старша частина маски CAN_FilterInit (& CAN_FilterInitStructure);
Тут бачимо дещо інший текст коду. У попередньому прикладі ми активували фільтр №0, а в цьому прикладі ми вже активуємо фільтр №1. Так як у нас частина значень не змінюється, то ми можемо їх опустити, і вказати тільки ключові параметри для нового фільтра - номер фільтра і новий діапазон адрес. Тепер всі повідомлення з ідентифікаторами в діапазоні 0x00D0 - 0x00DF також будуть проходити через фільтр і потрапляти в наш обробник переривання. Всі інші ідентифікатори відсіюватимуться на апаратному рівні.
Всього ми можемо використовувати 14 фільтрів (номера з 0-го по 13-й). Кожен фільтр може мати свою структуру і режим фільтрації (по масці або за ідентифікатором). Допускається, що повідомлення можуть пройти за двома різними фільтрам. Пріоритет повідомлення в цьому випадку буде виставлений згідно зі схемою, наведеною вищє .
В останніх двох прикладах ми використовували 32-х бітний фільтр. Для стандартного кадру повідомлення це не вигідно, так як ми витрачаємо одну клітинку фільтра, використовуючи її лише наполовину. Можна заощадити одну клітинку, налаштувавши фільтр наступним чином:
Лістинг №4. Фільтрація включена - прийом стандартних повідомлень в діапазоні 0x07E0 - 0x07EF і в діапазоні 0x00D0 - 0x00DF // CAN filter init CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_FilterInitStructure.CAN_FilterNumber = 0; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // Режим роботи фільтра CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit; // Розрядність (масштабування) CAN_FilterInitStructure.CAN_FilterIdHigh = 0x07E0 << 5; // Ідентифікатор фільтра №1 CAN_FilterInitStructure.CAN_FilterIdLow = 0x00D0 << 5; // Ідентифікатор фільтра №2 CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x07E0 << 5; // Маска фільтра №1 CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0F0 << 5; // Маска фільтра №2 CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; // Номер буфера FIFO (у нас їх всього два) CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // Активність фільтра CAN_FilterInit (& CAN_FilterInitStructure);
Тут ми вказали 16-ти бітний формат фільтра і згідно формату , Ми налаштували два діапазони ідентифікаторів в одному фільтрі, помістивши другий діапазон і маску ідентифікаторів в молодші частини фільтра і маски.
З фільтром по масці ми розібралися, тепер спробуємо розібратися зі списком ідентифікаторів.
Спочатку в 32-х бітному масштабі фільтра:
Лістинг №5. Фільтрація включена - прийом стандартних повідомлень з ідентифікаторами: 0x07E0, 0x07E5 // CAN filter init CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_FilterInitStructure.CAN_FilterNumber = 0; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList; // Режим роботи фільтра CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // Розрядність (масштабування) CAN_FilterInitStructure.CAN_FilterIdHigh = 0x07E0 << 5; // Старша частина ідентифікатора №1 CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; // Молодша частина ідентифікатора №1 CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x07E5 << 5; // Старша частина ідентифікатора №2 CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; // Молодша частина ідентифікатора №2 CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; // Номер буфера FIFO (у нас їх всього два) CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // Активність фільтра CAN_FilterInit (& CAN_FilterInitStructure);
У наведеному прикладі ми додали в фільтр два ідентифікатора для стандартного формату повідомлення. Тепер апаратними засобами будуть фільтрувати всі повідомлення, ідентифікатори яких не рівні 0x07E0 і 0x07E5.
Знову ж таки, для стандартного кадру невигідно використовувати 32-х бітний масштаб - оптимальніше буде використовувати 16-ти бітний:
Лістинг №6. Фільтрація включена - прийом стандартних повідомлень з ідентифікаторами: 0x0700, 0x07E0, 0x07E5 і 0x00D3 // CAN filter init CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_FilterInitStructure.CAN_FilterNumber = 0; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList; // Режим роботи фільтра CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit; // Розрядність (масштабування) CAN_FilterInitStructure.CAN_FilterIdHigh = 0x07E0 << 5; // Ідентифікатор №1 CAN_FilterInitStructure.CAN_FilterIdLow = 0x07E5 << 5; // Ідентифікатор №2 CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0700 << 5; // Ідентифікатор №3 CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x00D3 << 5; // Ідентифікатор №4 CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; // Номер буфера FIFO (у нас їх всього два) CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // Активність фільтра CAN_FilterInit (& CAN_FilterInitStructure);
Таким чином, максимально нам доступний список з 56 ідентифікаторів (4 ідентифікатора на фільтр помножені на 14 фільтрів).
Установка інших фільтрів в банку налаштовується аналогічно наведеним вище прикладам.
Розширений розмір кадру повідомлення CAN (29 біт)
Тепер розглянемо все теж саме, але стосовно до розширеного формату кадру, довжиною в 29 біт. Будемо фільтрувати всі повідомлення, ідентифікатори яких в діапазоні 0x1fbf9000 - 0x1fbf9fff.
Відповідно до наведеного вище малюнку , Для розширених ідентифікаторів використовується інше зсув, ніж для повідомлень стандартного кадру.
Лістинг №7. Фільтрація включена - прийом розширених повідомлень в діапазоні 0x1FBF9000 - 0x1FBF9FFF #define CAN_IDE_32 0b00000100 // Для 32-х бітного масштабу ... ... // CAN filter init CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_FilterInitStructure.CAN_FilterNumber = 0; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // Режим роботи фільтра CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // Розрядність (масштабування) CAN_FilterInitStructure.CAN_FilterIdHigh = (uint16_t) (0x1fbf9000 >> 13); // Старша частина фільтра CAN_FilterInitStructure.CAN_FilterIdLow = (uint16_t) (0x1fbf9000 << 3) | CAN_IDE_32; // Молодша частина фільтра CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (uint16_t) (0x1ffff000 >> 13); // Старша частина маски CAN_FilterInitStructure.CAN_FilterMaskIdLow = (uint16_t) (0x1ffff000 << 3) | CAN_IDE_32; // Молодша частина маски CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO1; // Номер буфера FIFO (у нас їх всього два) CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // Активність фільтра CAN_FilterInit (& CAN_FilterInitStructure);
В старшу частину фільтра ми поміщаємо біти ідентифікатора зі зміщенням вправо на 13 біт, а в молодшу - ідентифікатор зі зміщенням вліво на 3 біти (так як у нас формат ідентифікатора uint32_t, а використовуємо всього 29 біт). Аналогічно заповнюємо і маску.
Тепер невеликий приклад, чому ідентифікатор дорівнює 0x1fbf9000, а маска дорівнює 0x1ffff000:
0x1fbf9000 - 0001 1111 1011 1111 1001 0000 0000 0000 0x1ffff000 - 0001 1111 1 1 11 1111 1 11 1 0000 0000 0000
Якщо 10-й, 18-19 біти залишити в масці рівними ідентифікатором, то в список діапазонів потраплять вісім! (2 ^ 3) діапазонів розміром по 0xFFF, в яких будуть варіюватися ці біти. Нагадаю, що нуль в масці означає те, що в отриманому ідентифікатор може бути будь-яке значення, одиниця - точна відповідність фільтру.
Зверніть увагу, що молодші частини фільтра і маски заповнюють біт IDE (Identifier Extension Bit). Це потрібно для того, щоб вказати фільтру, що він використовується для розширеного формату кадру.
Цікаве спостереження: якщо біт IDE не встановлювати, то в деяких випадках фільтр відпрацює і для стандартного формату кадру і для розширеного, за умови збігу перших n біт в стандартному і розширених ідентифікатори і "правильним" чином налаштованої маски.
Подібно біту IDE, необхідно встановлювати біт RTR (Remote Transmission Request), якщо Ви фільтруєте пакети в режимі кадру Remote (див. Мал. 3 ).
аналогічно лістингу №3 , Коли нам потрібно встановити кілька діапазонів ідентифікаторів, налаштовується код і для розширеного ідентифікатора повідомлення, з тією лише різницею, що треба заповнювати і старшу і молодшу частини фільтра і маски:
Лістинг №8. Фільтрація включена - прийом розширених повідомлень в діапазоні 0x1AB03000 - 0x1AB03FFF CAN_FilterInitStructure.CAN_FilterNumber = 1; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterIdHigh = (uint16_t) (0x1AB03000 >> 13); // Старша частина фільтра CAN_FilterInitStructure.CAN_FilterIdLow = (uint16_t) (0x1AB03000 << 3) | CAN_IDE_32; // Молодша частина фільтра CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (uint16_t) (0x1FFFF000 >> 13); // Старша частина маски CAN_FilterInitStructure.CAN_FilterMaskIdLow = (uint16_t) (0x1FFFF000 << 3) | CAN_IDE_32; // Молодша частина маски CAN_FilterInit (& CAN_FilterInitStructure);
У цьому прикладі ми також змінюємо номер фільтра і заповнюємо значення ідентифікатора повідомлення і його маски. Все інше - стандартно.
Якщо нам необхідно налаштувати фільтр за списком ідентифікаторів, то ми виставляємо режим фільтра в "CAN_FilterMode_IdList" і заповнюємо поля фільтра і маски. В даному випадку поля маски фільтра призначені для введення ідентифікатора №2, заповнюються також, як і поля фільтра (Ідентифікатор №1).
Якщо нам не потрібен ідентифікатор №2, то поля маски заповнюємо нулями.
Лістинг №9. Фільтрація включена - прийом розширених повідомлень з ідентифікаторами: 0x1FBF9000 і 0x1AB03000 CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_FilterInitStructure.CAN_FilterNumber = 0; // Номер фільтра, доступні з 0 по 13 CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList; // Режим роботи фільтра CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // Розрядність (масштабування) CAN_FilterInitStructure.CAN_FilterIdHigh = (uint16_t) (0x1fbf9000 >> 13); // Старша частина ідентифікатора №1 CAN_FilterInitStructure.CAN_FilterIdLow = (uint16_t) (0x1fbf9000 << 3) | CAN_IDE_32; // Молодша частина ідентифікатора №1 CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (uint16_t) (0x1AB03000 >> 13); // Старша частина ідентифікатора №2 CAN_FilterInitStructure.CAN_FilterMaskIdLow = (uint16_t) (0x1AB03000 << 3) | CAN_IDE_32; // Молодша частина ідентифікатора №2 CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO1; // Номер буфера FIFO (у нас їх всього два) CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // Активність фільтра CAN_FilterInit (& CAN_FilterInitStructure);
Не забуваємо виставляти біт IDE, щоб bxCan знав, що фільтр призначений для розширеного кадру.
Також, для розширеного формату кадру, я вказав для поля "CAN_FilterFIFOAssignment" значення CAN_FIFO1. Це означає, що з фільтрів для розширеного формату всі повідомлення будуть складатися в буфер FIFO1. Але для цього необхідно включити використання переривань для буфера FIFO1, а також обробляти в перериванні отримання повідомлень з цього буфера.
Для STM32F10x необхідно додатково включити переривання "CAN1_RX1_IRQn". При появі повідомлень в буфері FIFO1, буде викликатися саме це переривання. (Для FIFO0 - повідомлення відпрацьовуються по перериванню "USB_LP_CAN1_RX0_IRQn")
Лістинг №10. Налаштування буферів FIFO0 і FIFO1 // NVIC Configuration // Enable CAN1 RX0 interrupt IRQ channel NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init (& NVIC_InitStructure); // CAN FIFO0 message pending interrupt enable CAN_ITConfig (CAN1, CAN_IT_FMP0, ENABLE); // Enable CAN1 RX1 interrupt IRQ channel NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init (& NVIC_InitStructure); // CAN FIFO1 message pending interrupt enable CAN_ITConfig (CAN1, CAN_IT_FMP1, ENABLE);
А це сам порядок обробки переривань:
Лістинг №11. Обробка прийому повідомлень FIFO0 і FIFO1 // Отримання повідомлень з буфера FIFO0 void USB_LP_CAN1_RX0_IRQHandler (void) {... if (CAN_GetITStatus (CAN1, CAN_IT_FMP0)! = RESET) {// Перевіримо поштову скриньку FIFO0 CAN_Receive (CAN1, CAN_FIFO0, & RxMessage); // Отримаємо повідомлення з FIFO0 ...} ...} // Отримання повідомлень з буфера FIFO1 void CAN1_RX1_IRQHandler (void) {... if (CAN_GetITStatus (CAN1, CAN_IT_FMP1)! = RESET) {// Перевіримо поштову скриньку FIFO1 CAN_Receive (CAN1, CAN_FIFO1, & RxMessage); // Отримаємо повідомлення з FIFO1 ...} ...}
Невелике зауваження стосовно основної ідеї проекту SmartMODE.info:
Моя думка, що поділ за різними буферам краще зробити за важливістю повідомлень, наприклад, системні повідомлення шини (синхронізація, режими охорони, повідомлення безпеки) відправляти в буфер FIFO1, а всі інші повідомлення, як найменш важливі, направляти в буфер FIFO0.
Як бачимо - нічого складного.
На цьому з фільтрами все. Готовий приклад Ви можете завантажити в кінці статті. Для налагодження прикладу зручно використовувати "режим Silent Loopback" з встановленими брікпоінтамі на відправку та отримання повідомлень в / з шини. Як завжди приклад протестований і готовий до використання на мікроконтролерах STM32 серії f10x. Написаний в CooCox.
Висновок
Спочатку, настройка фільтрації повідомлень здається досить складним для розуміння процесом, але розібравшись один раз - ми отримуємо зручний і практичний інструмент для роботи з CAN шиною.
Обробка помилок в рамках даної статті не розглядається, тому що це частина великої статті по помилках в роботі з CAN і методам їх виявлення, ідентифікації та запобігання.
За основу для статті взято матеріали Programming Reference (переклад мій) для мікроконтролерів STM32 серії F10x з моїми коментарями і поясненнями.
Ну і наостанок, у вкладення додані вихідні прикладу роботи з фільтрами, описані в цій статті. Зверніть увагу на файл "main.c" - якщо дуже часто відправляти повідомлення, а швидкість шини виставлена "маленька", то bxCan буде поміщати повідомлення в поштові скриньки швидше, ніж ми їх будемо звідти забирати. Як підсумок - ми або втратимо частину повідомлень, або потрібно обробляти помилки переповнення поштових скриньок.
А що робити, якщо нам необхідна фільтрація декількох діапазонів?