/****************************************************************************/
/* Copyright 1992, 1993 MBARI                                               */
/****************************************************************************/
/* Summary  : IBC Quad 16 bit A to D Board Functions                        */
/* Filename : atod.c                                                        */
/* Author   : Andrew Pearce                                                 */
/* Project  : New ROV IBC Based Data Concentrator                           */
/* Version  : 1.0                                                           */
/* Created  : 09/17/92                                                      */
/* Modified : 03/26/93                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/ibc/boards/atod/atod.c,v 1.2 1997/05/07 15:25:09 pean Exp $
 * $Log: atod.c,v $
 * Revision 1.2  1997/05/07 15:25:09  pean
 * Cleaned up Include files and makefile for new directory structure.
 *
 * Revision 1.1.1.1  1997/05/02 18:51:26  pean
 * Initial check in of IBC microcontroller software
 *
 * Revision 1.2  93/07/02  09:20:21  09:20:21  pean (Andrew Pearce)
 * Minor bug fixes and enhancements for Prototype Vehicle Testing
 *
 * Revision 1.1  93/03/26  15:43:08  15:43:08  pean (Andrew Pearce)
 * Initial revision
 *
*/
/****************************************************************************/

#pragma model(196)
#pragma nosignedchar

#include "C:/C96/include/stddef.h"      /* C Standard Definitions           */
#include "const.h"                      /* Misc. constants - TRUE/FALSE etc */
#include "types.h"                      /* MBARI style guide declarations   */
#include "malloc.h"                     /* Malloc support routines          */
#include "microdef.h"                   /* Microcontroller Definitons       */
#include "microCmd.h"                   /* microcontroller commands         */
#include "applay.h"                     /* Application layer funcitions     */

#include "filter.h"                     /* General Filter Routines          */
#include "ibc_card.h"                   /* IBC Board Types and Addresses    */
#include "ibc_cmd.h"                    /* Core IBC application definitions */
#include "ibc_asm.h"                    /* IBC Assembly Language Functions  */
#include "pic.h"                        /* Intel 82C59 Interrupt Controller */

#include "atod.h"                       /* IBC A to D Board Definitions     */

/****************************************************************************/
/* Function    : AtoD_BoardSearch                                           */
/* Purpose     : Scans IBC backplane looking for Quad A to D Boards         */
/* Inputs      : Pointer to IBC Board Entry Table and maximum table size    */
/* Outputs     : Returns number of boards found                             */
/****************************************************************************/
    Int16
AtoD_BoardSearch( IBC_BoardEntry *IBC_Boards[], Int16 maxCards )
{
    Byte  *addr;                        /* AtoD Board base address          */
    Int16 board;                        /* Counts number of A/D boards found*/
    IBC_BoardEntry *boardEntry;         /* Pointer to A/D board data struct */

    board = 0;                          /* Set board count to zero          */
    if (maxCards > 0)                   /* Check for space in IBC Card Table*/
    {                                   /* Search for Quad A to D Boards    */
        for (addr =  (Byte *) QUAD_A_TO_D_0_ADDR;
             addr <= (Byte *) QUAD_A_TO_D_3_ADDR;
             addr+= (QUAD_A_TO_D_1_ADDR - QUAD_A_TO_D_0_ADDR) )
        {                               /* NULL boardEntry if board missing */
            if ((boardEntry = (IBC_BoardEntry *) AtoD_BoardInit(addr)) != NULL)
            {                           /* Add A/D data struct to IBC Table */
                IBC_Boards[board++] = boardEntry;
                if (board == maxCards)  /* Check if IBC Card Table is full  */
                    return(board);      /* Return number of A/D boards found*/
            } /* if */
        } /* for */
    } /* if */

    return(board);                      /* Return number of A/D boards found*/
} /* AtoD_BoardSearch() */

/****************************************************************************/
/* Function    : AtoD_BoardInit                                             */
/* Purpose     : Quad A to D Board Initialization routine                   */
/* Inputs      : Base address of quad A to D board                          */
/* Outputs     : Returns pointer to IBC board data structure                */
/****************************************************************************/
    AtoD_BoardEntry*
