STM32 и протокол CAN. Настройка в STM32CubeMx.

Приветствую всех на сайте MicroTechnics! Недавно мы разбирались с теоретическими аспектами работы протокола CANконтролем ошибок, организацией арбитража сообщений на шине и т. д. Так вот, сегодня, как и обещал, займемся практической стороной вопроса – реализуем прием и передачу данных по CAN на микроконтроллере STM32.

Для настройки периферии будем использовать STM32CubeMx, в качестве среды разработки я, как обычно, беру IAR. Осталось упомянуть про выбранный контроллер – им сегодня будет STM32F103VE. Но, как вы помните, при работе с STM32 нет никакой проблемы в том, чтобы перейти на другой микроконтроллер или другую IDE 🙂

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

В итоге у нас задействованы следующие пины контроллера:

STM32CubeMx CAN pinout.

Здесь мы уже активировали модуль CAN:

STM32 CAN module.

Но пока мы его только включили, необходимо еще выполнить настройку. И самым важным параметром является, конечно, скорость обмена данными. В случае с CAN ситуация не совсем такая, как с другими модулями STM32, для задания скорости мы должны настроить временные характеристики интерфейса, а не просто задать конкретное число:

Baudrate settings.

Давайте разберемся, что это за значения и за что они отвечают. Время передачи одного бита в CAN складывается из:

CAN timings.

Все длительности оцениваются через понятие кванта времени, для задания которого мы устанавливаем значение предделителя (в этом проекте он равен 8). Таким образом, мы получаем длительность 1 кванта:

T_{q} = 222.2\medspace нс

Первый сегмент на этой схеме – SYNC_SEG – используется для синхронизации всех узлов сети CAN. Ожидается, что фронт сигнала должен находиться внутри именно этого сегмента.

Второй сегмент – BS1 (Bit segment 1). Стандарт CAN включает в себя два сегмента PROP_SEG и PHASE_SEG1. Оба этих сегмента в STM32 относятся к сегменту BS1. PROP_SEG нужен для компенсации физических задержек в сети. А PHASE_SEG1 используется для компенсации ошибки смещения фазы сигнала.

Сегмент BS1 определяет положение sample point (точки захвата). В этой точке модуль CAN анализирует уровень сигнала на шине, то есть определяет принят рецессивный или доминантный бит.

И третий сегмент – BS2 (Bit segment 2). Он представляет из себя сегмент PHASE_SEG2 интерфейса CAN. Его назначение такое же как и у PHASE_SEG1.

Сегмент BS2 определяет положение transmit point (точки передачи), то есть того момента времени, когда модуль CAN выдает на линию определенный бит.

Длительности этих сегментов таковы:

  • SYNC_SEG: 1 квант времени
  • Bit segment 1 (BS1): 1 – 16 квантов
  • Bit segment 2 (BS2): 1 – 8 квантов

Устанавливая длительности различных сегментов, мы получаем время передачи одного бита в квантах. Зная, длительность самого кванта, мы легко получаем время передачи бита в секундах. А уже из этого мы рассчитываем скорость передачи данных по шине. Только при настройке CAN нужно пройти в обратном направлении 🙂

Пусть мы хотим задать скорость обмена равной 500 Кбит/с. Тогда время передачи одного бита:

T_{bit} = \frac{1\medspace с}{500000\medspace} = 2000\medspace нс

Длительность одного кванта мы уже задали равной 222,2 нс. Таким образом, время 1-го бита в квантах:

T = \frac{T_{bit}}{T_{q}} = 9

Эти 9 квантов нужно распределить между тремя сегментами. SYNC_SEG фиксирован (1 квант), значит остается 8 квантов на сегменты BS1 и BS2. Ставим в этом примере поровну – по 4 кванта и получаем нужную нам скорость обмена данными. И, наконец, можем двигаться дальше!

Включаем прерывания CAN и генерируем код:

Настройка CAN для STM32.

В main() находим функцию инициализации CAN:

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_CAN_Init();

Но на самом деле, на этом настройка не заканчивается. Дело в том, что в STM32 есть очень полезная функция фильтрации сообщений по ID. Как вы помните, ID в CAN относится не к устройству в сети, а к сообщению. То есть одно и то же устройство может рассылать сообщения с разными ID. А поскольку сеть является широковещательной, то приемник будет получать кучу сообщений, среди которых ему нужны только некоторые. И вот для этого и существует фильтрация сообщений. Благодаря аппаратной поддержке не нужно программно проверять ID всех принятых сообщений, можно всего лишь изначально настроить периферию на прием только нужных сообщений!

Настраивается этот механизм следующим образом:

CAN_FilterTypeDef canFilterConfig;
canFilterConfig.FilterBank = 0;
canFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
canFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
canFilterConfig.FilterIdHigh = 0x0000;
canFilterConfig.FilterIdLow = 0x0000;
canFilterConfig.FilterMaskIdHigh = 0x0000;
canFilterConfig.FilterMaskIdLow = 0x0000;
canFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
canFilterConfig.FilterActivation = ENABLE;
canFilterConfig.SlaveStartFilterBank = 14;
HAL_CAN_ConfigFilter(&hcan, &canFilterConfig);

Собственно, за «пропускаемые» через фильтр ID отвечают эти поля структуры CAN_FilterTypeDef:

