//=============================================================================
// Header file with definition of the software interface to the
// ROBO TX Controller program.
// Can be used for building C-programs which can run under control
// of the ROBO TX Controller firmware in download (local) mode.
//
// Erweiterte Version vom 20.10.2016:
// Um die Programmierung in C zu erleichtern, wurden die folgenden Prozeduren ergänzt:
//      i2cread, i2cwrite, txlamp, txmotor, txstartmotorsync, txaremotorsready,
//      txstartmotor, txmotorready,
//      txclearcounter, txgetcounter,
//      txtrack, txinput, txanalog, txvoltage, txdistance 
// Der Pointer zum Transfer Area p_ta ist global deklariert und daher in diesen 
// Prozeduren zugänglich.
//
// Disclaimer - Exclusion of Liability
//
// This software is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. It can be used and modified by anyone
// free of any license obligations or authoring rights.
//=============================================================================

#ifndef __ROBO_TX_PRG_H__
#define __ROBO_TX_PRG_H__

#include <math.h>

#define CALL_CYCLE_MS    1  // firmware calls program each millisecond

#define MIN(a, b) ((a <= b) ? a : b)
#define MAX(a, b) ((a >= b) ? a : b)

#define TA_VERSION          0x08010101  // Transfer Area version. Is used by firmware to check if format of the
                                        // Transfer Area used in program is the same as the one used in firmware.

#define PRG_MEM_START       0x30700000  // start address of the program memory
#define PRG_MEM_SIZE        0x000D0000  // size of the program memory

#define PRG_MAGIC           0x336699AA

#define MAX_FRAME_SIZE      1024  // maximum size of displayable frame (display buffer size)

#if defined(__cplusplus)
    #define NULL            0L
#else
    #define NULL            ((void *)0)
#endif

#undef  TRUE
#define TRUE                (1 == 1)

#undef  FALSE
#define FALSE               (!TRUE)

#ifndef __cplusplus
typedef enum
{
    false = FALSE,
    true  = TRUE
} bool;
#endif // __cplusplus

typedef signed char         CHAR8;
typedef unsigned char       UCHAR8;

typedef unsigned char       BOOL8;      //  boolean variable (should be TRUE or FALSE)
typedef unsigned short      BOOL16;     //  boolean variable (should be TRUE or FALSE)
typedef unsigned long       BOOL32;     //  boolean variable (should be TRUE or FALSE)

typedef signed char         INT8;
typedef signed short        INT16;
typedef signed int          INT32;

typedef unsigned char       UINT8;
typedef unsigned short      UINT16;
typedef unsigned long       UINT32;


#include "ROBO_TX_FW.h"

TA * p_ta;

BOOL8 istate[8] = {FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE};
BOOL8 mstate[4] = {FALSE,FALSE,FALSE,FALSE};

enum bt_commands_e
{
    CMD_NO_CMD,
    CMD_CONNECT,
    CMD_DISCONNECT,
    CMD_START_LISTEN,
    CMD_STOP_LISTEN,
    CMD_START_RECEIVE,
    CMD_STOP_RECEIVE,
    CMD_SEND
};

extern UCHAR8 bt_address_table[BT_CNT_MAX][BT_ADDR_LEN];

 // At the beginning of the program code should be this structure
struct prg_code_intro_s
{
    unsigned long           magic;      // PRG_MAGIC
    FT_VER                  ta_version; // TA_VERSION
    unsigned long           entry;      // entry point of the program, usually address of
                                        // the program dispatcher (PrgDisp) function
};

static void I2cCallback
(
    TA * p_ta_array,
    I2C_CB * p_data
);

static  void i2cread(UCHAR8 devaddr, UINT32 offset,  UCHAR8 flags)
{p_ta->hook_table.I2cRead (devaddr, offset, flags , I2cCallback);} 


static  void i2cwrite(UCHAR8 devaddr, UINT32 offset, UINT32 data,  UCHAR8 flags)
{p_ta->hook_table.I2cWrite (devaddr, offset, data, flags , I2cCallback);} 

static void txsinglelamp ( unsigned short  n, UINT16 duty)     
{  p_ta->output.duty[6] = 0; p_ta->output.duty[7] = 0; p_ta->output.duty[5] = 0;
   p_ta->output.duty[n] = duty ;
}
static void txlamp ( unsigned short  n, UINT16 duty)     
{ p_ta->output.duty[n] = duty ;
}

static void txclearcounter ( unsigned short MOTOR_IDX)
 {  p_ta->input.cnt_resetted[MOTOR_IDX] = FALSE;
    p_ta->output.cnt_reset_cmd_id[MOTOR_IDX]++;  
 }     
static INT16 txgetcounter ( unsigned short  MOTOR_IDX) 
 {  return p_ta->input.counter[MOTOR_IDX];
 }
