Использование CAN в STM8

Controller Area Network на STM8




Вместе с компилятором Raisonance и средой разработки Ride7 поставляется библиотека периферийных устройств для STM8S. У меня она находится по адресу:

"C:\Program Files\Raisonance\Ride\Examples\STM8\ST_Libraries\STM8A-S_StdPeriph_Lib-v2.0.0\Libraries\STM8S_StdPeriph_Driver"

Так вот там есть файл stm8s_can.c, а в нём всё необходимое для работы с CAN. Напомню, что такое CAN. Это такая последовательная пакетная широковещательная сеть, по-английски назыавется Controller Area Network. В сети передаются маленькие пакеты, максимум 8 байт. У них есть идентификаторы, длиной 11 бит (стандартные) или 29 бит (расширенные). Я рассматриваю только вариант, где используются стандартные идентификаторы.

Кроме того, пакеты бывают двух видов — обычный пакет, и запрос на передачу. Запрос это такой пакет, где выставлен бит RTR (remote transmission request).

Настройка

Чтобы работать с CAN, микроконтроллер должен брать частоту из внешнего кварца. Иначе модуль CAN не работает. Поэтому ставим кварц, настраиваем STM8 на работу от внешнего кварца, как описано здесь. В конец функции Init_CPU() добавляем настройку предделителя CAN:

CLK_CANConfig( CLK_CANDIVIDER_1 );

Выводы процессора CAN_TX и CAN_RX нужно настроить на вывод и ввод.

  // CAN_TX
  GPIO_Init( GPIOG, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST );
  // CAN_RX
  GPIO_Init( GPIOG, GPIO_PIN_1, GPIO_MODE_IN_PU_NO_IT );

Внизу будет ссылка на проект с описываемыми в этой статье исходниками для отладочной платы Reva v3.3. Я использую вот такую дочернюю плату STM8S208RB. В ней, чтобы заработал CAN, нужно организовать внешнюю частоту. Для этого на дочернюю плату можно проводком завести частоту 12 МГц, которая берётся с платы Reva (найдёте на плате пин, который соответствующим образом маркирован). Куда его заводить? На дочерней плате есть шесть «дырок» в левой части, как раз под установку кварца. Проводок можно воткнуть в правую нижнюю дырку, и всё заработает. У меня работает такая схема.

Ещё, на этой плате есть CAN Transiever. Чтобы работал CAN, нужно ещё и его включить. Делается это одной так:

  // Configure the CAN transceiver in active mode
  GPIO_Init( GPIOE, GPIO_PIN_6, GPIO_MODE_OUT_PP_LOW_FAST );

Инициализация

Дальше идёт инициализация модуля CAN. Полный исходный код смотрите в проекте, ссылка на которой расположена в конце статьи. Здесь покажу только основные строчки, на которые надо обратить внимание.

Параметры, влияющие на частоту передачи в шине CAN:

Параметры ниже подобраны мной для частоты передачи 25 КГц, в то время как частота микроконтроллера 12 МГц (а предделитель CAN равен 1, т.е. те же 12 МГц). Если эти параметры изменятся, то и следующие строчки тоже придётся менять, подбирать параметры заново.

Вычисляется это так:

QUANTA = (1 + BitSeg1 + BitSeg2)
CAN_FRQ = CPU_FRQ / (Presc * QUANTA)

Допустим, хотим частоту CAN 25 КГц, и частота процессора 12 МГц.

25000 = 12000000 / (Presc * QUANTA)

Надо подобрать такую (Presc * QUANTA), чтобы при делении 12 МГц на это число, получалась частота CAN 25 КГц. При вычислении QUANTA нужно иметь в виду, что BitSeg1 может быть в пределах от 1 до 16, а BitSeg2 в пределах от 1 до 8. QUANTA должна получиться в диапазоне от 8 до 25. В нашем случае (Presc * QUANTA) равно 480.

Допустим, выбрали BitSeg1 равным 7, а BitSeg2 равным 2. Тогда QUANTA = (1 + 7 + 2) = 10. Теперь узнаем значение предделителя. (Presc * QUANTA) должно равняться 480. Значит Presc равен 48.

Получаем такие строчки инициализации:

  // 25 КГц
  
  CAN_SynJumpWidth = CAN_SynJumpWidth_1TimeQuantum;
  CAN_BitSeg1 = CAN_BitSeg1_7TimeQuantum;
  CAN_BitSeg2 = CAN_BitSeg2_2TimeQuantum;
  CAN_Prescaler = 48;

Фильтры

Теперь о фильтрах. Фильтры нужны для того, чтобы микроконтроллер не тратил процессорное время на обработку всех приходящих по шине сообщений. Ведь их может быть довольно много, а микроконтроллер должен обрабатывать только какие-то конкретные сообщения с известными ему идентификаторами. Остальные надо фильтровать.

