/****************************************************************************/
/* Copyright 1991 to 1995, MBARI                                            */
/****************************************************************************/
/* Summary  : Micro controller board support library                        */
/* Filename : microlib.c                                                    */
/* Author   : Andrew Pearce                                                 */
/* Project  : Tiburon ROV Microcontroller Board Rev 2.0                     */
/* Version  : 1.0                                                           */
/* Created  : 04/25/91                                                      */
/* Modified : 03/23/95                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/lib/microlib.c,v 1.2 1997/05/07 15:52:56 pean Exp $
 * $Log: microlib.c,v $
 * Revision 1.2  1997/05/07 15:52:56  pean
 * Cleaned up Include files and makefile for new directory structure.
 *
 * Revision 1.1.1.1  1997/05/02 17:15:59  pean
 * Initial release of the microcontroller software after Tiburon
 * Moolpool Dive to test IView, Lapboxes, modified Power can
 * GF/5V using bus capacitance mode.
 *
 * Revision 1.1  92/05/14  09:14:29  09:14:29  pean (Andrew Pearce 408-647-3746)
 * Initial revision
 *
*/
/****************************************************************************/

#pragma model(196)
#pragma nosignedchar

#include "C:/C96/include/80C196.h"  /* 80196 Register mapping               */
#include "C:/C96/include/string.h"  /* string function library              */
#include "const.h"                  /* Misc. constants - TRUE/FALSE etc     */
#include "types.h"                  /* MBARI style guide declarations       */
#include "microasm.h"               /* Assembly language functions          */
#include "applay.h"                 /* Application layer functions          */
#include "serial.h"                 /* data link layer functions            */
#include "microdef.h"               /* Microcontrolller definitions         */
#include "microcmd.h"               /* microcontroller commands             */
#include "microlib.h"               /* Microcontroller Library decls.       */
#include "applic.h"                 /* Microcontroller applications         */

Extern  Void  groundFaultTest();    /* Hardware supported ground fault test */
Extern  Void  ledControl();         /* Controls state of on board LED       */

Extern  const Char micro_id[];      /* Micro Identification String          */
Extern  const Char softwareRev[];   /* Software Revision String             */
Extern  const Char *microSerialNo;  /* Micro serial number string           */
Extern  Word  ram_test_result;      /* Result of External Ram Test          */
MLocal  Word  board_status;         /* Board Status Word                    */

Global Reg Byte ioport1_copy;       /* Internal copy of Input/Output Port 1 */
Global Reg Byte ioport2_copy;       /* Internal copy of Input/Output Port 2 */
Global Word ledState;               /* Front Panel LED Status               */

/****************************************************************************/
/* Function    : bytesToWord                                                */
/* Purpose     : build word from high and low bytes - Big Endian format     */
/* Inputs      : high and low bytes                                         */
/* Outputs     : Returns word                                               */
/****************************************************************************/
    Word
bytesToWord(high, low)
    Reg Byte    high;
    Reg Byte    low;
{
    return (((Word) high << 8) | (low & 0xff));
} /* bytesToWord */

/****************************************************************************/
/* Function    : writeIoport1                                               */
/* Purpose     : safely modifies ioport 1 and updates an internal copy      */
/* Inputs      : Desired port value, and mode                               */
/* Outputs     : None, but ioport1 is updated                               */
/****************************************************************************/
    Void
writeIoport1(Word port_value, Word action)
{
    if (action == OR_PORT)
        ioport1_copy |= port_value;     /* OR value with port value copy    */

    else if (action == AND_PORT)
        ioport1_copy  &= port_value;    /* AND value with port value copy   */

    else if (action == SET_PORT)
        ioport1_copy = port_value;      /* SET port value copy to port value*/

    ioport1 = ioport1_copy;             /* write value to ioport1           */
} /* writeIoport1() */

/****************************************************************************/
/* Function    : writeIoport2                                               */
/* Purpose     : safely modifies ioport 2 and updates an internal copy      */
/* Inputs      : Desired port value, and mode                               */
/* Outputs     : None, but ioport2 is updated                               */
/****************************************************************************/
    Void
