[Из песочницы] Настройка GPIO в Coremodule 920
Требовалось уметь обращаться к GPIO на Coreodule 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: определяет точку входа для приложения 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:
#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);
}
Файл с описанием работы функций:
// 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 и получаем следующее:
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. Расширил допустимое пространство, заработало.
Заключение
На самом деле почти на каждой стадии было потрачено сильно больше времени, чем описано в этой статье, поэтому надеюсь статья пригодится ещё кому-нибудь.