From 01b5d0a6c869f9cc1293d249a2c65cfda27fea13 Mon Sep 17 00:00:00 2001 From: dplimin Date: Fri, 24 Nov 2023 14:45:18 +0300 Subject: [PATCH] new file: .settings/org.eclipse.core.resources.prefs new file: Core/Src/modbus.c new file: "\320\235\320\276\320\263\320\270 \320\270 \321\203\321\201\320\270\320\273\320\265\320\275\320\270\320\265.xlsx" --- .settings/org.eclipse.core.resources.prefs | 2 + Core/Src/modbus.c | 551 +++++++++++++++++++++ Ноги и усиление.xlsx | Bin 0 -> 16461 bytes 3 files changed, 553 insertions(+) create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 Core/Src/modbus.c create mode 100644 Ноги и усиление.xlsx diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..096b822 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding//Core/Src/modbus.c=UTF-8 diff --git a/Core/Src/modbus.c b/Core/Src/modbus.c new file mode 100644 index 0000000..dcfdc05 --- /dev/null +++ b/Core/Src/modbus.c @@ -0,0 +1,551 @@ +/* + * 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[3 + j]; + pardata.SENS = f.fl;*/ + //Если сюда попали то что то идет не так + 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; + } + } + } + } +} + + + + diff --git a/Ноги и усиление.xlsx b/Ноги и усиление.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d376c5af719a0aa617265b9148276c7f5e728c1a GIT binary patch literal 16461 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

)ANx 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 U=;I&$5*~p5gC7XW`NyaK2cGma-~a#s literal 0 HcmV?d00001