Elite Games - Свобода среди звезд!
.
  » GameMaker: LPT-порт и inpout32 | страница 1
Конференция предназначена для общения пилотов. Для удобства она разделена на каналы, каждый из которых посвящен определенной игре. Пожалуйста, открывайте темы только в соответствующих каналах и после того, как убедитесь, что данный вопрос не обсуждался ранее.

Поиск | Правила конференции | Фотоальбом | Регистрация | Список пилотов | Профиль | Войти и проверить личные сообщения | Вход

   Страница 1 из 1
 
Поиск в этой теме:
Железный канал: «GameMaker: LPT-порт и inpout32»
БулерМэн
 410 EGP


Рейтинг канала: 4(58)
Репутация: 68
Сообщения: 1580
Откуда: Гороховец
Зарегистрирован: 07.02.2006
Может быть, кому-то еще понадобится использовать LPT порт в своих "программах" на движке GM8.
В моем случае - мне это нужно для прямого управления станком с ЧПУ.



Чтобы работать с портом нам понадобится библиотека inpout32.dll
Ссылка на проект http://www.highrez.co.uk/downloads/inpout32/
Использовать буду 32-х битную версию, для 64-битной версии понадобится исправить две цифры в коде.
Помещаем библиотеку в директорию с проектом.

Так же понадобится самописная библиотека overlap_dll.dll исходный код которой есть ниже.

При помощи связки двух библиотек inpout32.dll, overlap_dll.dll можно генерировать сигнал 1kHz.
Это гораздо хуже чем драйвер Mach3: 24kHz - 100kHz


GM не поддерживает тип данный int, short, float. Т.к. библиотека inpout32 принимает и возвращает тип int - подключить непосредственно к GM данную библиотеку не получится, нужна прокладка между inpout32 и приложением.


Создаем проект overlap_dll.dll - он необходим для того, чтобы GM получал и отправлял данные в понятном для него формате - используя тип данных double.

Проект overlap_dll собирается на языке Си, в режиме DLL-библиотеки.
IDE для сборки - Dev-Cpp 4.9.9.2

 dllmain.c   (кликните здесь для просмотра)

Код:

#include  "dll.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>


typedef int   (__stdcall *lpOut32)(int, int);
typedef int   (__stdcall *lpInp32)(int);
typedef int   (__stdcall *lpIsInpOutDriverOpen)(int);
//typedef short   (__stdcall *lpIsXP64Bit)(void);

//Some global function pointers (messy but fine for an example)
lpOut32 gfpOut32;
lpInp32 gfpInp32;
lpIsInpOutDriverOpen gfpIsInpOutDriverOpen;
//lpIsXP64Bit gfpIsXP64Bit;


DLLIMPORT double InitDLL (double someint)
{
//Dynamically load the DLL at runtime (not linked at compile time)
      HINSTANCE hInpOutDll ;
      hInpOutDll = LoadLibrary ( "inpout32.dll" ) ;
      
        if ( hInpOutDll != NULL )
      {
         //gfpOut32 = (lpOut32)GetProcAddress(hInpOutDll, "Out32");
         //gfpInp32 = (lpInp32)GetProcAddress(hInpOutDll, "Inp32");
         gfpIsInpOutDriverOpen = (lpIsInpOutDriverOpen)GetProcAddress(hInpOutDll, "IsInpOutDriverOpen");
         //gfpIsXP64Bit = (lpIsXP64Bit)GetProcAddress(hInpOutDll, "IsXP64Bit");

         if (!gfpIsInpOutDriverOpen(1))
         {
               return -1;//Unable to open InpOut32 Driver!
            }
        }
        else
        {
           return -2;   //Unable to load InpOut32 DLL
        }
        return 1;
}


DLLIMPORT double ReadInput32 (double someint)
{
        HINSTANCE hInpOutDll ;
      hInpOutDll = LoadLibrary ( "inpout32.dll" ) ;
      
        if ( hInpOutDll != NULL )
      {

         gfpInp32 = (lpInp32)GetProcAddress(hInpOutDll, "Inp32");
           
            double iPort = someint;
            double wData = gfpInp32(iPort);   //Read the port
         
            FreeLibrary ( hInpOutDll ) ;
            return wData;

        }
        else
        {
           return -2;   //Unable to load InpOut32 DLL
        }
}

