Modbus_1_Deva4ki/modbus.c
Анастасия Салангина d86e2a8805 modbus rtu protocol operation
2023-06-06 12:51:27 +03:00

360 lines
11 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#define On 1
#define Off 0
#define Change_output 10
#define discrete_output_reading 11
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)
{
register int j; // register - спецификатор, который предполагает, что доступ к объекту будет быстрым
register unsigned int reg_crc = 0xFFFF; //объявление 16-битного регистра
while (length--)
{
reg_crc ^= *data++; //каждый байт-исключение складывается по исключающему ИЛИ с текущим значением регистра контр суммы. После последнего 8 сдвига следующий байт складывается с текущей величиной регистра контр суммы и процесс сдвига повторяется повторно 8 раз
for (j = 0; j < 8; j++)
{
if (reg_crc & 0x01) //проверка младшего бита
{
reg_crc = (reg_crc >> 1) ^ 0xA001; //проверочный код на основе полинома. Результат всегда сдвигается в сторону младшего бита с заполнением нулем старшего бита. Если младший бит = 1, то производится искл ИЛИ содержимого регистра контр суммы и полиномиального числа
}
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)
{
volatile unsigned char Temp = 0, Temp_1 = 0;
while (NUMBER >= 8)
{
NUMBER = NUMBER - 8;
Temp++; //определяем, в каком регистре нужно изменить либо считывать бит
}
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_) //проверка, что адрес не превышает допустимый
{
Error_modbasRtu(0x02); //недопустимый адрес
}
else
{
Number_bits = ModbasRtu_Register_address(5); //количество бит, которые нужно передать
while (adres >= 8) //узнаем номер ячейки массива, с которой начнем считывать данные
{
adres = adres - 8; //по завершению преобразования хранится бит, с которого нужно начинать считывание
Temp++; //номер байта в массиве к которому изначально происходит обращение
}
Data = Massiv[Temp]; //считываем данные
//считываем побитно и формируем данные для отправки
while (Number_bits > 0) //проверка, что все биты запроса переданы
{
Number_bits--;
if (Data & (1 << adres))
{
Temp2 |= (1 << adres2);
}
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_) //проверка, что адрес не превышает допустимый
{
Error_modbasRtu(0x02); //указанный в запросе адрес не существует
}
else
{
Number_bits = ModbasRtu_Register_address(5); //количество байт, которые нужно передать (старший и младший)
Data_Rx_ModbasRtu[2] = Number_bits * 2; //количество байт информащии, которые будут переданы
Adress = 3;
while (Number_bits > 0)
{
if (Vt == 1) //определение, что считывать - вход или выход
{
Data = Data_ModbasRtu_analog_input[address];
}
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 - НОМЕР ДИСКРЕТНОГО ВЫХОДА]
{
Error_modbasRtu(0x02);
}
else
{
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 - НОМЕР ДИСКРЕТНОГО ВЫХОДА]
break;
case 2:
//Modbus RTU чтение дискретного входа 0x02
Reading_Discrete_Output(Data_ModbasRtu_Binary_input, 11); //[11 - НОМЕР ДИСКРЕТНОГО ВХОДА]
break;
case 3:
Read_analog_input(Data_ModbasRtu_analog_Output, 11, 0); //Modbus RTU на чтение аналогового выхода 0x03 [11 - НОМЕР АНАЛОГОВОГО ВЫХОДА]
break;
case 4:
Read_analog_input(Data_ModbasRtu_analog_input, 11, 1); //Modbus RTU на чтение аналогового входа 0x04 [11 - НОМЕР АНАЛОГОВОГО ВХОДА]
break;
case 5:
//Modbus RTU на запись дискретного вывода 0x05
Changing_Discrete_Output();
break;
case 6:
//Modbus RTU на запись аналогового выхода 0x06
analog_output_recording();
break;
case 15:
//Modbus RTU на запись нескольких дискретных выводов 0x0F
asm("nop"); //команда протокола, которая предписывает ничего не делать
//break;
case 16:
//Modbus RTU на запись нескольких аналоговых выводов 0x10
asm("nop");
//break;
default:
//команды не подерживаются
Error_modbasRtu(0x01); //принятый код функции не может быть обработан
break;
}
}
//ПОДПРОГРАММЫ ДЛЯ ОБРАБОТКИ ЗНАЧЕНИЙ
//прочитать бит входов
char read_digital_inputs(volatile unsigned char Temp1)
{
return _Bin_input_Output(Temp1, On, Data_ModbasRtu_Binary_input, discrete_output_reading); //считать состояние выхода из буферного массива
}
//изменить бит входов
void change_digital_inputs(volatile unsigned char Temp1, volatile unsigned char Temp2)
{
_Bin_input_Output(Temp1, Temp2, Data_ModbasRtu_Binary_input, Change_output);
}
//прочитать бит выходов
char read_digital_Output(volatile unsigned char Temp1)
{
return _Bin_input_Output(Temp1, On, Data_ModbasRtu_Binary_Output, discrete_output_reading); //считать состояние выхода из буферного массива
}
//изменить бит выходов
void change_digital_Output(volatile unsigned char Temp1, volatile unsigned char Temp2)
{
_Bin_input_Output(Temp1, Temp2, Data_ModbasRtu_Binary_Output, Change_output);
}
//записать значение аналоговых выходов
void change_analogue_Output(volatile unsigned char nomer, int Data)
{
Data_ModbasRtu_analog_Output[nomer] = Data;
}
//записать значение аналоговых входов
void change_analogue_input(volatile unsigned char nomer, int Data)
{
Data_ModbasRtu_analog_input[nomer] = Data;
}
//считать значение аналоговых выходов
int read_analogue_Output(volatile unsigned char nomer)
{
return Data_ModbasRtu_analog_Output[nomer];
}
//считать значение аналоговых выходов
int read_analogue_input(volatile unsigned char nomer)
{
return Data_ModbasRtu_analog_input[nomer];
}
//(отправить по rb_put в кольцевой буфер ответ)
//выглядит как int rb_put(struct rb* _rb, char element)
void Data_Modbus_answer()
{
}