September 22nd, 2013

uOS на chipKIT Uno32 крутит 2мя шаговыми моторами

Открываю новую рубрику прикладных решений, в которой будут коллекционироваться наборы коротких решений для типовых задач.

Сегодня это одновременное управление 2мя шаговыми двигателями из 2х параллельных потоков с использованием операционной системы реального времени (RTOS) uOS, запущенной на pic32 на плате ChipKIT Uno32.

Результат работы, полученный Андреем Томиловым после нескольких бессонных ночей борьбы с линкером gcc:



Ключевые куски кода - из файла rraptor_uos_2motors/OS2.c:

Точка входа в программу - вызов uos_init - помещаем таймер (ниже он потребуется для разделения процессорного времени между двумя задачами) и создаем 2 задачи (task) с одинаковым приоритетом - по одной на каждый мотор:


void uos_init (void)
{
    ...
    
    timer_init (&timer, KHZ, 1);
    
    taskX=task_create(step_motor42_x,0, "MotorX", 1,stack1, sizeof(stack1));
    taskY=task_create(step_motor42_y,0, "MotorY", 1,stack2, sizeof(stack2));    
}


Сами задачи - просто вызывают процедуру вращения для соответствующего шагового мотора (функция step_motor):


void step_motor (step_data*);

void step_motor42_x (void *arg)
{
   step_motor(&datx);
}

void step_motor42_y (void *arg)
{
    step_motor(&daty);
}



Дальше вырезки из кода драйвера шагового мотора step_motor. Детали механизма управления шаговым мотором в данном контексте нас не интересуют. Можно только сказать, что этот код посылает на определенные ножки контроллера нули и единицы в определенной последовательности с определенной частотой. Для каждого мотора имеем несколько достаточно продолжительных циклов, совместить которые в рамках однопоточного кода просто не представляется возможным.

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


void step_motor (step_data* data)
{

    int i=0;
    ...
    if ((*data).dir==0)
    {
        while (i<(*data).step)
        {
            timer_delay(&timer,10);
    
            ...
                
            LATDCLR=(*data).MOTOR_PIN1;
            LATDCLR=(*data).MOTOR_PIN2;
            LATDCLR=(*data).MOTOR_PIN3;
            LATDSET=(*data).MOTOR_PIN4;
            mdelay((*data).time);
                
            i=i+1;    
        }
    ...
            
    }
        
    if ((*data).dir==1)
    {
        while (i<(*data).step)
        {
            timer_delay(&timer,10);
    
            LATDSET=(*data).MOTOR_PIN4;
            LATDCLR=(*data).MOTOR_PIN3;
            LATDCLR=(*data).MOTOR_PIN2;
            LATDCLR=(*data).MOTOR_PIN1;
            mdelay((*data).time);
                
            ...
                
            i=i+1;   
        }
        ...
    }
...
}




Добиться подобного результата в упрощенном режиме совместимости с Arduino было бы довольно проблематично, поэтому здесь можно еще раз отметить удачное решение в серии ChipKIT, с которыми можно легко построить семинар практического знакомства с микроэлектроникой на пару часов, а можно и делать нормальные вещи для реальных проектов в том числе с RTOS'ами.

uOS - модульная операционная система реального времени с открытым исходным кодом, которая умещается даже на ChipKIT Uno32. Все исходники, документация и примеры доступны на http://code.google.com/p/uos-embedded/ в том числе на русском языке. Автор uOS - Сергей Вакуленко ramlamyammambam.

Весь код примера, подсветка синтаксиса.