/*----------------------------------------------------------------------------
**
**	ShiftReg.c
**
** Control of extension shift register
** The extension shift register extends the I/O capability of the micro by
** using a chain of serial-parallel (for outputs) and parallel-serial (for
** inputs) shift registers. The chain shares the same clock and strobe line,
** and the micro has data pin that can be set to input or output to send or
** receive data to/from the chain.
**
** There are two sets of functions. The first set, SetBit() and ReadBit()
** just set and read the shadow registers. Bit setting and bit updating
** will be performed the next time the shift register is updated (every
** 100ms). The second set of functions, SetBitNow() and ReadBitNow()
** perform the action straight away, without the 100ms latency.
**
** The outputs are first in the chain (HC595s), followed by the inputs (HC165s).
** Output data is set on the shift register outputs when the strobe line
** goes high, and inputs are latched in when the strobe line is taken low.
**
**    OUTPUTS - 74HC595      INPUTS - 74HC165
**    -----------------      -----------------
**
**           -------                   -------
**  Data ---|Ds     |         Data ---|Ds     |
**   Clk ---|SHcp   |          Clk ---|CP     |
**     1 ---|/MR  Q7|--->        0 ---|/CE   Q|--->
**  Strb ---|STcp   |         Strb ---|/PL    |
**     1 ---|/OE    |                 |       |
**           -------                   -------
**
** CONNECTION:
**
**                  ^           ^           |
** ____          ___|___     ___|___     ___v___
**     |________| HC595 |___| HC595 |___| HC165 |__
**     | data | |_______|q  |_______|q  |_______|q |
**     |      |   |  |         |  |        |  |    |
**  H8S|______|___|__|_________|__|________|  |    |
**     | clk  |      |            |           |    |
**     |______|______|____________|___________|    |
** ____| strb |                                    |
**            |    _______                         |
**             ---|__10K__|------------------------
**
**--------------------------------------------------------------------------*/

#include "Hardware.h"
#include "Onboard.h"
#include "ShiftReg.h"


// These define the chain dimensions
#define CHAIN_LENGTH    24      /* Length of chain in bits     */
#define NUM_OUTPUTS     16      /* Number of outputs in chain  */
#define NUM_INPUTS      8       /* Number of inputs in chain   */

// These are the chain shadow registers. One byte per bit.
typedef struct
{
    unsigned char Inputs[NUM_INPUTS];
    unsigned char Outputs[NUM_OUTPUTS];
} TShiftRegChain;
TShiftRegChain Chain;



/*----------------------------------------------------------------------------
//
//  UpdateChain
//
// Updates the regsiter chain. Sets outputs and updates inputs.
//
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void UpdateChain(void)
{
    int i;

    //---------------------------------------------------------
    // Timing diagram:
    //
    // Data: IIIIOOOOI0000I0000...I0000OOOOOI
    //             _    _    _       _
    //  Clk: _____/ \__/ \__/ \...__/ \______
    //       _   ______________...______   __
    // Strb:  \_/                       \_/
    //
    // Func:   1 32  3 2  3 2    3  2   4  5
    //  1 = Dip strobe to latch inputs.
    //  2 = Rising clock edge reads output from H8 into SR,
    //      and sets input SR output ready to read.
    //  3 = Data line set to input, input data read.
    //  4 = Dip strobe. Rising edge clocks new outputs.
    //  5 = Line left as input to conserve power.
    //---------------------------------------------------------

    // 1. Dip strobe line to latch inputs.
    SR_CLOCK = 0;
    SR_STROBE = 0;
    SR_STROBE = 1;  /* Must go high here to allow HC7597 to clock */

    for (i = 0  ;  i < max(NUM_OUTPUTS,NUM_INPUTS)  ;  i++)
    {
        // 3. If still reading inputs, read input bit into shadow register
        if (i <= NUM_INPUTS)
        {
            SR_INPUT;
            Chain.Inputs[i] = SR_DATA;
        }

        if (i <= NUM_OUTPUTS)
        {
            // Set last output bit on data out
            SR_OUTPUT;
            SR_DATA = Chain.Outputs[i];
        }

        // 2. One clock
        SR_CLOCK = 1;
        SR_CLOCK = 0;
    }

    // 4. Now strobe the outputs, leaving strobe high.
    SR_STROBE = 0;
    SR_STROBE = 1;

    // 5. Leave line as an input afterwards
    SR_INPUT;
}


/*----------------------------------------------------------------------------
//
//  InitialiseShiftRegisterChain
//
//  Sets all outputs to the default state, and reads all input states
//  to preload the shadow registers.
//
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void InitialiseShiftRegisterChain(void)
{
    int i;

    // Clear all outputs in shadow register
    for (i=0 ; i<NUM_OUTPUTS ; i++)
        Chain.Outputs[i] = 0x00;

    // Read inputs
    UpdateChain();
}


/*----------------------------------------------------------------------------
//
//  ChainSetByte
//
// Sets 8 bits in the shadow register. The actual I/O lines will be updated
// with this bit the next time the chain is updated, every 100ms.
//
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void ChainSetByte(int ByteNumber, unsigned char Value)
{
    Chain.Outputs[ByteNumber*8+0] = ((Value & b00000001) ? 1 : 0);
    Chain.Outputs[ByteNumber*8+1] = ((Value & b00000010) ? 1 : 0);
    Chain.Outputs[ByteNumber*8+2] = ((Value & b00000100) ? 1 : 0);
    Chain.Outputs[ByteNumber*8+3] = ((Value & b00001000) ? 1 : 0);
    Chain.Outputs[ByteNumber*8+4] = ((Value & b00010000) ? 1 : 0);
    Chain.Outputs[ByteNumber*8+5] = ((Value & b00100000) ? 1 : 0);
    Chain.Outputs[ByteNumber*8+6] = ((Value & b01000000) ? 1 : 0);
    Chain.Outputs[ByteNumber*8+7] = ((Value & b10000000) ? 1 : 0);
}


/*----------------------------------------------------------------------------
//
//  ChainSetByteNow
//
// Sets 8 bits in the shadow register. The I/O lines are updated immediately.
//
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void ChainSetByteNow(int ByteNumber, unsigned char Value)
{
    ChainSetByte(ByteNumber, Value);
    UpdateChain();
}


/*----------------------------------------------------------------------------
//
//  ChainSetBit
//
// Sets the bit in the shadow register. The actual I/O line will be updated
// with this bit the next time the chain is updated, every 100ms.
//
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void ChainSetBit(int BitNumber, int State)
{
    Chain.Outputs[BitNumber] = State;
}


/*----------------------------------------------------------------------------
//
//  ChainSetBitNow
//
// Sets the bit in the shadow register, then updates the chain.
//
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void ChainSetBitNow(int BitNumber, int State)
{
    Chain.Outputs[BitNumber] = State;
    UpdateChain();
}


/*----------------------------------------------------------------------------
//
//  ChainReadBit
//
// Reads the bit from the shadow register. The value may be up to 100ms old.
//
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
unsigned char ChainReadBit(int BitNumber)
{
    return Chain.Inputs[BitNumber];
}


/*----------------------------------------------------------------------------
//
//  ChainReadBitNow
//
// Updates the chain, then reads the bit from the shaow register.
//
// Modification Record:
//  09-Oct-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
unsigned char ChainReadBitNow(int BitNumber)
{
    UpdateChain();
    return Chain.Inputs[BitNumber];
}

