[Home]New File From Vfs11

LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org

/* 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;
}

LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org
This page is read-only. Follow the BasicSteps to edit pages. | View other revisions
Last edited March 28, 2011 5:01 am by Kirk Wallace (diff)
Search:
Published under a Creative Commons License