555 lines
16 KiB
C
555 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 > mV) || (recivedData.IPZ > 1)|| (recivedData.OVERLOAD > 1))
|
||
{
|
||
tx[0] = iobuf[0];
|
||
tx[1] = (iobuf[1] | 0x80);
|
||
tx[2] = 0x03;
|
||
strtOut(3);
|
||
}
|
||
else
|
||
{
|
||
currentData=recivedData; // значения правильные копируем в рабочую структуру
|
||
|
||
if(iobuf[0]) //если адрес не широковещательный отвечаем
|
||
{
|
||
for(j = 0; j < 6; j++) {
|
||
tx[j] = iobuf[j];
|
||
}
|
||
|
||
strtOut(6);
|
||
needSave = true;//перенес ниже, может он сначала даст полноценный ответ, потом сохранит
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
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];
|
||
// todo: ввести проверку и прочую чепуху
|
||
currentData.SENS = f.fl;
|
||
strtOut(6);//перенес выше, надеюсь я не безнадежен
|
||
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;//перенес ниже, может он сначала даст полноценный ответ, потом сохранит
|
||
|
||
}
|
||
else
|
||
if(iobuf[6] == 8)
|
||
{
|
||
for(j = 0; j < 4; j++)
|
||
f.ch[3 - j] = iobuf[7 + j];
|
||
currentData.ACCEL = f.fl;
|
||
|
||
strtOut(6);
|
||
|
||
needSave = true;
|
||
}
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|