[TH] Try the SoftHost USB library for ESP32.

บทความนี้เป็นตัวอย่างการนำ ESP32 Soft Host ของ nathalis มาทดลองใช้งานและทดลองแสดงผลที่จอแสดงผล OLED ที่มีความละเอียด 128×64 จุด ดังภาพที่ 1 เพื่อรับข้อมูลจากแป้นพิมพ์ หรือเมาส์ ทำให้ใช้ GPIO เพียงไม่กี่ขาแต่สามารถรับข้อมูลจากแป้นพิมพ์ที่มีจำนวนปุ่มที่เยอะได้ ซึ่งโดยปกติแล้วตัวไมโครคอนโทรลเลอร์ ESP32 ไม่รองรับการทำเชื่อมต่อกับ USB โดยตรงจึงต้องอาศัยการเขียนโปรแกรมด้วยการใช้ตัวตั้งเวลาหรือ Timer มาตรวจสอบสถานะของสายสัญญาณขา D- และ D+ เพื่อนำมาประกอบกันเป็นข้อมูลในระดับไบต์และนำมาประกอบกันเป็นแพ็คของข้อมูลเพื่อทำการตีความต่อไป

ภาพที่ 1 อุปกรณ์และบอร์ดที่เชื่อมต่อกันเพื่อเป็นตัวอย่างในบทความนี้

การเชื่อมต่อ

ส่วนของการเชื่อมต่อระหว่าง USB Port กับ ESP32 เป็นดังภาพที่ 2 คือ

  • GPIO18 ต่อเข้ากับ D+
  • GPIO19 ต่อเข้ากับ D-
  • GND ต่อเข้ากับ GND ของพอร์ต USB
  • 5V ต่อเข้ากับ Vcc ของพอร์ต SB
ภาพที่ 2 การเชื่อมต่อระหว่าง ESP32 กับพอร์ต USB ฝั่งรับ

รายการไลบรารี

ไลบรารีที่ nathalis สร้างมานั้นรองรับพอร์ต USB ได้ 4 พอร์ต โดยกำหนดในโค้ดดังเช่นต่อไปนี้ และถ้าไม่ใช้ให้ใส่เป็น -1

#define DP_P0  18  // Data+
#define DM_P0  19  // Data-
#define DP_P1  -1  // not used
#define DM_P1  -1  // not used
#define DP_P2  -1  // not used
#define DM_P2  -1  // not used
#define DP_P3  -1  // not used
#define DM_P3  -1  // not used

usb_pins_config_t USB_Pins_Config =
{
  DP_P0, DM_P0,
  DP_P1, DM_P1,
  DP_P2, DM_P2,
  DP_P3, DM_P3
};

ส่วนของไฟล์ที่มากับ ESP32-USB-SOFTHOST1.1-LOWSPEED-KEYBOARD-AND-MOUSE ได้แก่

สิ่งที่พวกเราปรับแก้คือ ในโค้ดที่กำหนดไว้เป็น inline และทำให้คอมไพล์ไม่ผ่าน จึงเปลี่ยนโค้ดของ usb_host.c ดังนี้

#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>

#include "driver/gpio.h"
#include "sdkconfig.h"
#include "driver/timer.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "math.h"
#include "esp_heap_caps.h"

/*******************************
     warning!!!: any copy of this code or his part must include this:
   "The original was written by Dima Samsonov @ Israel [email protected] on 3/2021"
   Copyright (C) 2021  Dmitry Samsonov
********************************/

/*\
   Changes by tobozo (may 2021):
     - Backported calibration at init (last changes from from Samsonov's repo)
   Changes by tobozo (march 2021):
     - Arduino IDE compliance (mostly code regression to esp-idf 3.x)
     - Added callbacks (data and device detection)
  \*/


#include "usb_host.h"

// Arduino IDE complains about volatile at init, but we don't care
#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"

extern uint8_t DEBUGEXTRA_VAR;
uint8_t new_device = 0;


#define T_START   0b00000001
#define T_ACK     0b01001011
#define T_NACK    0b01011010
#define T_SOF    0b10100101
#define T_SETUP  0b10110100
#define T_DATA0  0b11000011
#define T_DATA1  0b11010010
#define T_DATA2  0b11100001
#define T_OUT    0b10000111
#define T_IN    0b10010110

#define T_ERR    0b00111100
#define T_PRE    0b00111100
#define T_NYET  0b01101001
#define T_STALL  0b01111000

// local non std
#define T_NEED_ACK   0b01111011
#define T_CHK_ERR    0b01111111

#define USB_LS_K  0
#define USB_LS_J  1
#define USB_LS_S  2

//most counters- uint_8t :  so  prevents overflow...

#define DEF_BUFF_SIZE 0x100

// somethins short  like ACK
#define SMALL_NO_DATA 36


///cpufreq (must be 240) /8 count = 30MHz  convinient number for measure 1.5MHz  of  low speed USB
static inline uint8_t _getCycleCount8d8(void)
{
  uint32_t ccount;
  __asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
  return ccount >> 3;
}

static inline uint32_t _getCycleCount32(void)
{
  uint32_t ccount;
  __asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
  return ccount;
}

int TRANSMIT_TIME_DELAY = 110;  //delay each bit transmit
int TIME_MULT           = 25;    //received time factor delta clocks* TIME_MULT/TIME_SCALE
int TM_OUT              = 64;    //receive time out no activity on bus
#define  TIME_SCALE       1024

//#define TEST
#ifdef TEST
#define TOUT  1000
#else
#define TOUT  (TM_OUT)
#endif

#define SET_I { PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[DP_PIN]); PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[DM_PIN]); GPIO.enable_w1tc = (1 << DP_PIN) | (1 << DM_PIN);  }
#define SET_O { GPIO.enable_w1ts = (1 << DP_PIN) | (1 << DM_PIN);  PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[DP_PIN]); PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[DM_PIN]);  }
#define SE_J  { *snd[1][0] = (1 << DM_PIN);*snd[1][1] = (1 << DP_PIN); }
#define SE_0  { *snd[2][0] = (1 << DM_PIN);*snd[2][1] = (1 << DP_PIN); }

#define READ_BOTH_PINS ((GPIO.in&RD_MASK)>>RD_SHIFT)

//must be setup ech time with setPins
uint32_t DP_PIN;
uint32_t DM_PIN;

uint32_t DM_PIN_M;
uint32_t DP_PIN_M;
uint16_t M_ONE;
uint16_t P_ONE;
uint32_t RD_MASK;
uint32_t RD_SHIFT;
//end must be setup ech time with setPins

// temporary used insize lowlevel
volatile   uint8_t received_NRZI_buffer_bytesCnt;
uint16_t   received_NRZI_buffer[DEF_BUFF_SIZE];

volatile uint8_t transmit_bits_buffer_store_cnt;
//uint8_t transmit_bits_buffer_store[DEF_BUFF_SIZE];
uint8_t* transmit_bits_buffer_store = (uint8_t*)&received_NRZI_buffer[0];

volatile uint8_t transmit_NRZI_buffer_cnt;
uint8_t  transmit_NRZI_buffer[DEF_BUFF_SIZE];

volatile uint8_t decoded_receive_buffer_head;
volatile uint8_t decoded_receive_buffer_tail;
uint8_t decoded_receive_buffer[DEF_BUFF_SIZE];
// end temporary used insize lowlevel


void (*delay_pntA)() = NULL;
#define cpuDelay(x) {(*delay_pntA)();}
void setDelay(uint8_t ticks)
{
  // opcodes of void test_delay() {__asm__ (" nop"); __asm__ (" nop"); __asm__ (" nop"); ...}
  //36 41 00 3d f0 1d f0 00 // one  nop
  //36 41 00 3d f0 3d f0 3d f0 3d f0 3d f0 1d f0 00  // five  nops
  //36 41 00 3d f0 3d f0 3d f0 3d f0 3d f0 3d f0 1d  f0 00 00 00 //
  int    MAX_DELAY_CODE_SIZE = 0x280;
  uint8_t*     pntS;
  // it can't execute but can read & write
  if (!delay_pntA) {
    pntS = malloc(MAX_DELAY_CODE_SIZE);
  } else {
    pntS = heap_caps_realloc(delay_pntA, MAX_DELAY_CODE_SIZE, MALLOC_CAP_8BIT);
  }
  uint8_t* pnt = (uint8_t*)pntS;
  //put head of delay procedure
  *pnt++ = 0x36;
  *pnt++ = 0x41;
  *pnt++ = 0;
  for (int k = 0; k < ticks; k++) {
    //put NOPs
    *pnt++ = 0x3d;
    *pnt++ = 0xf0;
  }
  //put tail of delay procedure
  *pnt++ = 0x1d;
  *pnt++ = 0xf0;
  *pnt++ = 0x00;
  *pnt++ = 0x00;
  // move it to executable memory segment
  // it can't  write  but can read & execute
  delay_pntA = heap_caps_realloc(pntS, MAX_DELAY_CODE_SIZE, MALLOC_CAP_EXEC);
}


typedef struct
{
  uint8_t cmd;
  uint8_t addr;
  uint8_t eop;

  uint8_t  dataCmd;
  uint8_t  bmRequestType;
  uint8_t  bmRequest;
  uint16_t wValue;
  uint16_t wIndex;
  uint16_t wLen;
} Req;



enum DeviceState
{
  NOT_ATTACHED,
  ATTACHED,
  POWERED,
  DEFAULT,
  ADDRESS,
  PARSE_CONFIG,
  PARSE_CONFIG1,
  PARSE_CONFIG2,
  PARSE_CONFIG3,
  POST_ATTACHED,
  RESET_COMPLETE,
  POWERED_COMPLETE,
  DEFAULT_COMPL
};



enum CallbackCmd
{
  CB_CHECK,
  CB_RESET,
  CB_WAIT0,
  CB_POWER,
  CB_TICK,
  CB_2,
  CB_2Ack,
  CB_3,
  CB_4,
  CB_5,
  CB_6,
  CB_7,
  CB_8,
  CB_9,
  CB_WAIT1
};



//Req rq;
typedef struct
{
  int isValid;
  int selfNum;
  int epCount;
  int cnt;

  uint32_t DP;
  uint32_t DM;

  volatile enum CallbackCmd cb_Cmd;
  volatile enum DeviceState fsm_state;
  volatile uint16_t wires_last_state;

  sDevDesc desc;
  sCfgDesc cfg;
  Req rq;

  int counterNAck;
  int counterAck;

  uint8_t descrBuffer[DEF_BUFF_SIZE];
  uint8_t descrBufferLen;

  volatile int bComplete;
  volatile int in_data_flip_flop;

  int cmdTimeOut;

  uint32_t ufPrintDesc;
  int numb_reps_errors_allowed;

  uint8_t acc_decoded_resp[DEF_BUFF_SIZE];
  uint8_t acc_decoded_resp_counter;

  int asckedReceiveBytes;
  int transmitL1Bytes;
  uint8_t transmitL1[DEF_BUFF_SIZE];

  uint8_t Resp0[DEF_BUFF_SIZE];
  uint8_t R0Bytes;
  uint8_t Resp1[DEF_BUFF_SIZE];
  uint8_t R1Bytes;

} sUsbContStruct;

