Санкт-Петербургский государственный университет
Опубликован: 14.07.2013 | Доступ: свободный | Студентов: 509 / 178 | Длительность: 06:03:00
Специальности: Программист
Лекция 4:

Автоматизированное управление мобильным роботом

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();  //завершение задачи
}

Конечно, текст самой задачи, реализующей логику работы регулятора, существенно меньше описания технической части программы. С одной стороны, это делает даже маленькую по содержанию программу объемной, но с другой, дает пространство для тонкой настройки параметров. Эта настройка, по сути, шаблонная и ее легко скопировать из другой задачи.