DLLIMPORT double WriteOutput32 (double someport, double somedata)
{
         
        HINSTANCE hInpOutDll ;
      hInpOutDll = LoadLibrary ( "inpout32.dll" ) ;
      
        if ( hInpOutDll != NULL )
      {
             
          gfpOut32 = (lpOut32)GetProcAddress(hInpOutDll, "Out32");
             
          short iPort = someport;
          WORD wData = somedata;
        gfpOut32(iPort, wData);
       
        FreeLibrary ( hInpOutDll ) ;
        return 1;// просто вернуть 1, ничего не значит
       
        }
        else
        {
           return -2;   //Unable to load InpOut32 DLL
        }
}


DLLIMPORT double Step ( double port, double first_data, double second_data, double impulse_count, double acceleration, double time_interval )
{
        HINSTANCE hInpOutDll ;
      hInpOutDll = LoadLibrary ( "inpout32.dll" ) ;
      
        if ( hInpOutDll != NULL )
      {
             
          gfpOut32 = (lpOut32)GetProcAddress(hInpOutDll, "Out32");
         
          int counter = 0;
          int accel_ = acceleration;
         
          //TODO:
          //необходимо более плавное ускорение
         
               short iPort = port;
               WORD fData = first_data;
               WORD sData = second_data;
              //  acceleration = 100
              int first_set = 0;
                       
          for(counter=0;counter<impulse_count;counter+=1)
          {

             gfpOut32(port, fData);
             
               if(second_data>0)
               {
                  gfpOut32(port, sData);
               }
             
             if(first_set==0)
             {
                   if(acceleration - 1 > 0)
                 {
                      acceleration-=1;           
                   }
                   else
                   {
                     first_set = 1;   
                   }
               }
               else
               {
                 if(acceleration < accel_ && counter+accel_ >= impulse_count )
               {
                  acceleration+=1;           
                 }
               }

             _sleep(time_interval+acceleration);// 100-->time_interval
          }
       
        FreeLibrary ( hInpOutDll ) ;
        return 1;// просто вернуть 1, ничего не значит
       
        }
        else
        {
           return -2;   //Unable to load InpOut32 DLL
        }
}
         
         

BOOL APIENTRY DllMain (HINSTANCE hInst      ,// Library instance handle.
                       DWORD reason         ,// Reason this function is being called.
                       LPVOID reserved      )// Not used.
{
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        break;

      case DLL_PROCESS_DETACH:
        break;

      case DLL_THREAD_ATTACH:
        break;

      case DLL_THREAD_DETACH:
        break;
    }
   
    // Returns TRUE on success, FALSE on failure
    return TRUE;
}





 dll.h   (кликните здесь для просмотра)

Код:

#ifndef _DLL_H_
#define _DLL_H_

#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else // Not BUILDING_DLL
# define DLLIMPORT __declspec (dllimport)
#endif // Not BUILDING_DLL

DLLIMPORT double InitDLL ( double someint );
DLLIMPORT double ReadInput32 (double someint);
DLLIMPORT double WriteOutput32 (double someport, double somedata);
DLLIMPORT double Step ( double port, double first_data, double second_data, double impulse_count, double acceleration, double time_interval );

// аналогично файлу dllmain.c

#endif // _DLL_H_



Помещаем полученную библиотеку overlap_dll.dll в директорию с проектом программы на движке GM8, там же должна лежать библиотека inpout32.dll

Подробности:
 Cкрытый текст   (кликните здесь для просмотра)
В проекте приложения GM8 создаем скрипты:
 init_LPT   (кликните здесь для просмотра)

Код:

global.InitDLL_ = external_define("overlap_dll.dll","InitDLL",dll_cdecl,ty_real,1,ty_real);
global.ReadInput32_ = external_define("overlap_dll.dll","ReadInput32",dll_cdecl,ty_real,1,ty_real);
global.WriteOutput32_ = external_define("overlap_dll.dll","WriteOutput32",dll_cdecl,ty_real,2,ty_real,ty_real);



 ReadInput32   (кликните здесь для просмотра)
Код:
return external_call(global.ReadInput32_,argument0);


 WriteOutput32   (кликните здесь для просмотра)
Код:
return external_call(global.WriteOutput32_,argument0, argument1);
 Step   (кликните здесь для просмотра)
Код:


return external_call(global.Step_,argument0, argument1,argument2,argument3,argument4,argument5);



Данная функция позволяет выполнить перемещение по выбранной оси.
Ось по прежнему нужно задавать адресом и указывать конкретные данные.
Пример вызова:
Код:
Step(888,48,32,200, 50, 10);