sUsbContStruct * current;


uint32_t* snd[4][2]  =
{
  { &GPIO.out_w1tc, &GPIO.out_w1ts },
  { &GPIO.out_w1ts, &GPIO.out_w1tc },
  { &GPIO.out_w1tc, &GPIO.out_w1tc },
  { &GPIO.out_w1tc, &GPIO.out_w1tc }
} ;


#ifdef WR_SIMULTA
uint32_t sndA[4]  = {0, 0, 0, 0};
#endif



void restart()
{
  transmit_NRZI_buffer_cnt = 0;
}



void decoded_receive_buffer_clear()
{
  decoded_receive_buffer_tail = decoded_receive_buffer_head;
}



void decoded_receive_buffer_put(uint8_t val)
{
  decoded_receive_buffer[decoded_receive_buffer_head] = val;
  decoded_receive_buffer_head++;
}



uint8_t decoded_receive_buffer_get()
{
  return decoded_receive_buffer[decoded_receive_buffer_tail++];
}



uint8_t decoded_receive_buffer_size()
{
  return (uint8_t )(decoded_receive_buffer_head - decoded_receive_buffer_tail);
}



uint8_t cal5()
{
  uint8_t   crcb;
  uint8_t   rem;

  crcb = 0b00101;
  rem =  0b11111;

  for (int k = 16; k < transmit_bits_buffer_store_cnt; k++) {
    int rb = (rem >> 4) & 1;
    rem   =  (rem << 1) & 0b11111;

    if (rb ^ (transmit_bits_buffer_store[k] & 1)) {
      rem ^= crcb;
    }
  }
  return (~rem) & 0b11111;
}



uint32_t cal16()
{
  uint32_t   crcb;
  uint32_t   rem;

  crcb = 0b1000000000000101;
  rem =  0b1111111111111111;

  for (int k = 16; k < transmit_bits_buffer_store_cnt; k++) {
    int rb = (rem >> 15) & 1;
    rem   =  (rem << 1) & 0b1111111111111111;

    if (rb ^ (transmit_bits_buffer_store[k] & 1)) {
      rem ^= crcb;
    }
  }
  return (~rem) & 0b1111111111111111;
}



void seB(int bit)
{
  transmit_bits_buffer_store[transmit_bits_buffer_store_cnt++] = bit;
}



void pu_MSB(uint16_t msg, int N)
{
  for (int k = 0; k < N; k++) {
    seB(msg & (1 << (N - 1 - k)) ? 1 : 0);
  }
}



void pu_LSB(uint16_t msg, int N)
{
  for (int k = 0; k < N; k++) {
    seB(msg & (1 << (k)) ? 1 : 0);
  }
}



void repack()
{
  int last = USB_LS_J;
  int cntOnes = 0;
  transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_J;
  for (int k = 0; k < transmit_bits_buffer_store_cnt; k++) {
    if (transmit_bits_buffer_store[k] == 0) {
      if (last == USB_LS_J || last == USB_LS_S) {
        last = USB_LS_K;
      } else {
        last = USB_LS_J;
      }
      cntOnes = 0;
    } else if (transmit_bits_buffer_store[k] == 1) {
      cntOnes++;
      if (cntOnes == 6) {
        transmit_NRZI_buffer[transmit_NRZI_buffer_cnt] = last;
        transmit_NRZI_buffer_cnt++;
        if (last == USB_LS_J) {
          last = USB_LS_K;
        } else {
          last = USB_LS_J;
        }
        cntOnes = 0;
      }
      if (last == USB_LS_S) {
        last = USB_LS_J;
      }
    }
    transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = last;
  }

  transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_S;
  transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_S;

  transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_J;
  transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_J;
  transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_J;
  transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_J;

  transmit_bits_buffer_store_cnt = 0;
}



uint8_t rev8(uint8_t j)
{
  uint8_t res = 0;
  for (int i = 0; i < 8; i++) {
    res <<= 1;
    res |= (j >> i) & 1;
  }
  return res;
}



uint16_t rev16(uint16_t j) {
  uint16_t res = 0;
  for (int i = 0; i < 16; i++) {
    res <<= 1;
    res |= (j >> i) & 1;
  }
  return res;
}



#ifdef DEBUG_ALL
uint16_t debug_buff[0x100];
#endif



int parse_received_NRZI_buffer()
{

  if (!received_NRZI_buffer_bytesCnt) return 0;

  uint32_t   crcb;
  uint32_t   rem;

  crcb = 0b1000000000000101;
  rem =  0b1111111111111111;

  int  res = 0;
  int  cntOnes = 0;

  int terr  = 0;
  uint8_t  current_res = 0xfe;
  uint16_t prev = received_NRZI_buffer[0];
  int      start = -1;
  uint8_t  prev_smb = M_ONE;
#ifdef DEBUG_ALL
  debug_buff[0] = received_NRZI_buffer_bytesCnt;
  uint8_t rcnt = 1;
  debug_buff[received_NRZI_buffer_bytesCnt] = 0xff;
#endif
  for (int i = 1; i < received_NRZI_buffer_bytesCnt; i++) {
    //define 2.5
    uint16_t curr = (prev & 0xff00) + (((received_NRZI_buffer[i] - prev)) & 0xff);
    prev          = received_NRZI_buffer[i];

    uint8_t smb = curr >> 8;
    int tm = (curr & 0xff);
    //debug_buff[i] = tm | (smb<<8);
    if ( tm < 2  || (smb == 0) ) {
      //terr+=tm<4?tm : 4;
      terr += tm;
    } else {
      //terr = 0;
      int delta = ((((curr + terr) & 0xff)) * TIME_MULT + TIME_SCALE / 2) / TIME_SCALE;

      for (int k = 0; k < delta; k++) {
        int incc = 1;

        if (prev_smb != smb) {
          if (cntOnes != 6) {
            current_res = current_res * 2 + 0;
          } else {
            incc = 0;
          }
          cntOnes = 0;
        } else {
          current_res = current_res * 2 + 1;
          cntOnes++;
        }
        if (start >= 0) {
          start += incc;
        }
        if (current_res == 0x1 && start < 0 ) {
          start = 0;
        }
        if ( (start & 0x7) == 0 && incc) {
          if (start == 8) {
            res = current_res;
          }
#ifdef DEBUG_ALL
          debug_buff[rcnt++] = current_res;
#endif
          decoded_receive_buffer_put(current_res);
          if (start > 8) {
            for (int bt = 0; bt < 8; bt++) {
              int rb = (rem >> 15) & 1;
              rem   =  (rem << 1) & 0b1111111111111111;
              if (rb ^ ((current_res >> (7 - bt)) & 1)) {
                rem ^= crcb;
              }
            }
          }
        }

        prev_smb = smb;
      }
      terr = 0;
    }
  }
#ifdef DEBUG_ALL
  debug_buff[rcnt++] = 0xff;
#endif
  rem &= 0b1111111111111111;
  if (rem == 0b1111111111111111) {
    return res;
  }
  if (rem == 0x800d) {
    return  T_NEED_ACK;
  } else {
    return  T_CHK_ERR;
  }
}



//#define WR_SIMULTA
void sendOnly()
{
  uint8_t k;
  SET_O;
#ifdef WR_SIMULTA
  uint32_t out_base = GPIO.out;
  sndA[0] = (out_base | DP) & ~DM;
  sndA[1] = (out_base | DM) & ~DP;
  sndA[2] = (out_base ) & ~(DP | DM);
  sndA[3] = out_base | (DM | DP);
#endif
  for (k = 0; k < transmit_NRZI_buffer_cnt; k++) {
    //usb_transmit_delay(10);
    cpuDelay(TRANSMIT_TIME_DELAY);
#ifdef WR_SIMULTA
    GPIO.out = sndA[transmit_NRZI_buffer[k]];
#else
    *snd[transmit_NRZI_buffer[k]][0] = DM_PIN_M;
    *snd[transmit_NRZI_buffer[k]][1] = DP_PIN_M;
#endif
  }
  restart();
  SET_I;
}


void sendRecieveNParse()
{
  register uint32_t R3;
  register uint16_t *STORE = received_NRZI_buffer;
  //__disable_irq();
  sendOnly();
  register uint32_t R4;// = READ_BOTH_PINS;

START:
  R4 = READ_BOTH_PINS;
  *STORE = R4 | _getCycleCount8d8();
  STORE++;
  R3 = R4;
  //R4 = READ_BOTH_PINS;
  //if(R4!=R3)  goto START;
  if ( R3 ) {
    for (int k = 0; k < TOUT; k++) {
      R4   = READ_BOTH_PINS;
      if (R4 != R3)  goto START;
    }
  }
  //__enable_irq();
  received_NRZI_buffer_bytesCnt = STORE - received_NRZI_buffer;
}



int sendRecieve()
{
  sendRecieveNParse();
  return parse_received_NRZI_buffer();
}



void SOF()
{
  if (1) {
    repack();
  }
  sendOnly();
}



void pu_Addr(uint8_t cmd, uint8_t addr, uint8_t eop)
{
  pu_MSB(T_START, 8);
  pu_MSB(cmd, 8); //setup
  pu_LSB(addr, 7);
  pu_LSB(eop, 4);
  pu_MSB(cal5(), 5);
  repack();
}



void pu_ShortCmd(uint8_t cmd)
{
  pu_MSB(T_START, 8);
  pu_MSB(cmd, 8); //setup
  pu_MSB(0, 16);
  repack();
}



void pu_Cmd(uint8_t cmd, uint8_t bmRequestType, uint8_t bmRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLen)
{
  pu_MSB(T_START, 8);
  pu_MSB(cmd, 8); //setup
  pu_LSB(bmRequestType, 8);
  pu_LSB(bmRequest, 8);
  pu_LSB(wValue, 16);
  pu_LSB(wIndex, 16);
  pu_LSB(wLen, 16);
  pu_MSB(cal16(), 16);
  repack();
}



uint8_t ACK_BUFF[0x20];
int    ACK_BUFF_CNT = 0;


void ACK()
{
  transmit_NRZI_buffer_cnt = 0;
  if (ACK_BUFF_CNT == 0) {
    pu_MSB(T_START, 8);
    pu_MSB(T_ACK, 8); // ack
    repack();
    memcpy(ACK_BUFF, transmit_NRZI_buffer, transmit_NRZI_buffer_cnt);
    ACK_BUFF_CNT = transmit_NRZI_buffer_cnt;
  } else {
    memcpy(transmit_NRZI_buffer, ACK_BUFF, ACK_BUFF_CNT);
    transmit_NRZI_buffer_cnt = ACK_BUFF_CNT;
  }
  sendOnly();
}


