CANopen на процессоре Blackfin ADSP-BF504F Печать
Добавил(а) microsin   

В этой статье кратко описывается портирование стека CANopen апноута AN945 от Microship [1] на процессор ADSP-BF504 (ранее этот стек портировал на ARM AT91SAM7X [2]). Описывается простое устройство CANopen, которое имеет один TPDO, один RPDO, поддерживает чтение/запись словаря объектов через SDO и демонстрирует обмен данными пакетов PDO в обоих направлениях.

[Утилиты, которые использовались для тестирования]

В качестве главного устройства сети (мастера CANopen) использовался USB-адаптер SYSTEC [4] вместе с программой CANopen DeviceExplorer 2.6.2. Для проверки словаря использовалась утилита Vector CANeds 3.6.92 SP3. Для анализа пакетов, передаваемых по сети, использовался анализатор CANalyst-II [5].

Утилиты CANopen DeviceExplorer, CANeds и рабочий проект для VisualDSP++ 5.0 можно скачать по ссылке [6].

[Описание работы устройства CANopen]

Демонстрационное устройство узла CANopen работает на скорости 125 килобит/сек (можно поменять скорость переназначением макроса CAN_BAUDRATE). Такая скорость была выбрана потому, что оценочная (не лицензионная) версия CANopen DeviceExplorer позволяет работать только на скорости 125 килобит/сек. Устройству назначен номер узла 2 (задается значением переменной _uCO_nodeID), и оно может зажигать тестовый светодиод (подключен к ножке PF0) по значению первого байта в пакете RPDO, и может передавать в пакете TPDO значения ножек входов портов PF8 и PF9.

ADSP BF504F CANopen sch

[Проблемы, которые пришлось решить при портировании]

В качестве исходного кода был взят порт [2], и в него были внесены изменения, касающиеся привязки к аппаратуре ADSP-BF50x (в основном это коснулось модуля CO_CANDRV.c, см. врезку ниже). Нашел одну из ошибок при обработке SDO и выдаче данных из словаря: перечисление DICT_CTL обрабатывалось компилятором Blackfin как u16, и DICT_CTL ctl в структуре DICT_OBJ не соответствовало u8 ctl в структуре DICT_OBJECT_TEMPLATE. Исправил структуру DICT_OBJ, заменив в ней DICT_CTL ctl на u8 ctl.

Еще одна проблема, с которой долго провозился: код ISR приема не хотел работать, если у него включена оптимизация. Если код обработчика CAN_RX_HANDLER обрамить директивами #pragma optimize_off и #pragma optimize_as_cmd_line, то все работет нормально, а иначе не работает. С остальным кодом такой проблемы не было (нормально работает и в конфигурации Debug, и в конфигурации Release с настройками по умолчанию). Оказалось, что нужно читать регистр CAN_MBRIF1 в переменную с модификатором volatile, после такого исправления проблема исчезла.

/*****************************************************************************
 * Низкоуровневый драйвер стека CANopen (портировано на ADSP-BF50x
 * с кода ECAN чипа PIC18F Microchip из апноута AN945).
 *****************************************************************************
 * Company:       Microchip Technology Incorporated
 * FileName:      CO_CANDRV.C
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Ross Fosler      11/13/03 ...
 *****************************************************************************/
//#include "CO_DEFS.DEF"        // Глобальные определения
#include < services_types.h >
#include < cdefBF504F.h >
#include < builtins.h >
#include < sys\exception.h >
#include "CO_TYPES.H"
#include "CO_CANDRV.H"        // Службы драйвера
#include "CO_NMTE.H"
#include "CO_COMM.H"
#include "CO_SDO1.H"
#include "CO_PDO.H"
#include "CO_NMT.H"
#include "CO_SYNC.h"
#include "pins.h"
 
