掌機 - Miyoo A30 - C/C++ - Joystick



main.c

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/input.h>

#define SERIAL_GAMEDECK         "/dev/ttyS0"
#define MIYOO_AXIS_MAX_COUNT    16
#define MIYOO_PLAYER_MAGIC      0xFF
#define MIYOO_PLAYER_MAGIC_END  0xFE
#define MIYOO_PAD_FRAME_LEN     6

struct MIYOO_PAD_FRAME {
    uint8_t magic;
    uint8_t unused0;
    uint8_t unused1;
    uint8_t axis0;
    uint8_t axis1;
    uint8_t magicEnd;
};

static int s_fd = -1;
static int g_lastX = 0;
static int g_lastY = 0;

static int MIYOO_ADC_MAX_X  = 200;
static int MIYOO_ADC_ZERO_X = 137;
static int MIYOO_ADC_MIN_X  = 76;

static int MIYOO_ADC_MAX_Y  = 200;
static int MIYOO_ADC_ZERO_Y = 135;
static int MIYOO_ADC_MIN_Y  = 72;

static int MIYOO_ADC_DEAD_ZONE   = 10;
static int MIYOO_AXIS_INT8_DRIFT = 5;

static struct MIYOO_PAD_FRAME s_frame = {0};
static int32_t s_miyoo_axis[MIYOO_AXIS_MAX_COUNT] = {0};
static int32_t s_miyoo_axis_last[MIYOO_AXIS_MAX_COUNT] = {0};

int uart_open(const char *port)
{
    int fd = -1;

    fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
    if (-1 == fd) {
        printf("Failed to open uart\n");
        return -1;
    }

    if (fcntl(fd, F_SETFL, 0) < 0) {
        printf("Failed to call fcntl\n");
        return -1;
    }
    return fd;
}

static void uart_close(int fd)
{
    close(fd);
}

static int uart_set(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
    int i = 0;
    int speed_arr[] = {B115200, B19200, B9600, B4800, B2400, B1200, B300};
    int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300};
    struct termios options = {0};

    if (tcgetattr(fd, &options) != 0) {
        printf("Failed to get uart attributes\n");
        return -1;
    }

    for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
        if (speed == name_arr[i]) {
            cfsetispeed(&options, speed_arr[i]);
            cfsetospeed(&options, speed_arr[i]);
        }
    }

    options.c_cflag |= CLOCAL;
    options.c_cflag |= CREAD;
    switch (flow_ctrl) {
    case 0:
        options.c_cflag &= ~CRTSCTS;
        break;
    case 1:
        options.c_cflag |= CRTSCTS;
        break;
    case 2:
        options.c_cflag |= IXON | IXOFF | IXANY;
        break;
    }

    options.c_cflag &= ~CSIZE;
    switch (databits) {
    case 5:
        options.c_cflag |= CS5;
        break;
    case 6:
        options.c_cflag |= CS6;
        break;
    case 7:
        options.c_cflag |= CS7;
        break;
    case 8:
        options.c_cflag |= CS8;
        break;
    default:
        return -1;
    }

    switch (parity) {
    case 'n':
    case 'N':
        options.c_cflag &= ~PARENB;
        options.c_iflag &= ~INPCK;
        break;
    case 'o':
    case 'O':
        options.c_cflag |= (PARODD | PARENB);
        options.c_iflag |= INPCK;
        break;
    case 'e':
    case 'E':
        options.c_cflag |= PARENB;
        options.c_cflag &= ~PARODD;
        options.c_iflag |= INPCK;
        break;
    case 's':
    case 'S':
        options.c_cflag &= ~PARENB;
        options.c_cflag &= ~CSTOPB;
        break;
    default:
        return -1;
    }

    switch (stopbits) {
    case 1:
        options.c_cflag &= ~CSTOPB;
        break;
    case 2:
        options.c_cflag |= CSTOPB;
        break;
    default:
        return -1;
    }

    options.c_oflag &= ~OPOST;
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_iflag &= ~(INLCR | ICRNL | IGNCR);
    options.c_oflag &= ~(ONLCR | OCRNL);
    options.c_cc[VTIME] = 1;
    options.c_cc[VMIN] = 1;

    tcflush(fd, TCIFLUSH);
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        return -1;
    }
    return 0;
}

static int uart_init(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
    if (uart_set(fd, speed, flow_ctrl, databits, stopbits, parity) == -1) {
        return -1;
    }
    return 0;
}

static int uart_read(int fd, char *rcv_buf, int data_len)
{
    int f_sel;
    fd_set f_set;
    fd_set f_read;
    struct timeval time = {0};

    FD_ZERO(&f_read);
    FD_SET(fd, &f_read);

    time.tv_sec = 10;
    time.tv_usec = 0;
    f_sel = select(fd + 1, &f_read, NULL, NULL, &time);
    if (f_sel) {
        return read(fd, rcv_buf, data_len);
    }
    return 0;
}