void timerCallBack()
{
  decoded_receive_buffer_clear();

  if (current->cb_Cmd == CB_CHECK) {
    SET_I;
    current->wires_last_state = READ_BOTH_PINS >> 8;
    if (current->wires_last_state == M_ONE) {
      // low speed

    } else if (current->wires_last_state == P_ONE) {
      //high speed

    } else if (current->wires_last_state == 0x00) {
      // not connected
    } else if (current->wires_last_state == (M_ONE + P_ONE) ) {
      //????
    }

    new_device = 1;

    current->bComplete = 1;
  } else if (current->cb_Cmd == CB_RESET) {
    SOF();
    sendRecieveNParse();
    SET_O;
    SE_0;
    current->cmdTimeOut  =   31;
    current->cb_Cmd         =   CB_WAIT0;
  } else if (current->cb_Cmd == CB_WAIT0) {
    if (current->cmdTimeOut > 0) {
      current->cmdTimeOut--;
    } else {
      //sendRecieveNParse();
      current->bComplete     = 1;
    }
  } else if (current->cb_Cmd == CB_WAIT1) {
    SOF();
    if (current->cmdTimeOut > 0) {
      current->cmdTimeOut--;
    } else {
      sendRecieveNParse();
      current->wires_last_state = READ_BOTH_PINS >> 8;
      current->bComplete     = 1;
    }
  } else if (current->cb_Cmd == CB_POWER) {

    // for TEST
#ifdef TEST
    SOF();
    sendRecieve();
    SOF();
    SOF();
#else
    SET_O;
    SE_J;
    SET_I;
    current->cmdTimeOut  =    2;
    current->cb_Cmd  = CB_WAIT1;
#endif
  } else if (current->cb_Cmd == CB_TICK) {
    SOF();
    current->bComplete     =         1;
  } else if (current->cb_Cmd == CB_3) {
    SOF();
    pu_Addr(current->rq.cmd, current->rq.addr, current->rq.eop);
    pu_Cmd(current->rq.dataCmd, current->rq.bmRequestType, current->rq.bmRequest, current->rq.wValue, current->rq.wIndex, current->rq.wLen);
    int res = sendRecieve();
    if (res == T_ACK) {
      current->cb_Cmd = CB_4;
      current->numb_reps_errors_allowed = 8;
      return ;
    } else {
      current->numb_reps_errors_allowed--;
      if (current->numb_reps_errors_allowed > 0) {
        return ;
      } else {
        current->cb_Cmd = CB_TICK;
        current->bComplete = 1;
      }
    }
  } else if (current->cb_Cmd == CB_4) {
    SOF();
    pu_Addr(T_OUT, current->rq.addr, current->rq.eop);
    //reB();
    pu_MSB(T_START, 8);
    pu_MSB(T_DATA1, 8); //setup
    for (int k = 0; k < current->transmitL1Bytes; k++) {
      pu_LSB(current->transmitL1[k], 8);
    }
    pu_MSB(cal16(), 16);
    repack();
    sendRecieveNParse();
    pu_Addr(T_IN, current->rq.addr, current->rq.eop);
    //setup
    sendRecieveNParse();
    ACK();
    current->cb_Cmd = CB_TICK;
    current->bComplete = 1;
  } else if (current->cb_Cmd == CB_5) {
    SOF();
    pu_Addr(current->rq.cmd, current->rq.addr, current->rq.eop);
    pu_Cmd(current->rq.dataCmd, current->rq.bmRequestType, current->rq.bmRequest, current->rq.wValue, current->rq.wIndex, current->rq.wLen);
    sendRecieveNParse();

    int res = parse_received_NRZI_buffer();
    if (res == T_ACK) {
      current->cb_Cmd = CB_6;
      current->in_data_flip_flop = 1;
      current->numb_reps_errors_allowed = 4;
      current->counterAck ++;
      return ;
    } else {
      //SOF();
      current->counterNAck ++;
      current->numb_reps_errors_allowed--;
      if (current->numb_reps_errors_allowed > 0) {
        // current->cb_Cmd = CB_TICK;
        current->acc_decoded_resp_counter = 0;
        return ;
      } else {
        current->cb_Cmd = CB_TICK;
        current->bComplete       =  1;
      }
    }
  } else if (current->cb_Cmd == CB_6) {
    SOF();
    pu_Addr(T_IN, current->rq.addr, current->rq.eop);
    //setup
    sendRecieveNParse();
    // if receive something ??
    if (current->asckedReceiveBytes == 0 && current->acc_decoded_resp_counter == 0 && received_NRZI_buffer_bytesCnt < SMALL_NO_DATA &&  received_NRZI_buffer_bytesCnt > SMALL_NO_DATA / 4 ) {
      ACK();

      current->cb_Cmd = CB_TICK;
      current->bComplete       =  1;
      return ;
    }
    int res = parse_received_NRZI_buffer();
    if (res == T_NEED_ACK) {
      //SOF();
      if (decoded_receive_buffer_size() > 2) {
        decoded_receive_buffer_get();
        uint8_t sval = decoded_receive_buffer_get();
        if ((current->in_data_flip_flop & 1) == 1) {
          if (sval == T_DATA1) {

          } else {
            current->cb_Cmd = CB_7;
            return ;
          }
        } else {
          if (sval == T_DATA0) {

          } else {
            current->cb_Cmd = CB_7;
            return ;
          }
        }
        current->in_data_flip_flop++;
        int bytes = decoded_receive_buffer_size() - 2;
        for (int kk = 0; kk < bytes; kk++) {
          current->acc_decoded_resp[current->acc_decoded_resp_counter] = rev8(decoded_receive_buffer_get());
          current->acc_decoded_resp_counter++;
          current->asckedReceiveBytes--;
        }

        if (bytes <= 0) {

          current->acc_decoded_resp_counter = 0;
          current->asckedReceiveBytes = 0;
          current->cb_Cmd = CB_TICK;
          current->bComplete = 1;
        } else {
          current->cb_Cmd = CB_7;
          return ;
        }
      } else {
        current->acc_decoded_resp_counter = 0;
        current->asckedReceiveBytes = 0;
        current->cb_Cmd = CB_TICK;
        current->bComplete = 1;
        return ;
      }
    } else {
      current->numb_reps_errors_allowed--;
      if (current->numb_reps_errors_allowed > 0) {
        return ;
      } else {
        current->cb_Cmd = CB_TICK;
        current->bComplete = 1;
      }
    }
  } else if (current->cb_Cmd == CB_7) {
    SOF();
    pu_Addr(T_IN, current->rq.addr, current->rq.eop);
    //setup
    sendRecieveNParse();
    ACK();
    if (current->asckedReceiveBytes > 0) {
      current->cb_Cmd = CB_6;
      return ;
    }
    current->cb_Cmd = CB_8;
  } else if (current->cb_Cmd == CB_8) {
    SOF();
    pu_Addr(T_OUT, current->rq.addr, current->rq.eop);
    pu_ShortCmd(T_DATA1);
    sendOnly();
    current->cb_Cmd = CB_TICK;
    current->bComplete = 1;
  } else if (current->cb_Cmd == CB_2Ack) {
    SOF();
    pu_Addr(T_IN, current->rq.addr, current->rq.eop);
    //setup
    sendRecieveNParse();
    if (received_NRZI_buffer_bytesCnt < SMALL_NO_DATA / 2) {
      // no data , seems NAK or something like this
      current->cb_Cmd = CB_TICK;
      current->bComplete = 1;
      return ;
    }
    ACK();
    current->cb_Cmd = CB_TICK;
    current->bComplete = 1;
  } else if (current->cb_Cmd == CB_2) {
    SOF();
    pu_Addr(T_IN, current->rq.addr, current->rq.eop);
    //setup
    sendRecieveNParse();
    if (received_NRZI_buffer_bytesCnt < SMALL_NO_DATA / 2) {
      // no data , seems NAK or something like this
      current->cb_Cmd = CB_TICK;
      current->bComplete = 1;
      return ;
    }
    int res = parse_received_NRZI_buffer();
    if (res == T_NEED_ACK) {
      if (decoded_receive_buffer_size() > 2) {
        decoded_receive_buffer_get();
        decoded_receive_buffer_get();
        int bytes = decoded_receive_buffer_size() - 2;
        for (int kk = 0; kk < bytes; kk++) {
          current->acc_decoded_resp[current->acc_decoded_resp_counter] = rev8(decoded_receive_buffer_get());
          current->acc_decoded_resp_counter++;
          current->asckedReceiveBytes--;
        }
      }
      current->asckedReceiveBytes = 0;
      current->cb_Cmd = CB_2Ack;
      return ;
    } else {
      current->numb_reps_errors_allowed--;
      if (current->numb_reps_errors_allowed > 0) {
        return ;
      } else {
        current->cb_Cmd = CB_TICK;
        current->bComplete = 1;
      }
    }
    current->cb_Cmd = CB_TICK;
    current->bComplete = 1;
    current->asckedReceiveBytes = 0;
  }
}



void Request( uint8_t cmd, uint8_t addr, uint8_t eop, uint8_t dataCmd, uint8_t bmRequestType, uint8_t bmRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLen, uint16_t waitForBytes)
{
  current->rq.cmd  = cmd;
  current->rq.addr = addr;
  current->rq.eop  = eop;
  current->rq.dataCmd = dataCmd;
  current->rq.bmRequestType = bmRequestType;
  current->rq.bmRequest = bmRequest;
  current->rq.wValue = wValue;
  current->rq.wIndex = wIndex;
  current->rq.wLen = wLen;

  current->numb_reps_errors_allowed = 4;
  current->asckedReceiveBytes = waitForBytes;
  current->acc_decoded_resp_counter    = 0;
  current->cb_Cmd = CB_5;
}



void RequestSend(uint8_t cmd,   uint8_t addr, uint8_t eop,
                 uint8_t  dataCmd, uint8_t bmRequestType, uint8_t bmRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLen, uint16_t transmitL1Bytes, uint8_t* data)
{
  current->rq.cmd  = cmd;
  current->rq.addr = addr;
  current->rq.eop  = eop;
  current->rq.dataCmd = dataCmd;
  current->rq.bmRequestType = bmRequestType;
  current->rq.bmRequest = bmRequest;
  current->rq.wValue = wValue;
  current->rq.wIndex = wIndex;
  current->rq.wLen = wLen;
  current->transmitL1Bytes = transmitL1Bytes;
  for (int k = 0; k < current->transmitL1Bytes; k++) {
    current->transmitL1[k] = data[k];
  }
  current->numb_reps_errors_allowed = 4;
  current->acc_decoded_resp_counter    = 0;
  current->cb_Cmd = CB_3;
}


void RequestIn(uint8_t cmd,   uint8_t addr, uint8_t eop, uint16_t waitForBytes)
{
  current->rq.cmd  = cmd;
  current->rq.addr = addr;
  current->rq.eop  = eop;
  current->numb_reps_errors_allowed = 4;
  current->asckedReceiveBytes = waitForBytes;
  current->acc_decoded_resp_counter    = 0;
  current->cb_Cmd = CB_2;
}


void (*printDataCB)(uint8_t usbNum, uint8_t byte_depth, uint8_t* data, uint8_t data_len) = NULL;

void set_print_cb( printcb_t cb )
{
  printDataCB = cb;
};



void (*onDetectCB)(uint8_t usbNum, void *device) = NULL;

void set_ondetect_cb( ondetectcb_t cb )
{
  onDetectCB = cb;
}