////////////////////////////////////////////////////////////////
// Обработчик прерывания приема CAN
//#pragma optimize_off
//EX_INTERRUPT_HANDLER(CAN_RX_HANDLER)
EX_REENTRANT_HANDLER(CAN_RX_HANDLER)
{
   u16 bit_pos = 0;
   char mbID;
   CANmessage m;  // содержит сообщение CAN
 
   //Внимание: эта переменная обязательно должна быть volatile,
   // иначе код ISR будет работать только при отключенной
   // оптимизации.
   volatile u16 mbim_status = *pCAN_MBRIF1;
   
   while (!(mbim_status & 0x8000))
   {
      mbim_status << = 1;
      bit_pos++;
   }
   mbID = (15 - bit_pos);
   m.cob_id = (*(u16*)CAN_MB_ID1(mbID) & BASEID) >> 2;       //11-битный ID
   m.rtr = (*(u16*)CAN_MB_ID1(mbID) & RTR)?true:false;
   m.len = *(u16*)CAN_MB_LENGTH(mbID) & DLC;
   *(u16*)(&m.data[6]) = SWAPBYTES(*(u16*)CAN_MB_DATA0(mbID));
   *(u16*)(&m.data[4]) = SWAPBYTES(*(u16*)CAN_MB_DATA1(mbID));
   *(u16*)(&m.data[2]) = SWAPBYTES(*(u16*)CAN_MB_DATA2(mbID));
   *(u16*)(&m.data[0]) = SWAPBYTES(*(u16*)CAN_MB_DATA3(mbID));
   // Если как минимум состояние pre-operational, и если хендл допустимый,
   // то декодируем сообщение и генерируем соответствующее событие.
   if (COMM_STATE_PREOP)
   {
      switch(m.cob_id >> 7)
      {
      case NMT:
         //proceedNMTstateChange(d,m);
         if (COMM_NETCTL_NMT_EN)
         {
            _CO_COMM_NMT_RXEvent(&m);
         }
         break;
      case SYNCcode:
         if (COMM_NETCTL_SYNC_EN)
         {
            if (!COMM_STATE_STOP)
               _CO_COMM_SYNC_RXEvent(&m);
         }
         break;
      case TIME_STAMP:
         //if (COMM_NETCTL_TMSTP_EN)
         //{
         //   if (!COMM_STATE_STOP)
         //      _CO_COMM_TIME_RXEvent();
         //}
         break;
      case PDO1tx:
         break;
      case PDO1rx:
         _CO_COMM_PDO1_RXEvent(&m);
         break;
      case PDO2tx:
      case PDO2rx:
      case PDO3tx:
      case PDO3rx:
      case PDO4tx:
      case PDO4rx:
         break;
      case SDOtx:
         break;
      case SDOrx:
         if (!COMM_STATE_STOP)
         {
            if (COMM_SDO_1_EN)
            {
               _CO_COMM_SDO1_RXEvent(&m);
            }
         }
         break;
      case NODE_GUARD:
         if (COMM_NETCTL_NMTE_EN)
         {
            //proceedNODE_GUARD(d,m);
            _CO_COMM_NMTE_RXEvent(&m);
         }
         break;
      default:
         break;
      }
   }
   *pCAN_MBRIF1 = (1 << mbID);
   ssync();
}
 