AtoD_BoardInit( Byte *boardBaseAddr )
{
     AtoD_BoardEntry *AtoD_Board;        /* Pointer to board entry          */
     Nat32 AtoD_result;                  /* Result of reading A/D convertor */
     Int16 chan;                         /* Channel index counter           */

                                         /* Check if board exists by reading*/
                                         /* A to D result register          */
                                         /* Returns OK if board asserts xack*/
     AtoD_result = ibcBusReadWord((Word *) (boardBaseAddr + AtoD_AtoD_REG));

                                        /* Mask off Status from AtoD_result*/
     if (((Nat16) (AtoD_result & 0x0000ffff)) == ERROR )
             return( (AtoD_BoardEntry *) NULL);
                                        /* Board doesn't exist return NULL  */

                                        /* Allocate space for board entry   */
    AtoD_Board = (AtoD_BoardEntry *) malloc( sizeof (AtoD_BoardEntry) );
    if (AtoD_Board == (AtoD_BoardEntry *) NULL )
            return( (AtoD_BoardEntry *) NULL);
                                        /* Insufficient memory return ERROR */

                                        /* Fill out board entry structure   */
    AtoD_Board->IBC_Board.baseAddress   = boardBaseAddr;
    AtoD_Board->IBC_Board.boardType     = QUAD_A_TO_D;
    AtoD_Board->IBC_Board.intrLevel     = IBC_INT_LVL_OFF;

    AtoD_Board->AtoDChannel = 0;        /* Clear A to D channel             */
    AtoD_Board->convertorBusy = FALSE;  /* Set convertion status to idle    */

                                        /* Clear out raw data values        */
    for (chan = 0; chan < AtoD_MAX_CHANS; chan++)
        AtoD_Board->raw_AtoD[chan] = 0;

    return (AtoD_Board);                /* Return pointer to A/D data struct*/
} /* AtoD_BoardInit() */

/****************************************************************************/
/* Function    : AtoD_BoardCommand                                          */
/* Purpose     : Send command to A to D Board A/D convertor                 */
/* Inputs      : A to D Board data strcuture, command to send               */
/* Outputs     : Returns ERROR or OK if write is successful                 */
/****************************************************************************/
    Int16
AtoD_BoardCommand( AtoD_BoardEntry *AtoD_Board, Byte command )
{
    if (AtoD_Board == NULL)             /* Check for valid A/D data struct  */
        return (ERROR);
                                        /* Range check A to D command       */
    if ( (command >= AtoD_CHAN_1) && (command <= AtoD_CHAN_3) )
                                        /* Save command for A/D board ISR   */
        AtoD_Board->AtoDChannel = command - 1;
                                        /* Send command to A to D convertor */
                                        /* Return result of IBC Bus Write   */
     return (ibcBusWriteByte(AtoD_Board->IBC_Board.baseAddress +
             (Byte *) AtoD_AtoD_REG, command ));
} /* AtoD_BoardCommand() */

/****************************************************************************/
/* Function    : AtoD_BoardInterruptOn                                      */
/* Purpose     : Generate Interrupt from A to D Board                       */
/* Inputs      : A to D board data structure                                */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_BoardInterruptOn( AtoD_BoardEntry *AtoD_Board )
{
    Nat16 delay;                        /* Used for short delay loop        */
                                        /* Start A/D conversion             */
    AtoD_BoardCommand( AtoD_Board, AtoD_CHAN_0 );
                                        /* Short delay for end of conversion*/
    for (delay = 0; delay < 5000; delay++);
} /* AtoD_BoardInterruptOn() */

/****************************************************************************/
/* Function    : AtoD_BoardInterruptOff                                     */
/* Purpose     : Clear Interrupt from A to D Board                          */
/* Inputs      : A to D board data structure                                */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_BoardInterruptOff( AtoD_BoardEntry *AtoD_Board )
{                                       /* Read A to D result to clear intr */
     ibcBusReadWord( (Word*)
         (AtoD_Board->IBC_Board.baseAddress + AtoD_AtoD_REG) );

     AtoD_BoardCommand( AtoD_Board, AtoD_DISABLE );
} /* AtoD_BoardInterruptOff() */