void (*onLedBlinkCB)(int on_off) = NULL;

void set_onled_blink_cb( onledblinkcb_t cb )
{
  onLedBlinkCB = cb;
}


void fsm_Mashine()
{
  if (!current->bComplete) return;
  current->bComplete = 0;

  if (current->fsm_state == 0) {
    current->epCount = 0;
    current->cb_Cmd     = CB_CHECK;
    current->fsm_state   = 1;
  }
  if (current->fsm_state == 1) {
    if (current->wires_last_state == M_ONE) { // if(1)
      current->cmdTimeOut = 100 + current->selfNum * 73;
      current->cb_Cmd      = CB_WAIT0;
      current->fsm_state   = 2;
    } else {
      current->fsm_state   = 0;
      current->cb_Cmd      = CB_CHECK;
    }
  } else if (current->fsm_state == 2) {
    current->cb_Cmd       = CB_RESET;
    current->fsm_state    = 3;
  } else if (current->fsm_state == 3) {
    current->cb_Cmd       = CB_POWER;
#ifdef TEST
    current->fsm_state    =  3;
#else
    current->fsm_state    =  4;
#endif
  } else if (current->fsm_state == 4) {
    Request(T_SETUP, ZERO_USB_ADDRESS, 0b0000, T_DATA0, 0x80, 0x6, 0x0100, 0x0000, 0x0012, 0x0012);
    current->fsm_state    = 5;
  } else if (current->fsm_state == 5) {
    if (current->acc_decoded_resp_counter == 0x12) {
      memcpy(&current->desc, current->acc_decoded_resp, 0x12);
      current->ufPrintDesc |= 1;
    } else {
      if (current->numb_reps_errors_allowed <= 0) {
        current->fsm_state    =  0;
        return;
      }
    }

    Request(T_SETUP, ZERO_USB_ADDRESS, 0b0000, T_DATA0, 0x00, 0x5, 0x0000 + ASSIGNED_USB_ADDRESS, 0x0000, 0x0000, 0x0000);
    current->fsm_state    =  6;

  } else if (current->fsm_state == 6) {
    current->cmdTimeOut = 5;
    current->cb_Cmd       = CB_WAIT1;
    current->fsm_state    = 7;
  } else if (current->fsm_state == 7) {
    Request(T_SETUP, ASSIGNED_USB_ADDRESS, 0b0000, T_DATA0, 0x80, 0x6, 0x0200, 0x0000, 0x0009, 0x0009);
    current->fsm_state    = 8;
  } else if (current->fsm_state == 8) {
    if (current->acc_decoded_resp_counter == 0x9) {
      memcpy(&current->cfg, current->acc_decoded_resp, 0x9);
      current->ufPrintDesc |= 2;
      Request(T_SETUP, ASSIGNED_USB_ADDRESS, 0b0000, T_DATA0, 0x80, 0x6, 0x0200, 0x0000, current->cfg.wLength, current->cfg.wLength);
      current->fsm_state    = 9;
    } else {
      current->fsm_state    = 0;
      return;
    }
  } else if (current->fsm_state == 9) {
    if (current->acc_decoded_resp_counter == current->cfg.wLength) {
      current->ufPrintDesc |= 4;
      current->descrBufferLen = current->acc_decoded_resp_counter;
      memcpy(current->descrBuffer, current->acc_decoded_resp, current->descrBufferLen);
      current->fsm_state    = 97;
    } else {
      current->cmdTimeOut = 5;
      current->cb_Cmd       = CB_WAIT1;
      current->fsm_state    = 7;
    }
  } else if (current->fsm_state == 97) {
    Request(T_SETUP, ASSIGNED_USB_ADDRESS, 0b0000, T_DATA0, 0x00, 0x9, 0x0001, 0x0000, 0x0000, 0x0000);
    current->fsm_state    = 98;
  } else if (current->fsm_state == 98) {
    // config interfaces??
    Request(T_SETUP, ASSIGNED_USB_ADDRESS, 0b0000, T_DATA0, 0x21, 0xa, 0x0000, 0x0000, 0x0000, 0x0000);
    current->fsm_state    = 99;
  } else if (current->fsm_state == 99) {
    uint8_t cmd1 = 0;
    RequestSend(T_SETUP, ASSIGNED_USB_ADDRESS, 0b0000, T_DATA0, 0x21, 0x9, 0x0200, 0x0000, 0x0001, 0x0001, &cmd1);
    current->fsm_state    = 100;
  } else if (current->fsm_state == 100) {
    if ( onLedBlinkCB ) onLedBlinkCB(0);
    RequestIn(T_IN,  ASSIGNED_USB_ADDRESS, 1, 8);
    current->fsm_state    = 101;
  } else if (current->fsm_state == 101) {
    if (current->acc_decoded_resp_counter >= 1) {
      current->ufPrintDesc |= 8;
      current->R0Bytes = current->acc_decoded_resp_counter;
      memcpy(current->Resp0, current->acc_decoded_resp, current->R0Bytes);
      if ( onLedBlinkCB ) onLedBlinkCB(1);
    }
    if (current->epCount >= 2) {
      RequestIn(T_IN,  ASSIGNED_USB_ADDRESS, 2, 8);
      current->fsm_state    = 102;
    } else {
      current->cmdTimeOut = 3;
      current->cb_Cmd        = CB_WAIT1;
      current->fsm_state      = 104;
    }
  } else if (current->fsm_state == 102) {
    if (current->acc_decoded_resp_counter >= 1) {
      current->ufPrintDesc |= 16;
      current->R1Bytes = current->acc_decoded_resp_counter;
      memcpy(current->Resp1, current->acc_decoded_resp, current->R0Bytes);
    }
    current->cmdTimeOut = 2;
    current->cb_Cmd        = CB_WAIT1;
    current->fsm_state      = 104;
  } else if (current->fsm_state == 104) {
    current->cmdTimeOut = 4;
    current->cb_Cmd        = CB_WAIT1;
#ifdef  DEBUG_REPEAT
    static int rcnt = 0;
    rcnt++;  //
    if (  (rcnt & 0xff) == 0 ||  (current->wires_last_state != M_ONE))
#else
    if (current->wires_last_state != M_ONE)
#endif
    {
      current->fsm_state      = 0;
      return ;
    }
    current->fsm_state      = 100;
  } else {
    current->cmdTimeOut = 2;
    current->cb_Cmd        = CB_WAIT1;
    current->fsm_state      = 0;
  }
}



void setPins(int DPPin, int DMPin)
{
  DP_PIN = DPPin;
  DM_PIN = DMPin;
  int diff = DPPin - DMPin;
  if (abs(diff) > 7) {
    printf("PIN DIFFERENCE MUST BE LESS 8!\n");
    return;
  }
  int MIN_PIN = (DPPin < DMPin) ? DPPin : DMPin;

  DM_PIN_M   = (1 << DMPin);
  DP_PIN_M    = (1 << DPPin);
  RD_MASK    = (1 << DPPin) | (1 << DMPin);
  int DIFF = MIN_PIN - 8;
  if (DIFF >= 0) {
    RD_SHIFT = DIFF;
    M_ONE = 1 << (DM_PIN - MIN_PIN);
    P_ONE = 1 << (DP_PIN - MIN_PIN);
  } else {
    RD_SHIFT = -DIFF;
    M_ONE = 2;
    P_ONE = 1;
  }
}


sUsbContStruct  current_usb[NUM_USB];


int checkPins(int dp, int dm)
{
  int diff = abs(dp - dm);
  if (diff > 7 || diff == 0) {
    return 0;
  }
  if ( dp < 8 || dp > 31) return 0;
  if ( dm < 8 || dm > 31) return 0;

  return 1;
}


float testDelay6(float freq_MHz)
{
  // 6 bits must take 4.0 uSec
#define SEND_BITS  120
  float res = 1;
  transmit_NRZI_buffer_cnt = 0;

  for (int k = 0; k < SEND_BITS / 2; k++) {
    transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_K;
    transmit_NRZI_buffer[transmit_NRZI_buffer_cnt++] = USB_LS_J;
  }
  uint32_t stim = _getCycleCount32();
  sendOnly();
  stim =  _getCycleCount32() - stim;
  res = stim * 6.0 / freq_MHz / SEND_BITS;
  printf("%d bits in %f uSec %f MHz  6 ticks in %f uS\n", SEND_BITS, stim / (float)freq_MHz, SEND_BITS * freq_MHz / stim, stim * 6.0 / freq_MHz / SEND_BITS);

  return res;
}

uint8_t arr[0x200];


void initStates(int DP0, int DM0, int DP1, int DM1, int DP2, int DM2, int DP3, int DM3)
{
  decoded_receive_buffer_head = 0;
  decoded_receive_buffer_tail = 0;
  transmit_bits_buffer_store_cnt = 0;

  int calibrated = 0;

  for (int k = 0; k < NUM_USB; k++) {
    current = &current_usb[k];
    if (k == 0) {
      current->DP = DP0;
      current->DM = DM0;
    } else if (k == 1) {
      current->DP = DP1;
      current->DM = DM1;
    } else if (k == 2) {
      current->DP = DP2;
      current->DM = DM2;
    } else if (k == 3) {
      current->DP = DP3;
      current->DM = DM3;
    }
    current->isValid = 0;
    if (checkPins(current->DP, current->DM)) {
      printf("USB#%d (pins %d %d) is OK!\n", k, current->DP, current->DM );
      current->selfNum = k;
      current->in_data_flip_flop       = 0;
      current->bComplete  = 1;
      current->cmdTimeOut = 0;
      current->ufPrintDesc = 0;
      current->cb_Cmd     = CB_CHECK;
      current->fsm_state  = 0;
      current->wires_last_state = 0;
      current->counterNAck = 0;
      current->counterAck = 0;
      current->epCount = 0;
      gpio_pad_select_gpio(current->DP);
      gpio_set_direction(current->DP, GPIO_MODE_INPUT);
      gpio_pulldown_en(current->DP);

      gpio_pad_select_gpio(current->DM);
      gpio_set_direction(current->DM, GPIO_MODE_INPUT);
      gpio_pulldown_en(current->DM);
      current->isValid = 1;

      // TEST
      setPins(current->DP, current->DM);

      if (!calibrated) {
        //calibrate delay divide 2
        int  uTime = 254;
        int  dTime = 0;

        rtc_cpu_freq_config_t  out_config;

        rtc_clk_cpu_freq_get_config(&out_config);
        printf("cpu freq = %d MHz\n", out_config.freq_mhz);

        TM_OUT = out_config.freq_mhz / 2;

        // 8  - func divided clock to 8, 1.5 - MHz USB LS
        TIME_MULT = (int)(TIME_SCALE / (out_config.freq_mhz / 8 / 1.5) + 0.5);
        printf("TIME_MULT = %d \n", TIME_MULT);

        int     TRANSMIT_TIME_DELAY_OPT = 0;
        TRANSMIT_TIME_DELAY = TRANSMIT_TIME_DELAY_OPT;
        setDelay(TRANSMIT_TIME_DELAY);
        float  cS_opt = testDelay6(out_config.freq_mhz);
#define OPT_TIME (4.00f)
        for (int p = 0; p < 9; p++) {
          TRANSMIT_TIME_DELAY = (uTime + dTime) / 2;
          setDelay(TRANSMIT_TIME_DELAY);
          float cS = testDelay6(out_config.freq_mhz);
          if (fabsf(OPT_TIME - cS) < fabsf(OPT_TIME - cS_opt)) {
            cS_opt = cS;
            TRANSMIT_TIME_DELAY_OPT = TRANSMIT_TIME_DELAY;
          }
          if (cS < OPT_TIME) {
            dTime = TRANSMIT_TIME_DELAY;
          } else {
            uTime = TRANSMIT_TIME_DELAY;
          }
        }
        TRANSMIT_TIME_DELAY = TRANSMIT_TIME_DELAY_OPT;
        setDelay(TRANSMIT_TIME_DELAY);
        printf("TRANSMIT_TIME_DELAY = %d time = %f error = %f%% \n", TRANSMIT_TIME_DELAY, cS_opt, (cS_opt - OPT_TIME) / OPT_TIME * 100);
      }
    } else {
      if ( current->DP == -1 && current->DM == -1 ) {
        printf("USB#%d is disabled by user configuration\n", k);
      } else {
        printf("USB#%d (pins %d %d) has errors and will be disabled !\n", k, current->DP, current->DM );
      }
    }
  }
}