writeIoport2(Word port_value, Word action)
{
    if (action == OR_PORT)
        ioport2_copy  |= port_value;    /* OR value with port value copy    */

    else if (action == AND_PORT)
        ioport2_copy  &= port_value;    /* AND value with port value copy   */

    else if (action == SET_PORT)
        ioport2_copy = port_value;      /* SET port value copy to port value*/

    ioport2 = ioport2_copy;             /* write value to ioport2           */
} /* writeIoport2() */

/****************************************************************************/
/* Function    : taskDelay                                                  */
/* Purpose     : waits the specified number of 10 millisecond periods       */
/* Inputs      : Time to wait in 10 msecs units                             */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
taskDelay(clock_ticks)
    Nat16 clock_ticks;                  /* Number of 10 Msecs to wait       */
{
    Nat16 current_ticks;                /* Saves current ticker value       */

    if (clock_ticks > 0)
    {
        current_ticks = read_sysclock();
                                        /* wait for next clock tick         */
        while (current_ticks == read_sysclock());
                                        /* OK, Now wait the specified ticks */
        while ((read_sysclock() - current_ticks) < clock_ticks);
    } /* if */
} /* taskDelay */

/****************************************************************************/
/* Function    : readAnalogChannel                                          */
/* Purpose     : read the value of an analog channel                        */
/* Inputs      : channel number to read                                     */
/* Outputs     : Returns the analog value (-512 to + 511) 10 bits           */
/****************************************************************************/
    Int16
readAnalogChannel(channel)
    Reg Byte    channel;                /* Analog channel number 0 to 7     */
{
    Int16   ad_value;                   /* return value                     */

                                        /* Start conversion for this channel*/
    ad_command = A_TO_D_START_NOW | channel;
                                        /* wait for conversion to complete  */
    while ((ad_result_lo & A_TO_D_BUSY) != 0);
                                        /* read value from A to D           */
    ad_value = (bytesToWord(ad_result_hi, ad_result_lo & A_TO_D_MASK) >> 6);
    return (ad_value - 0x0200);

} /* readAnalogChannel */

/****************************************************************************/
/* Function    : toggleLed                                                  */
/* Purpose     : Change state of on board LED                               */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
toggleLED( Void )
{
    ledState = ~ledState;
    ledControl(ledState);
} /* toggleLed */

/****************************************************************************/
/* Function    : initBoardStatus                                            */
/* Purpose     : initialize microcontroller software status word            */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/* Side effects: Global board status value is updated                       */
/****************************************************************************/
    Void
initBoardStatus( Void )
{
/*
    ram_test_failed  Bit 0;
    serial_com_error Bit 1;
    ground_fault     Bit 2;
*/

    board_status = 0;                   /* Clear all error conditions      */

    if (ram_test_result != 0)           /* Set bit 0 of status if Ram Failed*/
        board_status |= RAM_TEST_MASK;

} /* initBoardStatus() */

/****************************************************************************/
/* Function    : microResetCmd                                              */
/* Purpose     : Performs microcontroller reset command                     */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
microResetCmd( Void )
{
    replyToCommand(1, CMD_OK);          /* Command executed OK              */
    taskDelay(10);                      /* Wait 100 milliseconds for serial */
    reset_processor();                  /* Do hardware reset                */
} /* microResetCmd() */

/****************************************************************************/
/* Function    : microIdentify                                              */
/* Purpose     : sends the microcontroller software identifcation string    */
/* Inputs      : None                                                       */
/* Outputs     : None, but string is sent to serial port                    */
/****************************************************************************/
    Void
microIdentify( Void )
{
    writeDataPacket((Char *) micro_id, (Int16) strlen((const char *)
             micro_id));
} /* microIdentify() */

/****************************************************************************/
/* Function    : microGetSerNoCmd                                           */
/* Purpose     : sends the micro board serial number string                 */
/* Inputs      : Serial number string                                       */
/* Outputs     : None, but string is sent to serial port                    */
/****************************************************************************/
    Void