где
888 - адрес регистра data
48 - шаг оси Z против часовой, движение инструмента вниз
32 - выключение шага
200 - количество импульсов
50 - "ускорение", разгоняет с указанного временного интервала до номинала и так же замедляет до указанного интервала в конце.
10 - временной интервал в мс между импульсами, определяет скорость движения

иначе:
Step(адрес,код_направления_вращения_оси_N,код_выключения_шага,количество_импульсов, интервал_ускорения, интервал_шага);

Готовые коды перемещения по осям, для ручного управления:
Код:

    if(keyboard_check(vk_pagedown))//Z--
    {
      Step(888,32,48,80, 6, 0);
    }
   
    if(keyboard_check(vk_pageup))//Z+
    {
      Step(888,0,16,80, 6, 0);
    }
   
    if(keyboard_check(vk_up))//Y+
    {
       Step(888,0,4,80, 6, 0);
    }
   
    if(keyboard_check(vk_right))//X+
    {
       Step(888,0,1,80, 6, 0);
    }
   
    if(keyboard_check(vk_down))//Y-
    {
       Step(888,12,8,80, 6, 0);
    }
   
    if(keyboard_check(vk_left))//X--
    {
        Step(888,2,3,80, 6, 0);
    }


Дистанции перемещения по осям (4-й аргумент функции Step):
200 имп - 2.5 мм
80 имп - 1 мм
40 имп - 0.5 мм
20 имп - 0.25 мм
10 имп - 0.125 мм
5 имп - 0.0625 мм
4 имп - 0.05 мм
3 имп - 0.0375 мм
2 имп - 0.025 мм
1 имп - 0.0125 мм




 InitDLL   (кликните здесь для просмотра)

Так же, можно создать не обязательный скрипт InitDLL, который отвечает за проверку загрузки библиотек и в случае успеха выдает число 1, и 0 если неудача:
Код:
return external_call(global.InitDLL_,argument0);

Вызывать данную функцию необходимо с любым числовым аргументом, например InitDLL(1); Число в скобках не играет роли.



В каком-либо объекте создаем событие Game Start, добавляем пиктограмму Код и вставляем следующий текст:
init_LPT();
Данный код загрузит библиотеки для работы с портом.

В любом месте программы вызываем функцию ReadInput32(888) - считать данные регистра DATA
Аргумент ReadInput32 может принимать следующие значения:

888 Регистр данных (Data)
889 Статус (Status) Данные записываются только внешним устройством.
890 Управление (Control) Записывать может только компьютер.

Если необходимо записать данные в LPT-порт, указываем имя скрипта
WriteOutput32(890,223);
где 890 это адрес регистра управления, 223 это данные записываемые в регистр, число в десятичной системе счисления, в двоичном виде - 11011111.
Биты в данном регистре инвертированы поэтому в значении 11011111 шестой бит равный 0 будет указывает подключенному контроллеру подать удерживающие питание на шаговые двигатели, бит 6 или контакт 14 порта.

В данный момент использую 4-х осевой контроллер шаговых двигателей LPT-DRV 1.02 состоящий из платы опторазвязки и коммутации и четырех драйверов, к сожалению на устаревшей микросхеме A49895LTD, в гугле не находится.
 Cкрытый текст   (кликните здесь для просмотра)

Но есть похожая микросхема, не уверен что полный аналог, того же производителя: Allegro MicroSystems, LLC A4989SLDTR-T
ссылка


 Немного о данных порта и его адресах:   (кликните здесь для просмотра)

Цитата:

{ Номера портов LPT }
LPT1 : byte = $10; // база $3BC
LPT2 : byte = $20; // $378
LPT3 : byte = $30; // $278
{Смещения регистров порта}
LPT_DATA_REG : byte = 0; // Регистр данных
LPT_STATE_REG : byte = 1; // Регистр состояния
LPT_CONTROL_REG : byte = 2; // Регистр управления
LPT_EPP_ADDRESS : byte = 3; // Регистр адреса EPP
LPT_EPP_DATA : byte = 4; // Регистр данных EPP


Соответственно, если адрес порта 0x378 то в десятичной системе счисления это число 888, что соответствует адресу регистра данных DATA
889 - это адрес регистра STATUS 0x379, то есть смещение на 1 байт относительно 0x378
890 - смещение 2 байта, регистр управления CONTROL.

Еще два регистра "Регистр адреса EPP" и "Регистр данных EPP" о них можно почитать здесь: https://wm-help.net/lib/b/book/3065756330/9