static void defaule_cal_config(void)
{
    MIYOO_ADC_MAX_X = 200;
    MIYOO_ADC_ZERO_X = 137;
    MIYOO_ADC_MIN_X = 76;
    MIYOO_ADC_MAX_Y = 200;
    MIYOO_ADC_ZERO_Y = 135;
    MIYOO_ADC_MIN_Y = 72;
}

static int filterDeadzone(int newAxis, int oldAxis)
{
    if (abs(newAxis - oldAxis) < 5) {
        return 1;
    }
    return 0;
}

static int limitValue8(int value)
{
    if (value > 127) {
        value = 127;
    }
    else if (value < -128) {
        value = -128;
    }
    return value;
}

static void check_axis_event(void)
{
    int i = 0;

    for (i = 0; i < MIYOO_AXIS_MAX_COUNT; i++) {
        if (s_miyoo_axis[i] != s_miyoo_axis_last[i]) {
            if (!filterDeadzone(s_miyoo_axis[i], s_miyoo_axis_last[i])) {
                if (i == 0) {
                    g_lastX = limitValue8(s_miyoo_axis[i]);
                    printf("X %d\n", g_lastX);
                }
                else if (i == 1) {
                    g_lastY = limitValue8(s_miyoo_axis[i]);
                    printf("Y %d\n", g_lastY);
                }
            }
        }
        s_miyoo_axis_last[i] = s_miyoo_axis[i];
    }
}

static int miyoo_frame_to_axis_x(uint8_t rawX)
{
    int value = 0;

    if (rawX > MIYOO_ADC_ZERO_X) {
        value = (rawX - MIYOO_ADC_ZERO_X) * 126 / (MIYOO_ADC_MAX_X - MIYOO_ADC_ZERO_X);
    }

    if (rawX < MIYOO_ADC_ZERO_X) {
        value = (rawX - MIYOO_ADC_ZERO_X) * 126 / (MIYOO_ADC_ZERO_X - MIYOO_ADC_MIN_X);
    }

    if (value > 0 && value < MIYOO_ADC_DEAD_ZONE) {
        return 0;
    }

    if (value < 0 && value > -(MIYOO_ADC_DEAD_ZONE)) {
        return 0;
    }
    return value;
}

static int miyoo_frame_to_axis_y(uint8_t rawY)
{
    int value = 0;

    if (rawY > MIYOO_ADC_ZERO_Y) {
        value = (rawY - MIYOO_ADC_ZERO_Y) * 126 / (MIYOO_ADC_MAX_Y - MIYOO_ADC_ZERO_Y);
    }

    if (rawY < MIYOO_ADC_ZERO_Y) {
        value = (rawY - MIYOO_ADC_ZERO_Y) * 126 / (MIYOO_ADC_ZERO_Y - MIYOO_ADC_MIN_Y);
    }

    if (value > 0 && value < MIYOO_ADC_DEAD_ZONE) {
        return 0;
    }

    if (value < 0 && value > -(MIYOO_ADC_DEAD_ZONE)) {
        return 0;
    }
    return value;
}

static int parser_miyoo_input(const char *cmd, int len)
{
    int i = 0;
    int p = 0;

    if ((!cmd) || (len < MIYOO_PAD_FRAME_LEN)) {
        return - 1;
    }

    for (i = 0; i < len - MIYOO_PAD_FRAME_LEN + 1; i += MIYOO_PAD_FRAME_LEN) {
        for (p = 0; p < MIYOO_PAD_FRAME_LEN - 1; p++) {
            if ((cmd[i] == MIYOO_PLAYER_MAGIC) && (cmd[i + MIYOO_PAD_FRAME_LEN - 1] == MIYOO_PLAYER_MAGIC_END)) {
                memcpy(&s_frame, cmd + i, sizeof(s_frame));
                break;
            }
            else {
                i++;
            }
        }
    }
    s_miyoo_axis[ABS_X] = miyoo_frame_to_axis_x(s_frame.axis0);
    s_miyoo_axis[ABS_Y] = miyoo_frame_to_axis_y(s_frame.axis1);
    check_axis_event();
    return 0;
}

static int miyoo_init_serial_input(void)
{
    int err = 0;

    memset(&s_frame, 0, sizeof(s_frame));
    memset(s_miyoo_axis, 0, sizeof(s_miyoo_axis));
    memset(s_miyoo_axis_last, 0, sizeof(s_miyoo_axis_last));
    s_fd = uart_open(SERIAL_GAMEDECK);
    err = uart_init(s_fd, 9600, 0, 8, 1, 'N');
    if (s_fd <= 0) {
        return -1;
    }
    return 0;
}

static void miyoo_close_serial_input(void)
{
}

int main(int argc, char **argv)
{
    int i = 0;
    int len = 0;
    char rcv_buf[255] = {0};

    miyoo_init_serial_input();
    while (1) {
        len = uart_read(s_fd, rcv_buf, 99);
        if (len > 0) {
            rcv_buf[len] = '\0';
            parser_miyoo_input(rcv_buf, len);
        }
        usleep(10);
    }
    uart_close(s_fd);
    s_fd = -1;

    miyoo_close_serial_input();
    return 0;
}