/* MT_X308: Modbus/TCP to Modbus/RTU Master/Slave converter 7188EX + X308 Compiler: BC++ 3.1 Compile mode: large Project: user.c v7000.c ..\LIB\7188EL.Lib ..\LIB\TCPIPL.Lib ..\LIB\VCOM_NNNN.Lib, with NNNN being the lib file's version. ..\LIB\MBT7_NNN.Lib, Modbus/TCP library for 7188E, NNN being the lib's version. Communication: Modbus Master <= Modbus/TCP => Ethernet port of 7188EX <==> [Modbus Kernel] <==> COM Ports of 7188EX <==> Modbus/RTU slave device <==> X-board Hardware: 7188EX + X308 Memory mapping iMemory_DO[00]~[05] <====> X308 DO Ch0~Ch5 iMemory_AI[00]~[03] <====> X308 AI Ch0~Ch3 Note: to use MBT7_NNN.Lib, the project is a little different to Xserver demos. You must remove vModbus.c from this project. [08/Dec/2006] by Liam */ #include #include "..\lib\7188e.h" #include "..\lib\vxcomm.h" #include "..\Lib\MBTCP.h" // Modbus/TCP Head file for I-7188E #include "..\Lib\Xboard\X308.h" // Change this if you don't mount a X board on // expansion bus. int iScanCOMPort=1; // of Modbus Kernel int iScanUserProcess=0; int iScanXboardProcess=0; int ScanUserProcess(int iProcess); int ScanXboard(int iProcess); //The I/O memory mapping unsigned char far iMemory_DI[100]; unsigned char far iMemory_DO[100]; int far iMemory_AI[100]; int far iMemory_AO[100]; void UserCount(void) { // user's timer trigger function // // In this function cannot use any function that will use the hardware signal "clock", // Such as: // 1. ClockHigh(),ClockLow(), ClockHighLow(), // 2. Any EEPROM functions. // 3. Any 5DigitLed functions. // 4. Any NVRAM function. // 5. Any RTC function.(GetTime(),SetTime(),GetDate(),SetDate()) // // refer to demo9 for example code } void UserInit(void) { /* In this function, user CAN: 1. initialize user's program. 2. set time interval for calling UserCount(). 3. set initial value of I/O or variables for UserLoopFun(). 4. set initial value of I/O or variables for another functions. 5. change the default TCP PORT 10000/9999/502 to others value. [after vcom3004.lib] Syntax: Port10000=newport_10000; for calling UserCmd (user.c) Port9999=newport_9999; for calling VcomCmd7000 (v7000.c) Port502=newport_502; for calling VcomCmdModbus (vModbus.c) [after vcom3002.lib] PortUser=newport_User; for calling VcomCmdUser (user.c) [after vcom3005.lib] Default port value: Port10000=10000; Port9999=9999; Port502=502; PortUser=0; If the port value is 0, Xserver will not listen that port. That means the port will be disable. */ int iRet; //======= Begin of Modbus Kernel ======= iRet=InitModbus(iMemory_DI,iMemory_DO,iMemory_AI,iMemory_AO); if(iRet==0) { // Initial Modbus configuration success. } else { // Initial Modbus configuration failure. } //======= End of Modbus Kernel======= iRet=X308_Init(); // Change this if you don't mount a X board on expansion bus. Port9999=0; //Disable listening TCP port 9999 to speed up 7188E. } void UserLoopFun(void) { //======= Begin of Modbus Kernel ======= if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusRTU_Slave) CheckModbusRTURequest(iScanCOMPort); // Is any request from Modbus/RTU Master ? if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusASCII_Slave) CheckModbusASCIIRequest(iScanCOMPort); // Is any request from Modbus/ASCII Master ? if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusRTU_Gateway) { SendModbusRequest(iScanCOMPort); // Passes request to modbus slave device. CheckResponseTimeout(iScanCOMPort); // If response timeout, sets iModbusAction // to IDLE status. CheckModbusResponse(iScanCOMPort); // Is any response from modbus slave device? SendModbusResponse(iScanCOMPort); // Passes response to Modbus/RTU Master. } iScanCOMPort++; if(iScanCOMPort>iTotalCOMPort) iScanCOMPort=1; //======= End of Modbus Kernel ======= iScanUserProcess=ScanUserProcess(iScanUserProcess); iScanXboardProcess=ScanXboard(iScanXboardProcess); // Change this if you don't // plug a X board on expansion bus. } int UserCmd(unsigned char *Cmd,unsigned char *Response) { sprintf(Response, "%s", Cmd); return 1; // return ok } int VcomUserBinaryCmd(TCPREADDATA *p) { /* VXCOMM.EXE 2.6.12(09/04/2001) or later will support this function. TCP PORT 10000, command 23 will call this function. user can get the following message: p->ReadUartChar : the buffer store the command data(include "23") p->Length : the command data length(include the two byte "23") p->Socket : the socket number that receive the command, that is when the user function want return message to the client, just use the socket to send data. use: VcomSendSocket(p->Socket,pdata,datalength); */ VcomSendSocket(p->Socket, "User-defined command(23)", 24); // return 24 bytes. return 1; /* any value will be accept */ } int VcomCmdUser(TCPREADDATA *p) { /* VCOM3005 (Feb,22,2002) or later will call this function for PortUser. When packets received by TCP PORT PortUser(user defined) of 7188E/8000E, Xserver will call this function. user can get the following message: p->ReadUartChar : the buffer store the command data. Maximum length of p->ReadUartChar is 32767 bytes. p->Length : the command data length. p->Socket : the socket number that receive the command, that is when the user function wants return message to the client, just use the socket to send data. usage: VcomSendSocket(p->Socket,pdata,datalength); */ /* here just send back the command to the client. */ VcomSendSocket(p->Socket, p->ReadUartChar, p->Length); return 1; /* any value will be accept */ } //===========================================================// // Functions about Modbus // //===========================================================// /***************************/ /* [ScanUserProcess] */ /* for 7188E/7188XB */ /* version 1.0.1 */ /* date: 9,Aug,2002 */ /***************************/ int ScanUserProcess(int iProcess) { /* iProcess: executes which process? return: executes which process next time? This function scans the X board and stores the results to the memory. Thus, users can use Modbus protocol to read the memory to get the values of the Xboard. You should cut the hole scan process to many small processes. That can reduce execution time of this function and speed up the scan rate. Xboard and user's process share the internal registers. To use the internal registers, you must notice don't overlap the registers. For X308 Memory mapping iMemory_DO[00]~[05] <====> X308 DO Ch0~Ch5 iMemory_AI[00]~[03] <====> X308 DI Ch0~Ch3 */ int iPort,iRet,i; int iNextProcess; switch(iProcess) { case 0: // Add user's process here. // For example, add CRC error number to AI register. iMemory_AI[0x10]=0x7188; //Address 0x10 is an example, //you can change it to suit your application. iNextProcess=1; break; case 1: // Add user's process here // For example, add Timeout error number to AI register. iMemory_DI[0x11]=1; //Address 0x11 is an example,=1 to set DI[0x11]=high. //you can change it to suit your application. iNextProcess=0; break; default: iNextProcess=0; break; } return iNextProcess; } /***************************/ /* [ScanXboard] */ /* for 7188E/7188XB */ /* version 1.0.1 */ /* date: 9,Aug,2002 */ /***************************/ int ScanXboard(int iProcess) { /* iProcess: executes which process? return: executes which process next time? This function scans the X board and stores the results to the memory. Thus, users can use Modbus protocol to read the memory to get the values of the Xboard. You should cut the hole scan process to many small processes. That can reduce execution time of this function and speed up the scan rate. Xboard and user's process share the internal registers. To use the internal registers, you must notice don't overlap the registers. Memory mapping iMemory_DO[00]~[05] <====> X308 DO Ch0~Ch5 iMemory_AI[00]~[03] <====> X308 DI Ch0~Ch3 */ int i,iNextProcess; float fValue; int iValue; switch(iProcess) { case 0: // Analog input ==> memory (Ch0) fValue=X308_AnalogIn(0); iMemory_AI[0]=(int)(fValue*3276.7); // 0~10 V ==> 0~32767 iNextProcess=1; break; case 1: // Analog input ==> memory (Ch1) fValue=X308_AnalogIn(1); iMemory_AI[1]=(int)(fValue*3276.7); // 0~10 V ==> 0~32767 iNextProcess=2; break; case 2: // Analog input ==> memory (Ch2) fValue=X308_AnalogIn(2); iMemory_AI[2]=(int)(fValue*3276.7); // 0~10 V ==> 0~32767 iNextProcess=3; break; case 3: // Analog input ==> memory (Ch3) fValue=X308_AnalogIn(3); iMemory_AI[3]=(int)(fValue*3276.7); // 0~10 V ==> 0~32767 iNextProcess=4; break; case 4: //Memory ==> digital output iValue=0; for(i=0; i<6; i++) // X308 has 6 DO channels { if(iMemory_DO[i]!=0) iValue+=(1<"); //for(i=0;i<*CommandLength;i++) // printCom1("[%02X] ",CommandData[i]&0xFF); } void Modbus_Response_Event(char* ResponseData,int* ResponseLength) { /* char* ResponseData: For Modbus/TCP, it includes 6 leading bytes. For Modbus/RTU, it doesn't include 6 leading bytes int* CommandLength: For Modbus/TCP, it includes 6 leading bytes. For Modbus/RTU, it doesn't include 6 leading bytes */ //If you want to change the content of the ResponseData, //you have to do 2 steps for Modbus/TCP or Modbus/RTU. //Step1: Change content (Note:you must know the modbus protocol well) //ResponseData[6]=0x19; //ResponseData[7]=0x75; //ResponseData[8]=0x04; //ResponseData[9]=0x01; //Step2: Update data length //*ResponseLength=10; //int i; //printCom1("\n\r Out==>"); //for(i=0;i<*ResponseLength;i++) // printCom1("[%02X] ",ResponseData[i]&0xFF); //printCom1("\n\r"); }