// QDECODE.CPP  $Date: 2021-09-03 16:58:38 -0800 (Fri, 03 Sep 2021) $
// Copyright (c) 2006, Microstar Laboratories, Inc.
// https://www.mstarlabs.com/
// All rights reserved.
//
// License is granted to users of the "Develper's Toolkit for DAPL"
// to use this code under the conditions of "redistributables"
// as defined in the Developer's Toolkit for DAPL software license.
// Under those terms, this code can be copied, modified, compiled,
// and distributed with no fees.
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//   QDECODE( pdigital, bits, vcounter )
//
// Software-based quadrature signal pair decoder with 32-bit up-down
// counting. This command will monitor the state of the specified pair
// of digital inputs, and as logic transitions are identified,
// appropriately modify a signed 32-bit accumulator upward or downward
// so that other tasks can read the current angular position
// represented by the encoder signals.
//
// Parameters:
//
//   pdigital  - pipe with high-rate samples of digital port
//   bits      - location of encoder bits in digital port
//   vcounter  - 32-bit accumulator for up-down counts
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// The processing logic is represented by a state machine. This
// is also covered in the command description, but for completeness,
// the following is the decoder state table.
//
//   State 1 (Previously L-L)          State 2 (Previously L-H)
//     L-L / State 1 ( +0 )              L-H / State 2 (+0)
//     L-H / State 2 ( +1 )              H-H / State 3 (+1)
//     H-L / State 4 ( -1 )              L-L / State 1 (-1)
//
//   State 3 (Previously H-H)          State 4 (Previously H-L)
//     H-H / State 3 ( +0 )              H-L / State 4 (+0)
//     H-L / State 4 ( +1 )              L-L / State 1 (+1)
//     L-H / State 2 ( -1 )              H-H / State 3 (-1)
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#include  "DTDMOD.H"
#include  "DTD.H"
#define    BUFFER_LENGTH   256
#define    FOREVER         1

// - - - - - - - command interface section   - - - - - - - - - - - - -

extern "C" int __stdcall   QDECODE_entry (PIB **plib);

// Define the system callback function for identifying the command
// prior to execution runtime.
//
extern "C" __declspec(dllexport)
int    __stdcall  ModuleInstall(void *hModule)
{   return (CommandInstall(hModule, "QDECODE", QDECODE_entry, NULL)) ; }


// - - - - - - - - - State representation  - - - - - - - - - - - - - -

// These are the bit patterns on the selected signal bits
#define  STATEMASK    ( 0x0003 )
#define  STATE1_BITS    ( 0x00 )
#define  STATE2_BITS    ( 0x01 )
#define  STATE3_BITS    ( 0x03 )
#define  STATE4_BITS    ( 0x02 )

// Data type for identifying state transition operations.
typedef  struct st_trans  STATE;
typedef  long int         STATE_TRANSITION ( unsigned short, STATE * );

// The state is represented by a pointer to the state transition
// processing function to apply. Other kinds of processing might have a
// much more complex state.
struct st_trans {
  STATE_TRANSITION  *  pTransition;
};

// Identify the input bits that determine the next state
inline unsigned short   get_inbits(
    unsigned short inval, unsigned short inshift )
{
    inval >>= inshift;
    return (inval &= STATEMASK);
}


// - - - - - - - - - State update operations - - - - - - - - - - -

// A state transition operation will examine the input bit pattern
// and establish the next processing state. The return value is
// the amount that the output counter must be adjusted.
//
static long int    STATE0_TRANSITION (
  unsigned short   inbits, STATE * pState );
static long int    STATE1_TRANSITION (
  unsigned short   inbits, STATE * pState );
static long int    STATE2_TRANSITION (
  unsigned short   inbits, STATE * pState );
static long int    STATE3_TRANSITION (
  unsigned short   inbits, STATE * pState );
static long int    STATE4_TRANSITION (
  unsigned short   inbits, STATE * pState );

// The state initialization has the form of an "artificial transition"
// that establishes the initial state based on current input, and always
// returns zero.
//
static long int    STATE0_TRANSITION (
  unsigned short   inbits, STATE * pState )
{
    switch ( inbits )
    {
      case  STATE1_BITS:
        pState->pTransition = &STATE1_TRANSITION;
        break;
      case  STATE2_BITS:
        pState->pTransition = &STATE2_TRANSITION;
        break;
      case  STATE3_BITS:
        pState->pTransition = &STATE3_TRANSITION;
        break;
      case  STATE4_BITS:
        pState->pTransition = &STATE4_TRANSITION;
        break;
    }
    return 0L;
}

