📄 mssmartstar.c
字号:
/********* Sample Program ****************************************************
MSSmartStar.c Copyright(c) 2003 Z-World Engineering.
Legal Notice This program has been written by the Technical Support Staff at
Z-World in response to several customer requests. As such, it
has NOT had the testing and validation procedures which our
"standard" software products have. It is being made available
as a sample. There is no warranty, implied or otherwise.
DESCRIPTION: Sample program demonstrating modbus slave control via RS485
for the SmartStar expandable system.
This sample will allow control via a Modbus Master controller
of the following;
1. Relay output card
1. Digital I/O card
1. Analog input card (0-10VDC was used).
(Will read both RAW data and Calibrated VDC data.)
The modbus master software used with this sample can be
downloaded (Demo Version) via the following company link.
WinTECH Software Systems incorporated
http://www.win-tech.com
*****************************************************************************/
/****** Start of user configurable macros *********************************/
// This sets up the individual card locations, for this sample. Put the
// slot number next to the corresponding macro.
// Relay card location
#define RELAY_SLOT 1
// Digital IO card location
// this was setup using a single card with 16 inputs and 8 outputs, so the
// slot number will be the same for both.
#define INPUT_SLOT 2
#define OUTPUT_SLOT 2
// Analog input card location
#define ADC_SLOT 0
// Define the number of channels for each type of input
// Number of analog inputs on that slot
#define ADC_AMOUNT 10
// Number of relay outputs
#define RELAY_AMOUNT 8
// Number of digitial I/O
#define INPUT_AMOUNT 16
#define OUTPUT_AMOUNT 8
// This sets up the MODBUS address. It MUST be unique from any other modbus
// device that is on the bus.
#define SLAVE_ADDRESS 0x01
// The baudrate used for the MODBUS protocol.
#define MODBUS_BAUDRATE 9600 // 9600 is default, 19200 works to.
// Choose the protocol type, 0 for ASCII, 1 for RTU. This MUST match the
// Modbus master protocol selection.
#define MODBUS_TYPE 1
/****** Done with user configurable macros *********************************/
// Setup up the serial port buffer size
#define DINBUFSIZE 31
#define DOUTBUFSIZE 31
// Modbus slave library, it also calls ms_rab.lib
#use "msz_rab.lib"
// Output Shadow Registers
char acShad[RELAY_AMOUNT + OUTPUT_AMOUNT + 1];
/*
/*=========================================================================*\
msDone:
Called just after a received Modbus command has been
processed and just before the reply is sent. This function is intended
to be used to unlock resources that were locked by msStart(). Locking
resources may or may not be required, depending on how the msIn(),
msInput(), msOutRd() and msOutWr() functions are implemented in a
particular Modbus slave application. Note that Modbus command handler
functions in MS_RAB.LIB may make multiple calls to those functions while
responding to a single Modbus command.
\*=========================================================================*/
nodebug
void msDone(void)
{
// place any locked resources required.
}
/*=========================================================================*\
msStart:
Called just before a received Modbus packet is processed,
this function is primarily intended to be used to lock resources so
that data returned in one Modbus response packet are atomic. Locking
resources may or may not be required, depending on how the msIn(),
msInput(), msOutRd() and msOutWr() functions are implemented in a
particular Modbus slave application. Note that Modbus command handler
functions in MS_RAB.LIB may make multiple calls to those functions while
responding to a single Modbus command.
\*=========================================================================*/
nodebug
void msStart(void)
{
// place any locked resources required.
}
/*=========================================================================*\
msDinit:
Sets up and opens the serial port. default settings are 8 data bits
1 stop bit, no parity, no flow control.
\*=========================================================================*/
int msDinit(unsigned qBaud)
{
// Open the serial port. THIS MUST BE DONE PRIOR TO SETTING THE
// DATA BITS AND PARITY SETTINGS.
serDopen(qBaud);
// setup parity. Either PARAM_OPARITY, PARAM_EPARITY, PARAM_NOPARITY,
// or PARAM_2STOP
serDparity(PARAM_EPARITY);
// setup data bits. Either PARAM_7BIT, or PARAM_8BIT
serDdatabits(PARAM_8BIT);
// Set the Serial port mode. Used for Zworld SBC's only.
serMode(0);
return(1);
}
/*=========================================================================*\
msDtx:
This functions is used for enabling the RS485 transmitter.
\*=========================================================================*/
void msDtx()
{
// Turn on the transmitter
ser485Tx();
}
/*=========================================================================*\
msDrx:
This functions is used for disabling the RS485 transmitter.
\*=========================================================================*/
void msDrx()
{
// Make sure all of the data has been sent by;
// 1.) checking the write buffer for any bytes left
// 2.) checking the status of the Write interrupt transmit bit (2).
// 3.) checking the status of the Write interrupt data bit (3)
while (serDwrUsed() || BitRdPortI(SDSR,2) || BitRdPortI(SDSR,3));
// turn off the transmitter
ser485Rx();
// Since we echo what we send, flush the read buffer, so that you are
// ready for the next packet.
serDrdFlush();
}
/*=========================================================================*\
msOutRd: (01 Coil Status)
Used for reading output coil status. A user defined shadow register
is required in your code which will need to be updated when digital/relay
outputs are toggled. The function below only sends the value, it does
not update it.
\*=========================================================================*/
int msOutRd(unsigned wCoil, int *pnState)
{
// Check to see if a valid output channel is being called.
if (wCoil > (OUTPUT_AMOUNT + RELAY_AMOUNT) ) return MS_BADADDR;
// copy the contents of the coil shadow element.
*pnState = acShad[wCoil];
return 0;
}
/*=========================================================================*\
msOutWr: (01 Coil Writing)
Used for Writing to individual output coils. This sample sets up
the first OUTPUT_AMOUNT of coils to be the digital, and the rest
to be the RELAY_AMOUNT coils.
\*=========================================================================*/
int msOutWr(unsigned wCoil, int bState)
{
// Check to see if a valid output channel is being called.
if ( wCoil > (OUTPUT_AMOUNT + RELAY_AMOUNT) ) return MS_BADADDR;
// update the shadow used in the msOutRd function.
acShad[wCoil] = bState;
// If the Coil is a Digital Output;
if (wCoil <= OUTPUT_AMOUNT) digOut(ChanAddr(OUTPUT_SLOT,wCoil),bState);
// If the coil is a Relay Output;
else relayOut( ChanAddr(RELAY_SLOT, wCoil - OUTPUT_AMOUNT) ,bState) ;
return 0;
}
/*=========================================================================*\
msIn: (02: Input Status)
Used for reading the state of the individual digital inputs.
\*=========================================================================*/
int msIn(unsigned wCoil, int *pnState)
{
// Check to see if a valid input channel is being called.
if (wCoil > INPUT_AMOUNT) return MS_BADADDR;
// Read the input and store it.
*pnState = digIn(ChanAddr(INPUT_SLOT, wCoil));
return 0;
}
/*=========================================================================*\
msRead: (03: Reading from a Holding Register)
Used (in this sample)
For reading the individual CALIBRATED value of each adc inputs.
Since Modbus deals with unsigned ints (words), and our VDC input
function returns a float, the function will need to store the value
as two registers type casted into unsigned ints for the Modbus
protocol to accept properly. How this is done must be the same for
both the Master and the slave. This example will put the Most
Significant word of the float into the odd register, and the least
significant word into the even register. (ie register 1 and register
2 will hold the float value of adc input 0, register 3 and 4 will
hold adc input 1 etc.).
\*=========================================================================*/
int msRead(unsigned wReg, unsigned *pwValue)
{
static float calVal;
static char cVal[6];
// Since it will take two registers per input, you must double the
// amount of register neccessary.
if (wReg > (ADC_AMOUNT * 2) ) return MS_BADADDR;
// if the input is odd the get the new adc value
if ( !(wReg % 2) )
{
// You must divide the wReg value by 2 when getting the proper channel
calVal = anaInVolts(ChanAddr(ADC_SLOT, wReg/2));
// move the adc float value int a holding array of 4 bytes
*(float *) & cVal[0] = calVal;
// copy the first 2 bytes of the holding array into the register value
*pwValue = *(unsigned int *) &cVal[0];
}
// if the register is even
else
{
// copy the last 2 bytes of the holding array into the register value.
// DO NOT recalculate the input value.
*pwValue = *(unsigned int *) &cVal[2];
}
return 0;
}
/*=========================================================================*\
msWrite: (03: Writing to Holding Register)
Not used in this sample.
This could be used for write to a DAC card channel, much like reading
from a ADC card channel shown in msRead.
\*=========================================================================*/
nodebug
int msWrite(unsigned wReg, unsigned wValue)
{
// Place code here.
return 0;
}
/*=========================================================================*\
msInput: (04: Input Register)
Used (in this sample)
For reading the individual RAW DATA of each adc input.
\*=========================================================================*/
int msInput(unsigned wReg, unsigned *pwValue)
{
if ( wReg > ADC_AMOUNT )return MS_BADADDR;
// move the analog value into the register.
*pwValue = anaIn(ChanAddr(ADC_SLOT, wReg));
return 0;
}
void main(void)
{
auto int loop;
// Clear the output shadow array at startup of this sample
memset(acShad, 0, sizeof(acShad));
// initialize the SBC
brdInit();
#if (!MODBUS_TYPE)
// Open Serial port D in ASCII mode
msaDinit(SLAVE_ADDRESS, MODBUS_BAUDRATE);
#else
// Open Serial port D in RTU mode
msrDinit(SLAVE_ADDRESS, MODBUS_BAUDRATE);
#endif
// If there are analog inputs required, get the cal. constants from the
// eeprom.
if (ADC_AMOUNT > 0)
{
for (loop = 0 ; loop < ADC_AMOUNT ; loop++)
// read in the cal. constants.
anaInEERd(ChanAddr(ADC_SLOT, loop));
}
for( ;; )
{
// This is the Modbus slave handler.
msRun();
// Other Costates Here!!! (Make sure your code does not block.)
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -