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) { |