microGetSerNoCmd( Char *serialNumber )
{
    writeDataPacket(serialNumber, (Int16) strlen((const char *) serialNumber));
} /* microGetSerNoCmd() */

/****************************************************************************/
/* Function    : microSetSerNoCmd                                           */
/* Purpose     : Set the micro board serial number to the specified string  */
/* Inputs      : command buffer, buffer length and serial number string     */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
microSetSerNoCmd( Char *commandBuf, Int16 commandLen, Char *serialNumber,
         Int16 serNoLen )
{
    Word len;

    len = min(commandLen - sizeof(Word), serNoLen - 1);
                                    /* Get max length of string            */

                                    /* Extract Serial Number String        */
    memcpy(serialNumber, commandBuf + sizeof(Word), len);
    serialNumber[len] = '\0';       /* Add Null termination                */

    replyToCommand(1, CMD_OK);      /* Serial number updated so send OK    */
} /* microSetSerNoCmd() */

/****************************************************************************/
/* Function    : microSoftwareRevCmd                                        */
/* Purpose     : sends the software revision string                         */
/* Inputs      : None                                                       */
/* Outputs     : None, but string is sent to serial port                    */
/****************************************************************************/
    Void
microSoftwareRevCmd( Void )
{
    writeDataPacket((Char *) softwareRev, (Int16) strlen((const char *)
             softwareRev));
} /* microSoftwareRevCmd() */

/****************************************************************************/
/* Function    : microStatus                                                */
/* Purpose     : sends the microcontroller software status word             */
/* Inputs      : None                                                       */
/* Outputs     : None, but status word is sent to serial port               */
/****************************************************************************/
    Void
microStatus()
{
    writeWordPacket(board_status);  /* send the board status word           */
                                    /* to the serial port                   */
} /* microStatus() */

/****************************************************************************/
/* Function    : microPing                                                  */
/* Purpose     : ping command - echos sequence number to serial port        */
/* Inputs      : received command buffer                                    */
/* Outputs     : None, but sequence number is sent to serial port           */
/****************************************************************************/
    Void
microPing(command_buf)
    Byte    *command_buf;
{
    Word    sequenceNumber;
                                    /* read sequence number from buffer     */
    sequenceNumber =  wordFromBuf(command_buf, 2);
                                    /* Send back the sequence number        */
    replyToCommand(2, CMD_OK, sequenceNumber);
} /* microPing */

/****************************************************************************/
/* Function    : microSetGndFltTest                                         */
/* Purpose     : processes ground fault test On/Off serial port command     */
/* Inputs      : received command buffer                                    */
/* Outputs     : None, but command acknowledge sent and the ground fault    */
/*               test signal is enabled/disabled                            */
/****************************************************************************/
    Void
microSetGndFltTest(Byte *command_buf)
{
    Word    gnd_flt_test;
                                        /* read argument from buffer        */
    gnd_flt_test = wordFromBuf(command_buf, 2);

    if ((gnd_flt_test == ON) || (gnd_flt_test == OFF))
    {
        groundFaultTest(gnd_flt_test);  /* Turn Gnd Flt Test On/Off         */
        replyToCommand(1, CMD_OK);      /* Command executed OK              */
    } /* if */
    else
        replyToCommand(1, EARGUMENT);   /* Argument is out of range         */

} /* microSetGndFltTest() */

/****************************************************************************/
/* Function    : microReadMemory                                            */
/* Purpose     : reads up to 128 bytes from microcontoller memort           */
/* Inputs      : received command buffer                                    */
/* Outputs     : None, but memory contents are sent to serial port          */
/****************************************************************************/
    Void
microReadMemory(commandBuf)
    Byte    *commandBuf;
{
    Nat16 startAddr;
    Word  nBytes;
    Byte  replyBuf[130];

                                        /* Read start address from buffer   */
    startAddr = wordFromBuf(commandBuf, 2);

                                        /* Read number of bytes from buffer */
                                        /* Maximum size is 128 bytes        */
    nBytes = min(wordFromBuf(commandBuf, 4), 128);

    wordToBuf(replyBuf, nBytes);        /* Echo size at start of message    */
    memcpy(replyBuf + sizeof(Word), (const *) startAddr, nBytes);

    writeDataPacket(replyBuf, nBytes + sizeof(Word));   /* Transmit reply   */
} /* microReadMemory() */

