#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() { }