Требовалось уметь обращаться к GPIO на Coremodule 920 из под Windows 7 x86. Приложение, которому требовался доступ пишется мной на Java.

Поиск решений


  • Попробовать разобраться самостоятельно
  • Обратиться в техподдержку за помощью

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

Из ответа на мой вопрос следовало, что у них есть драйвер на C и только под Linux, что меня не устраивало.

Поиск Регистра

Открываем User Reference Manual. Смотрим, видим: 0500-053F PCH GPIO Configuration Ports. Определились с диапазоном, ищем конкретные адреса, а в описании нерабочая ссылка на PCH BD82QM67.

Ищем, находим: Intel 6 Series Chipset and Intel C200 Series Chipset Datasheet. Нашли даже слишком много GPIO. Смотрим внимательнее и видим, что нужные GPIO — это GP_LVL3—GPIO Level for Input or Output 3 Register и GP_LVL2—GPIO Level for Input or Output 2 Register. Смотрим на адреса: GPIOBASE +48h и GPIOBASE +38h соответственно. GPIOBASE — 0x500. Это выходит из диапазона 0x500-0x53F, будем считать, что в User Reference Manual ещё одна ошибка (ведь некорректная ссылка уже была).

Библиотеки привилегированного доступа

Адреса найдены, а обратиться к ним надо ещё суметь. Требуется привилегированный доступ. Были испробованы IOPort, Jioport, что-то пошло не так (требовали от меня повышения прав (т.е. привилегированного доступа)). Может быть я что-то делал не так, но не получилось. Потом мне посоветовали программу UserPort. в описании программы говорится о работе на Windows NT/XP.

Попытка №1

Берём UserPort.sys, кладём в C:\Windows\System32\drivers. Запускаем UserPort.exe. Видим, что там есть введённые пространства адресов. Добавляем 0x500 — 0x53F. Нажимаем Stop->Update->Start, выполняем перезагрузку ОС и… синий экран.



Попытка №2

Разница между запусками единственная — UserPort. Загружаемся в безопасном режиме, убираем UserPort.sys, перезагрузка и… система запустилась, всё корректно. Ищем причину. Возвращаем драйвер на место. Убираем пространства адресов, которые были введены по-умолчанию, перезагрузка и… система запустилась. Причина найдена: те самые пространства адресов, введённые по-умолчанию.

С++ Библиотека

К UserPort прилагается образец для работы с кодом:

Образец
void outport(unsigned int portid, unsigned int value)
{
	__asm mov edx, portid;
	__asm mov eax, value;
	__asm out dx, ax;
}
void outportb(unsigned int portid, unsigned char value)
{
	__asm mov edx, portid
	__asm mov al, value
	__asm out dx, al
}

unsigned char inportb(unsigned int portid)
{
	unsigned char value;

	__asm mov edx, portid
	__asm in al, dx
	__asm mov value, al
	return value;
}

unsigned int inport(unsigned int portid)
{
	auto value = 0;
	__asm mov edx, portid
	__asm in ax, dx
	__asm mov value, eax
	return value;
}


Пишем код библиотеки

Тут мне помогла статья Как подружить Java и C++. Часть первая и программа — библиотеки jnaerator-0.11-shaded, Bridj. Авто-генерируемый заголовок:

dllmain.cpp
// dllmain.cpp: определяет точку входа для приложения DLL.
#include "stdafx.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}


Файл, полученный через javah:

NativeGPIO.h
#pragma once
#include <jni.h>
/* Header for class NativeGPIO */
#include "stdafx.h"
/**
* »нтерфейс, позвол¤ющий обращатьс¤ к нижеперечисленным функци¤м извне (.dll)
*/
extern "C" {
#define GPIO1 0x538
#define GPIO2 0x548
	/**„итает gpio1 1 байт и возвращает как integer*/
	extern __declspec(dllexport) unsigned int readGPIO1();
	/**„итает gpio2 1 байт и возвращает как integer*/
	extern __declspec(dllexport) unsigned int readGPIO2();
	/**позвол¤ет читать байт по указаному адерсу*/
	extern __declspec(dllexport) unsigned char inportb(unsigned int portid);	
	/**позвол¤ет читать int по указаному адерсу*/
	extern __declspec(dllexport) unsigned int inport(unsigned int portid);
	/**позвол¤ет писать int по указаному адерсу*/
	extern __declspec(dllexport) void outport(unsigned int portid, unsigned int value);
	/**позвол¤ет писать байт по указаному адерсу*/
	extern __declspec(dllexport) void outportb(unsigned int portid, unsigned char value);

}


Файл с описанием работы функций:

NativeGPIO.cpp
// dllmain.cpp: определяет точку входа для приложения DLL.
// NativeGPIO.cpp: определяет экспортированные функции для приложения DLL.
//
#include "stdafx.h"
#include "NativeGPIO.h"
#include <windows.h>

void outport(unsigned int portid, unsigned int value)
{
	__asm mov edx, portid;
	__asm mov eax, value;
	__asm out dx, ax;
}
void outportb(unsigned int portid, unsigned char value)
{
	__asm mov edx, portid
	__asm mov al, value
	__asm out dx, al
}

unsigned char inportb(unsigned int portid)
{
	unsigned char value;

	__asm mov edx, portid
	__asm in al, dx
	__asm mov value, al
	return value;
}

