Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b69a643cd | ||
|
|
8be941b2b2 | ||
|
|
d86e2a8805 | ||
|
|
f3fc8394f4 | ||
|
|
8c21df6efd | ||
|
|
87095b646d | ||
|
|
17fee0413e |
@@ -0,0 +1,31 @@
|
|||||||
|
# Modbus устройство на AVR
|
||||||
|
# Задание
|
||||||
|
Устройство поключено к питанию всегда. Взаимодействие по интерфейсу UART (RS-485) и протоколу Modbus.
|
||||||
|
- Discrete Inputs - 4 штуки
|
||||||
|
- Coils - 4 штуки
|
||||||
|
- Input registers - 2 канала АЦП
|
||||||
|
- Holding Registers - 1 штука: адрес на шине Modbus.
|
||||||
|
Адрес по умолчанию 0х32. Адрес хранить в ПЗУ чипа.
|
||||||
|
Адреса данных определить самостоятельно.
|
||||||
|
# Участники
|
||||||
|
Южакова Лада
|
||||||
|
Салангина Анастасия
|
||||||
|
Макуц Ольга
|
||||||
|
# Задачи
|
||||||
|
Южакова Лада:
|
||||||
|
- Изучить, как переопределять адрес по умолчанию в ПЗУ
|
||||||
|
- Изучить ПЗУ
|
||||||
|
- Изучить АЦП
|
||||||
|
- Изучить, как определять адреса данных в ПЗУ
|
||||||
|
- Понять, в какой последовательности будет работать программа
|
||||||
|
- Изучить, что такое верхняя архитектура
|
||||||
|
|
||||||
|
Салангина Анастасия:
|
||||||
|
- Изучить Modbus
|
||||||
|
- Определить, как происходит передача по протоколу
|
||||||
|
|
||||||
|
Макуц Ольга:
|
||||||
|
- Изучить UART
|
||||||
|
- Разобрать назначение и способы использования регистров и вхоов-выходов
|
||||||
|
- Изучить, как использовать RX/TX
|
||||||
|
- Изучить кольцевой буфер
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#include "device_address.h"
|
|
||||||
|
|
||||||
uint8_t EEPROM_ReadByte(uint32_t adr)
|
|
||||||
{
|
|
||||||
while (EECR & (1<<EEPE));
|
|
||||||
EEAR = adr;
|
|
||||||
EECR |= (1<<EERE);
|
|
||||||
return EEDR;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EEPROM_WriteByte(uint32_t adr, uint8_t value)
|
|
||||||
{
|
|
||||||
while (EECR & (1<<EEPE));
|
|
||||||
EEAR = adr;
|
|
||||||
EEDR = value;
|
|
||||||
EECR |= (1<<EEMPE);
|
|
||||||
EECR |= (1<<EEPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t get_device_address(void)
|
|
||||||
{
|
|
||||||
if(is_valid_device_address())
|
|
||||||
{
|
|
||||||
return EEPROM_ReadByte(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return get_default_address();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_device_address(uint8_t address)
|
|
||||||
{
|
|
||||||
EEPROM_WriteByte(0, address);
|
|
||||||
EEPROM_WriteByte(1, ~address);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t get_default_address(void)
|
|
||||||
{
|
|
||||||
return DEFAULT_MODBUS_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_valid_device_address(void)
|
|
||||||
{
|
|
||||||
return ((EEPROM_ReadByte(0) ^ EEPROM_ReadByte(1)) == 0xFF);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#ifndef DEVICE_ADDRESS_H
|
|
||||||
#define DEVICE_ADDRESS_H
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <avr/io.h>
|
|
||||||
#define DEFAULT_MODBUS_ADDRESS (32)
|
|
||||||
|
|
||||||
uint8_t get_device_address(void);
|
|
||||||
void write_device_address(uint8_t address);
|
|
||||||
uint8_t get_default_address(void);
|
|
||||||
int is_valid_device_address(void);
|
|
||||||
|
|
||||||
#endif /*DEVICE_ADDRESS_H*/
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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*/
|
||||||
@@ -0,0 +1,452 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#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 <stdint.h>
|
||||||
|
|
||||||
|
#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*/
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user