static void txstartmotorsync ( unsigned short MASTER_MOTOR_IDX, unsigned short SLAVE_MOTOR_IDX,
unsigned short mdir, unsigned short sdir, UINT16 duty, UINT16 icount)     
{  
if (!mstate[MASTER_MOTOR_IDX]) {
   p_ta->config.motor[MASTER_MOTOR_IDX] = TRUE;
   mstate[MASTER_MOTOR_IDX] = TRUE;
   p_ta->state.config_id += 1;
}  
if (!mstate[SLAVE_MOTOR_IDX]) {
   p_ta->config.motor[SLAVE_MOTOR_IDX] = TRUE;
   mstate[SLAVE_MOTOR_IDX] = TRUE;
   p_ta->state.config_id += 1;
} 
if (mdir == 1) {
 p_ta->output.duty[2 * MASTER_MOTOR_IDX ] = duty;
                    p_ta->output.duty[2 * MASTER_MOTOR_IDX + 1] = 0; }
    else {if (mdir == 2) {
 p_ta->output.duty[2 * MASTER_MOTOR_IDX ] = 0;
                    p_ta->output.duty[2 * MASTER_MOTOR_IDX + 1] = duty; }
          else {p_ta->output.duty[2 * MASTER_MOTOR_IDX ] = 0;
                    p_ta->output.duty[2 * MASTER_MOTOR_IDX + 1] = 0;} } ;
 if (sdir == 1) {
 p_ta->output.duty[2 * SLAVE_MOTOR_IDX ] = duty;
                    p_ta->output.duty[2 * SLAVE_MOTOR_IDX + 1] = 0; }
    else {if (sdir == 2) {
 p_ta->output.duty[2 * SLAVE_MOTOR_IDX ] = 0;
                    p_ta->output.duty[2 * SLAVE_MOTOR_IDX + 1] = duty; }
          else {p_ta->output.duty[2 * SLAVE_MOTOR_IDX ] = 0;
                    p_ta->output.duty[2 * SLAVE_MOTOR_IDX + 1] = 0;} } ;
        // Master motor should run until counter reaches icount
        p_ta->output.distance[MASTER_MOTOR_IDX] = icount;

        // Slave motor should run until counter reaches icount
        p_ta->output.distance[SLAVE_MOTOR_IDX] = icount;

        // Link slave motor to master motor
        p_ta->output.master[SLAVE_MOTOR_IDX] = MASTER_MOTOR_IDX + 1;

        p_ta->input.motor_pos_reached[MASTER_MOTOR_IDX] = FALSE;
        p_ta->input.motor_pos_reached[SLAVE_MOTOR_IDX] = FALSE;

        // Start extended motor control command for master motor
        p_ta->output.motor_ex_cmd_id[MASTER_MOTOR_IDX]++;

        // Start extended motor control command for slave motor
        p_ta->output.motor_ex_cmd_id[SLAVE_MOTOR_IDX]++;
} 
static void txstartmotor ( unsigned short MASTER_MOTOR_IDX,
unsigned short mdir, UINT16 duty, UINT16 icount)     
{  
if (!mstate[MASTER_MOTOR_IDX]) {
   p_ta->config.motor[MASTER_MOTOR_IDX] = TRUE;
   mstate[MASTER_MOTOR_IDX] = TRUE;
   p_ta->state.config_id += 1;
}  
if (mdir == 1) {
 p_ta->output.duty[2 * MASTER_MOTOR_IDX ] = duty;
                    p_ta->output.duty[2 * MASTER_MOTOR_IDX + 1] = 0; }
    else {if (mdir == 2) {
 p_ta->output.duty[2 * MASTER_MOTOR_IDX ] = 0;
                    p_ta->output.duty[2 * MASTER_MOTOR_IDX + 1] = duty; }
          else {p_ta->output.duty[2 * MASTER_MOTOR_IDX ] = 0;
                    p_ta->output.duty[2 * MASTER_MOTOR_IDX + 1] = 0;} } ;
        // Master motor should run until counter reaches icount
        p_ta->output.distance[MASTER_MOTOR_IDX] = icount;

        p_ta->input.motor_pos_reached[MASTER_MOTOR_IDX] = FALSE;
 
        // Start extended motor control command for master motor
        p_ta->output.motor_ex_cmd_id[MASTER_MOTOR_IDX]++;
} 
 