Приведу один возможный пример фильтрации: 8-битный режим фильтрации по списку идентификаторов. Фильтрация в этом режиме возможна по битам 3-10 стандартного идентификатора. В следующем коде у идентификатора 0x5FC сдвигом отбрасываются три младших бита. Не смотрите на то, что другие переменные содержат в названии «mask». В этом режиме все они содержат биты идентификаторов, а не маски. Названы они так для того, чтобы можно было просто и незатейливо использовать их же для другого режима, где эти маски есть.

        
    CAN_FilterNumber = 0;
    CAN_FilterActivation = ENABLE;
    CAN_FilterMode = CAN_FilterMode_IdList;
    CAN_FilterScale = CAN_FilterScale_8Bit;

    CAN_FilterID1     = (0x5FC >> 3);
    CAN_FilterIDMask1 = 0;
    CAN_FilterID2     = 0;
    CAN_FilterIDMask2 = 0;
    CAN_FilterID3     = 0;
    CAN_FilterIDMask3 = 0;
    CAN_FilterID4     = 0;
    CAN_FilterIDMask4 = 0;

Таким образом, пропускаются только сообщения с идентификатором 0x5FC, и те, которые отличаются от него тремя младшими битами. Все остальные сообщения не пропускаются. Добавлять другие разрешённые идентификаторы следует в остальные CAN_FilterID*.

Отправка сообщений

Отправляются сообщения так: формируется пакет и отправляется, что тут ещё скажешь? Я использую такую функцию:

CAN_TxStatus_TypeDef CAN_Send(u16 uId, bool uRtr, u8 uLen, const void* const pData)
{
  CAN_TxStatus_TypeDef TxStatus;  
  CAN_TransmitMailBox_TypeDef TransmitMailbox;  
  u16 uSendLimit;
    
  if (!uRtr)
  {
    if (0 == uLen || uLen > 8 || NULL == pData) return CAN_TxStatus_Failed;
  }
  
  TransmitMailbox = CAN_Transmit(uId, CAN_Id_Standard, 
    uRtr ? CAN_RTR_Remote : CAN_RTR_Data, uLen, (u8*)pData);
    
  uSendLimit = 0xFFF; // Ограничение, чтобы не зависнуть на передаче
  do
  {
    TxStatus = CAN_TransmitStatus(TransmitMailbox); 
  } while (TxStatus != CAN_TxStatus_Ok && uSendLimit--);
  
  if (TxStatus != CAN_TxStatus_Ok)
  {
    printf("CAN: передача сообщения не удалась и отменена.\r\n");
    CAN_CancelTransmit(TransmitMailbox);
  } else
  {
    printf("CAN: Успешная передача сообщения.\r\n");
  }

  return TxStatus;
}

Получение сообщений

Принимаются сообщения в прерывании CAN. Прерывание будет наступать только по приходу сообщений, которые успешно преодолели фильтр. Прерывание выголядит так:

void CAN_RX_IRQHandler(void) interrupt 8
{
  u8 i;
  
  CAN_Receive();
  
  CanMsgIn.id = CAN_GetReceivedId();
  CanMsgIn.rtr = CAN_GetReceivedRTR();
  CanMsgIn.len = CAN_GetReceivedDLC();
  for (i = 0; i < CanMsgIn.len; i++)
  {
    CanMsgIn.dat[i] = CAN_GetReceivedData(i);  
  }  
  
  CAN_PrintMsg();
}

Дальше у меня тут вызывается CAN_PrintMsg(), которая просто выводит пришедшее сообщение на UART.

void CAN_PrintMsg(void)
{
  u8 i;
    
  if (!CanMsgIn.rtr)
  {
    printf("ИД: %04X\tДлина: %d\tRTR: %d\tДанные:", CanMsgIn.id, 
      (int)CanMsgIn.len, (int)CanMsgIn.rtr ? 1 : 0);
    for (i = 0; i < CanMsgIn.len; i++)
    {
      printf(" %02X", (int)CanMsgIn.dat[i]);
    }
  } 
    else
  {
    printf("ИД: %04X\tДлина: %d\tRTR: %d\t", CanMsgIn.id, 
      (int)CanMsgIn.len, (int)CanMsgIn.rtr ? 1 : 0);
    printf("(Запрос сообщения)");
  }
  printf("\r\n");
}

Проект

Скачать пример работы с CAN для STM8S: stm8-can.zip (189 Кб).



Автор: амдф
Дата: 26.10.2012


При копировании материалов хорошим тоном будет указание авторства и ссылка на сайт.