Выходные биты данных DATA
№бита шага
(пин шага)
№бита направления
(пин направления)
Ось X
0(2)
1(3)
Ось Y
2(4)
3(5)
Ось Z
4(6)
5(7)
Ось A
6(8)
7(9)
значение бита:
1 - сдвиг
0 - обнуление после сдвига
значение бита:
1 - вращение по часовой
0 - вращение против часовой


Выходные биты контроля CONTROL


Опция:
Реле1
Реле2
Питание
драйверов
ENABLE
PWM
№бита
3
2
1
0
значение бита:
значение бита:
значение бита:
значение бита:
1 - вкл
0- выкл
1 - вкл
0 - выкл
1 - питание удержания
0 - выключить питание
нет описания

Уточнение:
Значение 201 = [1100 1001] соответствует одному байту.
Регистр CONTROL содержит 8 бит, 4 из которых передаются непосредственно по контактам 1,14,16,17.

Включение питания драйверов происходит после установки второго бита (С1 на схеме ниже), он же ENABLE.

Контакты на плате управления:


Регистры управления:




Примечание: упомянутые адреса регистров DATA, STATUS, CONTROL будут работать только если в системе диапазон адресов для LPT-порта будет в пределах 0x378 ... 0x37F
В моем случае LPT-порт используется встроенный в материнскую плату, возможно что если использовать PCIe платы расширения - адреса будут другие.
Примечание 2: для 64 битной версии библиотеки overlap_dll необходимо собрать данную библиотеку в соответствующей системе и в коде указать inpout64 вместо inpout32
Примечание 3: для win7 64 битной версии системы пересборка overlap_dll не нужна, все работает и на основной 32х битной версии, проверено.

Обновление от 10 мая 2019 г:
- коды перемещения по осям для ручного управления станком (для управления станком нажать на кнопку Control и записать число 221, для отключения - 223)
- значения смещений в мм, для функции Step


Обновление от 28 апреля 2020 г:
- добавлены комментарии в самой программе и в исходниках

PS До обновления архива было 54 скачивания, спасибо за интерес!
PSS Обращаю Ваше внимание - это только заготовка программы управления станком, никакие GCOD'ы программа не умеет! Есть только управление клавишами, причем в управлении заложено ускорение, поэтому перемещаться каретка будет "волнами", а не постоянным движением.
Для постоянного движения необходимо в объекте Control, событии Draw изменить код:
Код:

...
    if(keyboard_check(vk_right))//X+
    {
       Step(888,0,1,80, 0, 4);// 5-й аргумент, число 0 - без ускорения
    }
...

Обработка кодов и перевод команд в Step инструкции пока не реализовано, но вы можете это сделать сами используя скрипт split_field и atoi не дожидаясь "релиза". Например: читаем строку файла gcode, разбираем ее на поля при помощи split_field, определяем, что содержится в указанных полях, и преобразуем числовые координаты в число real для последующих вычислений количества шагов и т.д.

Рекомендую обратить внимание на платы Ардуино Мега, Re-Arm32, и прошивки такие как Marlin и GRBL, которые позволяют заменить ПК полностью. ESP32 так же поддерживает sd карты для загрузки gcode

LPT_inpout32.rar
 Описание:
Все файлы проекта в архиве, + GMK-файл проекта для GameMaker8
 Имя файла:  LPT_inpout32.rar
 Размер файла:  1.34 MB
 Скачано:  10 раз(а)

_________________
Дулички и фигушки

Последний раз редактировалось: БулерМэн (04:05 03-06-2020), всего редактировалось 40 раз(а)
    Добавлено: 03:00 10-05-2019   
Железный канал: «GameMaker: LPT-порт и inpout32»
 
  
Показать: 
Предыдущая тема | Следующая тема |
К списку каналов | Наверх страницы
Цитата не в тему: Пойду удавлю кого-нить в КТВ.. (Pinocchio)

  » GameMaker: LPT-порт и inpout32 | страница 1
Каналы: Новости | Elite | Elite: Dangerous | Freelancer | Star Citizen | X-Tension/X-BTF | X2: The Threat | X3: Reunion | X3: Terran Conflict | X Rebirth | X4: Foundations | EVE Online | Orbiter | Kerbal Space Program | Evochron | VoidExpanse | Космические Миры | Онлайновые игры | Другие игры | Цифровая дистрибуция | play.elite-games.ru | ЗВ 2: Гражданская война | Творчество | Железо | Игра Мечты | Сайт
   Дизайн Elite Games V5 beta.18