canFilterConfig.FilterIdHigh = 0x0000;
canFilterConfig.FilterIdLow = 0x0000;
canFilterConfig.FilterMaskIdHigh = 0x0000;
canFilterConfig.FilterMaskIdLow = 0x0000;
  • FilterIdHigh – старшая часть ID
  • FilterIdLow– младшая часть ID
  • FilterMaskIdHigh– старшая часть маски
  • FilterMaskIdLow– младшая часть маски

В данном случае все значения равны 0x0000, это означает, что абсолютно все сообщения будут проходить через фильтр.

Значения FilterIdHigh и FilterIdLow определяют идентификатор, с которым будет сравниваться ID принятого сообщения. А FilterMaskIdHigh и FilterMaskIdLow отвечают за битовую маску, которая, в свою очередь, определяет, какие биты идентификатора будут проверяться, а какие – нет. Единица в маске означает, что бит, который соответствует положению этой единицы, будет проверен.

Сейчас на примере все станет понятно! 🙂

Итак, пусть мы хотим принимать только сообщения с ID из диапазона – 0x200 – 0x20F. Тогда настройка будет такой:

canFilterConfig.FilterIdHigh = 0x200 << 5;
canFilterConfig.FilterIdLow = 0x0000;
canFilterConfig.FilterMaskIdHigh = 0x7F0 << 5;
canFilterConfig.FilterMaskIdLow = 0x0000;

Обратите внимание, что для стандартного идентификатора значения  необходимо сместить влево  на 5 бит.

Запишем в двоичном виде значения 0x200 – 0x20F, смещенные на 5 битов:

Фильтрация CAN сообщений.

Получается, что мы должны проверять 7 старших битов ID, одинаковые во всех идентификаторах. И, в итоге, получаем значение битов маски равным 0x7F0<<5 (единицы соответствуют тем битам, которые должны соответствовать заданному в FilterIdHigh значению).

После настройки фильтра мы включаем модуль CAN и разрешаем прерывания по приему данных в FIFO0:

  HAL_CAN_Start(&hcan);
  HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING);

Теперь для приема сообщений CAN мы просто переопределяем соответствующую callback-функцию:

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
  CAN_RxHeaderTypeDef msgHeader;
  uint32_t msgId = 0;
  uint8_t msgData[8];
  
  HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &msgHeader, msgData);
    
  if (msgHeader.IDE == CAN_ID_EXT)
  {
    msgId = msgHeader.ExtId;
  }
  else
  {
    msgId = msgHeader.StdId;
  }
}

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

В STM32F10x реализованы 3 так называемых mailbox’а, то есть по сути почтовых ящика, в которые мы можем складывать наши сообщения. Далее микроконтроллер аппаратно выбирает, какое сообщение отправить в линию. Можно настроить механизм аналогичный FIFO, то есть первое помещенное в mailbox сообщение и отправлено будет первым. А можно настроить периферию на отправку сообщений в соответствии с приоритетом самих сообщений.

Для каждого из mailbox’ов есть соответствующий callback, который вызывается по окончанию передачи:

  • void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan)
  • void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan)
  • void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan)

Отправим тестовое сообщение:

  if (HAL_CAN_GetTxMailboxesFreeLevel(&hcan) != 0)
  {
    CAN_TxHeaderTypeDef msgHeader;
    uint8_t msgData[8];

    msgHeader.StdId = 0x200;
    msgHeader.DLC = 8;
    msgHeader.TransmitGlobalTime = DISABLE;
    msgHeader.RTR = CAN_RTR_DATA;
    msgHeader.IDE = CAN_ID_STD;
    
    uint32_t mailBoxNum = 0;
    
    for (uint8_t i = 0; i < 8; i++)
    {
      msgData[i] = i;
    }
    
    HAL_CAN_AddTxMessage(&hcan, &msgHeader, msgData, &mailBoxNum);
  }

Первым делом вызываем HAL_CAN_GetTxMailboxesFreeLevel(&hcan). Функция возвращает количество свободных mailbox’ов. Если есть хоть один свободный почтовый ящик, то начинаем формировать сообщение на передачу – устанавливаем параметры сообщения, идентификатор и, конечно же, непосредственно данные. Затем вызываем функцию HAL_CAN_AddTxMessage(), которая поместит наше новое сообщение в один из mailbox’ов и активирует соответствующий запрос на передачу.

Обратите внимание, что помимо данных сообщения мы передаем в эту функцию адрес переменной mailBoxNum. Здесь функция сохранит номер почтового ящика, в который помещено сообщение, этот номер можно использовать для дальнейшей работы, например, в callback’е по окончанию передачи.

Итак, в общем-то на этом все! CAN настроили, прием и передачу осуществили 🙂 Проект для статьи доступен по ссылке – MT CAN Example.

До скорых встреч и спасибо за внимание!

Поделиться!

Подписаться
Уведомление о
guest
6 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
Юрий
Юрий
26 дней назад

Архив поврежден

Юрий
Юрий
Reply to  Aveal
25 дней назад

Пишет архив поврежден

Алексей
Алексей
Reply to  Юрий
14 дней назад

Я скачал. Архив не поврежден!

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Август 2020
Пн Вт Ср Чт Пт Сб Вс
 12
3456789
10111213141516
17181920212223
24252627282930
31  

© 2013-2020 MicroTechnics.ru