//#pragma optimize_as_cmd_line
void canInit (u16 baudrate)
{
   //Инициализация ножек CAN:
   *pPORTG_FER |= (PG1 | PG2);
   *pPORTG_MUX |= PG3;
   *pPORTG_MUX &= ~PG2;
   *pPORTG_MUX |= PG5;
   *pPORTG_MUX &= ~PG4;
   ssync();
   //Установка скорости:
   *pCAN_TIMING = _TIMING;
   *pCAN_CLOCK  = CANBPR;
   ssync();
   //Настройка и разрешение работы ящиков:
   *pCAN_MD1 = 0xFFFF;     // ящики 0-15 на прием
   *pCAN_MD2 = 0x0000;     // ящики 16-31 на передачу
   *pCAN_MC1 = 0xFFFF;     // разрешить ящики 0-15
   *pCAN_MC2 = 0xFFFF;     // разрешить ящики 16-31
   ssync();
   u8 mbID;
   //Настройка передающих ящиков:
   for (mbID = 16; mbID < 32; mbID++)
   {
      //Очистка регистра CAN_MBxx_ID0 (расширенный ID):
      //*(pCAN_Ptr + 12) = 0;
      *(u16*)CAN_MB_ID0(mbID) = 0;
   }
   //Настройка идентификаторов ящиков приема:
   for (mbID = 0; mbID < 16; mbID++)
   {
      //Очистка регистра CAN_MBxx_ID0 (расширенный ID):
      *(u16*)CAN_MB_ID0(mbID) = 0;
      //Установка регистра CAN_MBxx_ID1 (базовый ID, AME):
      *(u16*)CAN_MB_ID1(mbID) = AME;
   }
   // установка масок для приема:
   volatile u16* pCAN_Ptr = pCAN_AM00L;
   int i;
   for(i=0; i < 16; ++i)
   {
      //Настройка регистра маски CAN_AMxxL:
      *pCAN_Ptr = 0;
      //Настройка регистра маски CAN_AMxxH:
      //*(pCAN_Ptr+2) = BASEID;     //всеразрешающая маска по базовому ID
      // Биты ID узла должны совпадать:
      *(pCAN_Ptr+2) = (~(0x007F << 2) | (_uCO_nodeID << 2)) & BASEID;
      pCAN_Ptr+=4;
   }
   //Инициализация прерывания, привязка CAN к приоритету IVG:
   *pSIC_IAR3 |= PX_IVG(CAN_RX_Peripheral, CAN_RX_IVG);
   register_handler(CAN_RX_IVG, CAN_RX_HANDLER);
   *pSIC_IMASK0 |= IRQ_CAN_RX;
   // Запрет режима конфигурации CAN (очистка бита CCR)
   *pCAN_CONTROL = 0;
   ssync();
   //Ожидание завершения режима конфигурирования (ожидание выхода из него):
   while(*pCAN_STATUS & CCA);
   *pCAN_MBIM1 = 0xFFFF;   // разрешить прерывания ящиков 0-15 (прием)
   ssync();
}
 
/*********************************************************************
 * Overview:      Эта функция используется, чтобы показать драйверу,
 *                что данные, длина и идентификатор CAN были загружены
 *                и готовы к отправке.
 * Портированная версия: передает сразу через первый свободный ящик.
 *
 * 1. Ищется первый свободный передающий ящик.
 * 2. В зависимости от event создается пакет и отправляется
 ********************************************************************/