void usb_process()
{
  for (int k = 0; k < NUM_USB; k++) {
    current = &current_usb[k];
    if (current->isValid) {
      setPins(current->DP, current->DM);
      timerCallBack();
      fsm_Mashine();
    }
  }
}


void printState()
{
  static int cntl = 0;
  cntl++;
  int ref = cntl % NUM_USB;
  sUsbContStruct * pcurrent = &current_usb[ref];
  if (!pcurrent->isValid) return ;
  if ((cntl % 200) < NUM_USB) {

    //--------------------------------------------------------------------------------
    if (DEBUGEXTRA_VAR == 1)
      printf("USB%d: Ack = %d Nack = %d %02x pcurrent->cb_Cmd = %d  state = %d epCount = %d \n",
             cntl % NUM_USB, pcurrent->counterAck,
             pcurrent->counterNAck,
             pcurrent->wires_last_state,
             pcurrent->cb_Cmd,
             pcurrent->fsm_state,
             pcurrent->epCount
            );
    if (pcurrent->wires_last_state == M_ONE &&  new_device == 1) {
      printf("USB 1.1 LOW SPEED (1.5Mbit/s) DEVICE DETECTED... supported. \n\n");
      new_device = 0;
    }
    if (pcurrent->wires_last_state == P_ONE &&  new_device == 1) {
      printf("USB 1.1 FULL SPEED (12Mbit/s) DEVICE DETECTED... not suported. \n\n");
      new_device = 0;
    }

    //--------------------------------------------------------------------------------

#ifdef DEBUG_ALL
    for (int k = 0; k < 20; k++) {
      printf("%04x ", debug_buff[k]);
    }
    printf("\n");
#endif

  }

  if (pcurrent->ufPrintDesc & 1) {
    pcurrent->ufPrintDesc &= ~(uint32_t)1;
    if ( onDetectCB ) {
      onDetectCB( ref, (void*)&pcurrent->desc );
    } else {
      printf("desc.bcdDevice       = %02x\n", pcurrent->desc.bcdDevice);
      printf("desc.iManufacturer   = %02x\n", pcurrent->desc.iManufacturer);
      printf("desc.iProduct        = %02x\n", pcurrent->desc.iProduct);
      printf("desc.iSerialNumber   = %02x\n", pcurrent->desc.iSerialNumber);
      printf("desc.bNumConfigurations = %02x\n", pcurrent->desc.bNumConfigurations);
    }

  }

  if (pcurrent->ufPrintDesc & 2) {
    pcurrent->ufPrintDesc &= ~(uint32_t)2;
  }

  if (pcurrent->ufPrintDesc & 8) {
    pcurrent->ufPrintDesc &= ~(uint32_t)8;

    if ( printDataCB ) {
      printDataCB( ref, 8, pcurrent->Resp0, pcurrent->R0Bytes );
    } else {
      printf("in0 :");
      for (int k = 0; k < pcurrent->R0Bytes; k++) {
        printf("%02x ", pcurrent->Resp0[k]);
      }
      printf("\n");
    }
  }

  if (pcurrent->ufPrintDesc & 16) {
    pcurrent->ufPrintDesc &= ~(uint32_t)16;

    if ( printDataCB ) {
      printDataCB( ref, 16, pcurrent->Resp1, pcurrent->R1Bytes );
    } else {
      printf("in1 :");
      for (int k = 0; k < pcurrent->R1Bytes; k++) {
        printf("%02x ", pcurrent->Resp1[k]);
      }
      printf("\n");
    }
  }

  if (pcurrent->ufPrintDesc & 4) {
    pcurrent->ufPrintDesc &= ~(uint32_t)4;
    sCfgDesc lcfg;
    sIntfDesc sIntf;
    HIDDescriptor hid[4];
    sEPDesc epd;
    int cfgCount   = 0;
    int sIntfCount   = 0;
    int hidCount   = 0;
    int pos = 0;
#define STDCLASS        0x00
#define HIDCLASS        0x03
#define HUBCLASS     0x09      /* bDeviceClass, bInterfaceClass */
#ifdef DEBUG_ALL
    printf("clear epCount %d self = %d\n", pcurrent->epCount, pcurrent->selfNum);
#endif
    pcurrent->epCount     = 0;
    while (pos < pcurrent->descrBufferLen - 2) {
      uint8_t len  =  pcurrent->descrBuffer[pos];
      uint8_t type =  pcurrent->descrBuffer[pos + 1];
      if (len == 0) {
        pos = pcurrent->descrBufferLen;
      }
      if (pos + len <= pcurrent->descrBufferLen) {
        //printf("\n");
        if (type == 0x2) {
          sCfgDesc cfg;
          memcpy(&cfg, &pcurrent->descrBuffer[pos], len);
          printf("cfg.wLength         = %02x\n", cfg.wLength);
          printf("cfg.bNumIntf        = %02x\n", cfg.bNumIntf);
          printf("cfg.bCV             = %02x\n", cfg.bCV);
          printf("cfg.bMaxPower       = %d\n", cfg.bMaxPower);
        } else if (type == 0x4) {
          sIntfDesc sIntf;
          memcpy(&sIntf, &pcurrent->descrBuffer[pos], len);
        } else if (type == 0x21) {
          hidCount++;
          int i = hidCount - 1;
          memcpy(&hid[i], &pcurrent->descrBuffer[pos], len);
        } else if (type == 0x5) {
          pcurrent->epCount++;
          sEPDesc epd;
          memcpy(&epd, &pcurrent->descrBuffer[pos], len);
#ifdef DEBUG_ALL
          printf("pcurrent->epCount = %d\n", pcurrent->epCount);
          printf("epd.bLength       = %02x\n", epd.bLength);
          printf("epd.bType         = %02x\n", epd.bType);
          printf("epd.bEPAdd        = %02x\n", epd.bEPAdd);
          printf("epd.bAttr         = %02x\n", epd.bAttr);
          printf("epd.wPayLoad      = %02x\n", epd.wPayLoad);
          printf("epd.bInterval     = %02x\n", epd.bInterval);
#endif
        }
      }
      pos += len;
    }
  }
}


#pragma GCC diagnostic pop

ส่วนที่เขียนเพิ่มสามารถศึกษาได้จากไฟล์ตัวอย่างที่ผู้เขียนไลบรารีได้แนบมาด้วย ซึ่งทีมเราสรุปหลักการคร่าว ๆ ได้ดังนี้

  • ต้องเรียก USH.init() เพื่อระบุฟังก์ชันสำหรับตอบสนองต่อการเริ่มต้นทำงานตรวจสอบอุปกรณ์ที่เสียบเข้ากับพอร์ต และฟังก์ชันที่ใช้ในการตอบสนองเมื่อเกิดเหตุการณ์อับอุปกรณ์ที่เชื่อมต่อ
  • ฟังก์ชันที่ตอบสนองต่อการเสียบอุปกรณ์เข้ากับพอร์ต ในที่นี้ใช้ชื่อตามต้นฉบับคือ my_USB_DetectCB() จะถูกเรียกเมื่อมีการเสียบอุปกรณ์เข้ากับพอร์ต ซึ่งตัวอย่างเมื่อยังไม่ได้เสียบอุปรกรณ์ทางทีมงานเราได้กำหนดให้แสดงผลดังภาพที่ 3 และเมื่อฟังก์ชันนี้ถูกเรียกใช้ได้สั่งให้เปลี่ยนการแสดงผลจึงได้ผลลัพธ์ดังภาพที่ 4
ภาพที่ 3 การแสดงผลเมื่อตอนเริ่มทำงานและไม่พบอุปกรณ์ที่พอร์ต USB
ภาพที่ 4 ตัวอย่างผลลัพธ์หลังจากพบการเสียบอุปกรณ์เข้ากับพอร์ต USB
  • ฟังก์ชันตอบสนองการรับข้อมูลจากอุปกรณ์ ในที่นี้ใช้ชื่อฟังก์ชันเป็น my_USB_PrintCB() ตามต้นฉบับ โดยในฟังก์ชันนี้มีการตรวจสอบความยาวของข้อมูลโดยถ้าข้อมูลยาว 8 ไบต์ นั่นเป็นอุปกรณ์แป้นพิมพ์ แต่ถ้ายาว 4 เป็นของเมาส์ ในตัวอย่างที่เขียขึ้นได้เพิ่มการตรวจสอบสถานะของแป้น CTRL และ Shift ด้านซ้ายว่าถูกกดอยู่หรือไม่ ถ้ากดจะแสดงข้อความดังภาพที่ 5 และ 6 แต่ถ้าไม่ได้กดจะไม่ปรากฏข้อความดังในตัวอย่างที่ 4 ส่วนกรณีที่กดแป้นอื่น ๆ ที่เป็นตัวอักษรจะให้แสดงสถานะว่าแป้นนั้นถูกกดและถูกปล่อยดังภาพที่ 7
ภาพที่ 5 ตัวอย่างผลลัพธ์เมื่อกดแป้น CTRL ด้านซ้าย
ภาพที่ 6 ตัวอย่างผลลัพธ์เมื่อกดแป้น Shift ด้านซ้าย
ภาพที่ 7ตัวอย่างผลลัพธ์เมื่อกดแป้นอื่น ๆ

โค้ดตัวอย่างที่ปรับปรุงแล้วเป็นดังนี้

#define U8G2_WITH_UNICODE
//#define DEBUG
//#define DEBUGEXTRA


#include <Arduino.h>
#include <U8g2lib.h>

extern "C" {
#ifdef DEBUGEXTRA
  uint8_t DEBUGEXTRA_VAR = 1;
#else
  uint8_t DEBUGEXTRA_VAR = 0;
#endif
}



#include "ESP32-USBSoftHost.hpp"
#include "usbkbd.h"                 // KeyboardReportParser

//================================================================================