/****************************************************************************/
/* Function    : AtoD_BoardIsr                                              */
/* Purpose     : A to D Board Interrupt Service Routine                     */
/* Inputs      : A to D board data structure                                */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_BoardIsr( AtoD_BoardEntry *AtoD_Board )
{
    Int16 channel;                      /* Channel number of conversion     */
    Nat32 AtoD_result;                  /* Result of reading A/D convertor  */

    channel = AtoD_Board->AtoDChannel;  /* Get channel number               */
                                        /* Read previous conversion value   */
    AtoD_result = ibcBusReadWord( (Word *)
        (AtoD_Board->IBC_Board.baseAddress + AtoD_AtoD_REG));

                                        /* Bump pointer to next A/D Channel */
    if ( (++AtoD_Board->AtoDChannel) < AtoD_MAX_CHANS )
                                        /* Start conversion for next channel*/
        ibcBusWriteByte( AtoD_Board->IBC_Board.baseAddress + AtoD_AtoD_REG,
            AtoD_Board->AtoDChannel + AtoD_CHAN_0 );
    else
    {
        AtoD_Board->convertorBusy = FALSE;  /* Set conversion status to idle*/
        ibcBusWriteByte( AtoD_Board->IBC_Board.baseAddress + AtoD_AtoD_REG,
            AtoD_DISABLE );
    } /* else */

                                        /* Mask off Status from AtoD_result */
    if ( ( (Nat16) (AtoD_result & 0x0000ffff)) == ERROR )
         return;                        /* IBC bus timeout, so exit         */

                                        /* Extract A/D result from high word*/
    AtoD_Board->raw_AtoD[channel] = (Nat16) (AtoD_result >> 16 );
} /* AtoD_BoardIsr() */

/****************************************************************************/
/* Function    : AtoD_RawAtoDValue                                          */
/* Purpose     : Return raw value for specified A to D channel              */
/* Inputs      : A to D board data structure, channel number to read        */
/* Outputs     : Returns latest A/D channel reading                         */
/****************************************************************************/
    Nat16
AtoD_RawAtoDValue( AtoD_BoardEntry *AtoD_Board, Int16 channel)
{
    if (AtoD_Board == NULL)             /* Check for valid A/D data struct  */
        return (ERROR);
                                        /* Return raw A/D value for channel */
    return (AtoD_Board->raw_AtoD[channel]);
} /* AtoD_RawAtoDValue() */

/****************************************************************************/
/* Function    : AtoD_RawAtoDValuesCmd                                      */
/* Purpose     : Process command to read raw A to D values from A to D board*/
/* Inputs      : IBC Board Table, number of IBC cards, command buffer       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_RawAtoDValuesCmd( IBC_BoardEntry* IBC_CardTable[],
     Int16 IBC_CardCount, Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Int16   chan;               /* A to D channel index                     */
    Byte    *boardAddr;         /* AtoD Board base address from command buf */
    AtoD_BoardEntry *AtoD_Board;/* Pointer to A to D board data structure   */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, QUAD_A_TO_D) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to A to D Board data struct  */
    AtoD_Board = (AtoD_BoardEntry *) IBC_CardTable[card];

                                /* Send number of channels to serial port   */
    replyToCommand(6, CMD_OK, AtoD_MAX_CHANS,
                                /* followed by the raw A/D measurements     */
        (Word) AtoD_Board->raw_AtoD[0],
        (Word) AtoD_Board->raw_AtoD[1],
        (Word) AtoD_Board->raw_AtoD[2],
        (Word) AtoD_Board->raw_AtoD[3] );
} /* AtoD_RawAtoDValuesCmd() */