void mCANSendMessage (TEventTX event)
{
   //Поиск свободного ящика для отправки:
   u16 mask=1;
   u16 mbID;
   TUnion32 data;
   
   for(mbID=16; mbID < 32; ++mbID)
   {
      if((mask & *pCAN_TRS2)==0)
         break;
      mask << = 1;
   }
   if (0 == mask)
   {
      // Нет свободных ящиков для отправки
      return;
   }
   
   switch(event)
   {
   case NMTE_BootTXEvent:
      // Установка COB-ID:
      setCAN_MBxx_ID1(mbID, 0x700L + _uCO_nodeID);
      // Установка длины 1:
      setCAN_MBxx_LENGTH(mbID, 1);
      // Загрузка данных (byte0=0):
      setCAN_MBxx_DATA3(mbID, ((u16)0) << 8);
      // Запуск отправки:
      *pCAN_TRS2 |= mask;
      ssync();
      break;
   case NMTE_NetCtlTXEvent:
      // Установка COB-ID:
      setCAN_MBxx_ID1(mbID, 0x700L + _uCO_nodeID);
      // Установка длины 1:
      setCAN_MBxx_LENGTH(mbID, 1);
      //Подготовка данных:
      _uNMTELocalState.byte = _uNMTELocalState.byte & 0x80;
      if (NMTE_NODE_GUARD_EN)
      {
         _uNMTELocalState.bits.b7 = (~_uNMTELocalState.bits.b7);
      }
      else
      {
         _uNMTELocalState.bits.b7 = 0;
      }
      if (COMM_STATE_STOP)
         _uNMTELocalState.byte = _uNMTELocalState.byte | 4;
      else if (COMM_STATE_OPER)
         _uNMTELocalState.byte = _uNMTELocalState.byte | 5;
      else if (COMM_STATE_PREOP)
         _uNMTELocalState.byte = _uNMTELocalState.byte | 127;
      // Загрузка данных (byte0, младший байт данных ящика):
      setCAN_MBxx_DATA3(mbID, ((u16)_uNMTELocalState.byte) << 8);
      // Запуск отправки:
      *pCAN_TRS2 |= mask;
      ssync();
      break;
   case SDO1_TXEvent:
      // Установка COB-ID
      setCAN_MBxx_ID1(mbID, 0x580L + _uCO_nodeID);
      // Установка длины 8:
      setCAN_MBxx_LENGTH(mbID, 8);
      // Загрузка данных
      if (_uSDO1ACode == E_SUCCESS)
      {
         // Перенос данных в буфер передачи:
         setCAN_MBxx_DATA0(mbID, SWAPBYTES(*(u16*)(_uSDO1TxBuf+6))); //Байты 6, 7
         setCAN_MBxx_DATA1(mbID, SWAPBYTES(*(u16*)(_uSDO1TxBuf+4))); //Байты 4, 5
         setCAN_MBxx_DATA2(mbID, SWAPBYTES(*(u16*)(_uSDO1TxBuf+2))); //Байты 2, 3
         setCAN_MBxx_DATA3(mbID, SWAPBYTES(*(u16*)(_uSDO1TxBuf+0))); //Байты 0, 1
      }
      else 
      {
         // Установка кода abort
         data.bytes[0] = (u8)0x80;
         // Установка мультиплексора
         data.bytes[1] = _uSDO1Dict.index.bytes[0];
         setCAN_MBxx_DATA3(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 0, 1
         data.bytes[2] = _uSDO1Dict.index.bytes[1];
         data.bytes[3] = _uSDO1Dict.subindex;
         setCAN_MBxx_DATA2(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 2, 3
         switch (_uSDO1ACode)
         {
         case E_TOGGLE:
            // Бит Toggle не переключился
            data.word = 0x05030000L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_SDO_TIME:
            // Таймаут протокола SDO
            data.word = 0x05040000L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_CS_CMD:
            // Спецификатор команды клиент/сервер недопустим или неизвестен
            data.word = 0x05040001L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_MEMORY_OUT:
            // Не хватает памяти
            data.word = 0x05040005L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_UNSUPP_ACCESS:
            // Не поддерживаемый доступ к объекту
            data.word = 0x06010000L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_CANNOT_READ:
            // Попытка прочитать объект, у которого доступ только на запись
            data.word = 0x06010001L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_CANNOT_WRITE:
            // Попытка записать объект, у которого доступ только на чтение
            data.word = 0x06010002L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_OBJ_NOT_FOUND:
            // Объект не существует в OD
            data.word = 0x06020000L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_OBJ_CANNOT_MAP:
            // Объект не может быть отображен (привязан к) на PDO
            data.word = 0x06040041L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_OBJ_MAP_LEN:
            // Количество и длина привязанных объектов превысили длину PDO
            data.word = 0x06040042L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_GEN_PARAM_COMP:
            // Причина в общей несовместимости параметра
            data.word = 0x06040043L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_GEN_INTERNAL_COMP:
            // Общая внутренняя несовместимость в устройстве
            data.word = 0x06040047L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_HARDWARE:
            // Ошибочный доступ из-за аппаратной ошибки
            data.word = 0x06060000L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_LEN_SERVICE:
            // Не соответствует тип данных, не соответствует длина параметра службы
            data.word = 0x06070010L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_LEN_SERVICE_HIGH:
            // Не соответствует тип данных, длина параметра службы слишком велика
            data.word = 0x06070012L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_LEN_SERVICE_LOW:
            // Не соответствует тип данных, длина параметра службы слишком мала
            data.word = 0x06070013L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_SUBINDEX_NOT_FOUND:
            // Sub-индекс не существует
            data.word = 0x06090011L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_PARAM_RANGE:
            // Превышен диапазон значения параметра
            data.word = 0x06090030L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_PARAM_HIGH:
            // Значение записываемого параметра слишком велико
            data.word = 0x06090031L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_PARAM_LOW:
            // Значение записываемого параметра слишком мало
            data.word = 0x06090032L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_MAX_LT_MIN:
            // Максимальное значение меньше, чем минимальное
            data.word = 0x06090036L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_GENERAL:
            // Общая ошибка
            data.word = 0x08000000L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_TRANSFER:
            // Данные не могут быть переданы или сохранены в приложение
            data.word = 0x08000020L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_LOCAL_CONTROL:
            // Данные не могут быть переданы или сохранены в приложение из-за локального управления
            data.word = 0x08000021L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         case E_DEV_STATE:
            // Данные не могут быть переданы или сохранены в приложение из-за текущего состояния устройства
            data.word = 0x08000022L;
            setCAN_MBxx_DATA0(mbID, (((u16)data.bytes[2]) << 8)|data.bytes[3]); //Байты 6, 7
            setCAN_MBxx_DATA1(mbID, (((u16)data.bytes[0]) << 8)|data.bytes[1]); //Байты 4, 5
            break;
         }
      }
      // Запуск отправки:
      *pCAN_TRS2 |= mask;
      ssync();
      break;
   case PDO1_TXEvent:
      // Установка COB-ID:
      setCAN_MBxx_ID1(mbID, uTPDOComm1.word);
      //Установка длины:
      setCAN_MBxx_LENGTH(mbID, _uPDO1.TPDO.len);
      // Загрузка данных для передачи
      setCAN_MBxx_DATA0(mbID, SWAPBYTES(*(u16*)(_uPDO1.TPDO.buf+6)));  //Байты 6, 7
      setCAN_MBxx_DATA1(mbID, SWAPBYTES(*(u16*)(_uPDO1.TPDO.buf+4)));  //Байты 4, 5
      setCAN_MBxx_DATA2(mbID, SWAPBYTES(*(u16*)(_uPDO1.TPDO.buf+2)));  //Байты 2, 3
      setCAN_MBxx_DATA3(mbID, SWAPBYTES(*(u16*)(_uPDO1.TPDO.buf+0)));  //Байты 0, 1
      // Запуск отправки:
      *pCAN_TRS2 |= mask;
      ssync();
      break;
#if CO_NUM_OF_PDO > 1
   case PDO2_TXEvent:
      break;
#endif
#if CO_NUM_OF_PDO > 2
   case PDO3_TXEvent:
      break;
#endif
#if CO_NUM_OF_PDO > 3
   case PDO4_TXEvent:
      break;
#endif
   }
}

Весь проект целиком вместе с сервисными утилитами можно скачать по ссылке [6].

[Ссылки]

1. AN945: A CANopen Stack for PIC18 ECANTM Microcontrollers site:microchip.com.
2. AN945: стек CANopen для PIC18 ECAN.
3. Интерфейс CAN ADSP-BF50x.
4. Visual Studio C#: работа с USB-CAN адаптером SYSTEC.
5. CANalyst-II - средство диагностики CAN и CANopen.
6. 170817New504F.ZIP - исходный код, документация, утилиты.