diff --git a/gpio.c b/gpio.c new file mode 100644 index 0000000..1d45ef4 --- /dev/null +++ b/gpio.c @@ -0,0 +1,103 @@ +#include "device_ring_buffer.h" +#include "device_adc.h" +#include "device_address.h" +#include "UART.h" +#include "modbus.h" +#include "timer.h" +#include "gpio.h" + + +//инициализация +void gpio_init(void) +{ + //Установка пинов pin_coils в режим выхода - 1 + DDRD |= (1 « PIN_COIL1); + DDRD |= (1 « PIN_COIL2); + DDRD |= (1 « PIN_COIL3); + DDRD |= (1 « PIN_COIL4); + + //Установка пинов pin_discrete_inputs в режим входа - 0 + DDRB &= ~(1 « PIN_DISCRETE_INPUT_1); + DDRB &= ~(1 « PIN_DISCRETE_INPUT_2); + DDRB &= ~(1 « PIN_DISCRETE_INPUT_3); + DDRB &= ~(1 « PIN_DISCRETE_INPUT_4); +} + +//чтение +uint8_t gpio_read(uint8_t pin) +{ + switch (pin) + { + //чтение pin_coils + case PIN_COIL1: + return (PIND & (1 « PIN_COIL1)) » PIN_COIL1; // Чтение значения на пине и сдвиг вправо на номер пина + case PIN_COIL2: + return (PIND & (1 « PIN_COIL2)) » PIN_COIL2; + case PIN_COIL3: + return (PIND & (1 « PIN_COIL3)) » PIN_COIL3; + case PIN_COIL4: + return (PIND & (1 « PIN_COIL4)) » PIN_COIL4; + + //чтение pin_discrete_inputs + case PIN_DISCRETE_INPUT_1: + return (PINB & (1 « PIN_DISCRETE_INPUT_1)) » PIN_DISCRETE_INPUT_1; + case PIN_DISCRETE_INPUT_2: + return (PINB & (1 « PIN_DISCRETE_INPUT_2)) » PIN_DISCRETE_INPUT_2; + case PIN_DISCRETE_INPUT_3: + return (PINB & (1 « PIN_DISCRETE_INPUT_3)) » PIN_DISCRETE_INPUT_3; + case PIN_DISCRETE_INPUT_4: + return (PINB & (1 « PIN_DISCRETE_INPUT_4)) » PIN_DISCRETE_INPUT_4; + + default: + return 0; + } +} + +//запись +void gpio_write(uint8_t pin, uint8_t value) +{ + switch (pin) + { + case PIN_COIL1: + if (value != 0x0000) + { + PORTD |= (1 « PIN_COIL1); + } + else + { + PORTD &= ~(1 « PIN_COIL1); + } + break; + case PIN_COIL2: + if (value != 0x0000) + { + PORTD |= (1 « PIN_COIL2); + } + else + { + PORTD &= ~(1 « PIN_COIL2); + } + break; + case PIN_COIL3: + if (value != 0x0000) + { + PORTD |= (1 « PIN_COIL3); + } + else + { + PORTD &= ~(1 « PIN_COIL3); + } + break; + case PIN_COIL4: + if (value != 0x0000) + { + PORTD |= (1 « PIN_COIL4); + } + else + { + PORTD &= ~(1 « PIN_COIL4); + } + break; + default: + break; +} \ No newline at end of file diff --git a/gpio.h b/gpio.h new file mode 100644 index 0000000..94c3c1c --- /dev/null +++ b/gpio.h @@ -0,0 +1,26 @@ +#ifndef GPIO_H +#define GPIO_H + +#include "device_ring_buffer.h" +#include "device_adc.h" +#include "device_address.h" +#include "UART.h" +#include "modbus.h" +#include "timer.h" + +#define PIN_COIL1 PD4 +#define PIN_COIL2 PD5 +#define PIN_COIL3 PD6 +#define PIN_COIL4 PD7 + +#define PIN_DISCRETE_INPUT_1 PB0 +#define PIN_DISCRETE_INPUT_2 PB1 +#define PIN_DISCRETE_INPUT_3 PB2 +#define PIN_DISCRETE_INPUT_4 PB3 + +void gpio_init(void); +uint8_t gpio_read(uint8_t pin); +void gpio_write(uint8_t pin, uint8_t value); + + +#endif /*GPIO_H*/ diff --git a/modbus.c b/modbus.c index 9830fa8..07059b4 100644 --- a/modbus.c +++ b/modbus.c @@ -1,360 +1,452 @@ - -#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 +#include "modbus.h" + +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); + +void modbus_init(void) +{ + 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) + { + return 0; + } + else + { + return 1; + } +} + +// Функция приема modbus-запроса RTU +void modbus_rtu(void) +{ + // состояние приема + if (state == 0) + { + 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(); + } + + else + { + // Проверяем линейный буфер РЅР° переполнение + if (size_of_packet == SIZE_MODBUS_PAKET) + { + // Буфер переполнился + // Ждем новый пакет + size_of_packet = 0; + continue; + } + else + { + time = get_millis(); + packet[size_of_packet] = data; + size_of_packet++; + } + } + } + // Проверка временного интервала между символами + else + { + if ((get_millis() - time) > MAX_PAUSE) + { + // Превышен таймаут + // Пакет закончился + packet_accepted = 1; + } + } + } + // Проверяем размер пакета + if (size_of_packet < 5) + { + size_of_packet = 0; + crc_modbus = 0xFFFF; + return; + } + + // считаем контрольную СЃСѓРјРјСѓ 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) + { + size_of_packet = 0; + crc_modbus = 0xFFFF; + // формируем ошибку произошла невосполнимая ошибка + return; + } + // проверка адреса устройства + else if (get_device_address() != packet[0]) + { + size_of_packet = 0; + crc_modbus = 0xFFFF; + // формируем ошибку адреса + return; + } + else + { + // Расчленяем пакет + state = 1; + return; + } + } +} + +// Функция отправки modbus-запроса РІ кольцевой буфер +void modbus_rtu_send(uint8_t answ_len) +{ + size_of_packet = 0; + // состояние отправки + if (state == 1) + { + // цикл отправки символов + while (state == 1) + { + // Отправка всех байт РІ кольцевой буфер + 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 + { + uint8_t value = 0; + while (number_of_registers_required > 0) + { + switch (first_register_device) + { + case COIL_1: + value |= (gpio_read(PIN_COIL1)); + break; + case COIL_2: + value |= (gpio_read(PIN_COIL2)); + break; + case COIL_3: + value |= (gpio_read(PIN_COIL3)); + break; + case COIL_4: + value |= (gpio_read(PIN_COIL4)); + break; + } + + first_register_device++; + number_of_registers_required--; + } + modbus_rtu_send(read_coils(get_device_address(), value, &modbus_answ)); + } + + break; + 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 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; + default: + modbus_rtu_send(modbus_err_answ(get_device_address(), packet[1], ILLEGAL_FUNCTION, &modbus_answ)); + break; + } +} + +// Функция вычисления контрольной СЃСѓРјРјС‹ CRC +uint16_t crc_chk(uint8_t *data, uint8_t length) +{ + 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; +} + +// Функция объединения РґРІСѓС… байт (старшего Рё младшего) +uint16_t bytes_unification(uint8_t high, uint8_t low) +{ + return (packet[high] << 8) | packet[low]; +} + +// РљРѕРґС‹ функций (для формирования ответа) + +// Чтение данных СЃ регистров флагов (функция СЃ РєРѕРґРѕРј 1) +uint8_t read_coils(uint8_t address, uint8_t value, uint8_t *modbus_answ) +{ + 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; +} + +// Чтение данных СЃ дискретных РІС…РѕРґРѕРІ (функция СЃ РєРѕРґРѕРј 2) +uint8_t read_discrete_inputs(uint8_t address, uint8_t value, uint8_t *modbus_answ) +{ + 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; +} + +// Чтение данных СЃ регистров хранения (функция СЃ РєРѕРґРѕРј 3) +uint8_t read_holding_registers(uint8_t address, uint16_t value, uint8_t *modbus_answ) +{ + 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; +} + +// Чтение данных СЃ регистров РІРІРѕРґР° (функция СЃ РєРѕРґРѕРј 4) +uint8_t read_input_registers(uint8_t address, uint16_t value, uint8_t *modbus_answ) +{ + 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; +} + +// Запись данных РІ регистр флагов (функция СЃ РєРѕРґРѕРј 5) +uint8_t write_single_coil(uint8_t address, uint16_t coil_addr, int on, uint8_t *modbus_answ) +{ + 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; +} + +// Запись данных РІ регистр (функция СЃ РєРѕРґРѕРј 6) +uint8_t write_single_register(uint8_t address, uint16_t register_addr, uint16_t new_value, uint8_t *modbus_answ) +{ + 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; +} + +// Сообщение РѕР± ошибке +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; +} diff --git a/modbus.h b/modbus.h index a172a4b..9d46628 100644 --- a/modbus.h +++ b/modbus.h @@ -1,25 +1,54 @@ - -#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*/ +#ifndef MODBUS_H +#define MODBUS_H + +#include "device_ring_buffer.h" +#include "device_adc.h" +#include "device_address.h" +#include "UART.h" +#include "timer.h" +#include "gpio.h" +#include + +#define SIZE_MODBUS_PAKET (32) +#define MAX_PAUSE (4) + +// Объявление адреса Slave +#define HOLDING_REGISTER_SLAVE_ADDRESS 100 + +// Объявление номеров Discrete Inputs +#define DISCRETE_INPUTS_1 0x00 +#define DISCRETE_INPUTS_2 0x01 +#define DISCRETE_INPUTS_3 0x02 +#define DISCRETE_INPUTS_4 0x03 + +// Объявление номеров Coil +#define COIL_1 0x00 +#define COIL_2 0x01 +#define COIL_3 0x02 +#define COIL_4 0x03 + +// Объявление номеров Input Registers +#define INPUT_REGISTERS 0x00 + +// Объявление РєРѕРґРѕРІ функций +#define READ_COILS 1 +#define READ_DISCRETE_INPUTS 2 +#define READ_HOLDING_REGISTERS 3 +#define READ_INPUT_REGISTERS 4 +#define WRITE_SINGLE_COIL 5 +#define WRITE_SINGLE_REGISTER 6 +#define WRITE_MULTIPLE_COILS 15 +#define WRITE_MULTIPLE_REGISTER 16 + +//Объявление РєРѕРґРѕРІ ошибок +#define ILLEGAL_FUNCTION 1 +#define ILLEGAL_DATA_ADDRESS 2 +#define SLAVE_DEVICE_FAILURE 4 + +uint8_t size_of_packet; + +void modbus_init(void); +void modbus_rtu (void); +void modbus_answer(void); + +#endif /*MODBUS_H*/ \ No newline at end of file diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..8f62920 --- /dev/null +++ b/timer.c @@ -0,0 +1,28 @@ +#include +#include + +static unsigned long millis = 0; + +void setup_timer() +{ + // Включаем режим РЎРўРЎ + TCCR0A = (1 << WGM01); + // Устанавливаем счетчик СЃ предделителем 64 + TCCR0B = (1 << CS01) | (1 << CS00); + // Устанавливаем значение сравнения для 1 РјСЃ + OCR0A = 250; + // Разрешаем прерывание РїРѕ совпадению + TIMSK0 = (1 << OCIE0A); + // Разрешаем РІСЃРµ прерывания + sei(); +} + +unsigned long get_millis() +{ + return millis; +} + +ISR(TIMER0_COMPA_vect) +{ + ++millis; +} \ No newline at end of file