unsigned int inport(unsigned int portid)
{
	auto value = 0;
	__asm mov edx, portid
	__asm in ax, dx
	__asm mov value, eax
	return value;
}


unsigned readGPIO2()
{
	jint value = inportb(GPIO2);
	return value;
}

unsigned readGPIO1()
{
	jint value = inportb(GPIO1);
	return value;
}


Методы inport/outport пробросил на всякий случай. Компилируем в .dll, библиотека готова.

Внедряем в программу

Используем JNAetrator и получаем следующее:

NativeGPIOLibrary
import org.bridj.BridJ;
import org.bridj.CRuntime;
import org.bridj.ann.Library;

/**
 * Created by Koksharov on 04.09.2015.
 * package = com.astra.app.neptun.applicationmanager
 * project = ApplicationManager
 */
@Library( "NativeGPIO" )
@org.bridj.ann.Runtime( CRuntime.class )
class NativeGPIOLibrary {
    {
        BridJ.register();
    }

    public final int GPIO2 = ( int ) 1352;
    public final int GPIO1 = ( int ) 1336;

    /**
     * Original signature : <code>int readGPIO1()</code>
     * <i>native declaration : line 8</i>
     * Функция для работы с GPIO 1, расположенным по адресу 0x538
     *
     * @return байт, содержащий значения регистров 1-4
     */
    native public int readGPIO1 ();

    /**
     * Original signature : <code>int readGPIO2()</code>
     * <i>native declaration : line 9</i>
     * Функция для работы с GPIO 2, расположенным по адресу 0x548
     *
     * @return байт, содержащий значения регистров 5-8
     */
    native public int readGPIO2 ();

    /**
     * Original signature : <code>char inportb(unsigned int)</code>
     * <i>native declaration : line 10</i>
     * Функция, позволяющая обращаться к произвольным регистрам (программа виснет, если
     * нет прав на чтение)
     *
     * @return байт, содержащий значения цеелвых регистра(-ов)
     */
    native public byte inportb ( int portid );

    /**
     * Original signature : <code>int inport(unsigned int)</code>
     * <i>native declaration : line 11</i>
     * Функция, позволяющая обращаться к произвольным регистрам (программа виснет, если
     * нет прав на чтение)
     *
     * @return четырёхбайтное целое, содержащее значения цеелвых регистра(-ов)
     */
    native public int inport ( int portid );

    /**
     * Original signature : <code>void outport(unsigned int, unsigned int)</code>
     * <i>native declaration : line 12</i>
     * Функция, позволяющая обращаться к произвольным регистрам (программа виснет, если
     * нет прав на запись, может привести к нежелательным последствиям при наличии
     * таковых)
     */
    native public void outport ( int portid, int value );

    /**
     * Original signature : <code>void outportb(unsigned int, unsigned char)</code>
     * <i>native declaration : line 13</i>
     * Функция, позволяющая обращаться к произвольным регистрам (программа виснет, если
     * нет прав на запись, может привести к нежелательным последствиям при наличии
     * таковых)
     */
    native public void outportb ( int portid, byte value );
}


Теперь напишем чтение в бесконечном цикле:

Пример использования
        for (;;){

            System.out.println(NativeGPIOLibrary.readGPIO1());
            System.out.println(NativeGPIOLibrary.readGPIO2());

            int GPIOValue1 = (NativeGPIOLibrary.inport(NativeGPIOLibrary.GPIO1));
            int GPIOValue2 = (NativeGPIOLibrary.inport(NativeGPIOLibrary.GPIO2));
            int GPIOValue_34 = (NativeGPIOLibrary.inport(NativeGPIOLibrary.GPIO_34));
            int GPIOValue_44 = (NativeGPIOLibrary.inport(NativeGPIOLibrary.GPIO_44));
            System.out.println(GPIOValue1 + " " + GPIOValue2 + " " + GPIOValue_34 + " " + GPIOValue_44);
            int gp1 = GPIOValue1;// & 0b1111110;
            int gp2 = GPIOValue2;// & 0b1111111;

            if ( (gp1 & 0b00001000) == 0 ){
                System.out.println("button 1 1 pressed");
            }
            if ( (gp1 & 0b00010000) == 0 ){
                System.out.println("button 1 2 pressed");
            }
            if ( (gp1 & 0b00100000) == 0 ){
                System.out.println("button 1 3 pressed");
            }
            if ( (gp2 & 0b00100000) == 0 ){
                System.out.println("button 2 1 pressed");
            }
            if ( (gp2 & 0b00010000) == 0 ){
                System.out.println("button 2 2 pressed");
            }
            if ( (gp2 & 0b01000000) == 0 ){
                System.out.println("button 2 3 pressed");
            }
            if ( (gp2 & 0b10000000) != 0 ){
                System.out.println("button OFF pressed");
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


Запускаем, смотрим: нехватка привилегий. Тут пришлось задуматься, что не так, а потом вспомнить про 0x500-0x53F и 0x500+0x48. Расширил допустимое пространство, заработало.

Заключение


На самом деле почти на каждой стадии было потрачено сильно больше времени, чем описано в этой статье, поэтому надеюсь статья пригодится ещё кому-нибудь.

Комментарии (0)