// The following functions implement the state transitions from the
// state table.
//
static long int    STATE1_TRANSITION (
  unsigned short   inbits, STATE * pState )
{
    long int   count_update;
    switch ( inbits )
    {
      case  STATE1_BITS:
      case  STATE3_BITS:
        count_update = 0L;
        break;
      case  STATE2_BITS:
        pState->pTransition = &STATE2_TRANSITION;
        count_update = 1L;
        break;
      case  STATE4_BITS:
        pState->pTransition = &STATE4_TRANSITION;
        count_update = -1L;
        break;
    }
    return count_update;
}

static long int    STATE2_TRANSITION (
  unsigned short   inbits, STATE * pState )
{
    long int   count_update;
    switch ( inbits )
    {
      case  STATE2_BITS:
      case  STATE4_BITS:
        count_update = 0L;
        break;
      case  STATE3_BITS:
        pState->pTransition = &STATE3_TRANSITION;
        count_update = 1L;
        break;
      case  STATE1_BITS:
        pState->pTransition = &STATE1_TRANSITION;
        count_update = -1L;
        break;
    }
    return count_update;
}

static long int    STATE3_TRANSITION (
  unsigned short   inbits, STATE * pState )
{
    long int   count_update;
    switch ( inbits )
    {
      case  STATE1_BITS:
      case  STATE3_BITS:
        count_update = 0L;
        break;
      case  STATE4_BITS:
        pState->pTransition = &STATE4_TRANSITION;
        count_update = 1L;
        break;
      case  STATE2_BITS:
        pState->pTransition = &STATE2_TRANSITION;
        count_update = -1L;
        break;
    }
    return count_update;
}

static long int    STATE4_TRANSITION (
  unsigned short   inbits, STATE * pState )
{
    long int   count_update;
    switch ( inbits )
    {
      case  STATE2_BITS:
      case  STATE4_BITS:
        count_update = 0L;
        break;
      case  STATE1_BITS:
        pState->pTransition = &STATE1_TRANSITION;
        count_update = 1L;
        break;
      case  STATE3_BITS:
        pState->pTransition = &STATE3_TRANSITION;
        count_update = -1L;
        break;
    }
    return count_update;
}


// - - - - -  command implementation section - - - - - - - - - - - - -

//
// Main Command Entry point
//
extern "C" int __stdcall   QDECODE_entry (PIB **plib)
{
  // Variables for accessing parameters
    void **argv;
    int argc;

  // Access to task parameters
    PIPE            * pDigPipe;         // digital bits access
    PBUF            * pDigHandle;
    unsigned short  * pBufStorage;
    short unsigned    in_shift;         // bit addressing
    long volatile   * pOutVar;          // accumulator

  // Run-time state
    STATE             current_state;
    int               iFetched, iNext;

  // Begin execution. Access task parameters
    argv =  param_process( plib, &argc, 3, 3,
      T_PIPE_W,           // stream of digital port bit patterns
      T_CONST_W,          // address of bit pair
      T_VAR_L             // output accumulator location
      ) ;

  // Connect to the input data stream
    pDigPipe  = (PIPE *)(argv[1]);
    pipe_open(pDigPipe,P_READ);
    pDigHandle = pbuf_open(pDigPipe,BUFFER_LENGTH);
    pBufStorage = (unsigned short *)pbuf_get_data_ptr(pDigHandle);

  // Determine the digital signal bit address. Even number less than 16.
    in_shift  = *(unsigned short *)(argv[2]);
    in_shift &= 0x000E;

  // Connect to the shared variable. Consume one input value to
  // establish the initial state.
    pOutVar   = (long volatile *)(argv[3]);
    *pOutVar  =  STATE0_TRANSITION (
        get_inbits((unsigned short) pipe_get(pDigPipe), in_shift),
        &current_state );

  // Begin continuous processing
    while (FOREVER)
    {
        // Fetch any available buffered data from digital port
        iFetched = pbuf_get(pDigHandle);

        // Process each received sample
        for  (iNext=0; iNext<iFetched; ++iNext)
        {
            *pOutVar += (current_state.pTransition) (
                get_inbits(pBufStorage[iNext], in_shift),
                &current_state );
        }
    }

  // Artificial, never exits until task shutdown
    return 0;
}

/* End of QDECODE module */