diff --git a/modbus.c b/modbus.c new file mode 100644 index 0000000..9830fa8 --- /dev/null +++ b/modbus.c @@ -0,0 +1,360 @@ + +#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() +{ + +} \ No newline at end of file diff --git a/modbus.h b/modbus.h new file mode 100644 index 0000000..a172a4b --- /dev/null +++ b/modbus.h @@ -0,0 +1,25 @@ + +#ifndef MODBUS_H +#define MODBUS_H + +int crc_chk(unsigned char* data, unsigned char length); +int ModbasRtu_Register_address(unsigned char Li); +char Data_integrity(); +char _Bin_input_Output(register unsigned char NUMBER, register unsigned char state, volatile unsigned char* Masiv, volatile unsigned char Sd); +void Reading_Discrete_Output(unsigned char* Massiv, register unsigned char Number_); +void Read_analog_input(unsigned char* Massiv, register unsigned char Number_, unsigned char Vt); +void Changing_Discrete_Output(void); +void analog_output_recording(void); +void Error_modbasRtu(volatile unsigned char Temp_Error); +void check_sum(register unsigned char Adress); +void modbasRtu_Answer(); +char read_digital_inputs(volatile unsigned char Temp1); +void change_digital_inputs(volatile unsigned char Temp1, volatile unsigned char Temp2); +char read_digital_Output(volatile unsigned char Temp1); +void change_digital_Output(volatile unsigned char Temp1, volatile unsigned char Temp2); +void change_analogue_Output(volatile unsigned char nomer, int Data); +void change_analogue_input(volatile unsigned char nomer, int Data); +int read_analogue_Output(volatile unsigned char nomer); +int read_analogue_input(volatile unsigned char nomer); + +#endif /*MODBUS_H*/