Россия, Новосибирск |
Автоматизированное управление мобильным роботом
7.9. nxtOSEK C
7.9.1. API nxtOSEK
Программный интерфейс ОС РВ nxtOSEK содержит низкоуровневые функции доступа к устройствам и функции-обертки (wrapper) для пакета ECRobot (последние имеют префикс ecrobot_) которые разработаны для создания программ управления устройствами в режиме реального времени.
При описании программного интерфейса отдельно помечались функции, которые являются надстройками над низкоуровневыми функциями (функции-обертки). Чтобы не иметь лишних потерь времени при выполнении программы, лучше воздержаться от их использования. Для использования программного интерфейса необходимо подключить в исходном коде заголовочный файл ecrobot_interface.h
7.9.2. Специальные процедуры nxtOSEK
В программе, написанной для ОС РВ nxtOSEK, должны быть описаны три специальные процедуры, так называемые функции-обработчики или функции-ловушки (hook routines): ecrobot_device_initialize, ecrobot_device_terminate, user_1ms_isr_type2. Их определение и описание должны быть в главном с-файле программы. Система вызывает их автоматически, первую в начале работы программы, вторую в конце, а третью периодически каждую миллисекунду. Что бы легче было понять, как правильно их использовать, рассмотрим примеры этих процедур из файла ecrobot_main.c программы NXTway.
-
void ecrobot_device_initialize(void): Эта процедура вызывается в самом начале работы программы. В ней нужно помещать вызовы функций инициализации датчиков и моторов.
void ecrobot_device_initialize(void) { /* Инициализировать используемые устройства */ ecrobot_set_light_sensor_active(NXT_PORT_S1); ecrobot_set_light_sensor_active(NXT_PORT_S3); ecrobot_init_sonar_sensor(NXT_PORT_S2); ecrobot_set_motor_speed(NXT_PORT_B, 0); ecrobot_set_motor_speed(NXT_PORT_C, 0); ecrobot_init_bt_connection(); }
-
void ecrobot_device_terminate(void): Эта процедура вызывается в случае, если нажали кнопку STOP или EXIT. В ней нужно помещать вызовы функций, корректно завершающих работу датчиков и моторов.
void ecrobot_device_terminate(void) { /* Завершить работу датчиков, остановить моторы */ ecrobot_set_light_sensor_inactive(NXT_PORT_S1); ecrobot_set_light_sensor_inactive(NXT_PORT_S3); ecrobot_set_motor_speed(NXT_PORT_B, 0); ecrobot_set_motor_speed(NXT_PORT_C, 0); ecrobot_term_sonar_sensor(NXT_PORT_S2); ecrobot_term_bt_connection(); }
-
void user_1ms_isr_type2(void): Эта процедура вызывается процедурой обработки прерываний второго типа, работающей с периодом 1 мсек. В ней, например, можно реализовать счетчик системных тактов подсистемы запуска задач (OSEK Alarm) для реализации Планировщика Периодических Вызовов Задач.
#include "kernel.h" #include "kernel_id.h" void user_1ms_isr_type2(void) { StatusType ercd; /* Увеличить счетчик системного времени */ ercd = SignalCounter(SysTimerCnt); if (ercd != E_OK) { ShutdownOS(ercd); } }
Пример
Опишем простой пример для демонстрации общих принцпов построения программ с использованием API для С nxtOSEK. Текст примера предоставил студент математико- механического факультета Михаил Липкович. В этом примере реализована управляющая программа для робота, движущегося вдоль стенки.
Прежде всего рассмотрим файл описания OSEK Implementation Language (OIL). Подробную справку по синтаксису OIL-файла можно найти на сайте концерна VXD/OSEK.
/* wall_osek.oil*/ #include "implementation.oil" CPU ATMEL_AT91SAM7S256 { OS LEJOS_OSEK { STATUS = EXTENDED; STARTUPHOOK = FALSE; ERRORHOOK = FALSE; SHUTDOWNHOOK = FALSE; PRETASKHOOK = FALSE; POSTTASKHOOK = FALSE; USEGETSERVICEID = FALSE; USEPARAMETERACCESS = FALSE; USERESSCHEDULER = FALSE; }; APPMODE appmode1{}; TASK Motion //определение свойств задачи Motion { AUTOSTART = FALSE; //не запускаться самой при запуске //программы PRIORITY = 2; //приоритет. чем больше число, тем //выше приоритет ACTIVATION = 1; //максимально число одновременно //работающих задач Motion SCHEDULE = FULL; //возможность вытеснять эту задачу //задачами более высокого приоритета STACKSIZE = 512; //размер стека }; ALARM cyclic_alarm1 //определение будильника (генератора //событий запуска задачи) { COUNTER = SysTimerCnt; //счетчик SysTimerCnt ACTION = ACTIVATETASK //при запуске будильника вызвать //задачу Motion { TASK = Motion; }; AUTOSTART = TRUE //автоазапуск будильника { ALARMTIME = 1; //время запуска будильника впервый //раз CYCLETIME = 50; //период последующих запусков APPMODE = appmode1; }; }; COUNTER SysTimerCnt //определение счетчика { MINCYCLE = 1; //минимально возможное число тиков //счетчика для использования //будильником MAXALLOWEDVALUE = 10000; //максимально //возможноезначение счетчика TICKSPERBASE = 1; //размер тика (в миллисекундах) }; };
Сам текст программы находится в файле wall_osek.c. Точкой входа этой программы (места с которого начинается выполнение программы) является задача task(Motion), как следует из файла wall_osek.oil, эта задача вызывается раз в 50 миллисекунд. Не следует забывать, что необходимо реализовать еще три процедуры ecrobot_device_initialize, ecrobot_device_terminate, user_1ms_isr_type2, которые формально нигде не используются, но система вызывает их автоматически.
/* wall_osek.c*/ #include "kernel.h" #include "kernel_id.h" #include "ecrobot_interface.h" //определение констант #define MOTOR_R NXT_PORT_C #define MOTOR_L NXT_PORT_A #define SONAR NXT_PORT_S1 #define K 1.2 //коэффициент //пропорционального регулятора #define D 20 //расстояние до стены DeclareTask(Motion); DeclareCounter(SysTimerCnt); //инициализация моторов и ультразвукового датчика //(выполняется перед всеми задачами) void ecrobot_device_initialize() { ecrobot_set_motor_speed(MOTOR_R, 0); ecrobot_set_motor_speed(MOTOR_L, 0); ecrobot_init_sonar_sensor(SONAR); } //выполняется после завершения всех задач void ecrobot_device_terminate() { ecrobot_set_motor_speed(MOTOR_R, 0); ecrobot_set_motor_speed(MOTOR_L, 0); ecrobot_term_sonar_sensor(SONAR); } //процедура обработки прерываний. необходима для получения //доступа к датчикам и сервомоторам void user_1ms_isr_type2(void) { (void)SignalCounter(SysTimerCnt); //увеличить счетчик //SysTimerCnt для вызова периодических задач } TASK(Motion) //задача, реализующая движение { S8 base_speed = 25, u = 0; u = (ecrobot_get_sonar_sensor(SONAR)-D)*K; ecrobot_set_motor_speed(MOTOR_L, (base_speed + u)%100); ecrobot_set_motor_speed(MOTOR_R, (base_speed - u)%100); TerminateTask(); //завершение задачи }
Конечно, текст самой задачи, реализующей логику работы регулятора, существенно меньше описания технической части программы. С одной стороны, это делает даже маленькую по содержанию программу объемной, но с другой, дает пространство для тонкой настройки параметров. Эта настройка, по сути, шаблонная и ее легко скопировать из другой задачи.