CANopen это протокол, основанный на шине Controller Area Network (CAN). Как подсказывает имя протокола, это открытый сетевой стандарт, принятый во всем мире. Хотя CANopen было создан для индустриальной автоматизации, он нашел широкое применение и для других, не индустриальных приложений.
Имеется множество вариантов применения CANopen. Эта статья фокусируется на описании спецификации DS-301 [2, 6], разработанной организацией CAN in Automation (CiA). Фактически большая часть дискуссии ограничена определенными областями спецификации, с акцентом на понимании, как пользователи могут разработать приложения на основе стека CANopen. Чтобы помочь иллюстрировать эти вопросы, было разработано простое приложение примера на основе спецификации CiA DS-401 - обычный модуль ввода вывода (Generic I/O Module). Дополнительный код предоставлен исключительно для демонстрации; таким образом, здесь нет детального обсуждения демонстрационного кода. Однако здесь будут часто использоваться примеры кода с комментариями из этого демо-приложения (здесь приведен перевод апноута AN945 [1], грубые смысловые опечатки по возможности исправлены). Незнакомые аббревиатуры и термины см. в разделе "Словарик" статьи [5].
Весь код, предоставленный в этом апноуте, был разработан для семейств микроконтроллеров PIC18F8680 и PIC18F4680, которые имеют на борту аппаратный интерфейс по технологии ECAN. Код написан для компилятора Microchip C18 v2.30 (или более свежей версии). Хотя код рассчитан на эти 2 семейства микроконтроллеров, его можно адаптировать и на другие семейства PIC18, на борту которых есть CAN.
Ожидается, что читатель уже имеет некоторые знания протокола CANopen, или имеет доступ к последним стандартам CANopen (перечисленным в разделе Ссылки), чтобы прояснить для себя теорию и/или критическую терминологию. Информация, представленная в этом апноуте, склоняется к пониманию реализации демо-приложения и разработке на её основе, вместо того, чтобы сосредотачиваться на многих деталях CANopen.
[Обзор стека]
Стек CANopen предоставляет нижние слои протокола. Этот дизайн включает в себя:
• Встроенную машину состояний для обработки всех коммуникаций между всеми узлами и объектами. • Сервер сервисного объекта по умолчанию (Default Service Data Object, SDO). • До 4 передающих и 4 принимающих объекта обработки данных (Process Data Objects), сокращенно TPDO и RPDO. • Поддержка явного обмена сообщений (Explicit Messaging) и сегментированного (Segmented Messaging). • Поддержка статического отображения PDO к переменным приложения (PDO mapping). • Структурированный словарь для объектов PDO и SDO. • Поддержка протоколов Node Guard / Life Guard. • Потребитель сообщений SYNC. • Генератор "сердцебиения" (Heartbeat Producer). • Поддержка драйвера ECAN.
Как можно увидеть из этого списка, стек CANopen, обсуждаемый здесь, разработан для приложений, которые ведут себя в сети как подчиненные (slave) устройства. Этот дизайн статичен по своей природе, что дает возможность получить более эффективное использование памяти кода.
Действительный код CANopen, написанный на языке C, дополнительно разбит на отдельные файлы исходного кода и заголовков. Это позволит пользователям выбрать для себя нужные службы, которые могут понадобиться для приложения, и селективно собрать для себя проект, удовлетворяющий определенным требованиям. Полный список файлов исходного кода представлен в таблице 1.
Конечно, реальное приложение и некоторые аспекты коммуникаций все еще должны быть разработаны пользователем. Предоставленный стек CANopen Stack дает базу, на которой можно построить свое приложение.
Таблица 1.
Имя файла
Описание
CO_CANDRV.c
Модуль драйвера ECAN. Эти файлы могут быть заменены другой реализацией драйвера, если это необходимо.
CO_CANDRV.h
CO_COMM.c
Службы поддержки коммуникаций (Communication Management). Требуется для всех приложений.
CO_COMM.h
CO_DEV.c
Файлы, специфичные для устройства (узла сети CANopen). Пользователь должен отредактировать эти файлы, чтобы учесть специфику устройства.
CO_DEV.h
CO_DICT.c
Словарь объектов (Object Dictionary, OD). Требуется для всех приложений.
CO_DICT.h
CO_DICT.def
CO_MAIN.c
Основные службы CANopen. Требуется для всех приложений.
CO_MAIN.h
CO_MEMIO.c
Функции копирования памяти, используемые OD. Требуется для всех приложений.
CO_MEMIO.h
CO_NMT.c
Конечная точка системы управления сетью CANopen.
CO_NMT.h
CO_NMTE.c
Конечная точка Node Guard, Heartbeat и Boot-up.
CO_NMTE.h
CO_PDO.c
Основные службы PDO.
CO_PDO.h
CO_PDO1.c
Конечные точки обработки объекта PDO. Предоставлены в формате шаблона, который требует разработки пользователя, чтобы учесть специфику приложения. Должны использоваться вместе с основными файлами служб PDO.
CO_PDO1.h
CO_PDO2.c
CO_PDO2.h
CO_PDO3.c
CO_PDO3.h
CO_PDO4.c
CO_PDO4.h
CO_SDO1.c
Конечная точка коммуникации по умолчанию сервера SDO.
CO_SDO1.h
CO_SYNC.c
Конечная точка коммуникации потребителя синхронизации.
CO_SYNC.h
CO_TOOLS.c
Инструментарий для преобразования форматов идентификаторов между стандартом Microchip и стандартом CANopen. Чтобы улучшить быстродействие процесса, все идентификаторы COB-ID сохраняются внутренне в формате Microchip. Когда COB-ID представляется по запросу, он преобразовывается в формат CANopen.
CO_TOOLS.h
CO_ABERR.h
Общие определения для ошибок. Требуется всеми приложениями.
[Модель кода firmware CANopen]
Программное обеспечение микроконтроллера (firmware) разработано в 3 уровнях, как это показано на рис. 1. Самый низкий уровень это драйвер ECAN, предоставляющий абстрагированную поддержку аппаратуры CAN. Уровень коммуникаций это главный интерфейс между драйвером и обработкой каждой индивидуальной конечной точки.
Помимо приложения, имеется также словарь объектов CANopen (OD). В сущности он находится за пределами объекта коммуникации, и непосредственно подключен к конечной точке SDO.
Рис. 1. Базовая модель firmware стека CANopen.
Драйвер. На нижнем уровне стека находится драйвер ECAN который обслуживается как абстрагированный аппаратный интерфейс. Он реализован в исходных файлах CO_CANDRV.c и CO_CANDRV.h.
Этот драйвер обрабатывает всю связанную с аппаратурой ECAN функциональность, и удобно абстрагируется от более сложной фильтрации, которая является частью протокола CAN. Дальше этот момент обсуждается более подробно.
Управление коммуникациями. Менеджер коммуникаций является частью общего объекта коммуникаций. Он предоставляет захват любых событий из драйвера и верхних уровней приложения, и диспетчеризирует их на соответствующую обработку коммуникационными sub-объектами и функциями. В сущности открытие, закрытие, передача в конечную точку и прием из неё - все это направляется менеджером коммуникаций, который реализован в файлах CO_COMM.c и CO_COMM.h.
Менеджер знает состояние как каждой конечной точки, так и глобальное состояние устройства. Таким образом, он может блокировать сообщения к конечным точкам, если это необходимо на основании локального или глобального состояния.
Другая функция менеджера - использование однобайтного метода "handle", поддерживаемого драйвером, чтобы декодировать события сообщения. Этот handle имеет определенную структуру, разработанную для повышения производительности; она значительно ускоряет декодирование 11-битного или 29-битного идентификатора CAN, чтобы определить, какая функция обработки нужна для определенного сообщения.
Конечные точки. Спецификация CANopen определяет несколько возможных конечных точек. Ниже перечислены 5 объектов конечной точки, которые реализованы в этом примере; в будущем могут быть доступны и другие объекты.
• Сервер SDO по умолчанию (Default Server SDO) • До 4 статических PDO • Потребитель сообщений синхронизации • Подчиненное устройство NMT • Node Guard или Heartbeat
Предоставлен сервер SDO по умолчанию. Термин "сервер" в данном контексте означает, что код сервера выполняет какие-то запросы клиента SDO (обычно это мастер сети CANopen). Путь коммуникаций SDO прямо привязан к OD; сообщения SDO содержат информацию, которая связывает SDO с определенным объектом. Данные каждого сообщения декодируются, проверяются на достоверность и (если они достоверны) выполняются как событие.
В сущности здесь работают 2 основные операции: чтение (read) и запись (write). Каждая полная передача SDO (которая может состоять из нескольких сообщений) будет либо читать, либо записывать один определенный объект OD. Объект SDO по умолчанию находится в исходных файлах CO_SDO1.c и CO_SDO1.h.
Путь коммуникаций PDO (Process Data Object) непосредственно отображен на объект (или объекты) приложения. Таким образом, путь подразумевается устройством, и в коммуникации не содержится никакой информации о пути. В сущности данные PDO внутренне отображены на один или большее количество объектов (по-русски можно сказать так: в программе есть некоторые переменные, и данные этих переменных присуствуют в PDO). Данные отображаются либо статически (отображение определяется один раз во время компиляции проекта), либо динамически (может быть установлена во время выполнения приложения с помощью обработки команд, поступающих серверу SDO). Одно сообщение может содержать в себе данные более чем одного объекта приложения.
Код firmware, предоставляемый с этим апноутом, поддерживает 4 объекта PDO по умолчанию. Все службы PDO предоставлены в исходных файлах CO_PDO.c и CO_PDO.h. Дополнительные файлы CO_PDOn.c и CO_PDOn.h (здесь n может иметь значение от 1 до 4) используются для реализации отдельных объектов PDO. Они предоставлены в форме шаблона, и должны быть разработаны для удовлетворения требований приложения.
Предоставлено подчиненное устройство, принимающее команды управления сетью (Network Management, сокращенно NMT), как этого требует стандарт CANopen. Объект NMT принимает команды для изменения состояния устройства, или сброса приложения устройства и/или его коммуникаций. На рис. 2 показана машина состояний CANopen, а также команды, которые вызывают срабатывание переходов между состояниями.
Рис. 2. Алгоритмическая машина состояний устройства CANopen.
Примечание: не снабженные метками переходы (которые показаны более черными линиями) являются автоматическими и не требуют внешнего события.
Обработка NMT предоставлена в исходных файлах CO_NMT.c и CO_NMT.h.
Спецификациями CANopen требуется, чтобы была одна конечная точка протокола Node Guard или Heartbeat (это 2 разных протокола, и требуется наличие поддержки одного из них). Имеется код обоих этих протоколов; однако в любой момент времени разрешен только один из этих методов поддержки целостности узлов сети (это также определено спецификациями).
Функционал конечной точки Node Guard и Heartbeat предоставлен в исходных файлах CO_NMTE.c и CO_NMTE.h.
Предоставлен один потребитель сообщений синхронизации (synchronization consumer, SYNC). Сообщение SYNC просто предоставляет приложению событие, по которому приложение генерирует любые синхронные сообщения PDO.
Исходные файлы CO_SYNC.c и CO_SYNC.h содержат в себе объект SYNC.
[Словарь объектов CANopen (OD)]
Функции OD предоставляют центральную информационную базу данных для устройства. Каждый объект в пределах устройства представлен в OD по индексу, sub-индексу, вместе с некоторой информацией о доступе. Объектом может быть как простым байтом, так и более сложной структурой данных. Таблица 2 показывает базовые области OD, которые определены по индексу, в соответствии со спецификацией CANopen.
Таблица 2. Диапазоны индексов областей внутри OD.
Индекс
Объект
0001-001F
Статический тип данных.
0020-003F
Комплексные типы данных.
0040-005F
Типы данных, специфичные для производителя.
0060-007F
Статические типы данных профиля устройства.
0080-009F
Комплексные типы данных профиля устройства.
00A0-0FFF
Зарезервировано.
1000-1FFF
Область профиля коммуникации.
2000-5FFF
Область профиля, специфичного для производителя.
6000-9FFF
Область стандартизованного профиля.
A000-FFFF
Зарезервировано.
Разработка и определение объектов словаря более подробно обсуждается в разделе "Объекты и OD".
С использованием индекса может быть получен доступ к определенному объекту. С точки зрения сети на устройство, доступ к объекту предоставляется через конечную точку SDO или PDO, как это показано на рис. 1. Функционал словаря CANopen реализован в следующих файлах:
Объекты SDO. Спецификацией требуются так называемые "стандартные объекты устройства" (Standard Device Objects, SDO), хотя они не показаны на рис. 1. Эти объекты SDO включают в себе такие данные, как статус, имя устройства, серийный номер, информация о версии.
SDO предоставлены в исходных файлах CO_DEV.c и CO_DEV.h.
[Объекты приложения]
На верхнем уровне стека находится объект приложения, который должен быть определен с учетом поддержки требований приложения, и включен в OD. Реальные объекты определяются и пишутся пользователями для специфики их приложения.
Другой дополнительный код Firmware. Имеются некоторые другие файлы, предоставленные для определения стандартных типов данных, определения ошибок, поддержки функций копирования памяти и предоставления инструментария для преобразования COB-ID. Вот они:• CO_TOOLS.c
Всего имеется 40 опций компиляции для конфигурирования исходного кода, учитывающих требования определенного приложения. Большинство этих опций используются для конфигурирования факторов, которые управляют скоростью бит CAN (параметры времени сегмента фазы, ширина скачка синхронизации, прескалер скорости, и т. д.). Все эти опции перечислены в таблице 3.
Таблица 3. Основные опции времени компиляции.
Имя
Описание
CAN_BITRATE0_BRGCON1
Настройка скорости по умолчанию для приложения. Значения BRGCON соответствуют конфигурациям для регистров BRGCON, и определяют все требуемые параметры для скорости шины CAN. Пользователю следует обратиться к соответствующему даташиту для получения подробной информации по конфигурированию этих регистров.
CAN_BITRATE0_BRGCON2
CAN_BITRATE0_BRGCON3
CAN_BITRATEn_BRGCON1
Настройка скорости n, где допустимый диапазон для n составляет от 1 до 8. Эти опциональные настройки могут использоваться вместо настроек скорости по умолчанию. Так же, как и с настройками скорости по умолчанию, здесь значения BRGCON соответствуют настройкам для регистра BRGCON.
CAN_BITRATEn_BRGCON2
CAN_BITRATEn_BRGCON3
CAN_BITRATEn
Разрешает использовать настройку скорости n.
CAN_MAX_RCV_ENDP
Устанавливает максимальное разрешенное количество конечных точек приема в драйвере. Рекомендуется использовать значение 8, чтобы поддерживать все конечные точки приема стека CANopen. Для этого параметра можно установить максимальное значение 16.
CO_NUM_OF_PDO
Устанавливает количество поддерживаемых PDO. Допустимое значение от 1 до 4.
CO_SPEED_UP_CODE
Разрешает использовать некоторые участки кода в виде встраиваемого ассемблера. С помощью установки этой опции повышается скорость выполнения кода.
CO_SDO1_MAX_RX_BUF
Устанавливает максимальное пространство буфера, используемого объектом SDO по умолчанию. Хорошее значение для этого параметра задается по размеру самого большого записываемого объекта.
CO_SDO1_MAX_SEG_TIME
Устанавливает максимальное время для сторожевого таймера SDO. Используется для ожидания завершенного сегмента перед сбросом машины состояния SDO.
Установка информации об устройстве. Спецификация CANopen идентифицирует некоторое количество объектов, которые идентифицируют конкретное устройство. Информация, специфическая для устройства, предоставляется через простой набор данных, который находится в OD. Эта информация должна быть включена в приложение. Таблица 4 перечисляет эти объекты.
Таблица 4. Стандартные объекты устройства (Standard Device Objects, SDO).
Имя объекта
Описание
rom unsigned long rCO_DevType
Тип устройства.
rom unsigned char rCO_DevName[]
Имя устройства.
rom unsigned char rCO_DevHardwareVer[]
Версия аппаратуры.
rom unsigned char rCO_DevSoftwareVer[]
Версия программного обеспечения.
rom unsigned char rCO_DevIdentityIndx
Индекс идентификации устройства.
rom unsigned long rCO_DevVendorID
Идентификатор производителя.
rom unsigned long rCO_DevProductCode
Код изделия.
rom unsigned long rCO_DevRevNo
Номер ревизии.
rom unsigned long rCO_DevSerialNo
Серийный номер устройства.
unsigned char uCO_DevErrReg
Регистр ошибки устройства.
unsigned long uCO_DevManufacturerStatReg
Регистр статуса, специфичный для производителя.
[Написание приложения]
При разработке приложения должна быть проделана значительная работа, касающаяся коммуникаций в соответствии со спецификациями CANopen. Предоставленный код упрощает этот труд, предоставляя обработку некоторых низкоуровневых коммуникаций. Кроме работы, необходимой для разработки самого приложения, должны быть также разработаны следующие элементы кода:
• Определение объектов приложения в OD • Разработка поддержки для сложных объектов • Разработка функций поддержки необходимых коммуникационных событий CANopen • Разработка PDO
Эта секция описывает набор инструментария, предоставленный в предлагаемом firmware. Описаны все функции событий и службы, которые могут потребоваться для любого приложения.
Основные службы. Протокол CANopen запускается вызовом функции mCO_InitAll(). Она генерирует сброс драйвера CAN, и приводит к отправке сообщения загрузки (boot-up message). Однако перед запуском протокола CANopen должны быть установлены в надлежащее состояние параметры по умолчанию, относящиеся к специфике коммуникаций. Например, значения node_id (идентификатор узла) и baudrate (скорость по шине CAN) критичны для правильного обмена сообщениями. К другим настройкам относятся опции Node Guard / Heartbeat, объект ошибок устройства, а также специфичный для производителя статус.
После запуска протокола вся обработка происходит через функции mCO_ProcessAllEvents() и mCO_ProcessAllTimeEvents(). Первая обрабатывает все основные коммуникации, связанные с передачей ил приемом сообщений CAN для каждой конечной точки. Вторая функция обрабатывает коммуникацию конечных точек, которая имеет требования по времени, такие как NMTE (Heartbeat/Node Guard) и любая конечная точка PDO. Функция mCO_ProcessAllEvents() должна быть вызвана достаточно часто, чтобы было можно захватить все события сообщения от драйвера. Функция mCO_ProcessAllTimeEvents() должна вызываться с интервалом 1 мс.
В описанном стеке используется свой формат идентификаторов CAN, привязанный к архитектуре CAN микроконтроллеров PIC, это так называемый формат MCHP (или формат Microchip). Соответствие между идентификатором в формате CANopen и идентификатором в формате MCHP показано ниже.
Это основная подпрограмма, из которой обрабатываются все события. Отсюда обрабатываются все события передачи и приема в Менеджере Коммуникаций. Эта функция должна вызываться так часто, как это только возможно, чтобы можно было обработать любые события коммуникации. Насколько часто это нужно - сильно зависит от драйвера и необходимости ответить на все события драйвера до наступления переполнения.
Пример:
voidmain(void)
{
// Выполнение любой специфичной для приложения инициализации:
TimerInit(); // инициализация таймера
mSYNC_SetCOBID(0x12); // Установка SYNC COB-ID (в формате MCHP)
mCO_SetNodeID(0x01); // Установка идентификатора узла
mCO_SetBaud(0x00); // Установка скорости
mNMTE_SetHeartBeat(0x00); // Установка начального heartbeat
mNMTE_SetGuardTime(0x00); // Установка начального guard time
mNMTE_SetLifeFactor(0x00); // Установка начального life time
mCO_InitAll(); // Инициализация CANopen для запускаwhile(1)
{
// Обработка событий CANopen:mCO_ProcessAllEvents();
// Обработка специфичных для приложения функций
...
if (TimerIsOverflowEvent())
{
// Событие таймера: сюда попадаем каждую 1 мс.// Обработка связанных с таймером событий:
mCO_ProcessAllTimeEvents();
// Выполнение других функций, привязанных к отсчету реального времени:
...
}
}
}
Это основная подпрограмма, из которой обрабатываются все события, связанные с реальным временем низкого разрешения. Эта функция должна быть вызвана с интервалом 1 мс. События высокого разрешения (обычно в области микросекунд) должны обрабатываться в приложении. Внутренне эта функция гарантирует, что все объекты стека, которые требуют управления по времени, получили свое событие тика.
Пример см. в предыдущей врезке, где приведено описание подпрограммы mCO_ProcessAllEvents.
Эта функция должна вызываться после установки всех начальных параметров объектов. Она выдает сброс драйверу CAN, и запускает открытие требуемых коммуникаций. После этого вызова узел появится в сети, и будет отправлено сообщение boot-up.
Функция mCO_InitAll в настоящее время является макросом-оберткой над функцией _CO_COMMResetEventManager. Она должна быть вызвана перед входом в главный цикл main приложения. Пример:
voidmain(void)
{
// В этом месте выполняются инициализации, специфичные
// (настройка выводов GPIO, аппаратуры и т. п.):
...
TimerInit(); // Инициализация таймера
mSYNC_SetCOBID(0x1000); // Установка SYNC COB-ID (в формате MCHP)
mCO_SetNodeID(0x02); // Установка Node ID
mCO_SetBaud(0x00); // Установка скорости CAN
mNMTE_SetHeartBeat(20000); // Установка начального heartbeat
mNMTE_SetGuardTime(0000); // Установка начального guard time
mNMTE_SetLifeFactor(0x00); // Установка начального life time
DemoInit(); // Инициализация демонстрационного объекта AN945mCO_InitAll(); // Инициализация стека CANopen для запуска, будет// отправлено сообщение boot-upwhile(1)
{
// Обработка событий CANopen:
mCO_ProcessAllEvents();
// Обработка событий, специфичных для приложения:
DemoProcessEvents();
// События таймера происходят с периодом 1 мс:if (TimerIsOverflowEvent())
{
// Обработка событий, связанных с таймером
mCO_ProcessAllTimeEvents();
}
}
}
Вызовите эту функцию для установки идентификатора узла node_id. Значение node_id должно быть типа unsigned char, где самый старший бит сброшен (его использование зарезервировано). Дополнительно спецификация CANopen резервирует значение 00h для NodeID. Таким образом, допустимое значение для идентификатора узла node_id находится в диапазоне 01h .. 7Fh включительно. Эта функция должна быть вызвана перед mCO_InitAll().
Параметр:
unsigned char node_id: идентификатор этого узла, допустим диапазон от 01h до 7Fh.
Пример см. во врезке, где приведено описание подпрограммы mCO_ProcessAllEvents.
Вызовите эту функцию, чтобы получить значение текущего ID, используемого стеком. Значение будет возвращено как unsigned char, допустимый диапазон 01h..7Fh.
Вызовите эту функцию, чтобы установить скорость по шине для узла. Значение bitrate должно быть в диапазоне 0..8 включительно. Любое другое значение приведет к использованию установки по умолчанию 0. Действительная скорость определяется константами в драйвере CAN. Эта функция должна быть вызвана перед mCO_InitAll().
Параметр:
unsigned char bitrate: значение для выбора скорости в диапазоне 0..8.
Пример см. во врезке, где приведено описание подпрограммы mCO_ProcessAllEvents.
Вызовите эту функцию, чтобы получить текущую настройку скорости, используемую этим узлом. Настройка будет возвращена как значение unsigned char. Точные значения скоростей, соответствующие настройке, определяются в драйвере CAN (см. раздел "Драйвер ECAN™").
Пример см. во врезке, где приведено описание подпрограммы mCO_ProcessAllEvents.
[События и службы PDO]
В этой секции описаны функции, используемые для поддержки PDO. Все они нужны для поддержки таких низкоуровневых коммуникаций, как открытие, закрытие и обмен данными со специфическими конечными точками PDO.
Разработка PDO. Критической задачей при разработке приложения CANopen является разработка объектов PDO. Должны быть приняты некоторые решения по поводу того, какие функции нужно поддерживать: выбор между динамическим и статическим отображением PDO (PDO mapping), выбор режима синхронизации передачи, и нужно ли поддерживать время запрета (inhibit time). Исходный код этого стека CANopen предоставляет базовый набор инструментария для поддержки коммуникации PDO для такого вида возможностей, с которыми может быть собран проект.
Критическими моментами для поддержки PDO является разработка кода обработки следующих элементов:
• События коммуникаций PDO • Отображение PDO (PDO Mapping) • Синхронизация PDO • Событие PDO (PDO Event) и время запрета (Inhibit time)
События коммуникаций PDO. У каждого разрешенного PDO будут в наличии некоторые события коммуникаций, для поддержки установки типовых аспектов PDO. События в действительности это функции обратного вызова (callback), указанные в OD для обработки определенных параметров коммуникаций PDO. Например, мастер сети отправляет запрос через SDO к подчиненному устройству, чтобы поменять тип PDO (см. спецификации стандарта для получения информации по типам коммуникации). Запрос передается вверх через стек к словарю, и в конечном счете попадает к функции, которая обрабатывает доступ к типу.
Пример 1 и Пример 2 демонстрируют связь между словарем OD и действительной функцией CO_COMM_TPDO1_TypeAccessEvent(). Пример 1 показывает элемент словаря. Пример 2 показывает действительную callback-функцию. В этом случае пример демонстрирует поддержку только для типов 0..240, 254 и 255 (типы передач PDO показаны в таблице 5). Обратите внимание, что ни одно из событий не обсуждается подробно, потому что они создаются разработчиком приложения и, таким образом, обрабатываются в коде разработчика.
voidCO_COMM_TPDO1_TypeAccessEvent(void)
{
unsignedchar tempType;
switch (mCO_DictGetCmd())
{
//case DICT_OBJ_INFO:// Получение информации об объекте.// // Приложение должно использовать эту ветку// // для загрузки структуры длиной, доступом
// // и информацией отображения.// break;case DICT_OBJ_READ: // Чтение объекта.// Запись типа в буфер*(uDict.obj->pReqBuf) = uDemoSyncSet;
break;
case DICT_OBJ_WRITE: // Запись объекта.
tempType =*(uDict.obj->pReqBuf);
if ((tempType >=0) && (tempType <=240))
{
// Установка нового типа и повторная синхронизация:
uDemoSyncCount = uDemoSyncSet = tempType;
}
elseif ((tempType ==254) || (tempType ==255))
{
uDemoSyncSet = tempType;
}
else
{
mCO_DictSetRet(E_PARAM_RANGE); //ошибка
}
break;
}
}
Таблица 5. Типы передач PDO.
Тип передачи
Символ синхронизации передачи PDO
Циклический
Не циклический
Синхронный
Асинхронный
RTR
0
x
x
1..240
x
x
241..251
Зарезервировано
252
x
x
253
x
x
254
x
255
x
Отображение PDO (PDO Mapping). Как уже упоминалось, отображение PDO на данные приложения может быть статическое или динамическое. Для поддержки каждого из этих вариантов не предоставлено никакого кода. Однако никакой код в реальности не нужен для предоставления статического отображения. Таким образом, статический код значительно проще, и требует меньше обработки для своей поддержки. Динамическое отображение PDO более сложное для реализации, потому что требует ссылок на словарь один или большее количество раз на один объект PDO. В этом варианте стека CANopen демонстрируется только статическое отображение.
Пример 3 показывает элемент в словаре. Действительное отображение это только данные ROM, как это показано в примере 4. Любые запросы к SDO по умолчанию для данных отображения в словаре будут приводить к чтению статических данных непосредственно из ROM. Подразумевается, что статические данные, сохраненные в ROM, имеют формат отображения, показанный на рис. 3, в соответствии со спецификацией CANopen.
rom unsignedlong uTPDO1Map =0x60000108;
rom unsignedlong uRPDO1Map =0x62000108;
rom unsignedlong uPDO1Dummy =0x00000008;
Синхронизация PDO. Объекты PDO могут быть синхронизированы путем привязки их функций к объекту SYNC. Синхронизация зависит от типа передачи. Типы передач перечислены в таблице 5 (см. врезку "События коммуникаций PDO").
Синхронизация заключается просто в использовании функции CO_COMMSyncEvent() для обработки конечной точки PDO. Это более подробно обсуждается в секции событий синхронизации, см. раздел "События и службы SYNC".
Таймеры. Поддерживается таймер событий (event timer), в то время как таймер запрета (inhibit timer) оставлен на предоставление дизайнеру приложения. Причина в том, что требуется более точная разрешающая способность по времени (100 мкс). Если приложению требуется таймер событий, можно обработать CO_PDO1LSTimerEvent(), чтобы получить события тика 1 мс.
Открывает конечную точку RPDO по её номеру. Здесь доступно только 4 объекта PDO. Обычно эта функция была бы вызвана в коммуникационном событии записи объекта RPDO. В сущности событие коммуникации записи объекта PDO генерируется, когда узел сети запрашивают на запуск коммуникаций PDO.
Параметр:
const unsigned char PDOnum: допустимый диапазон от 1 до 4. Это должно быть реальное число, не макрос.
Пример:
// Обработка события доступа к COB-ID
voidCO_COMM_RPDO1_COBIDAccessEvent(void)
{
switch (mCO_DictGetCmd())
{
case DICT_OBJ_READ: // Чтение объекта.// Трансляция формата MCHP COB в формат CANopen COB:
mTOOLS_MCHP2CO(mRPDOGetCOB(1));
// Возврат COB-ID*(unsignedlong*)(uDict.obj->pReqBuf) = mTOOLS_GetCOBID();
break;
case DICT_OBJ_WRITE: // Запись объекта.// Трансляция COB в формат MCHP:
mTOOLS_CO2MCHP(*(unsignedlong*)(uDict.obj->pReqBuf));
// Если это запрос остановки PDO:if ((*(UNSIGNED32 *)(&mTOOLS_GetCOBID())).PDO_DIS)
{
// И если принятый COB соответствует сохраненному COB и типу, то закрытьif (!((mTOOLS_GetCOBID() ^ mRPDOGetCOB(1)) &0xFFFFEFFF))
{
// Но закрыть только если конечная точка PDO была открытаif (mRPDOIsOpen(1))
{
mRPDOClose(1);
}
// Показать локальному объекту, что этот PDO запрещен
(*(UNSIGNED32 *)(&mRPDOGetCOB(1))).PDO_DIS =1;
}
else
{
mCO_DictSetRet(E_PARAM_RANGE); //ошибка
}
}
// Иначе если RPDO не открыт, то запустить RPDOelse
{
// И если принятый COB соответствует сохраненному COB и типу, то откроемif (!((mTOOLS_GetCOBID() ^ mRPDOGetCOB(1)) &0xFFFFEFFF))
{
// но откроем только елси конечная точка PDO была закрытаif (!mRPDOIsOpen(1))
{
mRPDOOpen(1);
}
// Показать локальному объекту, что этот PDO разрешен
(*(UNSIGNED32 *)(&mRPDOGetCOB(1))).PDO_DIS =0;
}
else
{
mCO_DictSetRet(E_PARAM_RANGE); //ошибка
}
}
break;
}
}
Эта функция запрашивает у Менеджера Коммуникаций, был ли принят новый PDO по указанному номеру.
Параметр:
const unsigned char PDOnum: допустимый диапазон от 1 до 4. Это должно быть реальное число, не макрос.
Возвращаемые значения:
TRUE: данные были приняты и готовы к обработке. FALSE: данные не были пока приняты.
Пример:
voidDemoProcessEvents(void)
{
unsignedchar change;
unsignedchar rise;
unsignedchar fall;
// Прочитать ножки входного порта
(*(UNSIGNED8 *)uLocalXmtBuffer).bits.b0 = PORTBbits.RB5;
(*(UNSIGNED8 *)uLocalXmtBuffer).bits.b1 = PORTBbits.RB4;
// Определить, поменялось ли значение на них
change = uIOinDigiInOld ^ uLocalXmtBuffer[0];
// Определить, были ли события нарастания
rise = (uIOinIntRise & change) & uLocalXmtBuffer[0];
// Определить, были ли события спада
fall = (uIOinIntFall & change) &~uLocalXmtBuffer[0];
// Определить, были ли события изменения
change = (uIOinIntChange & change);
// Присвоить текущее значение старому
uIOinDigiInOld = uLocalXmtBuffer[0];
// Если любое из этого верно, то показать условие прерыванияif (uIOinIntEnable & (change | rise | fall))
uDemoState.bits.b1 =1;
if (uDemoState.bits.b1)
{
switch (uDemoSyncSet)
{
case0: // Не циклическая синхронная передача.// Установка флага синхронной передачи
uDemoState.bits.b2 =1;
break;
case254: // Асинхронная передачаcase255:
// Установка флага асинхронной передачи
uDemoState.bits.b0 =1;
break;
}
}
// Если готовы к передачеif (mTPDOIsPutRdy(1) && uDemoState.bits.b0)
{
// Сказать стеку, что данные загружены для передачи:
mTPDOWritten(1);
// Сброс любых синхронных или асинхронных флагов:
uDemoState.bits.b0 =0;
uDemoState.bits.b1 =0;
}
// Если какие-либо данные были принятыif (mRPDOIsGetRdy(1))
{
// Записать первый байт буфера
LATD = uLocalRcvBuffer[0];
// Чтение PDO, освободить драйвер для приема других данных
mRPDORead(1);
}
}
Эта функция вызывается, чтобы показать Менеджеру Коммуникаций, что последнее принятое сообщение было прочитано и надлежащим образом обработано. Это позволяет Менеджеру Коммуникаций принять другое сообщение PDO от драйвера. Приложение должно просто сделать копию данных или даже обработать данные на лету.
const unsigned char PDOnum: допустимый диапазон от 1 до 4. Это должно быть реальное число, не макрос.
Пример см. во врезке с описанием функции mRPDOIsGetRdy.
Эта функция устанавливает RPDO COB-ID (параметр rpdoCOB), PDO указывается по номеру (параметр PDOnum, допустимый диапазон от 1 до 4). Это должно быть установлено перед открытием PDO. COB-ID должен быть представлен в стандартном формате Microchip (формат MCHP).
Параметры:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос. unsigned long rpdoCOB: COB-ID принимаемый этим PDO.
Пример:
voidDemoInit(void)
{
// Порт D весь работает как выход
LATD =0;
TRISD =0;
uDemoSyncSet =255;
uIOinFilter =0;
uIOinPolarity =0;
uIOinIntChange =1;
uIOinIntRise =0;
uIOinIntFall =0;
uIOinIntEnable =1;
uIOinDigiInOld = uLocalXmtBuffer[0] =0;
uLocalRcvBuffer[1] = uLocalXmtBuffer[1] =0;
uLocalRcvBuffer[2] = uLocalXmtBuffer[2] =0;
uLocalRcvBuffer[3] = uLocalXmtBuffer[3] =0;
uLocalRcvBuffer[4] = uLocalXmtBuffer[4] =0;
uLocalRcvBuffer[5] = uLocalXmtBuffer[5] =0;
uLocalRcvBuffer[6] = uLocalXmtBuffer[6] =0;
uLocalRcvBuffer[7] = uLocalXmtBuffer[7] =0;
// Преобразование в формат MCHP
mTOOLS_CO2MCHP(mCOMM_GetNodeID().byte +0xC0000180L);
// Сохранение COB
mTPDOSetCOB(1, mTOOLS_GetCOBID());
// Преобразование в формат MCHP
mTOOLS_CO2MCHP(mCOMM_GetNodeID().byte +0xC0000200L);
// Сохранение COBmRPDOSetCOB(1, mTOOLS_GetCOBID());
// Установка указателя на буферы
mTPDOSetTxPtr(1, (unsignedchar*)(&uLocalXmtBuffer[0]));
// Установка указателя на буферы
mRPDOSetRxPtr(1, (unsignedchar*)(&uLocalRcvBuffer[0]));
// Установка длины
mTPDOSetLen(1, 8);
}
Эта функция возвращает указатель на локальный буфер приема. Указатель должен быть установлен перед открытием коммуникаций с конечной точкой. Когда коммуникации открыты, то все сообщения будут сохранены по месту, на которое ссылается этот указатель.
Параметр:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос.
Эта функция устанавливает указатель на локальный буфер приема. Этот указатель должен быть установлен перед открытием коммуникаций с конечной точкой. Когда коммуникации открыты, то все сообщения будут сохранены по месту, на которое ссылается этот указатель.
Параметры:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос. unsigned char *pRXBUF: буфер для сообщений (размер не менее 8 байт).
Пример см. во врезке с описанием функции mRPDOSetCOB.
Открывает конечную точку TPDO. Здесь доступно только 4 объекта PDO. Обычно эта функция должна быть вызвана с объектом коммуникации TPDO при событии записи объекта. В сущности событие объекта коммуникаций PDO генерируется, когда узел сети запрашивает запуск коммуникаций PDO.
Параметр:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос.
Пример:
// Обработка событий доступа к COB-ID
voidCO_COMM_TPDO1_COBIDAccessEvent(void)
{
switch (mCO_DictGetCmd())
{
case DICT_OBJ_READ: // Чтение объекта// Трансляция COB формата MCHP в COB формата CANopen
mTOOLS_MCHP2CO(mTPDOGetCOB(1));
// Возврат COBID*(unsignedlong*)(uDict.obj->pReqBuf) = mTOOLS_GetCOBID();
break;
case DICT_OBJ_WRITE: // Запись объекта// Трансляция COB в формат MCHP
mTOOLS_CO2MCHP(*(unsignedlong*)(uDict.obj->pReqBuf));
// Если был запрос остановить PDOif ((*(UNSIGNED32 *)(&mTOOLS_GetCOBID())).PDO_DIS)
{
// И если принятый COB совпадает с сохраненным COB и типом, то закрываемif (!((mTOOLS_GetCOBID() ^ mTPDOGetCOB(1)) &0xFFFFEFFF))
{
// Но закрывает только если конечная точка PDO была открытаif (mTPDOIsOpen(1))
{
mTPDOClose(1);
}
// Показать локальному объекту, что этот PDO запрещен
(*(UNSIGNED32 *)(&mTPDOGetCOB(1))).PDO_DIS =1;
}
else
{
mCO_DictSetRet(E_PARAM_RANGE); //Ошибка
}
}
// Иначе если TPDO не открыт, то запускаем TPDOelse
{
// И если принятый COB совпадает с сохраненным COB и типом, то открываемif (!((mTOOLS_GetCOBID() ^ mTPDOGetCOB(1)) &0xFFFFEFFF))
{
// Но открываем только если конечная точка PDO была закрытаif (!mTPDOIsOpen(1))
{
mTPDOOpen(1);
}
// Показать локальному объекту, что этот PDO разрешен
(*(UNSIGNED32 *)(&mTPDOGetCOB(1))).PDO_DIS =0;
}
else
{
mCO_DictSetRet(E_PARAM_RANGE); //Ошибка
}
}
break;
}
}
Эта функция опрашивает Менеджер Коммуникаций, доступен ли слот для передачи PDO. Эта функция вернет true, если Менеджер готов принять сообщение для отправки по шине.
Параметр:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос.
Возвращаемые значения:
TRUE: Менеджер Коммуникаций готов принять новые данные. FALSE: Менеджер Коммуникаций занят передачей предыдущего сообщения.
Пример см. во врезке с описанием функции mRPDOIsGetRdy.
Показывает Менеджеру Коммуникаций, что сообщение для передачи было загружено. Это позволяет Менеджеру Коммуникаций поставить сообщение в очередь для передачи. Функция события CO_PDOTXFinEvent() вызывается, когда сообщение помещается на шину.
Параметр:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос.
Пример см. во врезке с описанием функции mRPDOIsGetRdy.
Эта функция устанавливает длину данных TPDO. Длина (length) должна быть в диапазоне от 0 до 8.
Параметры:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос. unsigned char length: длина PDO, должна быть в диапазоне 0 .. 8.
Пример см. во врезке с описанием функции mRPDOSetCOB.
Эта функция получает указатель на использующийся в настоящее время локальный буфер передачи. При передаче все сообщения будут переданы из места, на которое ссылается этот указатель.
Параметр:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос.
Эта функция устанавливает указатель на локальный буфер передачи. При передаче все сообщения будут переданы из места, на которое ссылается этот указатель.
Параметр:
const unsigned char PDOnum: допустимый диапазон 1 .. 4. Это должно быть реальное число, не макрос.
Пример см. во врезке с описанием функции mRPDOIsGetRdy.
Это функция обратного вызова (callback) события таймера. Она вызывается с интервалом 1 мс, если объект PDO разрешен. Обычно приложение должно использовать это функции для таймера события PDO, как это указано в стандарте CANopen.
Это callback-функция события завершения передачи. Это событие генерируется, когда сообщение, которое было поставлено в очередь на передачу, было отправлено по шине CAN.
[События и службы SYNC]
Имеется только одно событие, которое наступает из объекта SYNC: CO_COMMSyncEvent(). Это событие генерируется только когда принято сообщение SYNC, и это используется для синхронизации обработки PDO. Это событие должно быть проанализировано в коде приложения, где обрабатывается сообщение PDO.
Есть только 2 службы, полезные для поддержки объекта SYNC. Самая важная часть для установки COB-ID для объекта SYNC перед инициализацией коммуникаций CANopen, поскольку конечная точка автоматически открывается при инициализации.
Это единственное событие, которое генерирует объект SYNC. Это событие генерируется, когда принято сообщение SYNC, что используется для синхронизации обработки PDO.
Ниже показан простой пример функции обработки переменной синхронного типа PDO, которая по своей природе является циклической. Тип переменной задается в PDO Type (параметр коммуникаций TPDO по sub-индексу 2), который должен быть в диапазоне 1 .. 240 включительно.
voidCO_COMMSyncEvent(void)
{
// Обработка только если синхронный режимif ((uDemoSyncSet ==0) && (uDemoState.bits.b2))
{
// Сброс синхронной передачи и переход к асинхронной передаче
uDemoState.bits.b2 =0;
uDemoState.bits.b0 =1;
}
elseif ((uDemoSyncSet >=1) && (uDemoSyncSet <=240))
{
// Подстройка счетчика синхронизации
uDemoSyncCount--;
// Если наступило время генерации синхронного сообщения:if (uDemoSyncCount ==0)
{
// Сброс счетчика синхронизации
uDemoSyncCount = uDemoSyncSet;
// Запуск передачи PDO
uDemoState.bits.b0 =1;
}
}
}
Эта функция используется для установки COB-ID объекта SYNC. Она должна быть вызвана как минимум 1 раз перед инициализацией, чтобы правильно установить COB-ID в firmware системы.
Параметр:
unsigned long SYNC_COB: значение COB-ID в формате Microchip.
Пример см. во врезке с описанием функции mCO_ProcessAllEvents.
Эта функция используется, чтобы получить COB-ID используемый в настоящее время объектом SYNC.
Возвращаемое значение:
unsigned long SYNC_COB: значение COB-ID в формате Microchip (MCHP).
[События и службы NMT]
Представлено управление сетью (Network management) через объект NMT, который в сущности охватывает машину состояний узла (см. рис. 2).
Эта служба полезна для ввода узел сети в определенное состояние. Однако состояние будет меняться через обычные сетевые управляющие запросы от мастера NMT. Когда состояние поменялось по запросу мастера, генерируется событие. Ниже перечислены все события и службы NMT.
Вызовите эту функцию для запуска коммуникаций, которые были остановлены. Обычно функция автоматически вызывается подпрограммами обслуживания NMT в результате поступления запроса NMT от мастера, чтобы установить подходящее состояние узла.
Вызовите эту функцию для остановки узла, который находился в состоянии Operational или Pre-operational. Обычно функция автоматически вызывается подпрограммами обслуживания NMT в результате поступления запроса NMT от мастера, чтобы установить подходящее состояние узла.
Вызовите эту функцию, чтобы перевести узел сети в состояние Pre-operational. Обычно функция автоматически вызывается подпрограммами обслуживания NMT в результате поступления запроса NMT от мастера, чтобы установить подходящее состояние узла.
Вызовите эту функцию, чтобы перевести узел сети в состояние Operational. Обычно функция автоматически вызывается подпрограммами обслуживания NMT в результате поступления запроса NMT от мастера, чтобы установить подходящее состояние узла.
Эта callback-функция вызывается, когда пришел запрос на сброс приложения. Как будет обработан этот запрос - зависит от дизайна приложения. После обработки этого события будет сгенерировано событие CO_COMMResetEvent(). Коммуникации автоматически сбрасываются после обработки события CO_COMMResetEvent().
[События и службы Node Guard/Heartbeat]
Предоставлен комбинированный объект Node Guard/Heartbeat, как этого требует стандарт. Для него имеется небольшое количество служб для инициализации и получение информации об объекте.
Для объекта Node Guard/Heartbeat имеется только одно возможное генерируемое событие, которое относится специально к половинке node guard объекта. Функция CO_NMTENodeGuardErrEvent() вызывается, когда превышено время жизни (lifetime) объекта. Время жизни определено в спецификации как результат перемножения lifetime factor и guard time.
Вызовите эту функцию для установки Heartbeat. Параметр Heartbeat указывается как unsigned long в формате стандарта CANopen. Он устанавливается перед инициализацией коммуникаций.
Вызовите эту функцию для установки guard time. Параметр GuardTime это число unsigned long в формате стандарта CANopen. Он устанавливается перед инициализацией коммуникаций.
Эта callback-функция вызывается, когда происходит событие защиты узла (node guard event). Это момент времени, когда не было принято событие node guard в течение установленного времени жизни lifetime (результат умножения life time factor и guard time). Как будет это событие обработано - зависит от приложения.
[Объекты и OD]
В этой разработке каждый элемент словаря объектов (Object Dictionary, OD) это структура в памяти программ. Внутри каждой такой структуры находится информация, необходимая для идентификации объекта и его места размещения. Идентификационные данные достаточно гибкие, чтобы можно было различать друг от друга простые типы данных, массивы и структуры, определенные в качестве объектов OD. Также в качестве объекта может быть определена функция, и это дает настоящую гибкость для сложных объектов.
Структура объекта. Объект, определенный в OD, хранится в памяти программ микроконтроллера; структура объекта показана в Примере 5. Эта структура содержит достаточно информации, чтобы описать любой объект:
• index: индекс объекта. • subindex: sub-индекс объекта. • ctl: байт управления. Он определяет тип объекта. • len: длина объекта в байтах. • *pROM: указатель на объект или функцию обработки объекта. Этот указатель всегда приводится к типу unsigned char *.
Группы объектов. OD разбит на группы для быстрого поиска по словарю. Таким образом, каждый элемент OD должен быть сохранен в подходящей для этого группе. В таблице 6 идентифицированы все группы. Любой элемент OD должен быть размещен в числовом порядке в пределах соответствующей группы.
Таблица 6. Группы объектов OD.
Имя группы объектов
Индекс
Описание
DICTIONARY_DATA_TYPES
0000h
Типы данных, определенные в OD. Хотя типы данных определены в пределах OD, в стандарте указано, что поддержка этих типов со стороны словаря не требуется.
DICTIONARY_DEVICE_INFO
1000h
Эта группа относится к секции коммуникаций CANopen, и она содержит информацию, специфичную для устройства (узла сети), включая идентификаторы COB-ID, определенные конечные точки и статус.
DICTIONARY_SDO
1200h
Предоставлена одна группа для предоставления параметров SDO.
DICTIONARY_PDO1_RX_COMM
1400h
Отдельные группы предоставлены для коммуникационных параметров 4 RPDO.
DICTIONARY_PDO2_RX_COMM
1401h
DICTIONARY_PDO3_RX_COMM
1402h
DICTIONARY_PDO4_RX_COMM
1403h
DICTIONARY_PDO1_RX_MAP
1600h
Отдельные группы предоставлены для параметров отображения 4 RPDO.
DICTIONARY_PDO2_RX_MAP
1601h
DICTIONARY_PDO3_RX_MAP
1602h
DICTIONARY_PDO4_RX_MAP
1603h
DICTIONARY_PDO1_TX_COMM
1800h
Отдельные группы предоставлены для коммуникационных параметров 4 TPDO.
DICTIONARY_PDO2_TX_COMM
1801h
DICTIONARY_PDO3_TX_COMM
1802h
DICTIONARY_PDO4_TX_COMM
1803h
DICTIONARY_PDO1_TX_MAP
1A00h
Отдельные группы предоставлены для параметров отображения 4 TPDO.
DICTIONARY_PDO2_TX_MAP
1A01h
DICTIONARY_PDO3_TX_MAP
1A02h
DICTIONARY_PDO4_TX_MAP
1A03h
DICTIONARY_MANUFACTURER_SPECIFIC_1
2000h
Эти группы предоставлены для объектов, специфичных для производителя.
DICTIONARY_MANUFACTURER_SPECIFIC_2
3000h
DICTIONARY_MANUFACTURER_SPECIFIC_3
4000h
DICTIONARY_MANUFACTURER_SPECIFIC_4
5000h
DICTIONARY_STANDARD_1
6000h
Эти группы предоставлены для стандартных объектов CANopen.
DICTIONARY_STANDARD_2
7000h
DICTIONARY_STANDARD_3
8000h
DICTIONARY_STANDARD_4
9000h
Управляющие биты объекта. Как объект обрабатывается, зависит от его бит управления (object control bits). Объект может быть читаемый/записываемый, только для чтения, или даже функционально определен для приспособления к очень уникальным объектам. Таблица 7 определяет биты управляющего байта объекта (object control byte).
Таблица 7. Определения управляющих битов.
Бит
Имя
Описание
0
RD_BIT
Этот бит определяет доступ на чтение к объекту. Если он установлен, то объект можно прочитать из другого узла сети.
1
WR_BIT
Этот бит определяет доступ на запись к объекту. Если он установлен, то объект можно записать из другого узла сети.
2
ROM_BIT
Этот бит определяет, что объект размещен в ПЗУ. Установка этого бита не подразумевает, что объект не может быть записан. Он только определяет место хранения объекта.
3
EE_BIT
Этот бит определяет, что объект находится в EEPROM. Обратите внимание, что в настоящее время не предоставлена автоматическая поддержка доступа к EEPROM. Если бит EE_BIT установлен, то тогда бит FDEF_BIT также должен быть установлен, чтобы инструментарий доступа к OD знал, что разработчик приложения реализовал обработку доступа к EEDATA через свою специальную функцию.
4
FDEF_BIT
Этот бит показывает, что объект определен функционально. Обычно объекты определяются через функцию, если для него существуют специальные правила доступа, которые не применимы к простому статическому типу. Например это объект, который срабатывает на событии, тогда он должен быть определен функционально. Или другой пример - у объекта могут меняться правила доступа чтение/запись в зависимости от состояния приложения, в таком случае объект тоже должен быть определен через функцию. Также имейте в виду, что если этот бит установлен, то все другие биты могут быть определены внутри функции обработки объекта, кроме бита FSUB_BIT.
5
MAP_BIT
Этот бит определяет возможность отображения объекта. Таким образом, если этот бит установлен, то объект может быть отображен на PDO.
6
FSUB_BIT
Этот бит определяет, определен ли функционально весь массив sub-индексов. Т. е. для определенного индекса в словаре будет только одна запись. И все запросы доступа к любому sub-индексу будут обработаны функцией доступа к объекту. Это полезно для объектов, у которых все sub-индексы имеют одинаковый функционал, но требуют разных значений параметров; таким образом, в OD для объекта понадобится только одна запись.
7
-
В настоящий момент этот бит зарезервирован.
Чтобы упростить манипуляцию отдельными битами байта управления, предоставлена серия символических модификаторов битов. Таблица 8 предоставляет модификаторы логической операции И (AND) для управления объектом. Они могут быть скомбинированы вручную для формирования специфичного управления.
Таблица 8. Определения логического И для управляющих бит.
Бит
Описание
RD
Разрешено чтение.
N_RD
Чтение не разрешено.
WR
Разрешена запись.
N_WR
Запись не разрешена.
ROM
Объект базируется на ПЗУ.
N_ROM
Объект не базируется на ПЗУ.
EE
Объект базируется на EEPROM.
N_EE
Объект не базируется на EEPROM.
FDEF
Объект определен функционально.
N_FDEF
Объект не определен функционально.
MAP
Объект отображаемый.
N_MAP
Объект отобразить нельзя.
FSUB
Sub-индекс определен через функцию.
N_FSUB
Sub-индекс не определен через функцию.
Например, следующий оператор задает, что объект можно читать, записывать, он определен как функция, и может быть отображен на переменную обработки (mappable):
RD & WR & N_ROM & N_EE & FDEF & MAP & N_FSUB
Подобным образом в таблице 9 предоставлены типичные модификаторы логической операции ИЛИ (OR) для управления объектом. Они могут быть также скомбинированы с именами бит, показанными в таблице 8.
Таблица 9. Определения логического ИЛИ для управляющих бит.
Бит
Описание
CONST
Объект только для чтения, базирующийся в ROM.
RW
Объект можно читать и записывать.
RO
Объект только для чтения.
WO
Объект только для записи.
RW_EE
Объект, размещенный в EEPROM, который можно читать и записывать.
RO_EE
Объект, размещенный в EEPROM, который можно только читать.
WO_EE
Объект, размещенный в EEPROM, который можно только записывать.
FUNC
Функционально определенный объект.
Например, следующий оператор задает, что объект можно читать, записывать, он определен как функция, и может быть отображен на переменную обработки (то же самое, что и в предыдущем примере):
RW | FUNC | MAP_BIT
Некоторые примеры использования модификаторов бит показаны в Примере 6, элементы словаря 4, 8, 9 и 10.
Простые объекты. OD предоставляет поддержку простых объектов. Простой объект в сущности это такой объект, который может быть представлен как обычный тип данных. Это включает любой тип данных, поддерживаемый компилятором, а также массивы.
Простой объект определяется в OD по ссылке внутри словаря. Это иллюстрировано по первому элементу словаря в Примере 7. Запрос на чтение для этого объекта вернет данные, сохраненные в uCO_DevManufacturerStatReg; запрос на запись возвратит ошибку, поскольку это объект только для чтения.
Функционально определенный объект. Объекты определяются функцией, когда у него есть некоторые свойства, которые не соответствуют стандартному типу данных или массиву, как они определены на языке C. Например, переменная unsigned char MyObj, у которой нет необычных условий, не нуждается в определении функцией; однако если в MyObj бит 7 разрешает запись в MyObj, то это потребует специальной обработки, и объект должен быть определен функцией, подобно тому, как это сделано для идентификаторов COB-ID.
Объект определен по функции, когда в управляющем байте установлен бит FDEF_BIT. Это продемонстрировано во втором элементе Примера 7, который определяет COB-ID для объекта SYNC. В этом случае функция _CO_COMM_SYNC_COBIDAccessEvent() вызывается, когда осуществляется запрос на доступ к объекту по индексу 1005h, sub-индексу 0.
Правила написания функции обработки объекта. К объекту обращаются через SDO, PDO или каким-либо доступом из приложения. Если объект определен по функции, то всякий раз, когда происходит обращение к объекту, будет вызвана функция, определенная в OD для этого объекта. Есть 3 возможных события, которые функция обработки объекта должна поддерживать, когда произошло обращение к объекту:
• Read control: чтение управляющих бит, определенных этой функцией. Это относится ко всем битам, кроме FSUB_BIT и FDEF_BIT; эти биты должны быть определены в OD для объекта. • Read: чтение объекта, если он предназначен для чтения. • Write: запись объекта, если он предназначен для записи.
Пример 8 демонстрирует, как выглядит типичная функция обработки объекта. Пример 9 показывает обработчик для объекта TPDO1 COB-ID.
Пример 8, общий вид (шаблон) обработки функционального объекта:
voidMyObjectHandlingFunction(void)
{
switch (mCO_DictGetCmd())
{
case DICT_OBJ_INFO: // Получение информации об объекте.// Код в этом типе запроса должен модифицировать тип доступа. Например,// если объект может менять RO на RW на основании определенного// состояния приложения, то здесь должна быть обработана эта ситуация// изменения типа доступа. В большинстве ситуаций это может быть// опущено, поскольку информация об объекте статична; статичная// информация поддерживается непосредственно данными OD.break;
case DICT_OBJ_READ: // Чтение объекта.// Это запрос чтения объекта. Код в этом типе запроса должен обработать// любые перемещения данных и/или события, относящиеся к чтению.break;
case DICT_OBJ_WRITE: // Запись объекта.// Это запрос записи объекта. Код в этом типе запроса должен обработать// любые перемещения данных и/или события, относящиеся к записи.break;
}
}
Пример 9, реальная функциональная обработка объекта:
voidCO_COMM_TPDO1_COBIDAccessEvent(void)
{
switch (mCO_DictGetCmd())
{
case DICT_OBJ_READ: // Чтение объекта.// Трансляция формата MCHP COB в формат CANopen COB
mTOOLS_MCHP2CO(mTPDOGetCOB(1));
// Возврат COB-ID*(unsignedlong*)(uDict.obj->pReqBuf) = mTOOLS_GetCOBID();
break;
case DICT_OBJ_WRITE: // Запись объекта.// Трансляция COB в формат MCHP
mTOOLS_CO2MCHP(*(unsignedlong*)(uDict.obj->pReqBuf));
// Если это запрос остановки PDOif ((*(UNSIGNED32 *)(&mTOOLS_GetCOBID())).PDO_DIS)
{
// И если принятый COB совпадает с сохраненным COB и типом, то закроем PDOif (!((mTOOLS_GetCOBID() ^ mTPDOGetCOB(1)) &0xFFFFEFFF))
{
// но закроем только если конечная точка PDO была открытаif (mTPDOIsOpen(1)) {mTPDOClose(1);}
// показать локальному объекту, что PDO запрещен
(*(UNSIGNED32 *)(&mTPDOGetCOB(1))).PDO_DIS =1;
}
else
{
mCO_DictSetRet(E_PARAM_RANGE); //Ошибка
}
}
// Иначе если TPDO не открыт, то запуск TPDOelse
{
// И если принятый COB совпадает с сохраненным COB и типом, то откроем PDOif (!((mTOOLS_GetCOBID() ^ mTPDOGetCOB(1)) &0xFFFFEFFF))
{
// но откроем только если конечная точка PDO была закрытаif (!mTPDOIsOpen(1))
{
mTPDOOpen(1);
}
// показать локальному объекту, что PDO разрешен
(*(UNSIGNED32 *)(&mTPDOGetCOB(1))).PDO_DIS =0;
}
else
{
mCO_DictSetRet(E_PARAM_RANGE); //Ошибка
}
}
break;
}
}
Функция обработки объекта предоставляется с функциями и структурой для обработки запросов к объекту и от него. Это функции mCO_DictGetCmd() и mCO_DictSetRet(). Первая используется для получения команды, и вторая используется для возврата любых ошибок запрашивающему коду. Таблица 11 перечисляет ошибки, которые могут быть возвращены. В случае успешного запроса никакой ответ не требуется; OD подразумевает успех.
Запрашивающий код устанавливает указатель в словаре (uDict.obj) на свою локальную структуру DICT_OBJ. Эта структура содержит информацию как об объекте, так и о запрашивающем коде (структура определена в таблице 10). Пример 8 демонстрирует шаблон использования структуры в функции обработки объекта.
Таблица 10. Структура DICT_OBJ UDICT.
Элемент
Тип
Описание
pReqBuf
unsigned char *
Указатель на буфер запрашивающего кода. Этот указатель ссылается на данные в памяти, когда осуществляется запись объекта. Когда осуществляется чтение, этот указатель ссылается на место в памяти для буфера, которое предоставил запрашивающий код.
reqLen
unsigned int
Количество запрашиваемых байт. Это количество не должно превышать длины объекта.
reqOffst
unsigned int
Начальная точка запроса. Это предоставляется для поддержки порционных запросов, которые нужны по причине недостаточного места под буфер. Это наиболее полезно для запросов чтения; для запросов записи это вряд ли нужно, потому что скорее всего частичная запись объекта не будет желательна. Также этот параметр не нужно поддерживать, если количество байт в объекте PDO меньше или равно 8 (для объекта SDO это 4 байта).
index
unsigned int
Индекс CANopen.
subindex
unsigned char
Sub-индекс CANopen.
ctl
enum DICT_CTL
Тип доступа к памяти.
len
unsigned int
Размер объекта в байтах.
p
union DICT_PTRS
Указатели на объекты
Таблица 11. Определения ошибок.
Имя
Описание
E_SUCCESS
Успешное завершение операции, ошибок нет.
E_TOGGLE
Бит toggle не поменял свое значение.
E_SDO_TIME
Таймаут протокола SDO.
E_CS_CMD
Спецификатор команды клиент/сервер недопустим или неизвестен.
E_MEMORY_OUT
Недостаточно памяти.
E_UNSUPP_ACCESS
Не поддерживаемый доступ к объекту.
E_CANNOT_READ
Попытка чтения объекта, который предназначен только для записи.
E_CANNOT_WRITE
Попытка записи объекта, который предназначен только для чтения.
E_OBJ_NOT_FOUND
Объект не существует в OD.
E_OBJ_CANNOT_MAP
Объект не может быть отображен на PDO.
E_OBJ_MAP_LEN
Количество и длина объектов, которые должны быть отображены, превысили бы длину PDO.
E_GEN_PARAM_COMP
Общая несовместимость параметра.
E_GEN_INTERNAL_COMP
Общая внутренняя несовместимость в устройстве.
E_HARDWARE
Сбой доступа из-за аппаратной ошибки.
E_LEN_SERVICE
Тип данных не соответствует, длина параметра службы не соответствует.
E_LEN_SERVICE_HIGH
Тип данных не соответствует, длина параметра службы слишком велика.
E_LEN_SERVICE_LOW
Тип данных не соответствует, длина параметра службы слишком мала.
E_SUBINDEX_NOT_FOUND
Sub-индекс не существует.
E_PARAM_RANGE
Значение параметра вышло из диапазона (только для доступа на запись).
E_PARAM_HIGH
Значение параметра слишком велико.
E_PARAM_LOW
Значение параметра слишком мало.
E_MAX_LT_MIN
Максимальное значение меньше, чем минимальное.
E_GENERAL
Общая ошибка.
E_TRANSFER
Данные не могут быть перемещены или сохранены приложением.
E_LOCAL_CONTROL
Данные не могут быть перемещены или сохранены приложением из-за локального управления.
E_DEV_STATE
Данные не могут быть перемещены или сохранены приложением из-за текущего состояния устройства.
Службы OD. Есть несколько служб обработки словаря для использования конечной точкой SDO. Если это необходимо, это также может использоваться для динамического отображения PDO (dynamic PDO mapping).
Эта функция читает объект, определенный через myObj. Чтобы это использовать, информация объекта должна быть сохранена локально как структура DICT_OBJ, когда она передается в функцию mCO_DictObjectRead(). Внутри используется только ссылка.
В структуре DICT_OBJ находится необходимая информация, которая должна быть получена из объекта. Некоторая часть этой информации должна быть предоставлена вызывающей функцией, и другая информация должна быть предоставлена из OD. Функция mCO_DictObjectDecode() должна быть вызвана перед вызовом mCO_DictObjectRead(), чтобы получить доступ и информацию ссылки, сохраненные в OD. Другая информация должна быть предоставлена пользователем. В таблице 12 описана структура и источник информации для каждого элемента.
Таблица 12. Структура DICT_OBJ.
Элемент
Тип
Кем предоставляется
Описание
pReqBuf
unsigned char *
Пользователь
Указатель на буфер запрашивающего кода.
reqLen
unsigned int
Пользователь
Количество запрашиваемых байт.
reqOffst
unsigned int
Пользователь
Начальная точка запроса.
index
unsigned int
Пользователь.
Индекс CANopen.
subindex
unsigned char
Пользователь.
Sub-индекс CANopen.
ctl
enum DICT_CTL
mCO_DictObjectDecode()
Тип доступа к памяти.
len
unsigned int
mCO_DictObjectDecode()
Размер объекта в байтах.
p
union DICT_PTRS
mCO_DictObjectDecode()
Указатели на объекты
Параметры:
DICT_OBJ myObj: структура объекта, показанная в таблице 12, см. врезку с описанием функции mCO_DictObjectRead.
Возвращаемые значения отсутствуют. Используйте mCO_DictGetRet() для получения кода ошибки.
Пример:
voidMyFunc(void)
{
DICT_OBJ myLocalObj;
unsignedchar localArray[20];
// Указание объекта
myLocalObj.index =0x1008L;
myLocalObj.subindex =0x00;
// Получение информации, сохраненной в словаре OD
mCO_DictObjectDecode(myLocalObj);
// Указать локальное пространство, и что нужно читать данные
myLocalObj.pReqBuf = localArray;
myLocalObj.reqLen =0x8;
myLocalObj.reqOffst =0x0;
// Чтение объектаmCO_DictObjectRead(myLocalObj);
}
Эта функция записывает объект, заданный через myObj. Чтобы это использовать, информация объекта должна быть сохранена локально как структура DICT_OBJ и затем передана в функцию mCO_DictObjectWrite(). Внутри используется только ссылка.
Параметр:
DICT_OBJ myObj: структура объекта, показанная в таблице 12, см. врезку с описанием функции mCO_DictObjectRead.
Возвращаемые значения отсутствуют. Используйте mCO_DictGetRet() для получения кода ошибки.
Базовое использование функции подобно тому, как показано в примере врезки описания функции mCO_DictObjectRead.
Эта функция используется для заполнения любой статической информации для определенного объекта, который находится в OD. Объект задается через myObj, который должен быть определен локально и передан в эту функцию. Функция возьмет индекс и sub-индекс для поиска объекта в OD. Объект найден, то указатель, длина и некоторая управляющая информация будет загружена в структуру myObj (см. таблицу 12 во врезке с описанием функции mCO_DictObjectRead). Будет возвращена информация статуса, и она может быть получена вызовом функции mCO_DictGetRet().
Параметр:
DICT_OBJ myObj: структура объекта, как она определена в таблице 12 (см. врезку с описанием функции mCO_DictObjectRead).
Возвращаемое значение отсутствует. Используйте mCO_DictGetRet() для получения кода ошибки.
Базовое использование функции подобно тому, как показано в примере врезки описания функции mCO_DictObjectRead.
Эта функция используется для установки статуса возврата операции OD. Это используется только внутри функции обработки объекта.
Параметр:
unsigned char retVal: статус возврата запроса объекта. Все возможные значения этого параметра перечислены в таблице 11 (см. выше секцию "Функционально определенный объект").
Как использовать функцию см. в Примере 9 секции "Функционально определенный объект".
[Драйвер ECAN™]
Функции в этой секции описывают функциональный интерфейс драйвера ECAN. Обратите внимание, что драйвер, предоставленный с этим стеком CANopen, специально разработан для устройств PIC18F, на борту которых имеется аппаратура технологии ECAN. Можно использовать также внешний контроллер CAN, с другим драйвером и другими вызовами функций. В этом событии пользователю требуется предоставить подходящий драйвер.
Это функция обработчика события. Все поставленные в очередь события обрабатываются внутри этой функции. Эта функция вызывается из стека CANopen, когда вызывается CO_ProcessAllEvents.
Эта функция сбрасывает коммуникации CAN, и устанавливает подходящую скорость по шине. Эта функция вызывается из стека CANopen, когда получен запрос сброса либо от приложения, либо от мастера NMT.
Эта функция открывает коммуникации CAN, её следует рассматривать как запрос. В зависимости от состояния активности шины, коммуникации могут быть не открыты немедленно.
Эта функция используется, чтобы опросить драйвер: было ли событие переполнения буфера приема. Если найдено событие переполнения, то оно может быть удалено вызовом функции mCANErrClearOverFlow. Когда случилось условие переполнения, то одно или большее количество сообщений было потеряно. Как обработать эту ситуацию - зависит от приложения; спецификация CANopen не требует определенного метода обработки этого условия.
Возвращаемые значения:
TRUE: было переполнение буфера приема. FALSE: не было переполнения буфера приема.
Эта функция устанавливает текущее значение скорости бит (bitrate) шины CAN. Скорость не изменяется немедленно, это в действительности ставится в очередь драйвера, пока драйвер и аппаратура CAN не будут готовы принять изменение. Обычно эта функция вызывается только 1 раз в момент старта.
Параметр:
unsigned char CANBitRate: здесь может быть любое значение; однако считаются действительными только значения в диапазоне 0 .. 8. Все другие значения автоматически приведут к установке скорости по умолчанию, которая задается опцией 0. Все 9 опций скорости определены в файле CO_DEFS.DEF.
Эта функция вернет текущий битрейт, используемый драйвером.
Возвращаемые значения:
unsigned char: текущий битрейт. Достоверными будут только значения 0 .. 8; однако функция может вернуть другие значения, если в вызов mCANSetBitRate() было передано значение, отличающееся от диапазона 0 .. 8.
Эта функция сканирует доступные почтовые ящики в поиске открытого слота. Должен быть передан идентификатор CAN вместе с уникальным ненулевым дескриптором (handle) к этому идентификатору. Если найден слот, то все сообщения, содержащие предоставленный идентификатор CAN, будут приняты, и этот дескриптор будет использоваться для идентификации сообщения. В вызывающий код также будет возвращен дескриптор, если слот найден; иначе будет возвращено 0. Вызывающая функция должна хранить дескриптор, если конечная точка была освобождена позже без сброса.
Идентификатор CAN добавляется, но не активируется, пока шина и драйвер не будут готовы. В будущих модулях CAN эта функциональность очереди может быть удалена, в зависимости от доступной аппаратной поддержки.
Параметры:
unsigned char MsgTyp: уникальный дескриптор (handle) для идентификатора. Должен быть ненулевым. unsigned long COBID: идентификатор CAN допустимого сообщения.
Возвращаемое значение:
unsigned char hRet: статус возврата. Здесь будет 0 либо дескриптор (handle).
Эта функция сканирует почтовые ящики в поиске дескриптора (handle). Если найден, то идентификатор CAN удаляется из списка приема.
Происходит только постановка в очередь запроса удаления идентификатора CAN из списка. Таким образом, сообщения все еще могут приниматься, пока драйвер не будет в состоянии полностью удалить идентификатор CAN из аппаратуры. В будущих модулях CAN эта функциональность очереди может быть удалена, в зависимости от доступной аппаратной поддержки.
Параметр:
unsigned char hMsg: дескриптор (handle) для сообщения.
Эта функция опрашивает драйвер на условие RTR текущего сообщения. Перед этим запросом должна быть вызвана функция mCANIsGetReady для установки текущего сообщения.
Эта функция определяет событие приема. Если оно найдено, то связанный дескриптор (handle) помещается в буфер приема, во внутренний регистр, к которому можно получить доступ через mCANFetchRetStat. Иначе будет возвращен 0. Если ожидает допустимое сообщение, то оно должно быть обработано перед повторным вызовом этой функции.
Подразумевается доступ к буферу на последовательных вызовах приема, т. е. не требуется дескриптор для связанных функций чтения. Например, вызовы функций mCANGetDataLen() и mCANGetDataByten() подразумевают, что запрашиваются наиболее текущие принятые данные сообщения.
Вызов этой функции показывает драйверу, что текущее сообщение было обработано, и драйвер теперь освобождает используемый буфер для нового сообщения. Функция mCANIsGetReady должна быть вызвана перед этим запросом, для установки текущего сообщения.
Эта функция сканирует в поиске доступного выходного буфера. В случае успеха дескриптор, переданный в функцию, будет такой же, что и возвращенный; иначе будет возвращен 0. Для получения возвращенного значения должна быть вызвана функция mCANFetchRetStat.
Эта функция опрашивает драйвер на наличие любого сообщения, которое было помещено на шину, и возвращает дескриптор (handle) для сообщения, которое было отправлено. Для получения дескриптора этого сообщения должна быть вызвана функция mCANFetchRetStat.
Эта функция должна быть вызвана только один раз для индикации передачи. Вызов этой функции второй раз после получения индикации может не возвратить тот же самый дескриптор.
Это имя представляет 8 функций, где суффикс n может быть значением от 0 до 7. Каждая из этих функций может использоваться для установки соответствующего байта для отправки.
Эта функция используется для получения статуса операции тех функций, которые такой статус возвращают. В описании функций указано, возвращают ли они статус.
Возвращаемое значение:
unsigned char: статус последней операции.
[Заключительные указания по созданию приложения]
Разумеется, требуется обработать некоторые специфичные для CAN детали. Вот некоторые моменты, о которых нужно помнить:
• Объекты: определите и разработайте все объекты и функции их обработки, и привяжите их к OD. Объекты, которые определены через функцию, потребуют дополнительного кодирования из-за того, что требуется функция обработки; однако эти типы объектов очень гибки для использования.
• OD: поместите все объекты в свое собственное правильное место внутри словаря. Правильно определите информацию управления, длины и ссылки для объектов.
• Объекты PDO: они все еще должны быть определены и разработаны. Помните о том, что объекты PDO могут быть статическими или динамическими; статические всегда будут эффективнее по коду и обработке, но очевидно не такие гибкие, как динамические объекты PDO. Здесь есть также некоторое количество типов передачи PDO, которые зависят от специфики приложения. По этим причинам для разработчика предоставлен только базовый набор инструментария, так что разработчик может разработать более эффективный код для приложения.
• Интервалы времени: предоставьте базовый отсчет времени с помощью одного из таймеров или одного из внешних источников событий реального времени.
• Инициализация: разработайте правильный код инициализации. Многие объекты должны быть инициализированы из какого-нибудь статического источника данных, такого как ROM, EEPROM или даже перемычки, подключенные ко входным ножкам микроконтроллера.
• Код в функции main: разработайте эффективный код, работающий по принципу кооперативности, чтобы правильно захватить и обработать все события.
• События: имеется множество событий (events). Гарантируйте правильную обработку там, где это необходимо. Например, запросы сброса, поступающие через сеть, предоставлены как события для приложения. На усмотрение разработчика приложения оставлено, как нужно обработать запрос сброса (Reset request).
• Настройки времени компиляции: установите соответствующие опции, чтобы достичь оптимального использования ресурсов и эффективности.
[Использование ресурсов]
Сколько будет затрачено места под стек CANopen, сильно зависит от опций времени компиляции, а также от установленных оптимизаций компилятора. Разработчик приложения должен ожидать, что код стека с включенной оптимизацией займет около 7000 .. 10000 байт памяти программ и 300 байт памяти данных.
Использование всех оптимизаций доступно в компиляторе MPLAB® C18 (v2.30.01), и демо-приложение, предоставленное с этим апноутом, потребовало 7434 байта памяти программ (ROM) и 314 байт памяти данных (RAM).
[Ссылки]
1. AN945: A CANopen Stack for PIC18 ECANTM Microcontrollers site:microchip.com. 2. DS-301 (v 4.02), "CANopen Communication Profile for Industrial Systems Based on CAL". Erlangen: CAN in Automation e.V., 2002. 3. M. Farsi and M. Barbosa, CAN Implementation: Applications to Industrial Networks. Baldock, Hertfordshire: Research Studies Press, 2000. 4. 170711CANopen-IAR-AT91SAM7X.zip - исходный код, документация. 5. Обзор протокола CANopen. 6. CiA301: слой приложения и профиль коммуникации CANopen.