/****************************************************************************/
/* Function    : microWriteMemory                                           */
/* Purpose     : fills microcontroller memory with upto 128 bytes           */
/* Inputs      : received command buffer                                    */
/* Outputs     : None, but acknowledge is sent to serial port               */
/****************************************************************************/
    Void
microWriteMemory(commandBuf)
    Byte    *commandBuf;
{
    Nat16 startAddr;
    Word  nBytes;

                                        /* Read start address from buffer   */
    startAddr = wordFromBuf(commandBuf, 2);

                                        /* Read number of bytes from buffer */
                                        /* Maximum size is 128 bytes        */
    nBytes = min(wordFromBuf(commandBuf, 4), 128);

    memcpy((Byte *) startAddr, (const *)(commandBuf+sizeof(Byte) * 6),nBytes);
    replyToCommand(1, CMD_OK);          /* Command executed OK              */
} /* microWriteMemory() */

/****************************************************************************/
/* Function    : microScanADCCmd                                            */
/* Purpose     : Read 8 microcontroller A/D channels & send to serial port  */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
microScanADCCmd( Void )
{
    Byte chan;
    Word buffer[NUM_CPU_ADC_CHANS];
    Word *ptr = buffer;

    for (chan = 0; chan < NUM_CPU_ADC_CHANS; chan++)
        wordToBuf( (Byte*) ptr++, (Word) readAnalogChannel(chan));

                                        /* Send packet to serial port       */
    writeDataPacket( (Byte *) buffer, ptr - buffer);
} /* microScanADCCmd() */

/****************************************************************************/
/* Function    : microReadHSIPortCmd                                        */
/* Purpose     : Read state of HSI port bits and send to serial port        */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
microReadHSIPortCmd( Void )
{
    Byte old_ioc1;
                                    /* Select HSI/HSO pins to HSI inputs    */
    intDisable;                     /* Disable interrupts to change WRS     */
    wsr = HWINDOW15;                /* Select horizontal register window 15 */
    old_ioc1 = ioc1;                /* Read old value from IOC1             */
    wsr = HWINDOW0;                 /* Select horizontal register window 0  */
                                    /* Write new value to IOC1              */
    ioc1 = old_ioc1 & ~(IOC1_HSO_4_ENABLE | IOC1_HSO_5_ENABLE);
    intEnable;

                                     /* send HSI Status to serial port      */
    replyToCommand(2, CMD_OK, (Word) hsi_status);
} /* microReadHSIPortCmd() */

