Валерий, переоценил я свои силёнки, вообще не до этого сейчас. Делайте проще, как посоветовал UAVpilot выше. Или попытайтесь в выложенных в этой теме исходниках разобраться, код с комментариями, допиливайте под себя.Валерий писал(а):Как со временем у Вас ?
Частотник и Modbus. Универсальная заготовка.
- aekhv
- Мастер
- Сообщения: 393
- Зарегистрирован: 17 окт 2014, 15:03
- Репутация: 218
- Настоящее имя: Александр
- Откуда: г.Хабаровск
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
- N1X
- Мастер
- Сообщения: 3653
- Зарегистрирован: 16 фев 2015, 21:19
- Репутация: 1645
- Настоящее имя: Владимир
- Откуда: Беларусь, Гомель
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Ну вот и у меня начался забег по граблям. Часть собрал, часть еще нет
Вводная: ПЧ: MiCNO KE300, USB-RS485 самодельный изолированный. По сигналам претензий нет пока (проверял в на столе осциллографом, возможно еще сниму реальный обмен с шины, но пока что по логике проблема не здесь.)
Расскажу по порядку, может кому еще полезно будет, потом задам свои вопросы
Итак, первое:
Скомпилировал, настроил, получил пробему как у MGG вот тут: Re: Частотник и Modbus. Универсальная заготовка. #45
Только у меня картинка чуть другая: Прошу прощения за блондинистый скриншот, так было проще )
Как видим мастер пытается читать по адресу регистра 7001, в ответ получаем 01 03 80 01 00 04 3C
а в следующей попытке получаем просто 09
Так вот, это особенность частотника, как выше BentScrew и предполагал:
Это ответ частотинка о том, что ему не нравится в запросе. А т.к. он отсылает 80 01, то похоже мастер 80 игнорирует, т.к. ждет там 00, и читая дальше 01 считает что идет 1 байт данных, соответственно принимает пакет не полностью, а на байт меньше, 09 остается в буфере приемника и прилетает при следующей транзакции...
В общем если собрать пакет: "01 03 80 01 00 04 3C 09" то получаем нормальную CRC 3C 09.
А суть пакета похоже такая: 80 01 - ошибка связи; 00 04 - Неверный адрес
Вот табличка из мануала:
Теперь дальше. На текущих граблях я еще топчусь. Поправил адреса параметров, частотник начал отвечать, НО:
Первая пара вопрос-ответ проходит нормально, а за ней вопрос-таймаут
Попробовал в коде компонента после первого тайм-аута вставить повтор запроса - прошло.
Т.е. сразу после своего ответа мастеру частотник пропускает мимо ушей следующий запрос.
Я сначала думал, что это мой конвертер не успевает быстро включиться на передачу, но первый то байт тоже передается из режима "прием" включением в "передачу", да и на осциллографе посмотрел - переключение происходит четко.
Все выше описанное глухо не работает до скоростей от 300 до 19200 бод включительно, на 38400 картина меняется - связь начинает проходить, но с перебоями.
Т.е. индикатор на панельке даже зеленеет периодически... Больше, чем 38400 частотник не умеет. Еще с кол-вом стоповых битов поиграться хочу, по умолчанию в частотнике их 2 стоит, вроде правил, но еще раз перепроверить нужно. Ну и сегодня заберу лог. анализатор, на работе оставил, попробую обмен снять если что, чтобы понять как там что передается на самом деле...
Пост написал сейчас, т.к. думаю может кто решал уже такую проблему и подскажет что... Все же пока складывается ощущение, что это очередная особенность китайских вариантов протоколов... Была мысль, что может пауза в 3,5 символа между кадрами не выдержана и частотник глотает начало кадра, но это до снятия времянок с шины не понять, может сегодня успею глянуть...
Вводная: ПЧ: MiCNO KE300, USB-RS485 самодельный изолированный. По сигналам претензий нет пока (проверял в на столе осциллографом, возможно еще сниму реальный обмен с шины, но пока что по логике проблема не здесь.)
Расскажу по порядку, может кому еще полезно будет, потом задам свои вопросы
Итак, первое:
Скомпилировал, настроил, получил пробему как у MGG вот тут: Re: Частотник и Modbus. Универсальная заготовка. #45
Только у меня картинка чуть другая: Прошу прощения за блондинистый скриншот, так было проще )
Как видим мастер пытается читать по адресу регистра 7001, в ответ получаем 01 03 80 01 00 04 3C
а в следующей попытке получаем просто 09
Так вот, это особенность частотника, как выше BentScrew и предполагал:
Это ответ частотинка о том, что ему не нравится в запросе. А т.к. он отсылает 80 01, то похоже мастер 80 игнорирует, т.к. ждет там 00, и читая дальше 01 считает что идет 1 байт данных, соответственно принимает пакет не полностью, а на байт меньше, 09 остается в буфере приемника и прилетает при следующей транзакции...
В общем если собрать пакет: "01 03 80 01 00 04 3C 09" то получаем нормальную CRC 3C 09.
А суть пакета похоже такая: 80 01 - ошибка связи; 00 04 - Неверный адрес
Вот табличка из мануала:
Код: Выделить всё
9.6.5 Description data of communication fault information (fault code)
Communication Fault
Address Fault function description
8001 0000: No fault
0001: Password error
0002: Command error
0003: CRC check error
0004: Invalid address
0005: Invalid parameter
0006: Parameter changing invalid
0007: System locked
0008: EEPROM operating
Первая пара вопрос-ответ проходит нормально, а за ней вопрос-таймаут
Попробовал в коде компонента после первого тайм-аута вставить повтор запроса - прошло.
Т.е. сразу после своего ответа мастеру частотник пропускает мимо ушей следующий запрос.
Код: Выделить всё
spindle-vfd : updating frequency register [0.000000 RPM, 0x29] ...
[01][06][10][00][00][29][4C][D4]
Waiting for response (8)...
<01><06><10><00><00><29><4C><D4>
Write OK!
spindle-vfd : updating command register [0x5] ...
[01][06][20][00][00][05][42][09]
Waiting for response (8)...
ERROR Communication time out
Write FAIL!
[01][03][10][00][00][01][80][CA]
Waiting for response (7)...
<01><03><02><00><29><79><9A>
[01][03][10][00][00][01][80][CA]
Waiting for response (7)...
ERROR Communication time out
spindle-vfd : data reading error!
spindle-vfd : updating command register [0x5] ...
[01][06][20][00][00][05][42][09]
Waiting for response (8)...
<01><06><20><00><00><05><42><09>
Write OK!
task: main loop took 0.013080 seconds
task: main loop took 0.013085 seconds
[01][03][10][00][00][01][80][CA]
Waiting for response (7)...
<01><03><02><00><29><79><9A>
[01][03][10][00][00][01][80][CA]
Waiting for response (7)...
ERROR Communication time out
spindle-vfd : data reading error!
Все выше описанное глухо не работает до скоростей от 300 до 19200 бод включительно, на 38400 картина меняется - связь начинает проходить, но с перебоями.
Т.е. индикатор на панельке даже зеленеет периодически... Больше, чем 38400 частотник не умеет. Еще с кол-вом стоповых битов поиграться хочу, по умолчанию в частотнике их 2 стоит, вроде правил, но еще раз перепроверить нужно. Ну и сегодня заберу лог. анализатор, на работе оставил, попробую обмен снять если что, чтобы понять как там что передается на самом деле...
Пост написал сейчас, т.к. думаю может кто решал уже такую проблему и подскажет что... Все же пока складывается ощущение, что это очередная особенность китайских вариантов протоколов... Была мысль, что может пауза в 3,5 символа между кадрами не выдержана и частотник глотает начало кадра, но это до снятия времянок с шины не понять, может сегодня успею глянуть...
- aekhv
- Мастер
- Сообщения: 393
- Зарегистрирован: 17 окт 2014, 15:03
- Репутация: 218
- Настоящее имя: Александр
- Откуда: г.Хабаровск
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Наверное скорость обмена как-то связана с тем, как быстро частотник готов принять новый запрос после того как ответил на предыдущий. Возможно после отправки ответа частник хочет присесть, подумать о жизни, вспомнить как он когда-то крутил с одним шпинделем... а тут бац, новый запрос, слишком быстро Что если перед каждым вызовом read_holding_registers() и preset_single_register() добавить задержку? Просто для эксперимента.
- Serg
- Мастер
- Сообщения: 21923
- Зарегистрирован: 17 апр 2012, 14:58
- Репутация: 5182
- Заслуга: c781c134843e0c1a3de9
- Настоящее имя: Сергей
- Откуда: Москва
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Modbus RTU — компактный двоичный вариант. Сообщения разделяются по паузе в линии. Сообщение должно начинаться и заканчиваться интервалом тишины, длительностью не менее 3,5 символов при данной скорости передачи. Во время передачи сообщения не должно быть пауз длительностью более 1,5 символов. Для скоростей более 19200 бод допускается использовать интервалы 1,75 и 0,75 мс, соответственно. Проверка целостности осуществляется с помощью CRC.
Я не Христос, рыбу не раздаю, но могу научить, как сделать удочку...
- N1X
- Мастер
- Сообщения: 3653
- Зарегистрирован: 16 фев 2015, 21:19
- Репутация: 1645
- Настоящее имя: Владимир
- Откуда: Беларусь, Гомель
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Вот и я пришел к выводу что отсюда ноги растут, бегло посмотрел modbus.c - никакого контроля пауз там нет. Добавил ручками nanosleep (350мкс) после каждой транзакции, это для 9600 бод и выше должно быть достаточно, вечером постараюсь успеть проверить.
- Serg
- Мастер
- Сообщения: 21923
- Зарегистрирован: 17 апр 2012, 14:58
- Репутация: 5182
- Заслуга: c781c134843e0c1a3de9
- Настоящее имя: Сергей
- Откуда: Москва
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Там должны быть милисекунды.N1X писал(а):Добавил ручками nanosleep (350мкс)
Я не Христос, рыбу не раздаю, но могу научить, как сделать удочку...
- N1X
- Мастер
- Сообщения: 3653
- Зарегистрирован: 16 фев 2015, 21:19
- Репутация: 1645
- Настоящее имя: Владимир
- Откуда: Беларусь, Гомель
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Да, 3.5 символа, а я решил, что 3.5 бита, спасибо, сразу потенциальный косяк ушел )
- N1X
- Мастер
- Сообщения: 3653
- Зарегистрирован: 16 фев 2015, 21:19
- Репутация: 1645
- Настоящее имя: Владимир
- Откуда: Беларусь, Гомель
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
В общем с добавлением задержки обмен пошел устойчиво.
Вот получившийся код модуля:
Добавленная функция называется "framestart_delay". Можно будет допилить расчет задержки в зависимости от выбранной скорости, пока просто для праверки задержка грубо посчитана на 9600. На этой скорости и проверялось...
Вот получившийся код модуля:
Код: Выделить всё
/*
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;
}
- Alexsh
- Опытный
- Сообщения: 100
- Зарегистрирован: 25 дек 2015, 22:28
- Репутация: 15
- Настоящее имя: Алексей
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Приветствую!
Подскажите пожалуйста сконфигурировать под этот частотник.
Буду весьма благодарен за помощь.
Подскажите пожалуйста сконфигурировать под этот частотник.
Буду весьма благодарен за помощь.
- Вложения
-
- Manual User 9000 series converter_rus.pdf
- (2.85 МБ) 1199 скачиваний
- aekhv
- Мастер
- Сообщения: 393
- Зарегистрирован: 17 окт 2014, 15:03
- Репутация: 218
- Настоящее имя: Александр
- Откуда: г.Хабаровск
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Подсказать тут всегда рады. Только что конкретно подсказать-то? Дайте больше конкретики. Что уже пробовали сделать, и что не получилось?
- N1X
- Мастер
- Сообщения: 3653
- Зарегистрирован: 16 фев 2015, 21:19
- Репутация: 1645
- Настоящее имя: Владимир
- Откуда: Беларусь, Гомель
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Под него сконфигурировать не получится. Эта заготовка под Modbus RTU, а данный частотник работаетв формате ASCII...
- Alexsh
- Опытный
- Сообщения: 100
- Зарегистрирован: 25 дек 2015, 22:28
- Репутация: 15
- Настоящее имя: Алексей
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Проблема со значениями регистров- не понятно где взять значения регистров чтения и записи.
Управляющие значения командного регистра нашел (кажется), но выглядят они примерно так:
СТОП сс=00
СБРО сс=01
ХОД ВПЕРЕД сс=01
и т.д.
В мануале читаю и не могу понять ничего..
Помогите разобраться на примере хотя бы одной-двух строчек.
В мануале по RS485 страницы 92-96.
Управляющие значения командного регистра нашел (кажется), но выглядят они примерно так:
СТОП сс=00
СБРО сс=01
ХОД ВПЕРЕД сс=01
и т.д.
В мануале читаю и не могу понять ничего..
Помогите разобраться на примере хотя бы одной-двух строчек.
В мануале по RS485 страницы 92-96.
- Alexsh
- Опытный
- Сообщения: 100
- Зарегистрирован: 25 дек 2015, 22:28
- Репутация: 15
- Настоящее имя: Алексей
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Спасибо, а под этот формат есть какие нибудь заготовки на форуме ?N1X писал(а):Под него сконфигурировать не получится. Эта заготовка под Modbus RTU, а данный частотник работаетв формате ASCII...
- N1X
- Мастер
- Сообщения: 3653
- Зарегистрирован: 16 фев 2015, 21:19
- Репутация: 1645
- Настоящее имя: Владимир
- Откуда: Беларусь, Гомель
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Не попадалось, по крайней мере мне.
- Alexsh
- Опытный
- Сообщения: 100
- Зарегистрирован: 25 дек 2015, 22:28
- Репутация: 15
- Настоящее имя: Алексей
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Очень жаль. будет пока аналоговое управление.
-
- Мастер
- Сообщения: 3673
- Зарегистрирован: 08 фев 2016, 16:33
- Репутация: 1010
- Настоящее имя: Манн Геннадий Геннадьевич
- Откуда: Москва
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Товарищи, у меня тут в сявзи с поправками товарища N1X, стало работать стабильней, но появились новые приколы, не могу найти множитель частоты, а именно, в лцнц показывает заданная 50, по факту выдает 20, на часотнике 50, в лцнц заданная 125.
И еще момент, почему то останавливается своим ходом.
И еще момент, почему то останавливается своим ходом.
http://www.cnc-club.ru/forum/viewtopic. ... 76#p304076 Поставки оборудования для ваших станков
https://www.instagram.com/dtw.moscow/
dtw.moscow@gmail.com
https://www.instagram.com/dtw.moscow/
dtw.moscow@gmail.com
- N1X
- Мастер
- Сообщения: 3653
- Зарегистрирован: 16 фев 2015, 21:19
- Репутация: 1645
- Настоящее имя: Владимир
- Откуда: Беларусь, Гомель
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Есть такое.
Ну и
т.к. частотник работает с заданичем по частоте в % от максимальных оборотов.
Вот полный текст модуля:
С твоим частотником должен нормально работать.
Вот так должно быть, ибо 5 - coast to stop, остановка выбегом, а 6 - deceleration to stop, соответственно с торможением#define CONTROL_Stop 0x0006 // стоп
MGG писал(а): в лцнц показывает заданная 50, по факту выдает 20, на часотнике 50,
Код: Выделить всё
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 *= 100;d_cmd *= 100;
Код: Выделить всё
#define SET_FREQUENCY_MODE 1
Вот полный текст модуля:
Код: Выделить всё
/*
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 0x0006 // стоп
#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) 3500000;
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.01;
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];
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.01;
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] * 60.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 *= 100;
} 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;
}
-
- Мастер
- Сообщения: 3673
- Зарегистрирован: 08 фев 2016, 16:33
- Репутация: 1010
- Настоящее имя: Манн Геннадий Геннадьевич
- Откуда: Москва
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Не пойму куда мне множитель в данном слуаче впихнуть, заданная и фактическая с разницей в 2.5 раза. Т.е. то что выдает лцнц в 2.5 раза меньше факта
http://www.cnc-club.ru/forum/viewtopic. ... 76#p304076 Поставки оборудования для ваших станков
https://www.instagram.com/dtw.moscow/
dtw.moscow@gmail.com
https://www.instagram.com/dtw.moscow/
dtw.moscow@gmail.com
-
- Мастер
- Сообщения: 3673
- Зарегистрирован: 08 фев 2016, 16:33
- Репутация: 1010
- Настоящее имя: Манн Геннадий Геннадьевич
- Откуда: Москва
- Контактная информация:
Re: Частотник и Modbus. Универсальная заготовка.
Разобрался!!!
#define STATUS_Command_Frequency 0x1001 // заданная (опорная) частота - тут стояло 1000, поменял, взлетело, хотя не думаю что это правильно.
#define STATUS_Output_Frequency 0x1001 // выходная частота
Но с рабочего конфига скорость не регулируется
Дорогой дневник, победил, это был регистр чтения
Вот с рабочего конфига скорость так и не регулируется. нет даже кнопок + и -, ну и в принципе ошибок много валится. Понять бы с чем это связано, с ошибками думаю надо землить, из за серв, только вот ГНД свистка на планету или в ближайшую землю?
#define STATUS_Command_Frequency 0x1001 // заданная (опорная) частота - тут стояло 1000, поменял, взлетело, хотя не думаю что это правильно.
#define STATUS_Output_Frequency 0x1001 // выходная частота
Но с рабочего конфига скорость не регулируется
Дорогой дневник, победил, это был регистр чтения
Код: Выделить всё
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.04;
data_ok_count++;
} else goto failed;
http://www.cnc-club.ru/forum/viewtopic. ... 76#p304076 Поставки оборудования для ваших станков
https://www.instagram.com/dtw.moscow/
dtw.moscow@gmail.com
https://www.instagram.com/dtw.moscow/
dtw.moscow@gmail.com