БулерМэн
421 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.
Так же есть прошивка Repetier и конфигуратор онлайн https://www.repetier.com/firmware/v092/
Обновление от 4 апреля 2021 г:
Все таки прошивка GRBL пока что лучшее, что я смог найти для станка с ЧПУ.
Суть в том, что контроллер на Ардуино принимает gcode.
Отправляет команды и неплохо визуализирует траектории программа Universal Gcode Sender.
PS Группа в телеграме: https://t.me/joinchat/GN6kpHCFzEFmZWMy
LPT_inpout32.rar |
Описание: |
Все файлы проекта в архиве, + GMK-файл проекта для GameMaker8 |
|
Имя файла: |
LPT_inpout32.rar |
Размер файла: |
1.34 MB |
Скачано: |
314 раз(а) |
_________________ Сосиска в хлебе
Последний раз редактировалось: БулерМэн (05:31 04-04-2021), всего редактировалось 44 раз(а) |