最近买到一种USB 接口的三轴 8 个按键的Joystick。下面这个样子。
用在最近做的一个工控项目上效果还不错。这里把代码公开了。
Joystick 的控制我以前写过两篇博客:
http://blog.csdn.net/liyuanbhu/article/details/51714045
http://blog.csdn.net/liyuanbhu/article/details/54809858
第一篇博客中把原理基本都讲清楚了。所以这里就只贴代码了。
代码的基本思路就是建立一个监听线程,轮询 joystick 的状态。不同的状态输出不同的结果。因为我的项目中都是只用到一个Joystick,所以代码也没考虑多个 Joystick 的情况。
#ifndef JOYSTICK_H
#define JOYSTICK_H
#include <QThread>
/**
* 3 轴 8 按键 Joystick 的驱动程序。
* 使用的是微软的 multimedia joystick API。
* 没有使用更流行的 DirectInput 技术。
*/
class JoystickThread : public QThread
{
Q_OBJECT
public:
explicit JoystickThread(QObject *parent = Q_NULLPTR);
/**
* @brief run 启动监听线程
*/
void run();
/**
* @brief stop 停止监听线程
*/
void stop();
signals:
/// 手柄 XY 轴的输出信号
/// 需 enableSignal(true), enableXYSignal(true) 才有效
void Joy_MoveForward();
void Joy_MoveBackward();
void Joy_MoveForwardStop();
void Joy_MoveBackwardStop();
void Joy_MoveLeft();
void Joy_MoveRight();
void Joy_MoveLeftStop();
void Joy_MoveRightStop();
/// 手柄 Z 轴的输出信号
/// 需 enableSignal(true), enableZSignal(true) 才有效
void Joy_MoveClockWise();
void Joy_MoveCCW();
void Joy_MoveClockWiseStop();
void Joy_MoveCCWStop();
/// 手柄按键的输出信号
/// 需 enableSignal(true), enableButtionSignal(true) 才有效
void Joy_Button1Pressed();
void Joy_Button2Pressed();
void Joy_Button3Pressed();
void Joy_Button4Pressed();
void Joy_Button5Pressed();
void Joy_Button6Pressed();
void Joy_Button7Pressed();
void Joy_Button8Pressed();
void Joy_Button1Released();
void Joy_Button2Released();
void Joy_Button3Released();
void Joy_Button4Released();
void Joy_Button5Released();
void Joy_Button6Released();
void Joy_Button7Released();
void Joy_Button8Released();
public slots:
/**
* @brief setXYThreshold 设置 XY 手柄的开启阈值。
* 由于手柄使用时间长了会由于机械问题在原点停不住。所以需要有个开启阈值。
* @param x x 阈值
* @param y y 阈值
*/
void setXYThreshold(int x, int y);
/**
* @brief setZThreshold 设置 Z 手柄的开启阈值。
* @param z z 阈值
*/
void setZThreshold(int z);
/**
* @brief enableSignal 可以用这个函数关闭所有的信号
* @param on
*/
void enableSignal(bool on) {m_signal = on;}
/**
* @brief enableXYSignal XY 手柄的使能控制,只有enableSignal(true)时才有效。
* 相当于对手柄的信号有两级控制,第一级是全局使能,第二级是这里的使能。
* @param on
*/
void enableXYSignal(bool on) {m_xy_signal = on;}
void enableZSignal(bool on) {m_z_signal = on;}
void enableButtionSignal(bool on ){m_button_signal = on;}
/**
* @brief setMonitorInterval 设置内部轮询的时间间隔
* @param interval 单位 ms,范围 20 - 10000。
*/
void setMonitorInterval(int interval);
private:
int Axis_stateMachine(int pos, int threshold, int &old_pos);
void AxisX_StateMachine(int xPos);
void AxisY_StateMachine(int yPos);
void AxisZ_StateMachine(int zPos);
void Button_StateMachine(int button);
int m_xThreshold;
int m_yThreshold;
int m_zThreshold;
int old_xPos;
int old_yPos;
int old_zPos;
int m_button[8];
bool m_run;
bool m_valid;
bool m_signal;
bool m_xy_signal;
bool m_z_signal;
bool m_button_signal;
int m_monitor_interval;
};
#endif // JOYSTICK_H
#include "Joystick3D.h"
#include <Windows.h>
#include <QDebug>
JoystickThread::JoystickThread(QObject *parent)
:QThread(parent),
m_valid(false),
m_run(true),
m_xThreshold(5000),
m_yThreshold(5000),
m_zThreshold(5000),
old_xPos(0),
old_yPos(0),
old_zPos(0),
m_signal(false),
m_xy_signal(false),
m_z_signal(false),
m_button_signal(false),
m_monitor_interval(50)
{
JOYINFO joyinfo;
if( joyGetNumDevs() > 0 && joyGetPos(JOYSTICKID1, &joyinfo) != JOYERR_UNPLUGGED )
{
m_valid = true;
qDebug() << "m_valid = true";
}
for(int i = 0; i < 8; i++)
{
m_button[i] = 0;
}
}
void JoystickThread::stop()
{
m_run = 0;
wait();
}
void JoystickThread::setXYThreshold(int x, int y)
{
m_xThreshold = abs(x);
m_yThreshold = abs(y);
}
void JoystickThread::setZThreshold(int z)
{
m_zThreshold = abs(z);
}
void JoystickThread::run()
{
JOYINFOEX joyinfoex;
joyinfoex.dwSize = sizeof(JOYINFOEX);
joyinfoex.dwFlags = JOY_RETURNALL;
while(m_valid && m_run)
{
if(joyGetPosEx(JOYSTICKID1, &joyinfoex) == JOYERR_NOERROR)
{
AxisX_StateMachine(joyinfoex.dwXpos);
AxisY_StateMachine(joyinfoex.dwYpos);
AxisZ_StateMachine(joyinfoex.dwZpos);
Button_StateMachine(joyinfoex.dwButtons);
}
msleep(m_monitor_interval);
}
}
void JoystickThread::setMonitorInterval(int interval)
{
m_monitor_interval = qBound(20, interval, 10000);
}
int JoystickThread::Axis_stateMachine(int pos, int threshold, int &old_pos)
{
pos = pos - 32767;
if(abs(pos) <= threshold)
{
pos = 0;
}
else if (pos > threshold)
{
pos = 1;
}
else
{
pos = -1;
}
if(pos == old_pos) return 0;
int ret;
switch(pos)
{
case -1:
ret = -1;
break;
case 0:
if(old_pos == -1)
{
ret = -2;
}
else
{
ret = 2;
}
break;
case 1:
ret = 1;
break;
}
//qDebug() << "o = " << old_pos << ", " << pos << ", ret =" << ret;
old_pos = pos;
return ret;
}
void JoystickThread::AxisX_StateMachine(int xPos)
{
int ret = Axis_stateMachine(xPos, m_xThreshold, old_xPos);
if(!m_signal || !m_xy_signal) return;
switch(ret)
{
case -1:
emit Joy_MoveLeft();
break;
case -2:
emit Joy_MoveLeftStop();
break;
case 2:
emit Joy_MoveRightStop();
break;
case 1:
emit Joy_MoveRight();
break;
default:
break;
}
}
void JoystickThread::AxisY_StateMachine(int yPos)
{
int ret = Axis_stateMachine(yPos, m_yThreshold, old_yPos);
if(!m_signal || !m_xy_signal) return;
switch(ret)
{
case -1:
emit Joy_MoveForward();
break;
case -2:
emit Joy_MoveForwardStop();
break;
case 2:
emit Joy_MoveBackwardStop();
break;
case 1:
emit Joy_MoveBackward();
break;
default:
break;
}
}
void JoystickThread::AxisZ_StateMachine(int zPos)
{
int ret = Axis_stateMachine(zPos, m_zThreshold, old_zPos);
if(ret == 0) return;
//qDebug() << zPos - 32767 << ", " << ret;
if(!m_signal || !m_z_signal) return;
switch(ret)
{
case -1:
emit Joy_MoveCCW();
break;
case -2:
emit Joy_MoveCCWStop();
break;
case 2:
emit Joy_MoveClockWiseStop();
break;
case 1:
emit Joy_MoveClockWise();
break;
default:
break;
}
}
void JoystickThread::Button_StateMachine(int button)
{
const int mask[8] = {1, 2, 4, 8, 16, 32, 64, 128};
for(int i = 0; i < 8; i++)
{
int n = button & mask[i];
if(n ^ m_button[i]) // 如果当前 button 与以前的 button 不同
{
m_button[i] = n;
if(!m_signal || !m_button_signal) continue;
if( n )
{
qDebug() << "Joy_Button" << i+1 << "Pressed()";
switch (i + 1)
{
case 1:
emit Joy_Button1Pressed();
break;
case 2:
emit Joy_Button2Pressed();
break;
case 3:
emit Joy_Button3Pressed();
break;
case 4:
emit Joy_Button4Pressed();
break;
case 5:
emit Joy_Button5Pressed();
break;
case 6:
emit Joy_Button6Pressed();
break;
case 7:
emit Joy_Button7Pressed();
break;
case 8:
emit Joy_Button8Pressed();
break;
default:
break;
}
}
else
{
qDebug() << "Joy_Button" << i+1 << "Released()";
switch (i + 1)
{
case 1:
emit Joy_Button1Released();
break;
case 2:
emit Joy_Button2Released();
break;
case 3:
emit Joy_Button3Released();
break;
case 4:
emit Joy_Button4Released();
break;
case 5:
emit Joy_Button5Released();
break;
case 6:
emit Joy_Button6Released();
break;
case 7:
emit Joy_Button7Released();
break;
case 8:
emit Joy_Button8Released();
break;
default:
break;
}
}
}
}
}