Compare commits

...

6 Commits

4 changed files with 442 additions and 12 deletions

View File

@ -11,15 +11,21 @@
Южакова Лада
Салангина Анастасия
Макуц Ольга
# Составляющие задачи
- Изучить Modbus (Салангина Анастасия)
- Изучить UART (Макуц Ольга)
- Определить, как происходит передача по протоколу (Салангина Анастасия)
- Разобрать назначение и способы использования регистров и вхоов-выходов (Макуц Ольга)
- Изучить, как переопределять адрес по умолчанию в ПЗУ (Южакова Лада)
- Изучить, как определять адреса данных в ПЗУ (Южакова Лада)
- Изучить, как использовать RX/TX (Макуц Ольга)
- Изучить, что такое верхняя архитектура (все)
- Изучить ПЗУ (Южакова Лада)
- Изучить кольцевой буфер (Южакова Лада)
- Понять, в какой полседовательности будет работать программа (все)
# Задачи
Южакова Лада:
- Изучить, как переопределять адрес по умолчанию в ПЗУ
- Изучить ПЗУ
- Изучить АЦП
- Изучить, как определять адреса данных в ПЗУ
- Понять, в какой последовательности будет работать программа
- Изучить, что такое верхняя архитектура
Салангина Анастасия:
- Изучить Modbus
- Определить, как происходит передача по протоколу
Макуц Ольга:
- Изучить UART
- Разобрать назначение и способы использования регистров и вхоов-выходов
- Изучить, как использовать RX/TX
- Изучить кольцевой буфер

39
main.c Normal file
View File

@ -0,0 +1,39 @@
/*
* main.c
*
* Created: 18.06.2023 19:53:46
* Author: Lada Yuzhakova
*/
#include "UART.h"
#include "modbus.h"
#include "timer.h"
#include "device_adc.h"
#include "device_ring_buffer.h"
#include "gpio.h"
#include <avr/io.h>
int main(void)
{
// Èíèöèàëèçàöèè
adc_init();
uart_initialize();
modbus_init();
gpio_init();
rb_initialize(get_rb_receive());
rb_initialize(get_rb_transmit());
// Çàïóñêàåì òàéìåð
setup_timer();
while(1)
{
// Ôóíêöèÿ ïðèåìà ìîäáàñ-çàïðîñà
modbus_rtu();
// Ôóíêöèÿ ôîðìèðîâàíèÿ ìîäáàñ-îòâåòà
modbus_answer();
// Îòïðàâëÿòü îòâåò ïî óàðò
UART_Transmit(get_rb_transmit());
}
return 0;
}

360
modbus.c Normal file
View File

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

25
modbus.h Normal file
View File

@ -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*/