#define DP_P0  18  // Data+
#define DM_P0  19  // Data-
#define DP_P1  -1  // not used
#define DM_P1  -1  // not used
#define DP_P2  -1  // not used
#define DM_P2  -1  // not used
#define DP_P3  -1  // not used
#define DM_P3  -1  // not used

usb_pins_config_t USB_Pins_Config =
{
  DP_P0, DM_P0,
  DP_P1, DM_P1,
  DP_P2, DM_P2,
  DP_P3, DM_P3
};


//********************************************************************************
uint8_t essential_key = 0;
uint8_t lastkey[6] = {0}; ///max 6 keys can be pressed
byte* keymap;
//********************************************************************************
//EMULATE JOYSTICK BY KEYBOARDS:
uint8_t KEY_UP = 0;      /// ↑
uint8_t KEY_DOWN = 0;    /// ↓
uint8_t KEY_LEFT = 0;    /// ←
uint8_t KEY_RIGHT = 0;   /// →
uint8_t KEY_CROSS = 0;   /// X
uint8_t KEY_SQUARE = 0;  /// □
uint8_t KEY_SHARE = 0;   /// START
uint8_t KEY_OPTIONS = 0; /// SELECT
uint8_t KEY_CIRCLE = 0;  /// O
uint8_t KEY_TRIANGLE = 0; /// Δ
//********************************************************************************
const char keycharmap[256][6] = {
  " ", "ESC", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "PRINT", "SCRLL", "PAUSE",
  "~", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "BACKS", "INSER", "HOME", "PUP", "NUMLC", "NUM /", "NUM *", "NUM -",
  "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\", "DEL", "END", "PDOWN", "NUM 7", "NUM 8", "NUM 9", "NUM +",
  "CAPS", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "ENTER", "NUM 4", "NUM 5", "NUM 6",
  "SHIFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT", "UP", "NUM 1", "NUM 2", "NUM 3", "ENTER",
  "CTRL", "ALT", "SPACE", "ALT", "CTRL", "LEFT", "DOWN", "RIGHT", "NUM 0", "NUM .", "GUI", "GUI", "MENU"
};
//********************************************************************************
uint8_t mouse_button = 0;
uint8_t mouse_left_button = 0;
uint8_t mouse_right_button = 0;
uint8_t mouse_middle_button = 0;
int16_t mouse_X_POSITION = 0;
int16_t mouse_Y_POSITION = 0;
int16_t mouse_Z_POSITION = 0;
//********************************************************************************

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);


//================================================================================

bool usbActived = false;
void setup() {


  u8g2.begin();

  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_etl14thai_t );
  u8g2.drawUTF8(10, 10, "ESP32-USB-KB");
  u8g2.drawUTF8(4, 30, "> USB init");
  u8g2.sendBuffer();

  Serial.begin(115200);
  keymap = (byte*)malloc(256); ///malloc array !
  for (int idx = 0; idx < 256; idx++) {
    keymap[idx] = 0;
  }
  delay(200);
  Serial.println("USB init: ");
  delay(1000);

  USH.init( USB_Pins_Config, my_USB_DetectCB, my_USB_PrintCB);
}

void clrScr() {
  u8g2.setDrawColor(0);
  u8g2.drawBox(4, 30, 120, 20);
  u8g2.setDrawColor(1);
}

void loop() {
  clrScr();
  if (keymap[0x5c]) {
    u8g2.drawUTF8(4, 50, ">KB.L-CTRL");
  }
  if (keymap[0x4b]) {
    u8g2.drawUTF8(4, 50, ">KB.L-SHIFT");
  }
  u8g2.sendBuffer();
}



//================================================================================
static void my_USB_DetectCB( uint8_t usbNum, void * dev )
{
  sDevDesc *device = (sDevDesc*)dev;

  u8g2.setDrawColor(0);
  u8g2.drawBox(4, 10, 120, 20);
  u8g2.setDrawColor(1);
  u8g2.drawUTF8(4, 30, "> USB Ready");
  u8g2.sendBuffer();


#ifdef DEBUG
  printf("New device detected on USB#%d\n", usbNum);
  printf("desc.bcdUSB             = 0x%04x\n", device->bcdUSB);
  printf("desc.bDeviceClass       = 0x%02x\n", device->bDeviceClass);
  printf("desc.bDeviceSubClass    = 0x%02x\n", device->bDeviceSubClass);
  printf("desc.bDeviceProtocol    = 0x%02x\n", device->bDeviceProtocol);
  printf("desc.bMaxPacketSize0    = 0x%02x\n", device->bMaxPacketSize0);
  printf("desc.idVendor           = 0x%04x\n", device->idVendor);
  printf("desc.idProduct          = 0x%04x\n", device->idProduct);
  printf("desc.bcdDevice          = 0x%04x\n", device->bcdDevice);
  printf("desc.iManufacturer      = 0x%02x\n", device->iManufacturer);
  printf("desc.iProduct           = 0x%02x\n", device->iProduct);
  printf("desc.iSerialNumber      = 0x%02x\n", device->iSerialNumber);
  printf("desc.bNumConfigurations = 0x%02x\n", device->bNumConfigurations);
  // if( device->iProduct == mySupportedIdProduct && device->iManufacturer == mySupportedManufacturer ) {
  //   myListenUSBPort = usbNum;
  // }
#endif
}

char msg[32];

static void my_USB_PrintCB(uint8_t usbNum, uint8_t byte_depth, uint8_t* data, uint8_t data_len)
{
  //--------------------------------------------------------------------------------
  if (data_len == 8) { //it is keyboard
    //--------------------------------------------------------------------------------
    if ((data[0] & 1) == 1 && (essential_key & 1) != 1) {
      essential_key = data[0];
      keymap[0x5c] = 1; // LEFT CTRL

    } else if ((essential_key & 1) == 1 && (data[0] & 1) != 1) {
      essential_key = data[0];
      keymap[0x5c] = 0; // LEFT CTRL
    }
    if ((data[0] & 2) == 2 && (essential_key & 2) != 2) {
      essential_key = data[0];
      keymap[0x4b] = 1; // LEFT SHIFT
    } else if ((essential_key & 2) == 2 && (data[0] & 2) != 2) {
      essential_key = data[0];
      keymap[0x4b] = 0; // LEFT SHIFT
    }
    if ((data[0] & 4) == 4 && (essential_key & 4) != 4) {
      essential_key = data[0];
      keymap[0x5d] = 1; // LEFT ALT
    } else if ((essential_key & 4) == 4 && (data[0] & 4) != 4) {
      essential_key = data[0];
      keymap[0x5d] = 0; // LEFT ALT
    }
    if ((data[0] & 8) == 8 && (essential_key & 8) != 8) {
      essential_key = data[0];
      keymap[0x66] = 1; // LEFT GUI
    } else if ((essential_key & 8) == 8 && (data[0] & 8) != 8) {
      essential_key = data[0];
      keymap[0x66] = 0; // LEFT GUI
    }
    if ((data[0] & 0x10) == 0x10 && (essential_key & 0x10) != 0x10) {
      essential_key = data[0];
      keymap[0x60] = 1; // RIGHT CTRL
    } else if ((essential_key & 0x10) == 0x10 && (data[0] & 0x10) != 0x10) {
      essential_key = data[0];
      keymap[0x60] = 0; // RIGHT CTRL
    }
    if ((data[0] & 0x20) == 0x20 && (essential_key & 0x20) != 0x20) {
      essential_key = data[0];
      keymap[0x56] = 1; // RIGHT SHIFT
    } else if ((essential_key & 0x20) == 0x20 && (data[0] & 0x20) != 0x20) {
      essential_key = data[0];
      keymap[0x56] = 0; // RIGHT SHIFT
    }
    if ((data[0] & 0x40) == 0x40 && (essential_key & 0x40) != 0x40) {
      essential_key = data[0];
      keymap[0x5f] = 1; // RIGHT ALT
    } else if ((essential_key & 0x40) == 0x40 && (data[0] & 0x40) != 0x40) {
      essential_key = data[0];
      keymap[0x5f] = 0; // RIGHT ALT
    }
    if ((data[0] & 0x80) == 0x80 && (essential_key & 0x80) != 0x80) {
      essential_key = data[0];
      keymap[0x67] = 1; // RIGHT GUI
    } else if ((essential_key & 0x80) == 0x80 && (data[0] & 0x80) != 0x80) {
      essential_key = data[0];
      keymap[0x67] = 0; // RIGHT GUI

    }
    //--------------------------------------------------------------------------------
    for (int8_t tmp = 0; tmp < sizeof(lastkey); tmp++) {

      if (data[tmp + 2] != 1) {
        if (lastkey[tmp] != data[tmp + 2] && lastkey[tmp] == 0) {
          //pressed key
          lastkey[tmp] = data[tmp + 2];
          keypress(lastkey[tmp]); // <----PRESSED KEY
          printf("pressed --> %s\n", keycharmap[keypress(lastkey[tmp])]);
        } else if (lastkey[tmp] != data[tmp + 2] && data[tmp + 2] != 0) {
          if (lastkey[tmp + 1] == data[tmp + 2]) {
            keyrelease(lastkey[tmp]); // RELEASED KEY
            printf("released --> %s\n", keycharmap[keypress(lastkey[tmp])]);
            for (int8_t i = tmp; i < sizeof(lastkey) - 1; i++) lastkey[i] = lastkey[i + 1];
          }
        } else if (data[tmp + 2] == 0 && lastkey[tmp] != 0) {
          //released key
          keyrelease(lastkey[tmp]); // RELEASED KEY
          printf("released --> %s\n", keycharmap[keypress(lastkey[tmp])]);
          lastkey[tmp] = 0;
        }
      } else {

        for (uint16_t tmp = 0; tmp < 256; tmp++) keymap[tmp] = 0; // RELEASE ALL KEYS!
      }
    }
    //--------------------------------------------------------------------------------
  } else if (data_len == 4) { //it is mouse
    if ((data[0] & 1) == 1 && (mouse_button & 1) != 1) {
      mouse_button = data[0];
      mouse_left_button = 1;
#ifdef DEBUG
      printf("LEFT MOUSE BUTTON PRESSED\n");
#endif
    } else if ((mouse_button & 1) == 0 && (data[0] & 1) != 0) {
      mouse_button = data[0];
      mouse_left_button = 0;
#ifdef DEBUG
      printf("LEFT MOUSE BUTTON RELEASED\n");
#endif
    }
    if ((data[0] & 2) == 2 && (mouse_button & 2) != 2) {
      mouse_button = data[0];
      mouse_right_button = 1;
#ifdef DEBUG
      printf("RIGHT MOUSE BUTTON PRESSED\n");
#endif
    } else if ((mouse_button & 2) == 0 && (data[0] & 2) != 0) {
      mouse_button = data[0];
      mouse_right_button = 0;
#ifdef DEBUG
      printf("RIGHT MOUSE BUTTON RELEASED\n");
#endif
    }
    if ((data[0] & 4) == 4 && (mouse_button & 4) != 4) {
      mouse_button = data[0];
      mouse_middle_button = 1;
#ifdef DEBUG
      printf("MIDDLE MOUSE BUTTON PRESSED\n");
#endif
    } else if ((mouse_button & 4) == 0 && (data[0] & 4) != 0) {
      mouse_button = data[0];
      mouse_middle_button = 0;
#ifdef DEBUG
      printf("MIDDLE MOUSE BUTTON RELEASED\n");
#endif
    }
    //--------------------------------------------------------------------------------
    if (data[1] != 0) { //X POSITION
      if ((uint8_t)data[1] > 0 && (uint8_t)data[1] < 128 ) { // +X
        mouse_X_POSITION += (uint8_t)data[1];
#ifdef DEBUG
        printf("MOUSE X POSITION: %d [+%d]\n", mouse_X_POSITION, data[1]);
#endif
      } else if ((uint8_t)data[1] > 127 && (uint8_t)data[1] < 256 ) { // -X
        mouse_X_POSITION -= (256 - (uint8_t)data[1]);
#ifdef DEBUG
        printf("MOUSE X POSITION: %d [-%d]\n", mouse_X_POSITION, (256 - data[1]));
#endif
      }
    }
    //--------------------------------------------------------------------------------
    if (data[2] != 0) { //Y POSITION
      if ((uint8_t)data[2] > 0 && (uint8_t)data[2] < 128 ) { // +Y
        mouse_Y_POSITION += (uint8_t)data[2];
#ifdef DEBUG
        printf("MOUSE Y POSITION: %d [+%d]\n", mouse_Y_POSITION, data[2]);
#endif
      } else if ((uint8_t)data[2] > 127 && (uint8_t)data[2] < 256 ) { // -Y
        mouse_Y_POSITION -= (256 - (uint8_t)data[2]);
#ifdef DEBUG
        printf("MOUSE Y POSITION: %d [-%d]\n", mouse_Y_POSITION, (256 - data[2]));
#endif
      }
    }
    //--------------------------------------------------------------------------------
    if (data[3] != 0) { //Z POSITION
      if ((uint8_t)data[3] > 0 && (uint8_t)data[3] < 128 ) { // +Z
        mouse_Z_POSITION += (uint8_t)data[3];
#ifdef DEBUG
        printf("MOUSE Z POSITION: %d [+%d]\n", mouse_Z_POSITION, data[3]);
#endif
      } else if ((uint8_t)data[3] > 127 && (uint8_t)data[3] < 256 ) { // -Z
        mouse_Z_POSITION -= (256 - (uint8_t)data[3]);
#ifdef DEBUG
        printf("MOUSE Z POSITION: %d [-%d]\n", mouse_Z_POSITION, (256 - data[3]));
#endif
      }
      //--------------------------------------------------------------------------------
    }
    //--------------------------------------------------------------------------------
  } else { //I do not know what is it, this device?

  }
  //--------------------------------------------------------------------------------
#ifdef DEBUG
  printf("\n");
#endif
}

