/* The lines with '//*** (XX)' are comments that point out the changes that may be
needed to make a new C component from the gs2_vfd.c file. Much of the original file
has been snipped to make this document shorter. Copy gs2_vfd.c to its own directory,
such as ~/emc2/my_new_mbdevice/ . Rename and edit the file. Copy the Makefile from the
vfs11_vfd.c source directory to this new device directory and edit to replace the
vfs11_vfd name to the new device name. Compile and install with the command
"make && sudo make install". - Kirk Wallace 20011/03/27 */
/*
//*** (1) Put new component name here
Based on a work (test-modbus program, part of libmodbus) which is
Copyright (C) 2001-2005 Stéphane Raimbault <stephane.raimbault@free.fr>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.
...
-s or --stopbits {1,2} (default 1)
Set serial stop bits to 1 or 2
-t or --target <n> (default 1)
Set MODBUS target (slave) number. This must match the device number you set on the GS2.
-v or --verbose
Turn on debug messages. Note that if there are serial errors, this may become annoying.
At the moment, it doesn't make much difference most of the time.
*/
#include <stdio.h>
#include <unistd.h>
#include <time.h>
....
#include "hal.h"
#include "modbus.h"
//*** (2) Document and describe new register sets
/* Read Registers:
0x2100 = status word 1
0x2101 = status word 2
...
0x210C = Firmware revision (never saw anything other than 0 here)
total of 13 registers */
#define START_REGISTER_R 0x2100 //*** (2) Enter new Modbus slave register start address here
#define NUM_REGISTERS_R 13 //*** (2) Enter the new number of registers
/* Write registers:
0x91A = Speed reference, in 1/10Hz increments
...
0x91E = serial fault reset, 0=no reset, 1 = reset fault
total of 5 registers */
#define START_REGISTER_W 0x091A
#define NUM_REGISTERS_W 5
//*** (2) If there are more register sets, add them as needed
#undef DEBUG
//#define DEBUG
//*** (3) Add more variable declarations as needed to match the defines above
/* modbus slave data struct */
typedef struct {
int slave; /* slave address */
int read_reg_start; /* starting read register number */
int read_reg_count; /* number of registers to read */
int write_reg_start; /* starting write register number */
int write_reg_count; /* number of registers to write */
} slavedata_t;
//*** (4) declare pointers needed to create HAL pins (see 9 below).
//*** and declare associated variables if needed for data conditioning
/* HAL data struct */
typedef struct {
hal_s32_t *stat1 // status words from the VFD. Maybe split these out sometime
hal_s32_t *stat2;
....
hal_bit_t *err_reset; // reset errors when 1
hal_s32_t ack_delay; // number of read/writes before checking at-speed
hal_bit_t old_run; // so we can detect changes in the run state
hal_bit_t old_dir;
hal_bit_t old_err_reset;
} haldata_t;
static int done;
//*** (5) Enter new name. This is the base name for HAL pins and loadusr command
char *modname = "gs2_vfd";
static struct option long_options[] = {
{"bits", 1, 0, 'b'},
{"device", 1, 0, 'd'},
{"debug", 0, 0, 'g'},
{"help", 0, 0, 'h'},
{"name", 1, 0, 'n'},
{"parity", 1, 0, 'p'},
{"rate", 1, 0, 'r'},
{"stopbits", 1, 0, 's'},
{"target", 1, 0, 't'},
{"verbose", 0, 0, 'v'},
{0,0,0,0}
};
static char *option_string = "b:d:hn:p:r:s:t:v";
static char *bitstrings[] = {"5", "6", "7", "8", NULL};
static char *paritystrings[] = {"even", "odd", "none", NULL};
static char *ratestrings[] = {"110", "300", "600", "1200", "2400", "4800", "9600",
"19200", "38400", "57600", "115200", NULL};
static char *stopstrings[] = {"1", "2", NULL};
static void quit(int sig) {
done = 1;
}
//*** (6) This line is for a particular feature of this application and may not be needed
static int comm_delay = 0; // JET delay counter for at-speed
int match_string(char *string, char **matches) {
int len, which, match;
which=0;
match=-1;
if ((matches==NULL) || (string==NULL)) return -1;
len = strlen(string);
while (matches[which] != NULL) {
if ((!strncmp(string, matches[which], len)) && (len <= strlen(matches[which]))) {
if (match>=0) return -1; // multiple matches
match=which;
}
++which;
}
return match;
}
//*** (7) Much of this function is data conditioning which may not apply
//*** to any another application. The heart of the function is the call of one
//*** or more Modbus write functions, such as shown below 'preset_single_register'.
//*** Other functions are for writing to coils, multiple registers, etc.
int write_data(modbus_param_t *param, slavedata_t *slavedata, haldata_t *haldata) {
// int write_data[MAX_WRITE_REGS];
int retval;
hal_float_t hzcalc;
if (haldata->motor_hz<10)
haldata->motor_hz = 60;
if ((haldata->motor_RPM < 600) || (haldata->motor_RPM > 5000))
haldata->motor_RPM = 1800;
hzcalc = haldata->motor_hz/haldata->motor_RPM;
retval=preset_single_register(param, slavedata->slave, slavedata->write_reg_start, abs((int)(*(haldata->speed_command)*hzcalc*10)));
...
if (*(haldata->spindle_on)==0){ // JET reset at-speed
*(haldata->at_speed) = 0;
}
haldata->retval = retval;
return retval;
}
void usage(int argc, char **argv) {
printf("Usage: %s [options]\n", argv[0]);
printf(
"This is a userspace HAL program, typically loaded using the halcmd \"loadusr\" command:\n"
" loadusr gs2_vfd\n"
...
"-v or --verbose\n"
" Turn on debug messages. Note that if there are serial errors, this may become annoying.\n"
" At the moment, it doesn't make much difference most of the time.\n");
}
//*** (6) Much of this function is data conditioning which may not apply
//*** to any another application. The heart of the function is the call of one
//*** or more Modbus read functions, such as shown below 'read_holding_registers'.
//*** Other functions are for reading coils, input registers, etc.
int read_data(modbus_param_t *param, slavedata_t *slavedata, haldata_t *hal_data_block) {
int receive_data[MAX_READ_HOLD_REGS]; /* a little padding in there */
int retval;
/* can't do anything with a null HAL data block */
if (hal_data_block == NULL)
return -1;
/* but we can signal an error if the other params are null */
if ((param==NULL) || (slavedata == NULL)) {
hal_data_block->errorcount++;
return -1;
}
retval = read_holding_registers(param, slavedata->slave, slavedata->read_reg_start,
slavedata->read_reg_count, receive_data);
if (retval==slavedata->read_reg_count) {
...
}
} else {
hal_data_block->retval = retval;
hal_data_block->errorcount++;
retval = -1;
}
return retval;
}
int main(int argc, char **argv)
{
int retval;
modbus_param_t mb_param;
haldata_t *haldata;
slavedata_t slavedata;
int hal_comp_id;
struct timespec loop_timespec, remaining;
int baud, bits, stopbits, debug, verbose;
char *device, *parity, *endarg;
int opt;
int argindex, argvalue;
done = 0;
//*** (7) The following may be changed to match the best defaults for the new application
// assume that nothing is specified on the command line
baud = 38400;
bits = 8;
stopbits = 1;
debug = 0;
verbose = 0;
device = "/dev/ttyS0";
parity = "odd";
//*** (8) The following needs to match the above defines and declarations
/* slave / register info */
slavedata.slave = 1;
slavedata.read_reg_start = START_REGISTER_R;
slavedata.read_reg_count = NUM_REGISTERS_R;
slavedata.write_reg_start = START_REGISTER_W;
slavedata.write_reg_count = NUM_REGISTERS_R;
// process command line options
while ((opt=getopt_long(argc, argv, option_string, long_options, NULL)) != -1) {
switch(opt) {
case 'b': // serial data bits, probably should be 8 (and defaults to 8)
argindex=match_string(optarg, bitstrings);
if (argindex<0) {
printf("gs2_vfd: ERROR: invalid number of bits: %s\n", optarg);
...
case 'h':
default:
usage(argc, argv);
exit(0);
break;
}
}
printf("%s: device='%s', baud=%d, bits=%d, parity='%s', stopbits=%d, address=%d, verbose=%d\n",
modname, device, baud, bits, parity, stopbits, slavedata.slave, debug);
/* point TERM and INT signals at our quit function */
/* if a signal is received between here and the main loop, it should prevent
some initialization from happening */
signal(SIGINT, quit);
signal(SIGTERM, quit);
/* Assume 38.4k O-8-1 serial settings, device 1 */
modbus_init_rtu(&mb_param, device, baud, parity, bits, stopbits, verbose);
mb_param.debug = debug;
/* the open has got to work, or we're out of business */
if (((retval = modbus_connect(&mb_param))!=0) || done) {
printf("%s: ERROR: couldn't open serial device\n", modname);
goto out_noclose;
}
/* create HAL component */
hal_comp_id = hal_init(modname);
if ((hal_comp_id < 0) || done) {
printf("%s: ERROR: hal_init failed\n", modname);
retval = hal_comp_id;
goto out_close;
}
/* grab some shmem to store the HAL data in */
haldata = (haldata_t *)hal_malloc(sizeof(haldata_t));
if ((haldata == 0) || done) {
printf("%s: ERROR: unable to allocate shared memory\n", modname);
retval = -1;
goto out_close;
}
//*** (9) Define the new HAL pins here
retval = hal_pin_s32_newf(HAL_OUT, &(haldata->stat1), hal_comp_id, "%s.status-1", modname);
if (retval!=0) goto out_closeHAL;
...
retval = hal_param_s32_newf(HAL_RW, &(haldata->ack_delay), hal_comp_id, "%s.ack-delay", modname);
if (retval!=0) goto out_closeHAL;
//*** (10) If the pin variables need default values put the value for the new ones here
/* make default data match what we expect to use */
*(haldata->stat1) = 0;
*(haldata->stat2) = 0;
...
haldata->old_err_reset = -1;
hal_ready(hal_comp_id);
//*** (11) Most applications will read and write registers so from here down will stay unchanged
/* here's the meat of the program. loop until done (which may be never) */
while (done==0) {
read_data(&mb_param, &slavedata, haldata);
write_data(&mb_param, &slavedata, haldata);
/* don't want to scan too fast, and shouldn't delay more than a few seconds */
if (haldata->looptime < 0.001) haldata->looptime = 0.001;
if (haldata->looptime > 2.0) haldata->looptime = 2.0;
loop_timespec.tv_sec = (time_t)(haldata->looptime);
loop_timespec.tv_nsec = (long)((haldata->looptime - loop_timespec.tv_sec) * 1000000000l);
nanosleep(&loop_timespec, &remaining);
}
retval = 0; /* if we get here, then everything is fine, so just clean up and exit */
out_closeHAL:
hal_exit(hal_comp_id);
out_close:
modbus_close(&mb_param);
out_noclose:
return retval;
}