/****************************************************************************/
/* Function    : microWriteHSOPortCmd                                       */
/* Purpose     : Process command to set state of HSO.5 and HSO.6 port bits  */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
microWriteHSOPortCmd(Byte *commandBuf)
{
    Byte    old_ioc1;
    Word    command;
    Word    hso4State, hso5State;
                                    /* Select HSI/HSO pins to HSI inputs    */
    intDisable;                     /* Disable interrupts to change WRS     */
    wsr = HWINDOW15;                /* Select horizontal register window 15 */
    old_ioc1 = ioc1;                /* Read old value from IOC1             */
    wsr = HWINDOW0;                 /* Select horizontal register window 0  */
                                    /* Write new value to IOC1              */
    ioc1 = old_ioc1 | IOC1_HSO_4_ENABLE | IOC1_HSO_5_ENABLE;
    intEnable;

    wordsFromBuf(commandBuf, 3, &command, &hso4State, &hso5State);

    intDisable;                 /* Disable Interrupts while changing HSO    */
                                /* Wait for HSO holding register to empty   */
    while (ios0 & IOS0_HOLD_REG_FULL);

    if (hso4State != 0)
    {
        hso_command = HSO_CMD_USE_T1 | HSO_CMD_NO_INTR |
                     HSO_CMD_SET_PIN | HSO_CMD_HSO_PIN_4;
        hso_time = timer1 + HSO_MIN_STATE_TIME;
    } /* if */
    else
    {
        hso_command = HSO_CMD_USE_T1 | HSO_CMD_NO_INTR |
                     HSO_CMD_CLR_PIN | HSO_CMD_HSO_PIN_4;
        hso_time = timer1 + HSO_MIN_STATE_TIME;
    } /* else */

    if (hso5State)
    {
        hso_command = HSO_CMD_USE_T1 | HSO_CMD_NO_INTR |
                     HSO_CMD_SET_PIN | HSO_CMD_HSO_PIN_5;
        hso_time = timer1 + HSO_MIN_STATE_TIME;
    } /* if */
    else
    {
        hso_command = HSO_CMD_USE_T1 | HSO_CMD_NO_INTR |
                     HSO_CMD_CLR_PIN | HSO_CMD_HSO_PIN_5;
        hso_time = timer1 + HSO_MIN_STATE_TIME;
    } /* if */

    intEnable;                  /* Disable Interrupts while changing HSO    */

    replyToCommand(1, CMD_OK);  /* Send Command OK to serial port           */
} /* microWriteHSOPortCmd() */

/****************************************************************************/
/* Function    : microSetLinkTimeoutCmd                                     */
/* Purpose     : Process command to set packet receive timeout time         */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
microSetLinkTimeoutCmd(command_buf)
    Byte    *command_buf;           /* Buffer containing arguments          */
{
    Nat16 linkRxTimeout;
                                    /* Get link rx timeout from command buf */
    linkRxTimeout =  wordFromBuf(command_buf,sizeof(Word));

    replyToCommand(1, CMD_OK);      /* Send command OK                      */
    setLinkRxTimeout(linkRxTimeout);/* Change timeout value after cmd reply */
} /* microSetLinkTimeoutCmd() */

/****************************************************************************/
/* Function    : microGetLinkTimeoutCmd                                     */
/* Purpose     : Process command to get packet receive timeout time         */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
microGetLinkTimeoutCmd( Void )
{
    replyToCommand( 2, CMD_OK, getLinkRxTimeout() );
} /* microGetLinkTimeoutCmd() */

/****************************************************************************/
/* Function    : processMicroCommands                                       */
/* Purpose     : Decode generic microcontroller serial port commands        */
/* Inputs      : Command buffer and command buffer length                   */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
processMicroCommands( Char *commandBuf, Int16 commandLen )
{
                                /* try to recognize the command and call    */
                                /* the appropriate handler                  */
    switch (wordFromBuf(commandBuf, 0))
    {
        case MICRO_APP | MICRO_RESET:
            microResetCmd();        /* Reset Microcontroller Command        */
            break;

        case MICRO_APP | MICRO_IDENT:
            microIdentify();        /* Send Identification String           */
            break;

       case MICRO_APP | GET_MICRO_SER_NO:
            microGetSerNoCmd( (Char *) microSerialNo );
            break;                  /* Send Micro Board Serial Number       */

       case MICRO_APP | SET_MICRO_SER_NO:
            microSetSerNoCmd( commandBuf, commandLen,
                    (Char*) microSerialNo, MICRO_SERNO_LEN );
            break;                  /* Send Micro Board Serial Number       */

        case MICRO_APP | GET_STATUS:
            microStatus();          /* Send Status Word                     */
            break;

        case MICRO_APP | SOFTWARE_REV:
            microSoftwareRevCmd();  /* Send Software Revision String        */
            break;

        case MICRO_APP | READ_MEMORY:
            microReadMemory(commandBuf);
            break;                  /* Read microcontroller memory          */

        case MICRO_APP | WRITE_MEMORY:
            microWriteMemory(commandBuf);
            break;                  /* Write microcontroller memory         */

        case MICRO_APP | MICRO_PING:
            microPing(commandBuf);  /* Send ping message reply              */
            break;

       case MICRO_APP | READ_ADC_CHANS:
            microScanADCCmd();
            break;

        case MICRO_APP | READ_HSI_PORT:
            microReadHSIPortCmd();
            break;

        case MICRO_APP | WRITE_HSO_PORT:
            microWriteHSOPortCmd(commandBuf);
            break;
                                     /* Set link timeout time               */
        case MICRO_APP | SET_LINK_RX_TIME:
            microSetLinkTimeoutCmd(commandBuf);
            break;

                                     /* Get link timeout time               */
        case MICRO_APP | GET_LINK_RX_TIME:
            microGetLinkTimeoutCmd();
            break;

        default:
                                    /* Command received but not valid so    */
                                    /* Send pack an Invalid Command message */
            replyToCommand( 1, ECOMMAND );
            break;

    } /* switch */
} /* processMicroCommands() */

