547 lines
16 KiB
C
547 lines
16 KiB
C
|
/*
|
|||
|
* modbus.c
|
|||
|
*
|
|||
|
* Created on: 28 пїЅпїЅпїЅ 2018 пїЅ.
|
|||
|
* Author: Shukov
|
|||
|
*/
|
|||
|
// Includes --------------------------------------------------------------------
|
|||
|
#include "modbus.h"
|
|||
|
#include "my.h"
|
|||
|
#include "usart.h"
|
|||
|
#include "main.h"
|
|||
|
// External function -----------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
// External variables ----------------------------------------------------------
|
|||
|
extern __IO UserData_TypeDef currentData;
|
|||
|
|
|||
|
#define LO(x) ((uint8_t) ((x) & 0xff))
|
|||
|
#define HI(x) ((uint8_t) (((x) >> 8) & 0xff))
|
|||
|
|
|||
|
|
|||
|
//extern __IO uint32_t STATUS;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// Global variables ------------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
|
|||
|
uint8_t iobuf[256];
|
|||
|
uint8_t tx[256];
|
|||
|
|
|||
|
uint8_t timeout;
|
|||
|
uint16_t lastbyte; //последний байт в сообщении
|
|||
|
uint16_t lastbyteToSend; //последний байт на отправку
|
|||
|
uint16_t iolen = 0; // счетчик для буфера
|
|||
|
uint8_t ioa; //ваще не шарю нахуя
|
|||
|
bool sendreq = false; //влаг готовности к отправке
|
|||
|
bool setbaud = false; //при смене скорости условие для переинициализации уарта
|
|||
|
|
|||
|
extern char pString[16]; // палки шишки - без этого не копирует floatToAscII
|
|||
|
extern uint8_t buff[16];
|
|||
|
extern int reload_flag; //флаг перегрузки
|
|||
|
|
|||
|
|
|||
|
bool send = true;
|
|||
|
__IO uint16_t delayREDE;
|
|||
|
__IO bool needSave = false;
|
|||
|
__IO bool needParam = false;
|
|||
|
__IO bool needReset = false;
|
|||
|
__IO bool POVERKA = false;
|
|||
|
__IO bool needPoverka = false;
|
|||
|
__IO bool needFactory = false;
|
|||
|
|
|||
|
const int8_t inversely[] = {3, 1, -1,-3};
|
|||
|
|
|||
|
|
|||
|
void StartTransfer(void) //todo жесько отключаю прерывания это неправильно
|
|||
|
{
|
|||
|
int a = delayREDE;
|
|||
|
while(a)
|
|||
|
{
|
|||
|
a--; a++; a--; a++; a--;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
HAL_GPIO_WritePin(USART1_RE_GPIO_Port, USART1_RE_Pin, GPIO_PIN_SET); //todo это нужно только п//это ужасная вещь - но обеспечивает постоянный прием
|
|||
|
//HAL_UART_Transmit_IT(&huart1, tx, lastbyte);
|
|||
|
__disable_irq();
|
|||
|
HAL_UART_Transmit(&huart1, tx, lastbyteToSend,100);// возможно не отправит из за того что приемник в прерывании
|
|||
|
memset(tx,0,sizeof(tx));
|
|||
|
__enable_irq();
|
|||
|
HAL_GPIO_WritePin(USART1_RE_GPIO_Port, USART1_RE_Pin, GPIO_PIN_RESET);
|
|||
|
Transfer_Complete();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void strtOut(uint16_t n)
|
|||
|
{
|
|||
|
uint16_t crc;
|
|||
|
|
|||
|
if(tx[0]) // если на отправку что то есть то
|
|||
|
{
|
|||
|
lastbyteToSend = n + 2; // добавляем 2 байта под CRC
|
|||
|
crc = Crc16_TX(n);
|
|||
|
tx[n] = LO(crc);
|
|||
|
tx[n + 1] = HI(crc); //todo: Внимание костыль & 0xFF!!!! // Добавляем контрольную сумму
|
|||
|
sendreq = true; //Устанавливаем флаг требования отправки
|
|||
|
}
|
|||
|
iolen = 0; // сбрасываем длинну сообщения
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
uint16_t Crc16(uint16_t len)
|
|||
|
{
|
|||
|
uint16_t i;
|
|||
|
uint16_t crc = 0xFFFF;
|
|||
|
|
|||
|
for(i = 0; i < len; i++) {
|
|||
|
crc = (crc >> 8) ^ Crc16Table[(crc & 0xFF) ^ iobuf[i]];
|
|||
|
}
|
|||
|
return crc;
|
|||
|
}
|
|||
|
|
|||
|
uint16_t Crc16_TX(uint16_t len)
|
|||
|
{
|
|||
|
uint16_t i;
|
|||
|
uint16_t crc = 0xFFFF;
|
|||
|
|
|||
|
for(i = 0; i < len; i++) {
|
|||
|
crc = (crc >> 8) ^ Crc16Table[(crc & 0xFF) ^ tx[i]];
|
|||
|
}
|
|||
|
return crc;
|
|||
|
}
|
|||
|
|
|||
|
//-----------------------------------------
|
|||
|
// пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ-пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ
|
|||
|
//-----------------------------------------
|
|||
|
void SetBaudRate(){ //todo: сделать установку скорости
|
|||
|
timeout = time35[currentData.BAUD];
|
|||
|
delayREDE = sendtime[currentData.BAUD];
|
|||
|
|
|||
|
MX_USART1_UART_DeInit();
|
|||
|
MX_USART1_UART_Init();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//float32_t tmpf;
|
|||
|
uint16_t hp16;
|
|||
|
|
|||
|
void Transfer_Complete(void)
|
|||
|
{
|
|||
|
__IO uint16_t a;
|
|||
|
|
|||
|
a = delayREDE;
|
|||
|
while(a)
|
|||
|
{
|
|||
|
a--; a++; a--; a++; a--; // какая то задержка пусть будет
|
|||
|
}
|
|||
|
|
|||
|
if(setbaud) // наверное нужно для применения новых настроек скорости
|
|||
|
{
|
|||
|
setbaud = 0;
|
|||
|
needSave = true;
|
|||
|
SetBaudRate();
|
|||
|
}
|
|||
|
|
|||
|
//__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TC);
|
|||
|
HAL_GPIO_WritePin(USART1_RE_GPIO_Port, USART1_RE_Pin, GPIO_PIN_RESET);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void Receive_Complete(void){ //я сейчас попробую раскоментировать тебя
|
|||
|
// не забывай что команды подаются little endian формате.
|
|||
|
|
|||
|
/// перенес в надежде на чудо
|
|||
|
uint16_t j;
|
|||
|
uint8_t *pch;
|
|||
|
usshort crc, addr, regs;
|
|||
|
usfloat f;
|
|||
|
uint8_t tmp, tmp1;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
///
|
|||
|
|
|||
|
timeout = time35[currentData.BAUD]; // какие то мутные таймауты от скорости всегда двойки(((
|
|||
|
iobuf[iolen++] = buff[0];
|
|||
|
//iobuf[iolen++] = (uint8_t) (USART1->DR & 0xff); // прибывший байт принимаем в из регистра в буфер
|
|||
|
|
|||
|
if((iobuf[0] == currentData.OWN) || (!iobuf[0])) //если нулевая посылка совпадает с адресом устройства или не равен себе
|
|||
|
{
|
|||
|
if(iolen > 1)
|
|||
|
{
|
|||
|
if(iolen == 2) // если длинна сообщения 2
|
|||
|
{
|
|||
|
switch(iobuf[1]) // смотрим на команды
|
|||
|
{
|
|||
|
case 0x03: //команда на чтение
|
|||
|
lastbyte = 7; //последний байт в сообщении будет 7
|
|||
|
break;
|
|||
|
case 0x10:
|
|||
|
lastbyte = 6; // если на запись то 6
|
|||
|
break;
|
|||
|
default:
|
|||
|
lastbyte = 3; // если не то не то то 3
|
|||
|
tx[0] = iobuf[0]; //в отправку заряжаем принятый адрес
|
|||
|
tx[1] = (iobuf[1] | 0x80); //берем прешедшую команду и обвосьмериваем - помечаем как ошибку
|
|||
|
tx[2] = 0x01; // принятый код функции не может быть обработан
|
|||
|
strtOut(3); // на отправку
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if(iolen > lastbyte) //если длинна принятой строки больше чем последний байт
|
|||
|
{
|
|||
|
switch(iobuf[1]) //смотрим на командц
|
|||
|
{
|
|||
|
|
|||
|
case 0x03: // если команда на чтение
|
|||
|
iolen = 0; // ставим счетчик буфера на 0
|
|||
|
crc.ch[0] = iobuf[6]; // берем из буфера CRC
|
|||
|
crc.ch[1] = iobuf[7];
|
|||
|
if(Crc16(6) == crc.sh) // Проверяем её
|
|||
|
{
|
|||
|
addr.ch[1] = iobuf[2]; //адрес первого регистра hi
|
|||
|
addr.ch[0] = iobuf[3]; //lo
|
|||
|
regs.ch[1] = iobuf[4]; //колличество регистров hi
|
|||
|
regs.ch[0] = iobuf[5]; //колличесво регистров lo
|
|||
|
|
|||
|
|
|||
|
switch(addr.sh) // здесь union
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
|
|||
|
case 5001: // 5001 //тип заряда ACP=
|
|||
|
case 5002: // 5002 //фильтр верхних частот
|
|||
|
case 5003: // 5003 //нижних
|
|||
|
case 5004: //IKU 5004 //Коэф усиления
|
|||
|
case 5005: //IKE 5005 // Legacy a141
|
|||
|
case 5006: //IKD 5006 // Legacy a141
|
|||
|
case 5007: //IKS 5007 // Legacy a141
|
|||
|
case 5008: //IPZ 5008 // признак плав земли
|
|||
|
case 5009: //OPZ 5009 // Legacy a141
|
|||
|
case 5010: //UNIT 5010 // Значения
|
|||
|
case 5011: //OVERLOAD 5011 // Использую для передачи перегрузки
|
|||
|
if(regs.ch[0] > (5012 - addr.sh))
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = (iobuf[1] | 0x80);
|
|||
|
tx[2] = 0x03;
|
|||
|
strtOut(3);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = iobuf[1];
|
|||
|
tx[2] = regs.ch[0] << 1;
|
|||
|
pch = (uint8_t *) ¤tData.IIN + ((addr.sh - 5001) << 1);
|
|||
|
for(j = 0; j < tx[2]; j++)
|
|||
|
tx[j + 3] = *(pch + (j ^ 1));
|
|||
|
|
|||
|
strtOut(3 + tx[2]);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case 7004: // Sens
|
|||
|
case 7502:
|
|||
|
tmp = 2;
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = iobuf[1];
|
|||
|
tx[2] = regs.ch[0] << 2;
|
|||
|
if(addr.sh == 7004)
|
|||
|
{
|
|||
|
tmp <<= 1;
|
|||
|
tx[2] >>= 1;
|
|||
|
}
|
|||
|
if(regs.ch[0] > tmp)
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = (iobuf[1] | 0x80);
|
|||
|
tx[2] = 0x03;
|
|||
|
strtOut(3);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
f.fl = currentData.SENS;
|
|||
|
tx[3] = f.ch[3];
|
|||
|
tx[4] = f.ch[2];
|
|||
|
tx[5] = f.ch[1];
|
|||
|
tx[6] = f.ch[0];
|
|||
|
strtOut(3 + tx[2]);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = (iobuf[1] | 0x80);
|
|||
|
tx[2] = 0x02;
|
|||
|
strtOut(3);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
case 0x10: // //запрос на запись
|
|||
|
|
|||
|
addr.ch[1] = iobuf[2];
|
|||
|
addr.ch[0] = iobuf[3];
|
|||
|
regs.ch[1] = iobuf[4];
|
|||
|
regs.ch[0] = iobuf[5];
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
switch(addr.sh) // Запись pardata
|
|||
|
{
|
|||
|
|
|||
|
case 4001: // 4001 //тип заряда ACP=
|
|||
|
case 4002: // 4002 //фильтр верхних частот
|
|||
|
case 4003: // 4003 //нижних
|
|||
|
|
|||
|
{
|
|||
|
if((regs.ch[0] > (4004 - addr.sh)) || (iobuf[6] != (regs.ch[0] << 1)))
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = (iobuf[1] | 0x80);
|
|||
|
tx[2] = 0x03;
|
|||
|
strtOut(3);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
j = 8 + iobuf[6];
|
|||
|
if(iolen > j)
|
|||
|
{
|
|||
|
iolen = 0;
|
|||
|
crc.ch[0] = iobuf[j - 1];
|
|||
|
crc.ch[1] = iobuf[j];
|
|||
|
if(crc.sh == Crc16(j - 1))
|
|||
|
{
|
|||
|
UserData_TypeDef recivedData =currentData;
|
|||
|
pch = (uint8_t *) &recivedData.OWN + ((addr.sh - 4001) << 1);
|
|||
|
for(j = 0; j < iobuf[6]; j++)
|
|||
|
*(pch + (j ^ 1)) = iobuf[j + 7];
|
|||
|
|
|||
|
if((recivedData.OWN > 247) || (recivedData.BAUD > 9) || (recivedData.INFB > 2))
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = (iobuf[1] | 0x80);
|
|||
|
tx[2] = 0x03;
|
|||
|
strtOut(3);
|
|||
|
}
|
|||
|
else //если значения корректны
|
|||
|
{
|
|||
|
currentData=recivedData; //Пишем значения в рабочую структуру
|
|||
|
if(!iobuf[0]) //если широковешательная то не отвечаем
|
|||
|
{
|
|||
|
setbaud = false; // снимаем флаг следующей смены
|
|||
|
needSave = true; // ставим флаг надобности сохранения
|
|||
|
SetBaudRate(); // устанавливаем свойства связи
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
setbaud = true; // по колбеку ТХ переинициализируем УАРТ
|
|||
|
|
|||
|
for(j = 0; j < 6; j++) {
|
|||
|
tx[j] = iobuf[j];
|
|||
|
}
|
|||
|
strtOut(6);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case 5001: // 5001 //тип заряда ACP=
|
|||
|
case 5002: // 5002 //фильтр верхних частот
|
|||
|
case 5003: // 5003 //нижних
|
|||
|
case 5004: //IKU 5004 //Коэф усиления
|
|||
|
case 5005: //IKE 5005 // Legacy a141
|
|||
|
case 5006: //IKD 5006 // Legacy a141
|
|||
|
case 5007: //IKS 5007 // Legacy a141
|
|||
|
case 5008: //IPZ 5008 // признак плав земли
|
|||
|
case 5009: //OPZ 5009 // Legacy a141
|
|||
|
case 5010: //UNIT 5010 // Значения
|
|||
|
if((regs.ch[0] > (5011 - addr.sh)) || (iobuf[6] != (regs.ch[0] << 1)))
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = (iobuf[1] | 0x80);
|
|||
|
tx[2] = 0x03;
|
|||
|
strtOut(3);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
j = 8 + iobuf[6];
|
|||
|
if(iolen > j)
|
|||
|
{
|
|||
|
iolen = 0;
|
|||
|
crc.ch[0] = iobuf[j - 1];
|
|||
|
crc.ch[1] = iobuf[j];
|
|||
|
if(crc.sh == Crc16(j - 1))
|
|||
|
{
|
|||
|
UserData_TypeDef recivedData =currentData; // должно работать как копирование
|
|||
|
pch = (uint8_t *) &recivedData.IIN + ((addr.sh - 5001) << 1);
|
|||
|
for(j = 0; j < iobuf[6]; j++)
|
|||
|
*(pch + (j ^ 1)) = iobuf[j + 7];
|
|||
|
|
|||
|
if((recivedData.IIN > 0) || (recivedData.IKU > Ku1000) || (recivedData.IFV > Hp10)
|
|||
|
|| (recivedData.IFN > Lp100000) || (recivedData.UNIT > H) || (recivedData.IPZ > 1)|| (recivedData.OVERLOAD > 1))
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] = (iobuf[1] | 0x80);
|
|||
|
tx[2] = 0x03;
|
|||
|
strtOut(3);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
currentData=recivedData; // значения правильные копируем в рабочую структуру
|
|||
|
needSave = true;
|
|||
|
if(iobuf[0]) //если адрес не широковещательный отвечаем
|
|||
|
{
|
|||
|
for(j = 0; j < 6; j++) {
|
|||
|
tx[j] = iobuf[j];
|
|||
|
}
|
|||
|
strtOut(6);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case 7004: // Запись Sens
|
|||
|
case 7502:
|
|||
|
tmp = 2;
|
|||
|
tmp1 = 2;
|
|||
|
if(addr.sh == 7004)
|
|||
|
{
|
|||
|
tmp <<= 1;
|
|||
|
tmp1 = 1;
|
|||
|
}
|
|||
|
if((regs.ch[0] > tmp) || (iobuf[6] != (regs.ch[0] << tmp1)))
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] |= 0x80; //модификация кода функции на ошибку
|
|||
|
tx[2] = 0x03; //Адрес данных указанный в запросе не доступен
|
|||
|
strtOut(3);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
j = 8 + iobuf[6];
|
|||
|
if(iolen > j)
|
|||
|
{
|
|||
|
crc.ch[0] = iobuf[j - 1];
|
|||
|
crc.ch[1] = iobuf[j];
|
|||
|
if(crc.sh == Crc16(j - 1))
|
|||
|
{
|
|||
|
if(iobuf[6] == 4)
|
|||
|
{
|
|||
|
for(j = 0; j < 4; j++)
|
|||
|
f.ch[3 - j] = iobuf[7 + j];
|
|||
|
//ввести проверку и прочую чепуху
|
|||
|
currentData.SENS = f.fl;
|
|||
|
if(currentData.SENS==1000) {
|
|||
|
currentData.IK0=1;
|
|||
|
currentData.IK1=0;
|
|||
|
currentData.IK2=0;
|
|||
|
currentData.IK3=0;
|
|||
|
currentData.IK4='~';
|
|||
|
currentData.IK5=0;
|
|||
|
}
|
|||
|
else if(currentData.SENS<1000&¤tData.SENS>=100) {
|
|||
|
char str[20] = {0};
|
|||
|
strcpy(str, FloatToASCII(currentData.SENS, -2));
|
|||
|
currentData.IK0=str[0]-48;
|
|||
|
currentData.IK1=str[1]-48;
|
|||
|
currentData.IK2=str[2]-48;
|
|||
|
currentData.IK3='~';//str[3];
|
|||
|
currentData.IK4=str[4]-48;
|
|||
|
currentData.IK5=str[5]-48;
|
|||
|
}
|
|||
|
else if(currentData.SENS<100&¤tData.SENS>=10)
|
|||
|
{
|
|||
|
char str[20] = {0};
|
|||
|
strcpy(str, FloatToASCII(currentData.SENS, -3));
|
|||
|
currentData.IK0=str[0]-48;
|
|||
|
currentData.IK1=str[1]-48;
|
|||
|
currentData.IK2=str[2];
|
|||
|
currentData.IK3=str[3]-48;
|
|||
|
currentData.IK4=str[4]-48;
|
|||
|
currentData.IK5=str[5]-48;
|
|||
|
}
|
|||
|
else //if(currentData.SENS<10);
|
|||
|
{
|
|||
|
char str[20] = {0};
|
|||
|
strcpy(str, FloatToASCII(currentData.SENS, -4));
|
|||
|
currentData.IK0=str[0]-48;
|
|||
|
currentData.IK1=str[1];
|
|||
|
currentData.IK2=str[2]-48;
|
|||
|
currentData.IK3=str[3]-48;
|
|||
|
currentData.IK4=str[4]-48;
|
|||
|
currentData.IK5=str[5]-48;
|
|||
|
}
|
|||
|
needSave = true;
|
|||
|
strtOut(6);
|
|||
|
}
|
|||
|
else
|
|||
|
if(iobuf[6] == 8)
|
|||
|
{
|
|||
|
for(j = 0; j < 4; j++)
|
|||
|
f.ch[3 - j] = iobuf[7 + j];
|
|||
|
currentData.ACCEL = f.fl;
|
|||
|
needSave = true;
|
|||
|
strtOut(6);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] |= 0x80; //модификация кода функции на ошибку
|
|||
|
tx[2] = 0x03; //Адрес данных указанный в запросе не доступен
|
|||
|
strtOut(3);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
default://Команда неопознана
|
|||
|
tx[0] = iobuf[0];
|
|||
|
tx[1] |= 0x80; //модификация кода функции на ошибку
|
|||
|
tx[2] = 0x02; //Адрес данных указанный в запросе не доступен
|
|||
|
strtOut(3);
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|