//********************************************************************************
uint8_t keypress(uint8_t scancode) {
  switch (scancode) {
    case 0x29: // ESC
      keymap[1] = 1;
      return (1);
      break;
    case 0x3a: // F1
      keymap[2] = 1;
      return (2);
      break;
    case 0x3b: // F2
      keymap[3] = 1;
      return (3);
      break;
    case 0x3c: // F3
      keymap[4] = 1;
      return (4);
      break;
    case 0x3d: // F4
      keymap[5] = 1;
      return (5);
      break;
    case 0x3e: // F5
      keymap[6] = 1;
      return (6);
      break;
    case 0x3f: // F6
      keymap[7] = 1;
      return (7);
      break;
    case 0x40: // F7
      keymap[8] = 1;
      return (8);
      break;
    case 0x41: // F8
      keymap[9] = 1;
      return (9);
      break;
    case 0x42: // F9
      keymap[0x0a] = 1;
      return (0x0a);
      break;
    case 0x43: // F10
      keymap[0x0b] = 1;
      return (0x0b);
      break;
    case 0x44: // F11
      keymap[0x0c] = 1;
      return (0x0c);
      break;
    case 0x45: // F12
      keymap[0x0d] = 1;
      return (0x0d);
      break;
    case 0x46: // PrintScreen
      keymap[0x0e] = 1;
      return (0x0e);
      break;
    case 0x47: // ScrollLock
      keymap[0x0f] = 1;
      return (0x0f);
      break;
    case 0x48: // Pause/Break
      keymap[0x10] = 1;
      return (0x10);
      break;
    case 0x35: // ~
      keymap[0x11] = 1;
      return (0x11);
      break;
    case 0x1e: // 1
      keymap[0x12] = 1;
      return (0x12);
      break;
    case 0x1f: // 2
      keymap[0x13] = 1;
      return (0x13);
      break;
    case 0x20: // 3
      keymap[0x14] = 1;
      return (0x14);
      break;
    case 0x21: // 4
      keymap[0x15] = 1;
      return (0x15);
      break;
    case 0x22: // 5
      keymap[0x16] = 1;
      return (0x16);
      break;
    case 0x23: // 6
      keymap[0x17] = 1;
      return (0x17);
      break;
    case 0x24: // 7
      keymap[0x18] = 1;
      return (0x18);
      break;
    case 0x25: // 8
      keymap[0x19] = 1;
      return (0x19);
      break;
    case 0x26: // 9
      keymap[0x1a] = 1;
      return (0x1a);
      break;
    case 0x27: // 0
      keymap[0x1b] = 1;
      return (0x1b);
      break;
    case 0x2d: // -
      keymap[0x1c] = 1;
      return (0x1c);
      break;
    case 0x2e: // =
      keymap[0x1d] = 1;
      return (0x1d);
      break;
    case 0x2a: // BACKSPACE
      keymap[0x1e] = 1;
      return (0x1e);
      break;
    case 0x2b: // TAB
      keymap[0x26] = 1;
      return (0x26);
      break;
    case 0x14: // Q
      keymap[0x27] = 1;
      return (0x27);
      break;
    case 0x1a: // W
      keymap[0x28] = 1;
      return (0x28);
      break;
    case 0x08: // E
      keymap[0x29] = 1;
      return (0x29);
      break;
    case 0x15: // R
      keymap[0x2a] = 1;
      return (0x2a);
      break;
    case 0x17: // T
      keymap[0x2b] = 1;
      return (0x2b);
      break;
    case 0x1c: // Y
      keymap[0x2c] = 1;
      return (0x2c);
      break;
    case 0x18: // U
      keymap[0x2d] = 1;
      return (0x2d);
      break;
    case 0x0c: // I
      keymap[0x2e] = 1;
      return (0x2e);
      break;
    case 0x12: // O
      keymap[0x2f] = 1;
      return (0x2f);
      break;
    case 0x13: // P
      keymap[0x30] = 1;
      return (0x30);
      break;
    case 0x2f: // {
      keymap[0x31] = 1;
      return (0x31);
      break;
    case 0x30: // }
      keymap[0x32] = 1;
      return (0x32);
      break;
    case 0x32: // \\
      keymap[0x33] = 1;
      return (0x33);
      break;
    case 0x39: // CAPSLOCK
      keymap[0x3b] = 1;
      return (0x3b);
      break;
    case 0x04: // A
      keymap[0x3c] = 1;
      return (0x3c);
      break;
    case 0x16: // S
      keymap[0x3d] = 1;
      return (0x3d);
      break;
    case 0x07: // D
      keymap[0x3e] = 1;
      return (0x3e);
      break;
    case 0x09: // F
      keymap[0x3f] = 1;
      return (0x3f);
      break;
    case 0x0a: // G
      keymap[0x40] = 1;
      return (0x40);
      break;
    case 0x0b: // H
      keymap[0x41] = 1;
      return (0x41);
      break;
    case 0x0d: // J
      keymap[0x42] = 1;
      return (0x42);
      break;
    case 0x0e: // K
      keymap[0x43] = 1;
      return (0x43);
      break;
    case 0x0f: // L
      keymap[0x44] = 1;
      return (0x44);
      break;
    case 0x33: // ;
      keymap[0x45] = 1;
      return (0x45);
      break;
    case 0x34: // '
      keymap[0x46] = 1;
      return (0x46);
      break;
    case 0x28: // ENTER
      keymap[0x47] = 1;
      return (0x47);
      break;
    case 0x1d: // Z
      keymap[0x4c] = 1;
      return (0x4c);
      break;
    case 0x1b: // X
      keymap[0x4d] = 1;
      return (0x4d);
      break;
    case 0x06: // C
      keymap[0x4e] = 1;
      return (0x4e);
      break;
    case 0x19: // V
      keymap[0x4f] = 1;
      return (0x4f);
      break;
    case 0x05: // B
      keymap[0x50] = 1;
      return (0x50);
      break;
    case 0x11: // N
      keymap[0x51] = 1;
      return (0x51);
      break;
    case 0x10: // M
      keymap[0x52] = 1;
      return (0x52);
      break;
    case 0x36: // ,
      keymap[0x53] = 1;
      return (0x53);
      break;
    case 0x37: // .
      keymap[0x54] = 1;
      return (0x54);
      break;
    case 0x38: // /
      keymap[0x55] = 1;
      return (0x55);
      break;
    case 0x2c: // SPACEBAR
      keymap[0x5e] = 1;
      return (0x5e);
      break;
    case 0x65: // CONTEXT BUTTON
      keymap[0x68] = 1;
      return (0x68);
      break;
    case 0x49: // INSERT
      keymap[0x1f] = 1;
      return (0x1f);
      break;
    case 0x4a: // HOME
      keymap[0x20] = 1;
      return (0x20);
      break;
    case 0x4b: // PAGEUP
      keymap[0x21] = 1;
      return (0x21);
      break;
    case 0x4c: // DELETE
      keymap[0x34] = 1;
      return (0x34);
      break;
    case 0x4d: // END
      keymap[0x35] = 1;
      return (0x35);
      break;
    case 0x4e: // PAGEDOWN
      keymap[0x36] = 1;
      return (0x36);
      break;
    case 0x52: // UP
      keymap[0x57] = 1;
      return (0x57);
      break;
    case 0x50: // LEFT
      keymap[0x61] = 1;
      return (0x61);
      break;
    case 0x51: // DOWN
      keymap[0x62] = 1;
      return (0x62);
      break;
    case 0x4f: // RIGHT
      keymap[0x63] = 1;
      return (0x63);
      break;
    case 0x53: // NUMLOCK
      keymap[0x22] = 1;
      return (0x22);
      break;
    case 0x54: // NUM /
      keymap[0x23] = 1;
      return (0x23);
      break;
    case 0x55: // NUM *
      keymap[0x24] = 1;
      return (0x24);
      break;
    case 0x56: // NUM -
      keymap[0x25] = 1;
      return (0x25);
      break;
    case 0x57: // NUM +
      keymap[0x3a] = 1;
      return (0x3a);
      break;
    case 0x58: // NUM ENTER
      keymap[0x5b] = 1;
      return (0x5b);
      break;
    case 0x63: // NUM .
      keymap[0x65] = 1;
      return (0x65);
      break;
    case 0x62: // NUM 0
      keymap[0x64] = 1;
      return (0x64);
      break;
    case 0x59: // NUM 1
      keymap[0x58] = 1;
      return (0x58);
      break;
    case 0x5a: // NUM 2
      keymap[0x59] = 1;
      return (0x59);
      break;
    case 0x5b: // NUM 3
      keymap[0x5a] = 1;
      return (0x5a);
      break;
    case 0x5c: // NUM 4
      keymap[0x48] = 1;
      return (0x48);
      break;
    case 0x5d: // NUM 5
      keymap[0x49] = 1;
      return (0x49);
      break;
    case 0x5e: // NUM 6
      keymap[0x4a] = 1;
      return (0x4a);
      break;
    case 0x5f: // NUM 7
      keymap[0x37] = 1;
      return (0x37);
      break;
    case 0x60: // NUM 8
      keymap[0x38] = 1;
      return (0x38);
      break;
    case 0x61: // NUM 9
      keymap[0x39] = 1;
      return (0x39);
      break;
    default:
      return (0);
      break;
  }
}
//********************************************************************************
uint8_t keyrelease(uint8_t scancode) {
  switch (scancode) {
    case 0x29: // ESC
      keymap[1] = 0;
      return (1);
      break;
    case 0x3a: // F1
      keymap[2] = 0;
      return (2);
      break;
    case 0x3b: // F2
      keymap[3] = 0;
      return (3);
      break;
    case 0x3c: // F3
      keymap[4] = 0;
      return (4);
      break;
    case 0x3d: // F4
      keymap[5] = 0;
      return (5);
      break;
    case 0x3e: // F5
      keymap[6] = 0;
      return (6);
      break;
    case 0x3f: // F6
      keymap[7] = 0;
      return (7);
      break;
    case 0x40: // F7
      keymap[8] = 0;
      return (8);
      break;
    case 0x41: // F8
      keymap[9] = 0;
      return (9);
      break;
    case 0x42: // F9
      keymap[0x0a] = 0;
      return (0x0a);
      break;
    case 0x43: // F10
      keymap[0x0b] = 0;
      return (0x0b);
      break;
    case 0x44: // F11
      keymap[0x0c] = 0;
      return (0x0c);
      break;
    case 0x45: // F12
      keymap[0x0d] = 0;
      return (0x0d);
      break;
    case 0x46: // PrintScreen
      keymap[0x0e] = 0;
      return (0x0e);
      break;
    case 0x47: // ScrollLock
      keymap[0x0f] = 0;
      return (0x0f);
      break;
    case 0x48: // Pause/Break
      keymap[0x10] = 0;
      return (0x10);
      break;
    case 0x35: // ~
      keymap[0x11] = 0;
      return (0x11);
      break;
    case 0x1e: // 1
      keymap[0x12] = 0;
      return (0x12);
      break;
    case 0x1f: // 2
      keymap[0x13] = 0;
      return (0x13);
      break;
    case 0x20: // 3
      keymap[0x14] = 0;
      return (0x14);
      break;
    case 0x21: // 4
      keymap[0x15] = 0;
      return (0x15);
      break;
    case 0x22: // 5
      keymap[0x16] = 0;
      return (0x16);
      break;
    case 0x23: // 6
      keymap[0x17] = 0;
      return (0x17);
      break;
    case 0x24: // 7
      keymap[0x18] = 0;
      return (0x18);
      break;
    case 0x25: // 8
      keymap[0x19] = 0;
      return (0x19);
      break;
    case 0x26: // 9
      keymap[0x1a] = 0;
      return (0x1a);
      break;
    case 0x27: // 0
      keymap[0x1b] = 0;
      return (0x1b);
      break;
    case 0x2d: // -
      keymap[0x1c] = 0;
      return (0x1c);
      break;
    case 0x2e: // =
      keymap[0x1d] = 0;
      return (0x1d);
      break;
    case 0x2a: // BACKSPACE
      keymap[0x1e] = 0;
      return (0x1e);
      break;
    case 0x2b: // TAB
      keymap[0x26] = 0;
      return (0x26);
      break;
    case 0x14: // Q
      keymap[0x27] = 0;
      return (0x27);
      break;
    case 0x1a: // W
      keymap[0x28] = 0;
      return (0x28);
      break;
    case 0x08: // E
      keymap[0x29] = 0;
      return (0x29);
      break;
    case 0x15: // R
      keymap[0x2a] = 0;
      return (0x2a);
      break;
    case 0x17: // T
      keymap[0x2b] = 0;
      return (0x2b);
      break;
    case 0x1c: // Y
      keymap[0x2c] = 0;
      return (0x2c);
      break;
    case 0x18: // U
      keymap[0x2d] = 0;
      return (0x2d);
      break;
    case 0x0c: // I
      keymap[0x2e] = 0;
      return (0x2e);
      break;
    case 0x12: // O
      keymap[0x2f] = 0;
      return (0x2f);
      break;
    case 0x13: // P
      keymap[0x30] = 0;
      return (0x30);
      break;
    case 0x2f: // {
      keymap[0x31] = 0;
      return (0x31);
      break;
    case 0x30: // }
      keymap[0x32] = 0;
      return (0x32);
      break;
    case 0x32: // \\
      keymap[0x33] = 0;
      return (0x33);
      break;
    case 0x39: // CAPSLOCK
      keymap[0x3b] = 0;
      return (0x3b);
      break;
    case 0x04: // A
      keymap[0x3c] = 0;
      return (0x3c);
      break;
    case 0x16: // S
      keymap[0x3d] = 0;
      return (0x3d);
      break;
    case 0x07: // D
      keymap[0x3e] = 0;
      return (0x3e);
      break;
    case 0x09: // F
      keymap[0x3f] = 0;
      return (0x3f);
      break;
    case 0x0a: // G
      keymap[0x40] = 0;
      return (0x40);
      break;
    case 0x0b: // H
      keymap[0x41] = 0;
      return (0x41);
      break;
    case 0x0d: // J
      keymap[0x42] = 0;
      return (0x42);
      break;
    case 0x0e: // K
      keymap[0x43] = 0;
      return (0x43);
      break;
    case 0x0f: // L
      keymap[0x44] = 0;
      return (0x44);
      break;
    case 0x33: // ;
      keymap[0x45] = 0;
      return (0x45);
      break;
    case 0x34: // '
      keymap[0x46] = 0;
      return (0x46);
      break;
    case 0x28: // ENTER
      keymap[0x47] = 0;
      return (0x47);
      break;
    case 0x1d: // Z
      keymap[0x4c] = 0;
      return (0x4c);
      break;
    case 0x1b: // X
      keymap[0x4d] = 0;
      return (0x4d);
      break;
    case 0x06: // C
      keymap[0x4e] = 0;
      return (0x4e);
      break;
    case 0x19: // V
      keymap[0x4f] = 0;
      return (0x4f);
      break;
    case 0x05: // B
      keymap[0x50] = 0;
      return (0x50);
      break;
    case 0x11: // N
      keymap[0x51] = 0;
      return (0x51);
      break;
    case 0x10: // M
      keymap[0x52] = 0;
      return (0x52);
      break;
    case 0x36: // ,
      keymap[0x53] = 0;
      return (0x53);
      break;
    case 0x37: // .
      keymap[0x54] = 0;
      return (0x54);
      break;
    case 0x38: // /
      keymap[0x55] = 0;
      return (0x55);
      break;
    case 0x2c: // SPACEBAR
      keymap[0x5e] = 0;
      return (0x5e);
      break;
    case 0x65: // CONTEXT BUTTON
      keymap[0x68] = 0;
      return (0x68);
      break;
    case 0x49: // INSERT
      keymap[0x1f] = 0;
      return (0x1f);
      break;
    case 0x4a: // HOME
      keymap[0x20] = 0;
      return (0x20);
      break;
    case 0x4b: // PAGEUP
      keymap[0x21] = 0;
      return (0x21);
      break;
    case 0x4c: // DELETE
      keymap[0x34] = 0;
      return (0x34);
      break;
    case 0x4d: // END
      keymap[0x35] = 0;
      return (0x35);
      break;
    case 0x4e: // PAGEDOWN
      keymap[0x36] = 0;
      return (0x36);
      break;
    case 0x52: // UP
      keymap[0x57] = 0;
      return (0x57);
      break;
    case 0x50: // LEFT
      keymap[0x61] = 0;
      return (0x61);
      break;
    case 0x51: // DOWN
      keymap[0x62] = 0;
      return (0x62);
      break;
    case 0x4f: // RIGHT
      keymap[0x63] = 0;
      return (0x63);
      break;
    case 0x53: // NUMLOCK
      keymap[0x22] = 0;
      return (0x22);
      break;
    case 0x54: // NUM /
      keymap[0x23] = 0;
      return (0x23);
      break;
    case 0x55: // NUM *
      keymap[0x24] = 0;
      return (0x24);
      break;
    case 0x56: // NUM -
      keymap[0x25] = 0;
      return (0x25);
      break;
    case 0x57: // NUM +
      keymap[0x3a] = 0;
      return (0x3a);
      break;
    case 0x58: // NUM ENTER
      keymap[0x5b] = 0;
      return (0x5b);
      break;
    case 0x63: // NUM .
      keymap[0x65] = 0;
      return (0x65);
      break;
    case 0x62: // NUM 0
      keymap[0x64] = 0;
      return (0x64);
      break;
    case 0x59: // NUM 1
      keymap[0x58] = 0;
      return (0x58);
      break;
    case 0x5a: // NUM 2
      keymap[0x59] = 0;
      return (0x59);
      break;
    case 0x5b: // NUM 3
      keymap[0x5a] = 0;
      return (0x5a);
      break;
    case 0x5c: // NUM 4
      keymap[0x48] = 0;
      return (0x48);
      break;
    case 0x5d: // NUM 5
      keymap[0x49] = 0;
      return (0x49);
      break;
    case 0x5e: // NUM 6
      keymap[0x4a] = 0;
      return (0x4a);
      break;
    case 0x5f: // NUM 7
      keymap[0x37] = 0;
      return (0x37);
      break;
    case 0x60: // NUM 8
      keymap[0x38] = 0;
      return (0x38);
      break;
    case 0x61: // NUM 9
      keymap[0x39] = 0;
      return (0x39);
      break;
    default:
      return (0);
      break;
  }
}

สรุป

จากบทความนี้จะพบว่า การนำไลบรารีมาใช้ทำให้ลดการเขียนโค้ดไปได้เยอะ แต่ต้องมีการแก้ไขบางส่วนเพื่อให้คอมไพล์ผ่าน และต้อทำความเข้าใจกับหลักการทำงานของไลบรารีเพื่อให้สามารถนำมาใช้งานได้ตรงกับความต้องการของผู้นำมาใช้ต่อ ดังนั้น การเข้าใจหลักภาษา และทักษะในการไล่โค้ดเพื่อทำความเข้าใจจึงเป็นสิ่งที่สำคัญ ซึ่งต้องใช้ความอดทนและมานะในการพัฒนาทักษะนี้ … ขอให้สนุกกับการเขียนโปรแกรมครับ

(C) 2020-2022, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-11-24, 2022-01-26