|
|
|
@@ -1,360 +1,452 @@
|
|
|
|
|
#include "modbus.h"
|
|
|
|
|
|
|
|
|
|
#define On 1
|
|
|
|
|
#define Off 0
|
|
|
|
|
#define Change_output 10
|
|
|
|
|
#define discrete_output_reading 11
|
|
|
|
|
uint8_t state;
|
|
|
|
|
uint8_t packet[SIZE_MODBUS_PAKET];
|
|
|
|
|
uint8_t size_of_packet;
|
|
|
|
|
uint16_t crc_modbus;
|
|
|
|
|
uint16_t first_register_device;
|
|
|
|
|
uint16_t number_of_registers_required;
|
|
|
|
|
uint16_t crc_answ;
|
|
|
|
|
uint8_t modbus_answ[SIZE_MODBUS_PAKET];
|
|
|
|
|
uint16_t expected_crc;
|
|
|
|
|
unsigned long time;
|
|
|
|
|
uint8_t packet_accepted;
|
|
|
|
|
uint16_t last_possible_register;
|
|
|
|
|
|
|
|
|
|
uint16_t crc_chk(uint8_t *data, uint8_t length);
|
|
|
|
|
uint16_t bytes_unification(uint8_t high, uint8_t low);
|
|
|
|
|
uint8_t read_coils(uint8_t address, uint8_t value, uint8_t *modbus_answ);
|
|
|
|
|
uint8_t read_discrete_inputs(uint8_t address, uint8_t value, uint8_t *modbus_answ);
|
|
|
|
|
uint8_t read_holding_registers(uint8_t address, uint8_t value, uint8_t *modbus_answ);
|
|
|
|
|
uint8_t read_input_registers(uint8_t address, uint8_t value, uint8_t *modbus_answ);
|
|
|
|
|
uint8_t write_single_coil(uint8_t address, uint16_t coil_addr, int on, uint8_t *modbus_answ);
|
|
|
|
|
uint8_t write_single_register(uint8_t address, uint16_t register_addr, uint16_t new_value, uint8_t *modbus_answ);
|
|
|
|
|
uint8_t write_multiple_coils(uint8_t address, uint16_t first_coil_addr, uint16_t recorded_num, uint8_t *modbus_answ);
|
|
|
|
|
uint8_t write_multiple_registers(uint8_t address, uint16_t first_register_addr, uint16_t recorded_num, uint8_t *modbus_answ);
|
|
|
|
|
uint8_t modbus_err_answ(uint8_t address, uint8_t func_code, uint8_t err_code, uint8_t *modbus_answ);
|
|
|
|
|
void modbus_rtu_send(uint8_t answ_len);
|
|
|
|
|
|
|
|
|
|
volatile unsigned char Data_Rx_ModbasRtu[30];//массив принятых данных
|
|
|
|
|
volatile unsigned char quantity_Data_ModbasRtu; //количество принятых данных
|
|
|
|
|
//Volatile — ключевое слово языков C/C++, которое информирует компилятор о том, что значение переменной может меняться извне и что компилятор не будет оптимизировать эту переменную
|
|
|
|
|
volatile unsigned int Data_ModbasRtu_analog_input[1]; //данные на аналоговом входе [1 - НОМЕР АНАЛОГОВОГО ВХОДА]
|
|
|
|
|
volatile unsigned int Data_ModbasRtu_analog_Output[1]; //данные на аналоговом выходе [1 - НОМЕР АНАЛОГОВОГО ВЫХОДА]
|
|
|
|
|
volatile unsigned char Data_ModbasRtu_Binary_input[(1/8)+1]; //данные на дискретном входе [1 - НОМЕР ДИСКРЕТНОГО ВХОДА]
|
|
|
|
|
volatile unsigned char Data_ModbasRtu_Binary_Output[(1/8)+1]; //данные на дискретном выходе [1 - НОМЕР ДИСКРЕТНОГО ВЫХОДА]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//ПРИНЯТЬ ДАННЫЕ С КОЛЬЦЕВОГО БУФЕРА(Data_Rx_ModbasRtu)
|
|
|
|
|
//выглядит как int rb_get(struct rb* _rb, char *element)
|
|
|
|
|
Data_Rx_ModbasRtu = rb_get(&, 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//КОНТРОЛЬНАЯ СУММА(+)
|
|
|
|
|
int crc_chk(unsigned char* data, unsigned char length)
|
|
|
|
|
void modbus_init(void)
|
|
|
|
|
{
|
|
|
|
|
register int j; // register - спецификатор, который предполагает, что доступ к объекту будет быстрым
|
|
|
|
|
register unsigned int reg_crc = 0xFFFF; //объявление 16-битного регистра
|
|
|
|
|
while (length--)
|
|
|
|
|
size_of_packet = 0;
|
|
|
|
|
crc_modbus = 0xFFFF;
|
|
|
|
|
state = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void range_in_range(uint8_t a, uint8_t b, uint8_t x, uint8_t y)
|
|
|
|
|
{
|
|
|
|
|
uint8_t c = a + b - 1;
|
|
|
|
|
if (a >= x && a <= y) || (c >= x && c <= y)
|
|
|
|
|
{
|
|
|
|
|
reg_crc ^= *data++; //каждый байт-исключение складывается по исключающему ИЛИ с текущим значением регистра контр суммы. После последнего 8 сдвига следующий байт складывается с текущей величиной регистра контр суммы и процесс сдвига повторяется повторно 8 раз
|
|
|
|
|
for (j = 0; j < 8; j++)
|
|
|
|
|
{
|
|
|
|
|
if (reg_crc & 0x01) //проверка младшего бита
|
|
|
|
|
{
|
|
|
|
|
reg_crc = (reg_crc >> 1) ^ 0xA001; //проверочный код на основе полинома. Результат всегда сдвигается в сторону младшего бита с заполнением нулем старшего бита. Если младший бит = 1, то производится искл ИЛИ содержимого регистра контр суммы и полиномиального числа
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reg_crc = reg_crc >> 1; //если младший бит = 0, искл ИЛИ не делается
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return reg_crc; //регистр хранения конечного результата контр суммы
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//ОБЪЕДИНЕНИЕ ДВУХ БАЙТ(старшего и младшего)
|
|
|
|
|
int ModbasRtu_Register_address(unsigned char Li)
|
|
|
|
|
//Hi - старший байт
|
|
|
|
|
//Li - младший
|
|
|
|
|
{
|
|
|
|
|
register char Hi = Li - 1;
|
|
|
|
|
return Data_Rx_ModbasRtu[Hi] * 256 + Data_Rx_ModbasRtu[Li]; //считываем адрес старшего и младшего байта
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//АДРЕС
|
|
|
|
|
int Modbus_addr()
|
|
|
|
|
{
|
|
|
|
|
volatile unsigned int adres;
|
|
|
|
|
adres = ModbasRtu_Register_address(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//ПРОВЕРКА КОНТРОЛЬНОЙ СУММЫ В ПОЛУЧЕННОЙ ПОСЫЛКЕ ДАННЫХ
|
|
|
|
|
char Data_integrity()
|
|
|
|
|
{
|
|
|
|
|
register unsigned int Temp_2;
|
|
|
|
|
register unsigned char Temp_3;
|
|
|
|
|
quantity_Data_ModbasRtu = quantity_Data_ModbasRtu - 2; //убираем контрольную сумму от адресов
|
|
|
|
|
Temp_2 = crc_chk(Data_Rx_ModbasRtu, quantity_Data_ModbasRtu); //вычисляем контрольную сумму
|
|
|
|
|
Temp_3 = Temp_2; //выделяем старший байт с контрольной суммы
|
|
|
|
|
if (Data_Rx_ModbasRtu[quantity_Data_ModbasRtu] == Temp_3) //сравнимаем с таблицы старший байт с контрольной суммой
|
|
|
|
|
{
|
|
|
|
|
quantity_Data_ModbasRtu++; //объем данных увеличается
|
|
|
|
|
Temp_3 = (Temp_2 >> 8); //сдвиг на 8 бит
|
|
|
|
|
if (Data_Rx_ModbasRtu[quantity_Data_ModbasRtu] == Temp_3) //старший
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//РАБОТА С ДИСКРЕТНЫМИ ВХОДАМИ И ВЫВОДАМИ
|
|
|
|
|
char _Bin_input_Output(register unsigned char NUMBER, register unsigned char state, volatile unsigned char* Masiv, volatile unsigned char Sd)
|
|
|
|
|
// Функция приема modbus-запроса RTU
|
|
|
|
|
void modbus_rtu(void)
|
|
|
|
|
{
|
|
|
|
|
volatile unsigned char Temp = 0, Temp_1 = 0;
|
|
|
|
|
while (NUMBER >= 8)
|
|
|
|
|
// состояние приема
|
|
|
|
|
if (state == 0)
|
|
|
|
|
{
|
|
|
|
|
NUMBER = NUMBER - 8;
|
|
|
|
|
Temp++; //определяем, в каком регистре нужно изменить либо считывать бит
|
|
|
|
|
uint8_t data;
|
|
|
|
|
// Размер пакета 0
|
|
|
|
|
size_of_packet = 0;
|
|
|
|
|
// Записываем время начала приема пакета
|
|
|
|
|
time = get_millis();
|
|
|
|
|
// Пакет не принят
|
|
|
|
|
packet_accepted = 0;
|
|
|
|
|
|
|
|
|
|
// Цикл приема символов
|
|
|
|
|
while (!packet_accepted)
|
|
|
|
|
{
|
|
|
|
|
// Читаем символ из входящего кольцевого буфера
|
|
|
|
|
// Кольцевой буфер не пуст
|
|
|
|
|
if (count_elements(get_rb_receive()) > 0)
|
|
|
|
|
{
|
|
|
|
|
// Читаем символ из кольцевого буфера
|
|
|
|
|
rb_get(get_rb_receive(), &data);
|
|
|
|
|
if (size_of_packet == 0)
|
|
|
|
|
{
|
|
|
|
|
// Пришел новый пакет
|
|
|
|
|
packet[0] = data;
|
|
|
|
|
size_of_packet = 1;
|
|
|
|
|
time = get_millis();
|
|
|
|
|
}
|
|
|
|
|
Temp_1 = Masiv[Temp];
|
|
|
|
|
if (Sd == 10) //выполняется, если нужно изменить бит
|
|
|
|
|
{
|
|
|
|
|
if (state == On)
|
|
|
|
|
Temp_1 |= (1 << NUMBER);
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
Temp_1 &= ~(1 << NUMBER);
|
|
|
|
|
Masiv[Temp] = Temp_1;
|
|
|
|
|
}
|
|
|
|
|
else //выполняется, если нужно прочитать состояние бита
|
|
|
|
|
{
|
|
|
|
|
if (Temp_1 & (1 << NUMBER))
|
|
|
|
|
NUMBER = 1;
|
|
|
|
|
else
|
|
|
|
|
NUMBER = 0;
|
|
|
|
|
}
|
|
|
|
|
return NUMBER; //возвращает состояние прочитанного бита
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//КОДЫ ФУНКЦИИ :
|
|
|
|
|
|
|
|
|
|
//Чтение значений нескольких регистров флагов 0x01, Чтение значений нескольких дискретных входов 0x02
|
|
|
|
|
void Reading_Discrete_Output(unsigned char* Massiv, register unsigned char Number_)
|
|
|
|
|
{
|
|
|
|
|
volatile unsigned int adres, Number_bits;
|
|
|
|
|
register unsigned char Temp = 0, Data, Temp2 = 0, adres2 = 0, Temp3 = 2;
|
|
|
|
|
adres = ModbasRtu_Register_address(3); //адрес регистра, к которому обращается мастер
|
|
|
|
|
if (adres > Number_) //проверка, что адрес не превышает допустимый
|
|
|
|
|
// Проверяем линейный буфер на переполнение
|
|
|
|
|
if (size_of_packet == SIZE_MODBUS_PAKET)
|
|
|
|
|
{
|
|
|
|
|
Error_modbasRtu(0x02); //недопустимый адрес
|
|
|
|
|
// Буфер переполнился
|
|
|
|
|
// Ждем новый пакет
|
|
|
|
|
size_of_packet = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Number_bits = ModbasRtu_Register_address(5); //количество бит, которые нужно передать
|
|
|
|
|
while (adres >= 8) //узнаем номер ячейки массива, с которой начнем считывать данные
|
|
|
|
|
time = get_millis();
|
|
|
|
|
packet[size_of_packet] = data;
|
|
|
|
|
size_of_packet++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Проверка временного интервала между символами
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
adres = adres - 8; //по завершению преобразования хранится бит, с которого нужно начинать считывание
|
|
|
|
|
Temp++; //номер байта в массиве к которому изначально происходит обращение
|
|
|
|
|
}
|
|
|
|
|
Data = Massiv[Temp]; //считываем данные
|
|
|
|
|
//считываем побитно и формируем данные для отправки
|
|
|
|
|
while (Number_bits > 0) //проверка, что все биты запроса переданы
|
|
|
|
|
if ((get_millis() - time) > MAX_PAUSE)
|
|
|
|
|
{
|
|
|
|
|
Number_bits--;
|
|
|
|
|
if (Data & (1 << adres))
|
|
|
|
|
// Превышен таймаут
|
|
|
|
|
// Пакет закончился
|
|
|
|
|
packet_accepted = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Проверяем размер пакета
|
|
|
|
|
if (size_of_packet < 5)
|
|
|
|
|
{
|
|
|
|
|
Temp2 |= (1 << adres2);
|
|
|
|
|
size_of_packet = 0;
|
|
|
|
|
crc_modbus = 0xFFFF;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
adres2++;
|
|
|
|
|
adres++;
|
|
|
|
|
if (adres2 == 8)
|
|
|
|
|
{
|
|
|
|
|
adres2 = 0;
|
|
|
|
|
Temp3++;
|
|
|
|
|
Data_Rx_ModbasRtu[Temp3] = Temp2;
|
|
|
|
|
Temp2 = 0;
|
|
|
|
|
}
|
|
|
|
|
if (adres == 8)
|
|
|
|
|
{
|
|
|
|
|
adres = 0;
|
|
|
|
|
Temp++;
|
|
|
|
|
Data = Massiv[Temp]; //считываем данные
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (adres2 > 0)
|
|
|
|
|
{
|
|
|
|
|
Temp3++;
|
|
|
|
|
Data_Rx_ModbasRtu[Temp3] = Temp2;
|
|
|
|
|
}
|
|
|
|
|
Data_Rx_ModbasRtu[2] = Temp3 - 2; //количество переданных байт (без учета адреса и кода команды)
|
|
|
|
|
Temp3++;
|
|
|
|
|
check_sum(Temp3); //подсчитаем контрольную сумму для передачи данных
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Чтение значений нескольких регистров хранения 0x03, Чтение значений нескольких регистров ввода 0x04
|
|
|
|
|
void Read_analog_input(unsigned char* Massiv, register unsigned char Number_, unsigned char Vt)
|
|
|
|
|
//Vt - ввод или вывод
|
|
|
|
|
{
|
|
|
|
|
volatile unsigned int address, Number_bits, Data;
|
|
|
|
|
volatile unsigned char Adress = 4;
|
|
|
|
|
address = ModbasRtu_Register_address(3); //адрес регистра, к которому обращается мастер
|
|
|
|
|
if (address > Number_) //проверка, что адрес не превышает допустимый
|
|
|
|
|
// считаем контрольную сумму CRC и сравниваем с CRC в пакете
|
|
|
|
|
expected_crc = bytes_unification(size_of_packet - 2, size_of_packet - 1);
|
|
|
|
|
crc_modbus = crc_chk(packet, size_of_packet - 3);
|
|
|
|
|
// проверка контрольной суммы CRC
|
|
|
|
|
if (crc_modbus != expected_crc)
|
|
|
|
|
{
|
|
|
|
|
Error_modbasRtu(0x02); //указанный в запросе адрес не существует
|
|
|
|
|
size_of_packet = 0;
|
|
|
|
|
crc_modbus = 0xFFFF;
|
|
|
|
|
// формируем ошибку произошла невосполнимая ошибка
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// проверка адреса устройства
|
|
|
|
|
else if (get_device_address() != packet[0])
|
|
|
|
|
{
|
|
|
|
|
size_of_packet = 0;
|
|
|
|
|
crc_modbus = 0xFFFF;
|
|
|
|
|
// формируем ошибку адреса
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Number_bits = ModbasRtu_Register_address(5); //количество байт, которые нужно передать (старший и младший)
|
|
|
|
|
Data_Rx_ModbasRtu[2] = Number_bits * 2; //количество байт информащии, которые будут переданы
|
|
|
|
|
Adress = 3;
|
|
|
|
|
while (Number_bits > 0)
|
|
|
|
|
// Расчленяем пакет
|
|
|
|
|
state = 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Функция отправки modbus-запроса в кольцевой буфер
|
|
|
|
|
void modbus_rtu_send(uint8_t answ_len)
|
|
|
|
|
{
|
|
|
|
|
size_of_packet = 0;
|
|
|
|
|
// состояние отправки
|
|
|
|
|
if (state == 1)
|
|
|
|
|
{
|
|
|
|
|
if (Vt == 1) //определение, что считывать - вход или выход
|
|
|
|
|
// цикл отправки символов
|
|
|
|
|
while (state == 1)
|
|
|
|
|
{
|
|
|
|
|
Data = Data_ModbasRtu_analog_input[address];
|
|
|
|
|
// Отправка всех байт в кольцевой буфер
|
|
|
|
|
rb_put(get_rb_transmit(), modbus_answ[size_of_packet]);
|
|
|
|
|
|
|
|
|
|
// смещаем указатель
|
|
|
|
|
size_of_packet++;
|
|
|
|
|
if (size_of_packet == answ_len)
|
|
|
|
|
{
|
|
|
|
|
state = 0;
|
|
|
|
|
size_of_packet = 0;
|
|
|
|
|
crc_modbus = 0xFFFF;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Функция формирования modbus-ответа RTU
|
|
|
|
|
void modbus_answer()
|
|
|
|
|
{
|
|
|
|
|
switch (packet[1])
|
|
|
|
|
{
|
|
|
|
|
case READ_COILS:
|
|
|
|
|
first_register_device = bytes_unification(2, 3);
|
|
|
|
|
number_of_registers_required = bytes_unification(4, 5);
|
|
|
|
|
|
|
|
|
|
if (range_in_range(first_register_device, number_of_registers_required, COIL_1, COIL_4) && number_of_registers_required > 0)
|
|
|
|
|
{
|
|
|
|
|
modbus_rtu_send(modbus_err_answ(get_device_address(), packet[1], ILLEGAL_DATA_ADDRESS, &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Data = Data_ModbasRtu_analog_Output[address];
|
|
|
|
|
}
|
|
|
|
|
address++;
|
|
|
|
|
Massiv = &Data;
|
|
|
|
|
Data_Rx_ModbasRtu[Adress++] = Massiv[1]; //считываем старший байт
|
|
|
|
|
Data_Rx_ModbasRtu[Adress++] = Massiv[0]; //считываем младший байт
|
|
|
|
|
Number_bits = Number_bits - 1;
|
|
|
|
|
}
|
|
|
|
|
check_sum(Adress); //подсчитаем контрольную сумму для передачи данных
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Запись одного регистра флагов 0x05
|
|
|
|
|
void Changing_Discrete_Output(void)
|
|
|
|
|
{
|
|
|
|
|
register unsigned int address;
|
|
|
|
|
address = ModbasRtu_Register_address(3); //адрес регистра, к которому обращается мастер
|
|
|
|
|
if (address > 11) //проверка, что адрес не превышает допустимый [11 - НОМЕР ДИСКРЕТНОГО ВЫХОДА]
|
|
|
|
|
uint8_t value = 0;
|
|
|
|
|
while (number_of_registers_required > 0)
|
|
|
|
|
{
|
|
|
|
|
Error_modbasRtu(0x02);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
switch (first_register_device)
|
|
|
|
|
{
|
|
|
|
|
if (Data_Rx_ModbasRtu[4] == 255)
|
|
|
|
|
_Bin_input_Output(address, On, Data_ModbasRtu_Binary_Output, Change_output);
|
|
|
|
|
else
|
|
|
|
|
_Bin_input_Output(address, Off, Data_ModbasRtu_Binary_Output, Change_output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Запись одного регистра хранения 0x06
|
|
|
|
|
void analog_output_recording(void)
|
|
|
|
|
{
|
|
|
|
|
register int address;
|
|
|
|
|
address = ModbasRtu_Register_address(3);
|
|
|
|
|
if (address > 11) //[11 - НОМЕР АНАЛОГОВОГО ВЫХОДА]
|
|
|
|
|
{
|
|
|
|
|
Error_modbasRtu(0x02);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Data_ModbasRtu_analog_Output[address] = ModbasRtu_Register_address(5); //данные, которые нужно записать
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//ОШИБКА
|
|
|
|
|
void Error_modbasRtu(volatile unsigned char Temp_Error)
|
|
|
|
|
{
|
|
|
|
|
Data_Rx_ModbasRtu[1] |= (1 << 7);
|
|
|
|
|
Data_Rx_ModbasRtu[2] = Temp_Error; //код ошибки
|
|
|
|
|
check_sum(3); //подсчитаем контрольную сумму для передачи данных
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//ОТВЕТ КОНТРОЛЬНОЙ СУММЫ
|
|
|
|
|
void check_sum(register unsigned char Adress)
|
|
|
|
|
{
|
|
|
|
|
register unsigned int RC;
|
|
|
|
|
RC = crc_chk(Data_Rx_ModbasRtu, Adress); //вычисляем контрольную сумму
|
|
|
|
|
Data_Rx_ModbasRtu[Adress] = RC; //младший байт контрольной суммы
|
|
|
|
|
Adress++;
|
|
|
|
|
Data_Rx_ModbasRtu[Adress] = RC >> 8; //старший байт контрольной суммы
|
|
|
|
|
quantity_Data_ModbasRtu = Adress;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//ФОРМИРУЕМ ДЕЙСТВИЕ И ОТВЕТ НА ПРИНЯТЫЕ КОМАНДЫ ИЛИ ФОРМИРУЕМ ОТВЕТ ОБ ОШИБКАХ
|
|
|
|
|
void modbasRtu_Answer()
|
|
|
|
|
{
|
|
|
|
|
switch (Data_Rx_ModbasRtu[1])
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
//Modbus RTU чтение дискретного выхода 0x01
|
|
|
|
|
Reading_Discrete_Output(Data_ModbasRtu_Binary_Output, 11); //[11 - НОМЕР ДИСКРЕТНОГО ВЫХОДА]
|
|
|
|
|
case COIL_1:
|
|
|
|
|
value |= (gpio_read(PIN_COIL1));
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
//Modbus RTU чтение дискретного входа 0x02
|
|
|
|
|
Reading_Discrete_Output(Data_ModbasRtu_Binary_input, 11); //[11 - НОМЕР ДИСКРЕТНОГО ВХОДА]
|
|
|
|
|
case COIL_2:
|
|
|
|
|
value |= (gpio_read(PIN_COIL2));
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
case COIL_3:
|
|
|
|
|
value |= (gpio_read(PIN_COIL3));
|
|
|
|
|
break;
|
|
|
|
|
case COIL_4:
|
|
|
|
|
value |= (gpio_read(PIN_COIL4));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Read_analog_input(Data_ModbasRtu_analog_Output, 11, 0); //Modbus RTU на чтение аналогового выхода 0x03 [11 - НОМЕР АНАЛОГОВОГО ВЫХОДА]
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
first_register_device++;
|
|
|
|
|
number_of_registers_required--;
|
|
|
|
|
}
|
|
|
|
|
modbus_rtu_send(read_coils(get_device_address(), value, &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Read_analog_input(Data_ModbasRtu_analog_input, 11, 1); //Modbus RTU на чтение аналогового входа 0x04 [11 - НОМЕР АНАЛОГОВОГО ВХОДА]
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
//Modbus RTU на запись дискретного вывода 0x05
|
|
|
|
|
Changing_Discrete_Output();
|
|
|
|
|
case READ_DISCRETE_INPUTS:
|
|
|
|
|
first_register_device = bytes_unification(2, 3);
|
|
|
|
|
number_of_registers_required = bytes_unification(4, 5);
|
|
|
|
|
|
|
|
|
|
if (range_in_range(first_register_device, number_of_registers_required, DISCRETE_INPUTS_1, DISCRETE_INPUTS_4) && number_of_registers_required > 0)
|
|
|
|
|
{
|
|
|
|
|
modbus_rtu_send(modbus_err_answ(get_device_address(), packet[1], ILLEGAL_DATA_ADDRESS, &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
uint8_t value = 0;
|
|
|
|
|
while (number_of_registers_required > 0)
|
|
|
|
|
{
|
|
|
|
|
switch (first_register_device)
|
|
|
|
|
{
|
|
|
|
|
case DISCRETE_INPUTS_1:
|
|
|
|
|
value |= (gpio_read(PIN_DISCRETE_INPUT_1));
|
|
|
|
|
break;
|
|
|
|
|
case 6:
|
|
|
|
|
//Modbus RTU на запись аналогового выхода 0x06
|
|
|
|
|
analog_output_recording();
|
|
|
|
|
case DISCRETE_INPUTS_2:
|
|
|
|
|
value |= (gpio_read(PIN_DISCRETE_INPUT_2));
|
|
|
|
|
break;
|
|
|
|
|
case DISCRETE_INPUTS_3:
|
|
|
|
|
value |= (gpio_read(PIN_DISCRETE_INPUT_3));
|
|
|
|
|
break;
|
|
|
|
|
case DISCRETE_INPUTS_4:
|
|
|
|
|
value |= (gpio_read(PIN_DISCRETE_INPUT_4));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
first_register_device ++;
|
|
|
|
|
number_of_registers_required--;
|
|
|
|
|
}
|
|
|
|
|
modbus_rtu_send(read_discrete_inputs(get_device_address(), value, &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case READ_HOLDING_REGISTERS:
|
|
|
|
|
first_register_device = bytes_unification(2, 3);
|
|
|
|
|
number_of_registers_required = bytes_unification(4, 5);
|
|
|
|
|
|
|
|
|
|
if (first_register_device != HOLDING_REGISTER_SLAVE_ADDRESS || number_of_registers_required != 1)
|
|
|
|
|
{
|
|
|
|
|
modbus_rtu_send(modbus_err_answ(get_device_address(), packet[1], ILLEGAL_DATA_ADDRESS, &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
modbus_rtu_send(read_holding_registers(get_device_address(), get_device_address(), &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case READ_INPUT_REGISTERS:
|
|
|
|
|
first_register_device = bytes_unification(2, 3);
|
|
|
|
|
number_of_registers_required = bytes_unification(4, 5);
|
|
|
|
|
|
|
|
|
|
if (first_register_device != INPUT_REGISTERS || number_of_registers_required != 1)
|
|
|
|
|
{
|
|
|
|
|
modbus_rtu_send(modbus_err_answ(get_device_address(), packet[1], ILLEGAL_DATA_ADDRESS, &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
modbus_rtu_send(read_input_registers(get_device_address(), get_adc_value(), &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case WRITE_SINGLE_COIL:
|
|
|
|
|
first_register_device = bytes_unification(2, 3);
|
|
|
|
|
|
|
|
|
|
if (range_in_range(first_register_device, 1, COIL_1, COIL_4))
|
|
|
|
|
{
|
|
|
|
|
modbus_rtu_send(modbus_err_answ(get_device_address(), packet[1], ILLEGAL_DATA_ADDRESS, &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
uint8_t value = 0;
|
|
|
|
|
switch (first_register_device)
|
|
|
|
|
{
|
|
|
|
|
case COIL_1:
|
|
|
|
|
value = gpio_write(PIN_COIL1, packet[4]);
|
|
|
|
|
break;
|
|
|
|
|
case COIL_2:
|
|
|
|
|
value = gpio_write(PIN_COIL2, packet[4]);
|
|
|
|
|
break;
|
|
|
|
|
case COIL_3:
|
|
|
|
|
value = gpio_write(PIN_COIL3, packet[4]);
|
|
|
|
|
break;
|
|
|
|
|
case COIL_4:
|
|
|
|
|
value = gpio_write(PIN_COIL4, packet[4]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modbus_rtu_send(write_single_coil(get_device_address(), first_register_device, GPIORead(first_register_device), &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case WRITE_SINGLE_REGISTER:
|
|
|
|
|
first_register_device = bytes_unification(2, 3);
|
|
|
|
|
|
|
|
|
|
if (first_register_device != HOLDING_REGISTER_SLAVE_ADDRESS)
|
|
|
|
|
{
|
|
|
|
|
modbus_rtu_send(modbus_err_answ(get_device_address(), packet[1], ILLEGAL_DATA_ADDRESS, &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
write_device_address(bytes_unification(4, 5));
|
|
|
|
|
|
|
|
|
|
modbus_rtu_send(write_single_register(get_device_address(), first_register_device, get_device_address(), &modbus_answ));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case 15:
|
|
|
|
|
//Modbus RTU на запись нескольких дискретных выводов 0x0F
|
|
|
|
|
asm("nop"); //команда протокола, которая предписывает ничего не делать
|
|
|
|
|
//break;
|
|
|
|
|
case 16:
|
|
|
|
|
//Modbus RTU на запись нескольких аналоговых выводов 0x10
|
|
|
|
|
asm("nop");
|
|
|
|
|
//break;
|
|
|
|
|
default:
|
|
|
|
|
//команды не подерживаются
|
|
|
|
|
Error_modbasRtu(0x01); //принятый код функции не может быть обработан
|
|
|
|
|
modbus_rtu_send(modbus_err_answ(get_device_address(), packet[1], ILLEGAL_FUNCTION, &modbus_answ));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//ПОДПРОГРАММЫ ДЛЯ ОБРАБОТКИ ЗНАЧЕНИЙ
|
|
|
|
|
|
|
|
|
|
//прочитать бит входов
|
|
|
|
|
char read_digital_inputs(volatile unsigned char Temp1)
|
|
|
|
|
// Функция вычисления контрольной суммы CRC
|
|
|
|
|
uint16_t crc_chk(uint8_t *data, uint8_t length)
|
|
|
|
|
{
|
|
|
|
|
return _Bin_input_Output(Temp1, On, Data_ModbasRtu_Binary_input, discrete_output_reading); //считать состояние выхода из буферного массива
|
|
|
|
|
register int16_t j;
|
|
|
|
|
register uint16_t reg_crc = 0xFFFF;
|
|
|
|
|
while (length--)
|
|
|
|
|
{
|
|
|
|
|
reg_crc ^= *data++;
|
|
|
|
|
for (j = 0; j < 8; j++)
|
|
|
|
|
{
|
|
|
|
|
if (reg_crc & 0x01)
|
|
|
|
|
{
|
|
|
|
|
reg_crc = (reg_crc >> 1) ^ 0xA001;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reg_crc = reg_crc >> 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return reg_crc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//изменить бит входов
|
|
|
|
|
void change_digital_inputs(volatile unsigned char Temp1, volatile unsigned char Temp2)
|
|
|
|
|
// Функция объединения двух байт (старшего и младшего)
|
|
|
|
|
uint16_t bytes_unification(uint8_t high, uint8_t low)
|
|
|
|
|
{
|
|
|
|
|
_Bin_input_Output(Temp1, Temp2, Data_ModbasRtu_Binary_input, Change_output);
|
|
|
|
|
return (packet[high] << 8) | packet[low];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//прочитать бит выходов
|
|
|
|
|
char read_digital_Output(volatile unsigned char Temp1)
|
|
|
|
|
// Коды функций (для формирования ответа)
|
|
|
|
|
|
|
|
|
|
// Чтение данных с регистров флагов (функция с кодом 1)
|
|
|
|
|
uint8_t read_coils(uint8_t address, uint8_t value, uint8_t *modbus_answ)
|
|
|
|
|
{
|
|
|
|
|
return _Bin_input_Output(Temp1, On, Data_ModbasRtu_Binary_Output, discrete_output_reading); //считать состояние выхода из буферного массива
|
|
|
|
|
modbus_answ[0] = address; // Адрес
|
|
|
|
|
modbus_answ[1] = 0x01; // Код функции
|
|
|
|
|
modbus_answ[2] = 0x01; // Кол-во передаваемых байт
|
|
|
|
|
modbus_answ[3] = value; // Данные
|
|
|
|
|
crc_answ = crc_chk(modbus_answ, 4); // Подсчет контрольной суммы
|
|
|
|
|
modbus_answ[4] = crc_answ & 0xFF; // Первый байт контрольной суммы
|
|
|
|
|
modbus_answ[5] = (crc_answ >> 8) & 0xFF; // Второй байт контрольной суммы
|
|
|
|
|
return 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//изменить бит выходов
|
|
|
|
|
void change_digital_Output(volatile unsigned char Temp1, volatile unsigned char Temp2)
|
|
|
|
|
// Чтение данных с дискретных входов (функция с кодом 2)
|
|
|
|
|
uint8_t read_discrete_inputs(uint8_t address, uint8_t value, uint8_t *modbus_answ)
|
|
|
|
|
{
|
|
|
|
|
_Bin_input_Output(Temp1, Temp2, Data_ModbasRtu_Binary_Output, Change_output);
|
|
|
|
|
modbus_answ[0] = address; // Адрес
|
|
|
|
|
modbus_answ[1] = 0x02; // Код функции
|
|
|
|
|
modbus_answ[2] = 0x01; // Кол-во передаваемых байт
|
|
|
|
|
modbus_answ[3] = value; // Данные
|
|
|
|
|
crc_answ = crc_chk(modbus_answ, 4); // Подсчет контрольной суммы
|
|
|
|
|
modbus_answ[4] = crc_answ & 0xFF; // Первый байт контрольной суммы
|
|
|
|
|
modbus_answ[5] = (crc_answ >> 8) & 0xFF; // Второй байт контрольной суммы
|
|
|
|
|
return 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//записать значение аналоговых выходов
|
|
|
|
|
void change_analogue_Output(volatile unsigned char nomer, int Data)
|
|
|
|
|
// Чтение данных с регистров хранения (функция с кодом 3)
|
|
|
|
|
uint8_t read_holding_registers(uint8_t address, uint16_t value, uint8_t *modbus_answ)
|
|
|
|
|
{
|
|
|
|
|
Data_ModbasRtu_analog_Output[nomer] = Data;
|
|
|
|
|
modbus_answ[0] = address; // Адрес
|
|
|
|
|
modbus_answ[1] = 0x03; // Код функции
|
|
|
|
|
modbus_answ[2] = 0x01; // Кол-во передаваемых байт
|
|
|
|
|
modbus_answ[3] = value; // Первый байт данных
|
|
|
|
|
modbus_answ[4] = value >> 8; // Второй байт данных
|
|
|
|
|
crc_answ = crc_chk(modbus_answ, 5); // Подсчет контрольной суммы
|
|
|
|
|
modbus_answ[5] = crc_answ & 0xFF; // Первый байт контрольной суммы
|
|
|
|
|
modbus_answ[6] = (crc_answ >> 8) & 0xFF; // Второй байт контрольной суммы
|
|
|
|
|
return 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//записать значение аналоговых входов
|
|
|
|
|
void change_analogue_input(volatile unsigned char nomer, int Data)
|
|
|
|
|
// Чтение данных с регистров ввода (функция с кодом 4)
|
|
|
|
|
uint8_t read_input_registers(uint8_t address, uint16_t value, uint8_t *modbus_answ)
|
|
|
|
|
{
|
|
|
|
|
Data_ModbasRtu_analog_input[nomer] = Data;
|
|
|
|
|
modbus_answ[0] = address; // Адрес
|
|
|
|
|
modbus_answ[1] = 0x04; // Код функции
|
|
|
|
|
modbus_answ[2] = 0x01; // Кол-во передаваемых байт
|
|
|
|
|
modbus_answ[3] = value; // Первый байт данных
|
|
|
|
|
modbus_answ[4] = value >> 8; // Второй байт данных
|
|
|
|
|
crc_answ = crc_chk(modbus_answ, 5); // Подсчет контрольной суммы
|
|
|
|
|
modbus_answ[5] = crc_answ & 0xFF; // Первый байт контрольной суммы
|
|
|
|
|
modbus_answ[6] = (crc_answ >> 8) & 0xFF; // Второй байт контрольной суммы
|
|
|
|
|
return 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//считать значение аналоговых выходов
|
|
|
|
|
int read_analogue_Output(volatile unsigned char nomer)
|
|
|
|
|
// Запись данных в регистр флагов (функция с кодом 5)
|
|
|
|
|
uint8_t write_single_coil(uint8_t address, uint16_t coil_addr, int on, uint8_t *modbus_answ)
|
|
|
|
|
{
|
|
|
|
|
return Data_ModbasRtu_analog_Output[nomer];
|
|
|
|
|
modbus_answ[0] = address; // Адрес
|
|
|
|
|
modbus_answ[1] = 0x05; // Код функции
|
|
|
|
|
modbus_answ[2] = (coil_addr >> 8) & 0xFF; // Старший байт адреса
|
|
|
|
|
modbus_answ[3] = coil_addr & 0xFF; // Младший байт адреса
|
|
|
|
|
modbus_answ[4] = on ? 0xFF : 0x00; // Значение, которое нужно записать
|
|
|
|
|
modbus_answ[5] = 0x00;
|
|
|
|
|
crc_answ = crc_chk(modbus_answ, 6); // Подсчет контрольной суммы
|
|
|
|
|
modbus_answ[6] = crc_answ & 0xFF; // Первый байт контрольной суммы
|
|
|
|
|
modbus_answ[7] = (crc_answ >> 8) & 0xFF; // Второй байт контрольной суммы
|
|
|
|
|
return 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//считать значение аналоговых выходов
|
|
|
|
|
int read_analogue_input(volatile unsigned char nomer)
|
|
|
|
|
// Запись данных в регистр (функция с кодом 6)
|
|
|
|
|
uint8_t write_single_register(uint8_t address, uint16_t register_addr, uint16_t new_value, uint8_t *modbus_answ)
|
|
|
|
|
{
|
|
|
|
|
return Data_ModbasRtu_analog_input[nomer];
|
|
|
|
|
modbus_answ[0] = address; // Адрес
|
|
|
|
|
modbus_answ[1] = 0x06; // Код функции
|
|
|
|
|
modbus_answ[2] = (register_addr >> 8) & 0xFF; // Старший байт адреса
|
|
|
|
|
modbus_answ[3] = register_addr & 0xFF; // Младший байт адреса
|
|
|
|
|
modbus_answ[4] = (new_value >> 8) & 0xFF; // Старший байт значения
|
|
|
|
|
modbus_answ[5] = new_value & 0xFF; // Младший байт значения
|
|
|
|
|
crc_answ = crc_chk(modbus_answ, 6); // Подсчет контрольной суммы
|
|
|
|
|
modbus_answ[6] = crc_answ & 0xFF; // Первый байт контрольной суммы
|
|
|
|
|
modbus_answ[7] = (crc_answ >> 8) & 0xFF; // Второй байт контрольной суммы
|
|
|
|
|
return 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//(отправить по rb_put в кольцевой буфер ответ)
|
|
|
|
|
//выглядит как int rb_put(struct rb* _rb, char element)
|
|
|
|
|
void Data_Modbus_answer()
|
|
|
|
|
// Сообщение об ошибке
|
|
|
|
|
uint8_t modbus_err_answ(uint8_t address, uint8_t func_code, uint8_t err_code, uint8_t *modbus_answ)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
modbus_answ[0] = address; // Адрес
|
|
|
|
|
modbus_answ[1] = 0x80 + func_code; // Код функции + ошибка
|
|
|
|
|
modbus_answ[2] = err_code; // Код ошибки
|
|
|
|
|
crc_answ = crc_chk(modbus_answ, 3); // Подсчет контрольной суммы
|
|
|
|
|
modbus_answ[3] = crc_answ & 0xFF; // Первый байт контрольной суммы
|
|
|
|
|
modbus_answ[4] = (crc_answ >> 8) & 0xFF; // Второй байт контрольной суммы
|
|
|
|
|
return 5;
|
|
|
|
|
}
|