static void txmotor ( unsigned short MOTOR_IDX, unsigned short dir, UINT16 duty)     
{if (!mstate[MOTOR_IDX]) {
   p_ta->config.motor[MOTOR_IDX] = TRUE;
   mstate[MOTOR_IDX] = TRUE;
   p_ta->state.config_id += 1;
}  
if (dir == 1) {
 p_ta->output.duty[2 * MOTOR_IDX ] = duty;
                    p_ta->output.duty[2 * MOTOR_IDX + 1] = 0; }
    else {if (dir == 2) {
 p_ta->output.duty[2 * MOTOR_IDX ] = 0;
                    p_ta->output.duty[2 * MOTOR_IDX + 1] = duty; }
          else {p_ta->output.duty[2 * MOTOR_IDX ] = 0;
                    p_ta->output.duty[2 * MOTOR_IDX + 1] = 0;} } ;
 }
static INT16 txdistance ( unsigned short  SENSOR_IDX) 
{  if (!istate[SENSOR_IDX]) {
     // Configure input to "Distance" mode
    p_ta->config.uni[SENSOR_IDX].mode = MODE_ULTRASONIC;
    // Inform firmware that configuration was changed
    p_ta->state.config_id += 1;
    istate[SENSOR_IDX] = TRUE;
    }
    return p_ta->input.uni[SENSOR_IDX];
}  

static INT16 txtrack ( unsigned short  BUTTON_IDX) 
{  if (!istate[BUTTON_IDX]) {
     // Configure input to "Digital Voltage" mode
    p_ta->config.uni[BUTTON_IDX].mode = MODE_U;
    p_ta->config.uni[BUTTON_IDX].digital = TRUE;
    // Inform firmware that configuration was changed
    p_ta->state.config_id += 1;
    istate[BUTTON_IDX] = TRUE;
    }
   return p_ta->input.uni[BUTTON_IDX];
}   

static INT16 txaremotorsready ( unsigned short MASTER_MOTOR_IDX, unsigned short SLAVE_MOTOR_IDX) 
{  
if (istate[MASTER_MOTOR_IDX] && istate[SLAVE_MOTOR_IDX] ) {
    return p_ta->input.motor_pos_reached[MASTER_MOTOR_IDX] && p_ta->input.motor_pos_reached[SLAVE_MOTOR_IDX] ;}
else { return -1 ;}  
}
static INT16 txmotorready ( unsigned short MASTER_MOTOR_IDX) 
{  
if (istate[MASTER_MOTOR_IDX] ) {
    return p_ta->input.motor_pos_reached[MASTER_MOTOR_IDX] ;}
else { return -1 ;}  
}
static INT16 txinput ( unsigned short  BUTTON_IDX) 
{  if (!istate[BUTTON_IDX]) {
     // Configure input to "Digital 5 kOhm" mode
    p_ta->config.uni[BUTTON_IDX].mode = MODE_R;
    p_ta->config.uni[BUTTON_IDX].digital = TRUE;
    // Inform firmware that configuration was changed
    p_ta->state.config_id += 1;
    istate[BUTTON_IDX] = TRUE;
    }
   return p_ta->input.uni[BUTTON_IDX];
}   
static INT16 txanalog ( unsigned short  BUTTON_IDX) 
{  if (!istate[BUTTON_IDX]) {
     // Configure input to "Analog 5 kOhm" mode
    p_ta->config.uni[BUTTON_IDX].mode = MODE_R;
    p_ta->config.uni[BUTTON_IDX].digital = FALSE;
    // Inform firmware that configuration was changed
    p_ta->state.config_id += 1;
    istate[BUTTON_IDX] = TRUE;
    }
   return p_ta->input.uni[BUTTON_IDX];
}   
static INT16 txvoltage ( unsigned short  BUTTON_IDX) 
{  if (!istate[BUTTON_IDX]) {
     // Configure input to "Analog voltage" mode
    p_ta->config.uni[BUTTON_IDX].mode = MODE_U;
    p_ta->config.uni[BUTTON_IDX].digital = FALSE;
    // Inform firmware that configuration was changed
    p_ta->state.config_id += 1;
    istate[BUTTON_IDX] = TRUE;
    }
   return p_ta->input.uni[BUTTON_IDX];
}   


// This function is called only once by the program dispatcher (PrgDisp) at the very first call
// of the program dispatcher by the firmware
void PrgInit
(
    TA * p_ta,  // pointer to the array of transfer area parts
    int n_ta    // number of transfer area parts in array (equal to TA_COUNT)
);


// This function is called periodically by the program dispatcher (PrgDisp) with the period
// of CALL_CYCLE_MS

int PrgTic
(
    TA * p_ta,  // pointer to the array of transfer area parts
    int n_ta    // number of transfer area parts in array (equal to TA_COUNT)
);


// This function converts the status of the executed Bluetooth command to a string
// and displays this string on the display of a ROBO TX Controller
BOOL32 BtDisplayCommandStatus
(
    TA * p_ta,
    UCHAR8 * bt_address,
    UINT32 channel,
    enum bt_commands_e command,
    CHAR8 command_status
);


#endif // __ROBO_TX_PRG_H__
