Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #95, НОЯБРЬ 2006 г.

Eye of the xakep

Fagot (salieff@mail.ru)

Хакер, номер #095, стр. 095-124-5


КОД

if (ioperm(0x378, 1, 1)!=0) return false;

outb(curr_state, 0x378);

В основном режиме якорь мотора делает шаги по 90 градусов, но есть возможность уменьшить это значение. Если подавать напряжение на две смежные обмотки, то якорь повернется между ними в промежуточное положение. Таким образом, попеременно подавая напряжение на одиночные и парные обмотки, можно вращать якорь шагами по 45 градусов. Это называется микрошаговый режим, и мы его реализуем, как и шаговый.

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

Если долбить в LPT-порт с тактовой частотой процессора, то якорь просто не будет успевать повернуться в сторону нужной обмотки, поэтому после записи значения в порт нужно сделать паузу. Но тут появляется одна проблема: при вызове usleep шедулер ядра щелкает контекстом процесса, и возникают неучтенные паразитные задержки порядка 20-ти миллисекунд. А так как мы собираемся оперировать микросекундами, такие задержки для нас недопустимы. Нам нужно не отдавать контекст процесса и при этом самостоятельно вычислять время задержки. Для этой цели на архитектуре x86 есть фиктивный порт 0x80, запись в который ничего не делает, но занимает порядка одной микросекунды. Пользуясь этим, я сваял вот такую вспомогательную функцию:

КОД

inline static void p80usleep(size_t microsecs)

{

for (size_t i=0; i<microsecs; ++i) outb(0, 0x80);

}

Правда, на архитектурах, отличных от x86, такое работать не будет. Да там и ioperm/inb/outb скорее всего работать не будут:).

Когда все тонкости были учтены, я написал для моторов класс с вот таким public-интерфейсом:

КОД

class StepperMotor {

public:

typedef enum {Motor_A, Motor_B} MotorIndex;

bool Init(MotorIndex i, bool micro_flag=false, int start_pos=0);

void Shutdown(void);

void StepPlus(void);

void StepMinus(void);

};

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

Рулим

И вот, наконец, настала пора реализовать изначальную задумку. Я запустил интерфейс и функцию управления моторами в разных потоках и связал их очередью, в которой передавались координатные смещения. Если кликнуть правой кнопкой мыши на виджете отображения кадров, то мы переходим в режим управления моторами:

КОД

void QCEGLWidget::mousePressEvent(QMouseEvent * e) {

if (e->button()==Qt::RightButton) {

Назад на стр. 095-124-4  Содержание  Вперед на стр. 095-124-6