В общем с добавлением задержки обмен пошел устойчиво.
Код: Выделить всё
/*
spindle-vfd.c
Universal LinuxCNC component for MODBUS RTU VFD's by BentScrew.
Special for <cnc-club.ru>, September 2015.
http://www.cnc-club.ru/forum/viewtopic.php?f=15&t=9406
Based on gs2_vfd.c
Copyright (C) 2007, 2008 Stephen Wille Padnos, Thoth Systems, Inc.
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.
This program 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. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
This is a userspace HAL program, which may be loaded using the halcmd "loadusr" command:
loadusr spindle-vfd
There are several command-line options. Options that have a set list of possible values may
be set by using any number of characters that are unique. For example, --rate 5 will use
a baud rate of 57600, since no other available baud rates start with "5"
-b or --bits <n> (default 8)
Set number of data bits to <n>, where n must be from 5 to 8 inclusive
-d or --device <path> (default /dev/ttyUSB0)
Set the name of the serial device node to use
-g or --debug
Turn on debugging messages. This will also set the verbose flag. Debug mode will cause
all modbus messages to be printed in hex on the terminal.
-n or --name <string> (default spindle-vfd)
Set the name of the HAL module. The HAL comp name will be set to <string>, and all pin
and parameter names will begin with <string>.
-p or --parity {even,odd,none} (defalt none)
Set serial parity to even, odd, or none.
-r or --rate <n> (default 38400)
Set baud rate to <n>. It is an error if the rate is not one of the following:
110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
-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 VFD.
-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 <string.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <getopt.h>
#include <math.h>
#include "rtapi.h"
#include "hal.h"
#include "modbus.h"
/* Настройки связи по умолчанию, если в custom.hal параметры не были указаны явно */
#define DEFAULT_Slave 1 // адрес преобразователя частоты
#define DEFAULT_Device "/dev/ttyUSB0" // последовательный порт
#define DEFAULT_Baud 19200 // скорость, бод
#define DEFAULT_Bits 8 // количество бит: 5 .. 8
#define DEFAULT_Parity "none" // чётность: even, odd, none
#define DEFAULT_Stopbits 1 // стоп-биты: 1, 2
#define DEFAULT_Debug 0 // показывать запросы/ответы modbus? 0 - нет , 1 - да
#define DEFAULT_Verbose 0 // показывать сервисные сообщения? 0 - нет , 1 - да
/* Пауза между циклами чтения-записи, не изменяйте без необходимости */
#define DEFAULT_Looptime 0.1 // 0.001 .. 2.0
/* Регистры для чтения. Укажите значения из мануала на свой ПЧ! */
#define STATUS_Command_Frequency 0x1000 // заданная (опорная) частота
#define STATUS_Output_Frequency 0x1001 // выходная частота
#define STATUS_Output_Current 0x1004 // выходной ток
#define STATUS_Output_Voltage 0x1003 // выходное напряжение
#define STATUS_Output_Power 0x1005 // выходная мощность
#define STATUS_Output_Torque 0x1006 // выходной момент
#define STATUS_Motor_RPM 0x1007 // скорость вращения
#define STATUS_DC_Bus_Voltage 0x1002 // напряжение на шине DC
#define STATUS_Temperature 0x1011 // температура
#define STATUS_Uptime 0x101B // время наработки
#define STATUS_Fault_Code 0x8000 // код ошибки ПЧ
/* Регистры для записи. Укажите значения из мануала на свой ПЧ! */
#define COMMAND_REGISTER 0x2000 // команда вперёд/назад/стоп
#define SET_FREQUENCY_REGISTER 0x1000 // задание частоты
/* Управляющие значения командного регистра. Аналогично, см. мануал! */
#define CONTROL_Run_Fwd 0x0001 // вперёд
#define CONTROL_Run_Rev 0x0002 // назад
#define CONTROL_Stop 0x0005 // стоп
#define CONTROL_Fault_Reset 0x0007 // сброс ошибки
/* В зависимости от модели ПЧ скорость вращения может задаваться либо заданием частоты (Гц), либо
диапазоном 0 ... 100%, где 100% - максимальная скорость. Значения: 0 - частота, 1 - проценты */
#define SET_FREQUENCY_MODE 1
/* Минимальная безопасная скорость вращения, об/мин */
#define MIN_SPEED 00000.0
/* Максимальная скорость, об/мин. Если SET_FREQUENCY_MODE = 1, значение ниже будет принято за 100% */
#define MAX_SPEED 24000.0
/* Допустимое отклонение от заданной скорости, 0.01 = 1% */
#define AT_SPEED_TOLERANCE 0.01
/* Количество непрерывных успешных транзакций для уверенности в наличии связи по Modbus */
#define MIN_MODBUS_OK 10
/* HAL пины */
typedef struct {
/* выходы для мониторинга параметров ПЧ */
hal_float_t *command_frequency; // заданная (опорная) частота
hal_float_t *output_frequency; // выходная частота
hal_float_t *output_current; // выходной ток
hal_float_t *output_voltage; // выходное напряжение
hal_float_t *output_power; // выходная мощность
hal_float_t *output_torque; // выходной момент
hal_float_t *motor_rpm; // скорость вращения
hal_float_t *dc_bus_voltage; // напряжение на шине DC
hal_float_t *temperature; // температура
hal_s32_t *uptime; // время наработки
hal_s32_t *fault_code; // код ошибки ПЧ
/* служебные выходы */
hal_bit_t *at_speed; // заданная скорость достигнута
hal_bit_t *modbus_ok; // связь установлена
hal_s32_t *error_count; // количество ошибок приёма-передачи
hal_s32_t *error_code; // код последней ошибки связи
/* входы */
hal_float_t *command_speed; // задание скорости вращения, об/мин
hal_bit_t *spindle_on; // команда на запуск
hal_bit_t *spindle_fwd; // направление - вперёд
hal_bit_t *spindle_rev; // направление - назад
hal_bit_t *fault_reset; // сброс ошибки
} haldata_t;
/* Имя компонента по умолчанию,
при вызове из командной строки может быть изменено при помощи ключа "-n" */
char *modname = "spindle-vfd";
#undef DEBUG
unsigned short int data_ok_count; // Число непрерывных успешных транзакций, при ошибке обнуляется
unsigned short int error_count; // Количество ошибок приёма-передачи
int verbose; // Флаг расширенного логирования
int old_control; // Для контроля необходимости обновить командное слово
hal_float_t old_speed; // Для контроля необходимости обновить заданную скорость
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:ghn: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 int done;
static void quit(int sig) {
done = 1;
}
/* Функция используется для разбора аргументов командной строки */
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; // множественные совпадения
match=which;
}
++which;
}
return match;
}
/*===============Функция задержки перед кадром===================*/
void framestart_delay(void) {
struct timespec fs_dt, fs_rm;
fs_dt.tv_sec = 0;
fs_dt.tv_nsec = (long) 350000;
nanosleep(&fs_dt, &fs_rm);
}
/* Функция читает параметры из ПЧ */
int read_data(modbus_param_t *param, int slave, haldata_t *hal_data_block) {
int receive_data[MAX_READ_HOLD_REGS]; // массив слов для принятых данных, MAX_READ_HOLD_REGS = 100
int tmp, cnt;
/* Проверка на корректность вызова функции */
if (hal_data_block == NULL)
return -1;
if (param == NULL)
return -1;
/* ЧТЕНИЕ РЕГИСТРОВ: СПОСОБ 1. Один регистр - один запрос. Универсальный способ. */
// Функция возвращает число прочитанных 16-ти битных регистров, запрашиваем содержимое ОДНОГО регистра
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Command_Frequency, 1, receive_data);
// Если один регистр был прочитан, то передаём его соответствующему пину HAL
if (tmp == 1) {
/* Т.к. содержимое регистра - это 16-ти битное целое беззнаковое число,
требуется дополнительный множитель, чтобы получить корректное значение.
В данном случае это 0.1. Уточните верный множитель в мануале своего ПЧ. */
*(hal_data_block->command_frequency) = receive_data[0] * 0.1;
data_ok_count++;
} else goto failed;
// Повторяем столько раз, сколько регистров требуется прочитать
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Output_Frequency, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->output_frequency) = receive_data[0] * 0.1;
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Output_Current, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->output_current) = receive_data[0] * 0.1;
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Output_Voltage, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->output_voltage) = receive_data[0] * 0.1;
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Output_Power, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->output_power) = receive_data[0] * 0.1;
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Output_Torque, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->output_torque) = receive_data[0] * 0.1;
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Motor_RPM, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->motor_rpm) = receive_data[0] * 10.0;
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_DC_Bus_Voltage, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->dc_bus_voltage) = receive_data[0] * 0.1;
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Temperature, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->temperature) = receive_data[0] * 0.1;
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Uptime, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->uptime) = receive_data[0];
data_ok_count++;
} else goto failed;
framestart_delay();
tmp = read_holding_registers(param, slave, STATUS_Fault_Code, 1, receive_data);
if (tmp == 1) {
*(hal_data_block->fault_code) = receive_data[0];
data_ok_count++;
} else goto failed;
/*
ЧТЕНИЕ РЕГИСТРОВ: СПОСОБ 2. ПОСЛЕДОВАТЕЛЬНАЯ цепочка регистров читается ОДНИМ запросом.
Работает на всех ПЧ, поддерживающих функцию чтения 03, но возможны ограничения.
Например, Tecorp HC1C+ не может прочитать более 4-х регистров одной командой, поэтому для большего числа
регистров требуется несколько запросов. Чтобы узнать сколько регистров способен прочитать ваш ПЧ,
включите флаг отладки (--debug или -g) и запросите максимальное требуемое число. В логе будет видно
сколько регистров фактически возвращается в ответ на запрос.
ПРИМЕР ЛОГА:
spindle-vfd : requested 11 registers, see below how much registers returned
[01][03][01][00][00][0B][55][CD] // Запрос содержимого 11-ти [0x00][0x0B] регистров,
// начиная с регистра 256 [0x01][0x00]
Waiting for response (27)... // Ожидаемая длина ответного пакета в байтах, включая CRC и др.
<01><03><08><01><F4><00><00><00><00><00><00><E1><D4> // Фактически пакет содержит <0x08> байт данных (4 слова),
// хотя должно быть <0x16> байт (11 слов)
ERROR Communication time out // Время ожидания истекло
*/
/*
// Функция возвращает число прочитанных 16-ти битных регистров, запрашиваем cnt регистров
cnt = 11;
if (param->debug == 1) printf("%s : requested %d registers, see below how much registers returned\n", modname, cnt);
tmp = read_holding_registers(param, slave, STATUS_Command_Frequency, cnt, receive_data);
if (tmp == cnt) {
*(hal_data_block->command_frequency) = receive_data[0] * 0.1;
*(hal_data_block->output_frequency) = receive_data[1] * 0.1;
*(hal_data_block->output_current) = receive_data[2] * 0.1;
*(hal_data_block->output_voltage) = receive_data[3] * 0.1;
*(hal_data_block->output_power) = receive_data[4] * 0.1;
*(hal_data_block->output_torque) = receive_data[5] * 0.1;
*(hal_data_block->motor_rpm) = receive_data[6] * 10;
*(hal_data_block->dc_bus_voltage) = receive_data[7] * 0.1;
*(hal_data_block->temperature) = receive_data[8] * 0.1;
*(hal_data_block->uptime) = receive_data[9];
*(hal_data_block->fault_code) = receive_data[10];
data_ok_count++;
} else goto failed;
*/
// всё хорошо, ошибок нет :)
return 0;
// всё плохо, данные не получены :-/
failed:
error_count++; // увеличиваем счётчик ошибок
data_ok_count = 0; // сбрасываем счётчик успешных транзакций
*(hal_data_block->error_count) = error_count;
*(hal_data_block->error_code) = tmp;
*(hal_data_block->modbus_ok) = 0;
if (verbose == 1) printf("%s : data reading error!\n", modname);
return -1;
}
/* Функция передаёт в ПЧ задание скорости и команду на запуск/останов */
int write_data(modbus_param_t *param, int slave, haldata_t *hal_data_block) {
hal_float_t speed_cmd;
int control, tmp;
/* Корректируем заданную скорость, если она внезапно (!) отрицательна */
speed_cmd = abs(*hal_data_block->command_speed);
/* Проверка на выход за допустимые лимиты */
if (speed_cmd > MAX_SPEED)
speed_cmd = MAX_SPEED;
if (speed_cmd < MIN_SPEED)
speed_cmd = MIN_SPEED;
if (SET_FREQUENCY_MODE == 1) {
// Переводим заданную скорость (об/мин) в проценты (%)
// Пример: command_speed = MAX_SPEED = 24000,
// тогда speed_cmd = 24000 / (0.01 * 24000) = 100%
speed_cmd /= 0.01 * MAX_SPEED;
// Корректируем значение перед записью (см. мануал на свой ПЧ!)
// Пример: 100% = 100.0% = 1000
speed_cmd *= 10;
} else {
// Переводим заданную скорость (об/мин) в частоту (Гц)
// Пример: command_speed = MAX_SPEED = 24000,
// тогда speed_cmd = 24000 / 60 = 400Гц
speed_cmd /= 60.0;
// Корректируем значение перед записью (см. мануал на свой ПЧ!)
// Пример: 400Гц = 400.0Гц = 4000
speed_cmd *= 10;
}
// Если заданная скорость изменилась...
if (*hal_data_block->command_speed != old_speed) {
if (verbose == 1) printf("%s : updating frequency register [%f RPM, 0x%X] ... ",
modname, *hal_data_block->command_speed, (int)(speed_cmd));
// ...то записываем в ПЧ значение частоты (всего - один 16-ти битный регистр)
framestart_delay();
tmp = preset_single_register(param, slave, SET_FREQUENCY_REGISTER, (int)(speed_cmd));
// Проверка результата - сколько регистров записано?
if (tmp == 1) {
if (verbose == 1) printf("Write OK!\n");
old_speed = *hal_data_block->command_speed;
data_ok_count++;
} else {
goto failed;
}
}
/* Подготовка управляющего слова */
control = CONTROL_Stop; // Начальное состояние - "стоп"
if (*hal_data_block->spindle_on) { // Если подан сигнал на запуск...
if (*hal_data_block->spindle_fwd) {
control = CONTROL_Run_Fwd; // ...то "крутим вперёд"
}
if (*hal_data_block->spindle_rev) {
control = CONTROL_Run_Rev; // ...или "крутим назад"
}
}
if (*hal_data_block->fault_reset == 1) { // Кнопка "сбросить ошибку" нажата?
control = CONTROL_Fault_Reset;
}
// Если управляющее слово изменилось...
if (control != old_control) {
if (verbose == 1) printf("%s : updating command register [0x%X] ... ", modname, control);
// ...то записываем его в ПЧ
framestart_delay();
tmp = preset_single_register(param, slave, COMMAND_REGISTER, control);
// Проверка результата - сколько регистров записано?
if (tmp == 1) {
if (control == CONTROL_Fault_Reset) { // Если команда "сбросить ошибку" записана успешно, то...
*hal_data_block->fault_reset = 0; // ...пин деактивируем
}
if (verbose == 1) printf("Write OK!\n");
old_control = control;
data_ok_count++;
} else {
goto failed;
}
}
// всё хорошо, ошибок нет :)
return 0;
// всё плохо, данные не получены :-/
failed:
error_count++; // увеличиваем счётчик ошибок
data_ok_count = 0; // сбрасываем счётчик успешных транзакций
*(hal_data_block->error_count) = error_count;
*(hal_data_block->error_code) = tmp;
*(hal_data_block->modbus_ok) = 0;
if (verbose == 1) printf("Write FAIL!\n");
return -1;
}
/* Справка, вызывается ключом "-h", а также при любом неверном аргументе */
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 spindle-vfd\n"
"There are several command-line options. Options that have a set list of possible values may\n"
" be set by using any number of characters that are unique. For example, --rate 5 will use\n"
" a baud rate of 57600, since no other available baud rates start with \"5\"\n"
"-b or --bits <n> (default %d)\n"
" Set number of data bits to <n>, where n must be from 5 to 8 inclusive\n"
"-d or --device <path> (default %s)\n"
" Set the name of the serial device node to use\n"
"-g or --debug\n"
" Turn on debugging messages. This will also set the verbose flag. Debug mode will cause\n"
" all modbus messages to be printed in hex on the terminal.\n"
"-n or --name <string> (default %s)\n"
" Set the name of the HAL module. The HAL comp name will be set to <string>, and all pin\n"
" and parameter names will begin with <string>.\n"
"-p or --parity {even,odd,none} (defalt %s)\n"
" Set serial parity to even, odd, or none.\n"
"-r or --rate <n> (default %d)\n"
" Set baud rate to <n>. It is an error if the rate is not one of the following:\n"
" 110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200\n"
"-s or --stopbits {1,2} (default %d)\n"
" Set serial stop bits to 1 or 2\n"
"-t or --target <n> (default %d)\n"
" Set MODBUS target (slave) number. This must match the device number you set on the 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",
DEFAULT_Bits, DEFAULT_Device, modname, DEFAULT_Parity,
DEFAULT_Baud, DEFAULT_Stopbits, DEFAULT_Slave );
}
/* Основная функция */
int main(int argc, char **argv)
{
int retval;
modbus_param_t mb_param;
haldata_t *haldata;
int hal_comp_id;
struct timespec loop_timespec, remaining;
int slave, baud, bits, stopbits, debug;
char *device, *parity, *endarg;
int opt;
int argindex, argvalue;
float looptime;
double diff;
done = 0;
/* Применение настроек связи по умолчанию */
slave = DEFAULT_Slave;
device = DEFAULT_Device;
baud = DEFAULT_Baud;
bits = DEFAULT_Bits;
parity = DEFAULT_Parity;
stopbits = DEFAULT_Stopbits;
debug = DEFAULT_Debug;
verbose = DEFAULT_Verbose;
looptime = DEFAULT_Looptime;
/* Разбор параметров командной строки */
while ((opt=getopt_long(argc, argv, option_string, long_options, NULL)) != -1) {
switch(opt) {
case 'b': // число бит данных
argindex=match_string(optarg, bitstrings);
if (argindex<0) {
printf("%s: ERROR - invalid number of bits: %s\n", modname, optarg);
retval = -1;
goto out_noclose;
}
bits = atoi(bitstrings[argindex]);
break;
case 'd': // последовательный порт
if (strlen(optarg) > FILENAME_MAX) {
printf("%s: ERROR - device node name is too long: %s\n", modname, optarg);
retval = -1;
goto out_noclose;
}
device = strdup(optarg);
break;
case 'g': // флаг отладки
debug = 1;
verbose = 1;
break;
case 'n': // имя компонента
if (strlen(optarg) > HAL_NAME_LEN-20) {
printf("%s: ERROR - HAL module name too long: %s\n", modname, optarg);
retval = -1;
goto out_noclose;
}
modname = strdup(optarg);
break;
case 'p': // чётность
argindex=match_string(optarg, paritystrings);
if (argindex<0) {
printf("%s: ERROR - invalid parity: %s\n", modname, optarg);
retval = -1;
goto out_noclose;
}
parity = paritystrings[argindex];
break;
case 'r': // скорость связи
argindex=match_string(optarg, ratestrings);
if (argindex<0) {
printf("%s: ERROR - invalid baud rate: %s\n", modname, optarg);
retval = -1;
goto out_noclose;
}
baud = atoi(ratestrings[argindex]);
break;
case 's': // число стоп-бит
argindex=match_string(optarg, stopstrings);
if (argindex<0) {
printf("%s: ERROR - invalid number of stop bits: %s\n", modname, optarg);
retval = -1;
goto out_noclose;
}
stopbits = atoi(stopstrings[argindex]);
break;
case 't': // адрес устройства (slave)
argvalue = strtol(optarg, &endarg, 10);
if ((*endarg != '\0') || (argvalue < 1) || (argvalue > 254)) {
printf("%s: ERROR - invalid slave number: %s\n", modname, optarg);
retval = -1;
goto out_noclose;
}
slave = argvalue;
break;
case 'v': // флаг расширенного логирования
verbose = 1;
break;
case 'h': // вызов справки
default:
usage(argc, argv);
exit(0);
break;
}
}
printf("%s: device='%s', baud=%d, bits=%d, parity='%s', stopbits=%d, slave=%d, verbose=%d, debug=%d\n",
modname, device, baud, bits, parity, stopbits, slave, verbose, 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);
/* Инициализация последовательного порта */
modbus_init_rtu(&mb_param, device, baud, parity, bits, stopbits, debug);
mb_param.debug = debug;
if (((retval = modbus_connect(&mb_param))!=0) || done) {
printf("%s: ERROR - couldn't open serial device\n", modname);
goto out_noclose;
}
/* Создаём компонент HAL */
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;
}
/* Выделяем память для размещения пинов HAL */
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;
}
/* Создаём выходные пины */
retval = hal_pin_float_newf(HAL_OUT, &(haldata->command_frequency), hal_comp_id, "%s.command-frequency", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_float_newf(HAL_OUT, &(haldata->output_frequency), hal_comp_id, "%s.output-frequency", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_float_newf(HAL_OUT, &(haldata->output_current), hal_comp_id, "%s.output-current", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_float_newf(HAL_OUT, &(haldata->output_voltage), hal_comp_id, "%s.output-voltage", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_float_newf(HAL_OUT, &(haldata->output_power), hal_comp_id, "%s.output-power", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_float_newf(HAL_OUT, &(haldata->output_torque), hal_comp_id, "%s.output-torque", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_float_newf(HAL_OUT, &(haldata->motor_rpm), hal_comp_id, "%s.motor-rpm", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_float_newf(HAL_OUT, &(haldata->dc_bus_voltage), hal_comp_id, "%s.dc-bus-voltage", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_float_newf(HAL_OUT, &(haldata->temperature), hal_comp_id, "%s.temperature", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_s32_newf(HAL_OUT, &(haldata->uptime), hal_comp_id, "%s.uptime", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_s32_newf(HAL_OUT, &(haldata->fault_code), hal_comp_id, "%s.fault-code", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_bit_newf(HAL_OUT, &(haldata->at_speed), hal_comp_id, "%s.at-speed", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_bit_newf(HAL_OUT, &(haldata->modbus_ok), hal_comp_id, "%s.modbus-ok", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_s32_newf(HAL_OUT, &(haldata->error_count), hal_comp_id, "%s.error-count", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_s32_newf(HAL_OUT, &(haldata->error_code), hal_comp_id, "%s.error-code", modname);
if (retval!=0) goto out_closeHAL;
/* Создаём входные пины */
retval = hal_pin_float_newf(HAL_IN, &(haldata->command_speed), hal_comp_id, "%s.command-speed", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_bit_newf(HAL_IN, &(haldata->spindle_on), hal_comp_id, "%s.spindle-on", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_bit_newf(HAL_IN, &(haldata->spindle_fwd), hal_comp_id, "%s.spindle-fwd", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_bit_newf(HAL_IN, &(haldata->spindle_rev), hal_comp_id, "%s.spindle-rev", modname);
if (retval!=0) goto out_closeHAL;
retval = hal_pin_bit_newf(HAL_IN, &(haldata->fault_reset), hal_comp_id, "%s.fault-reset", modname);
if (retval!=0) goto out_closeHAL;
/* Инициализация пинов HAL и прочих переменных */
*haldata->command_frequency = 0;
*haldata->output_frequency = 0;
*haldata->output_current = 0;
*haldata->output_voltage = 0;
*haldata->output_power = 0;
*haldata->output_torque = 0;
*haldata->motor_rpm = 0;
*haldata->dc_bus_voltage = 0;
*haldata->temperature = 0;
*haldata->uptime = 0;
*haldata->fault_code = 0;
*haldata->at_speed = 0;
*haldata->modbus_ok = 0;
*haldata->error_count = 0;
*haldata->error_code = 0;
*haldata->spindle_on = 0;
*haldata->spindle_fwd = 0;
*haldata->spindle_rev = 0;
*haldata->fault_reset = 0;
hal_ready(hal_comp_id);
data_ok_count = 0;
error_count = 0;
old_control = -1;
old_speed = -1;
/* Сердце программы - в бесконечном цикле читаем и пишем параметры */
while (done==0) {
read_data(&mb_param, slave, haldata);
write_data(&mb_param, slave, haldata);
/* Сравнение заданной и текущей скорости */
if ((*haldata->command_speed != 0) && (*haldata->spindle_on))
diff = fabs(1. - (*haldata->motor_rpm / abs(*haldata->command_speed)));
else
diff = 999.9;
/* Заданная скорость достигнута? */
if (diff > AT_SPEED_TOLERANCE)
*haldata->at_speed = 0;
else
*haldata->at_speed = 1;
// Зажигаем лампочку Modbus OK
if (data_ok_count > MIN_MODBUS_OK)
*haldata->modbus_ok = 1;
/* Пауза */
if (looptime < 0.001) looptime = 0.001;
if (looptime > 2.0) looptime = 2.0;
loop_timespec.tv_sec = (time_t)(looptime);
loop_timespec.tv_nsec = (long)((looptime - loop_timespec.tv_sec) * 1000000000l);
nanosleep(&loop_timespec, &remaining);
}
retval = 0; /* Конец программы */
out_closeHAL:
hal_exit(hal_comp_id);
out_close:
modbus_close(&mb_param);
out_noclose:
return retval;
}
Добавленная функция называется "framestart_delay". Можно будет допилить расчет задержки в зависимости от выбранной скорости, пока просто для праверки задержка грубо посчитана на 9600. На этой скорости и проверялось...