
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)


enum keypad_state {                   //FSM states for key deboucing
 IDLE,
 OUTPUT_COL,
 DELAY,
 INPUT_DATA,
 KEY_RELEASE
};


typedef struct {                                                //hold the key info in order to build the USB msg
  key_pos pos;
  int lign;
  int col;
}key_cde;

#define KEY_READ_COUNT 7                                        //when down, key is read KEY_READ_COUNT time
#define KEY_READ_THRES 4                                        //if key was dwn more than KEY_READ_THRES time ->dwn is validated

#define KEYPAD_NBCOL 5
#define KEYPAD_NBLIGN 6

#define MAX_ICP_DELAY 0x000F
static int16 icp_delay = MAX_ICP_DELAY;

static keypad_state icp_state;                                   //machine state for port operation
static int icp_col;                                              //cycle through columns
static key_cde icp_keys[5];                                      //list of keys to send through USB


void icp_init(void){
int i,j;

  SET_TRIS_A(0b00001111);
  SET_TRIS_B(0b11111111);
  SET_TRIS_C(0b00000000);

  output_a(0x30);                       // set 1 on all col on port_a
  output_c(0x07);                       // set 1 on all col on port_b
  PORT_B_PULLUPS(TRUE);

  for (i=0;i<5;++i)      //col
    for(j=0;j<4;++j)     //lign
      icp_keymaps[i][j].pos = kpUp;

  printf("\r\nInit keyboard done");
}



int icp_task(void){
  int all_ligns;
  int lign;
  int key_is_dwn;
  int nb_keys = 0;                                           //nb ok key having state update -> will be proceed in main()

  switch (icp_state){
    case IDLE :
      icp_init();
      icp_state = OUTPUT_COL;
      break;

    case OUTPUT_COL :
      switch (icp_col) {                                     //Enable one column
        case 0 :
          //printf("\r\nenable_col1");
          output_a(0x20);
          output_c(0x07);
          break;

        case 1 :
          //printf("\r\nenable_col2");
          output_a(0x10);
          output_c(0x07);
          break;

        case 2 :
          //printf("\r\nenable_col3");
          output_a(0x30);
          output_c(0x06);
          break;

        case 3 :
          //printf("\r\nenable_col4");
          output_a(0x30);
          output_c(0x05);
          break;

        case 4 :
          //printf("\r\nenable_col5");
          output_a(0x30);
          output_c(0x03);
          break;

       default :
          printf("\r\nERROR : You must implenent code to handle col %u", icp_col);
      };
      icp_delay = MAX_ICP_DELAY;
      icp_state = DELAY;
      break;

    case DELAY :
      if (! --icp_delay ) {
         icp_state = INPUT_DATA;
      }break;

    case INPUT_DATA :
      all_ligns = (input_b() >> 2);

      //printf("\r\nCol=%u portB=%u", col, all_ligns);

      for (lign=0;lign<KEYPAD_NBLIGN;++lign){

        key_is_dwn = !(all_ligns & (1<<lign) );

        //printf("key_is_dwn=%u", key_is_dwn);

        switch (icp_keymaps[lign][icp_col].pos) {
          case kpUp :
            if (key_is_dwn){
              icp_keymaps[lign][icp_col].idx = KEY_READ_COUNT;
              icp_keymaps[lign][icp_col].cpt_dwn = 0;
              icp_keymaps[lign][icp_col].pos = kpCpt;
              //printf("\r\nKey [%u][%u] now down", lign, col);
            }
            break;

          case kpCpt :
            if (key_is_dwn){
              ++icp_keymaps[lign][icp_col].cpt_dwn;        // MAJ du compteur de Dwn
              //printf("\r\nKey [%u][%u] cpt=%u", lign, col, key_states[lign][col].cpt_dwn);
            }

            if (!icp_keymaps[lign][icp_col].idx){          //les 10 lectures ont été faites
              if (icp_keymaps[lign][icp_col].cpt_dwn > KEY_READ_THRES){
                //printf("\r\nKey #%u debounced [%u][%u]", nb_keys, lign, col);

                //HERE update du tableau result
                icp_keys[nb_keys].pos = kpDwn;
                icp_keys[nb_keys].lign = lign;
                icp_keys[nb_keys++].col = icp_col;
              }
              icp_keymaps[lign][icp_col].pos = kpDwn;
            }
            --icp_keymaps[lign][icp_col].idx;              //MAJ du compteur de nb de lecture
            break;


          case kpDwn :
            if (!key_is_dwn){
              icp_keymaps[lign][icp_col].pos = kpUp;


              //HERE update du tableau result
              icp_keys[nb_keys].pos = kpUp;
              icp_keys[nb_keys].lign = lign;
              icp_keys[nb_keys++].col = icp_col;

              //printf("\r\nKey #%u released [%u][%u] %u", nb_keys, keys_cde[nb_keys].lign, keys_cde[nb_keys].col, keys_cde[nb_keys].pos);
            }
            break;
         }//end Switch
      }//end For lign

      if (++icp_col == KEYPAD_NBCOL)                                 // gère les colones
        icp_col = 0;
      icp_state = OUTPUT_COL;
      break;

  }//end swtich

  return nb_keys;
}



/*48000000  32                          0.000682666
            64    187500  732.421875    0.001365333
            128   93750   366.2109375   0.002730667
            256   46875   183.1054688   0.005461333
*/