/****************************************************************************/

    Boolean
alarmThresholdCheck( Int16 value, Int16 warnThreshold,
    Int16 alarmThreshold, Int16 hysteresis, microAlarm *alarmStatus )
{
    microAlarm status = *alarmStatus;

    switch (status)
    {
        case MICRO_ALARM_OK:
            if ( value >= alarmThreshold )
               *alarmStatus = MICRO_ALARM;

            else if (value >= warnThreshold)
               *alarmStatus = MICRO_WARNING;
            break;

        case MICRO_WARNING:
            if (value >= alarmThreshold)
                *alarmStatus = MICRO_ALARM;

            else if (value < (warnThreshold - hysteresis) )
                *alarmStatus = MICRO_ALARM_OK;
            break;

        case MICRO_ALARM:
            if (value < warnThreshold)
                *alarmStatus = MICRO_ALARM_OK;

            else if (value < (alarmThreshold - hysteresis) )
                *alarmStatus = MICRO_WARNING;
            break;

    } /* select */

    return( (status != *alarmStatus) ? TRUE : FALSE);
                                    /* Return TRUE if alarm status changed */
} /* alarmThresholdCheck() */

/****************************************************************************/
/* Function    : thresholdTest                                              */
/* Purpose     : Compare value to a threshold with hysteresis               */
/* Inputs      : Value, threshold, hysteresis, pointer to previous state    */
/* Outputs     : State is changed and also returned by the function         */
/****************************************************************************/
    Boolean
thresholdTest(Int16 value, Int16 threshold, Int16 hysteresis, Boolean *status)
{
    if (value >= threshold)         /* If value >= threshold then alarm     */
    {
        if (*status == FALSE)       /* If status has changed                */
            *status = TRUE;         /* then return TRUE                     */
    } /* if */
    else
    {                               /* If status was true and has changed   */
        if ( (*status == TRUE) && (value < threshold - hysteresis) )
            *status = FALSE;        /* then report FALSE                    */
    } /* else */

    return(*status);                /* return status                        */
} /* thresholdTest() */

    Boolean
alarmBelowThresholdCheck( Int16 value, Int16 warnThreshold,
        Int16 alarmThreshold, Int16 hysteresis, microAlarm *alarmStatus )
{
    microAlarm status = *alarmStatus;

    switch (status)
    {
        case MICRO_ALARM_OK:
            if ( value <= alarmThreshold )
               *alarmStatus = MICRO_ALARM;

            else if (value <= warnThreshold)
               *alarmStatus = MICRO_WARNING;

            break;

        case MICRO_WARNING:
            if (value <= alarmThreshold)
                *alarmStatus = MICRO_ALARM;

            else if (value > (warnThreshold + hysteresis) )
                *alarmStatus = MICRO_ALARM_OK;
            break;

        case MICRO_ALARM:
            if (value > warnThreshold)
                *alarmStatus = MICRO_ALARM_OK;

            else if (value > (alarmThreshold + hysteresis) )
                *alarmStatus = MICRO_WARNING;
            break;

    } /* select */

    return( (status != *alarmStatus) ? TRUE : FALSE);
                                    /* Return TRUE if alarm status changed */
} /* alarmBelowThresholdCheck() */