/****************************************************************************/
/* Function    : AtoD_RawAtoDCmd                                            */
/* Purpose     : Process command to read raw A to D channel                 */
/* Inputs      : IBC Board Table, number of IBC cards, command buffer       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_RawAtoDCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Byte    *boardAddr;         /* A/D Board base address from command buf  */
    Nat16   card;               /* Card index into Card Table               */
    Int16   chan;               /* A to D channel index                     */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 2, &boardAddr, &chan);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        boardAddr, QUAD_A_TO_D) ) == ERROR) || (chan > AtoD_MAX_CHANS) )
    {                           /* Card not present or bad channel specified*/
        replyToCommand(1, EARGUMENT);
        return;                 /* So return ERROR                          */
    } /* if */

                                /* Send raw A/D reading to serial port      */
    replyToCommand(2, CMD_OK,
           (Word) ((AtoD_BoardEntry *) IBC_CardTable[card])->raw_AtoD[chan]);
} /* AtoD_RawAtoDCmd() */

/****************************************************************************/
/* Function    : AtoD_ConvertorDisableCmd                                   */
/* Purpose     : Process command to disable A to D convertor on A to D board*/
/* Inputs      : IBC Board Table, number of IBC cards, command buffer       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_ConvertorDisableCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Int16   chan;               /* Channel index counter                    */
    Byte    *boardAddr;         /* A/D Board base address from command buf  */
    AtoD_BoardEntry *AtoD_Board;/* Pointer to A to D board data structure   */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, QUAD_A_TO_D) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to A to D Board data struct  */
    AtoD_Board = (AtoD_BoardEntry *) IBC_CardTable[card];

                                /* Disable this A/D board's interrupt level */
    PIC_intDisable(IBC_CardTable[card]->intrLevel);
                                /* Send A/D Disable command to A/D convertor*/
    AtoD_BoardCommand( AtoD_Board, AtoD_DISABLE );

                                /* Clear out raw data values                */
    for (chan = 0; chan < AtoD_MAX_CHANS; chan++)
        AtoD_Board->raw_AtoD[chan] = 0;

    replyToCommand(1, CMD_OK);  /* Send CMD_OK to serial port               */
} /* AtoD_ConvertorDisableCmd() */

/****************************************************************************/
/* Function    : AtoD_ConvertorEnableCmd                                    */
/* Purpose     : Process command to enable A to D convertor on A to D board */
/* Inputs      : IBC Board Table, number of IBC cards, command buffer       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_ConvertorEnableCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* A/D Board base address from command buf  */
    AtoD_BoardEntry *AtoD_Board;/* Pointer to A to D board data structure   */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, QUAD_A_TO_D) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to A to D Board data struct  */
    AtoD_Board = (AtoD_BoardEntry *) IBC_CardTable[card];

                                /* Enable this A/D board's interrupt level  */
    PIC_intEnable(IBC_CardTable[card]->intrLevel);
                                /* Start A/D conversion for channel 0       */
    AtoD_BoardCommand( AtoD_Board, AtoD_CHAN_0 );

    replyToCommand(1, CMD_OK);  /* Send CMD_OK to serial port               */
} /* AtoD_ConvertorEnableCmd() */

/****************************************************************************/
/* Function    : AtoD_StartConversions                                      */
/* Purpose     : Starts scanning A to D Board Channels                      */
/* Inputs      : A to D Board data structure                                */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_StartConversions( AtoD_BoardEntry *AtoD_Board )
{
    if (AtoD_Board->convertorBusy) /* Exit if conversion in progress*/
        return;

    AtoD_Board->AtoDChannel = 0;        /* Clear A to D channel             */
    AtoD_Board->convertorBusy = TRUE;   /* Set conversion status to busy    */

                                        /* Start conversion for first chan  */
    ibcBusWriteByte( AtoD_Board->IBC_Board.baseAddress + AtoD_AtoD_REG,
        AtoD_Board->AtoDChannel + AtoD_CHAN_0 );

} /* AtoD_StartConversions() */

/****************************************************************************/
/* Function    : AtoD_BoardFunctions                                        */
/* Purpose     : A to D board real-time loop functions                      */
/* Inputs      : A to D Board data structure                                */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
AtoD_BoardFunctions( AtoD_BoardEntry *AtoD_Board )
{			/* Restart next scan, if necessary                 */
    AtoD_StartConversions( AtoD_Board );
} /* AtoD_BoardFunctions() */

