- admin8小时前管理员1
/* * STM32F407 串口双向透传 * ------------------------------------------------------------ * 485主机接口 : PB6(RX)/PB7(TX)/PB5(EN) 9600 * 井下数据口 : PA2(USART2_TX)/PA3(USART2_RX) 300 * 调试口 : PA9/PA10(USART1) 115200 */ #include <Arduino.h> #include <SoftwareSerial.h> // ==================== 硬件配置 ==================== #define RS485_RX PB7 #define RS485_TX PB6 #define RS485_EN PB5 #define EEPROM_BASE 0x080E0000 //flash 转EPROM 起始地址 // ==================== 引脚定义 ==================== // 开关 控制引脚 #define CL_0 PB0 // PB0 长短缆切换 #define CL_1 PB1 // PB1 长短缆切换 #define KZ PE10 // PE10 KZ1 // 增益 编码输出引脚 (1-16) #define ENCODE_PB2 PB2 // PB2 (最高位) #define ENCODE_PE7 PE7 // PE7 #define ENCODE_PE8 PE8 // PE8 #define ENCODE_PE9 PE9 // PE9 (最低位) // ADC 输入引脚 #define ADC1_PIN PA5 // PA5/ADC1 长短缆检测 #define DS_PIN PA6 // PA6/ADC2 控制器输出电压 #define CS_PIN PA7 // PA7/ADC3 控制器输出电流 // 解码管脚 #define PA1_PIN PA1 #define PA0_PIN PA0 // 使用你的方式定义串口 Uart Serial1(USART1); //调试用 //Uart JxSerial(USART2); //井下 SoftwareSerial rs485(RS485_RX, RS485_TX); SoftwareSerial JxSerial(PA3, PA2); // RX,TX uint8_t cdzyBuf[4]; //长短缆 增益设置 bool readRs485(uint8_t* buffer, uint16_t maxLen, uint16_t* len, uint32_t timeoutMs); bool readTypec(uint8_t* buffer, uint16_t maxLen, uint16_t* len, uint32_t timeoutMs); void processReceivedData(byte* receivedData, int length); // ============================================================ // 1. rs485数据接收回调函数 // ============================================================ // ==================== 485方向控制 ==================== void rs485SetReceive() { digitalWrite(RS485_EN, LOW); delayMicroseconds(10); } void rs485SetTransmit() { digitalWrite(RS485_EN, HIGH); delayMicroseconds(10); } // ==================== RS485发送 ==================== void rs485Write(uint8_t data) { rs485SetTransmit(); rs485.write(data); rs485.flush(); rs485SetReceive(); } void rs485WriteBuffer(uint8_t* buf, uint16_t len) { rs485SetTransmit(); rs485.write(buf, len); rs485.flush(); rs485SetReceive(); } // ============================================================ // 2. Flash写多个字节 // ============================================================ // 保存数据到Flash(2字节对齐) void saveData(uint8_t* data, uint16_t len) { HAL_FLASH_Unlock(); // 擦除扇区 FLASH_EraseInitTypeDef e = {}; e.TypeErase = FLASH_TYPEERASE_SECTORS; e.Sector = 11; e.NbSectors = 1; e.VoltageRange = FLASH_VOLTAGE_RANGE_3; uint32_t err; HAL_FLASHEx_Erase(&e, &err); // 2字节对齐写入 for (int i = 0; i < len; i += 2) { uint16_t w = 0xFFFF; if (i < len) w = data[i]; if (i + 1 < len) w |= (data[i + 1] << 8); HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, EEPROM_BASE + i, w); } HAL_FLASH_Lock(); } // =============== 读取数据 =============== void loadData(uint8_t* buf, uint16_t len) { for (int i = 0; i < len; i++) { buf[i] = *(uint8_t*)(EEPROM_BASE + i); } } // ============================================================ // ============================================================ // 3. 长短缆管脚赋值 增益管脚赋值 // ============================================================ // 长短缆管脚赋值 void setDecode(uint8_t val) { digitalWrite(CL_0, val & 0x01); digitalWrite(CL_1, (val >> 1) & 0x01); } // 增益管脚赋值 void setEncode(uint8_t val) { digitalWrite(ENCODE_PE9, val & 0x01); digitalWrite(ENCODE_PE8, (val >> 1) & 0x01); digitalWrite(ENCODE_PE7, (val >> 2) & 0x01); digitalWrite(ENCODE_PB2, (val >> 3) & 0x01); } // ============================================================ // 4. ----------超时接收 包含 485 和 设置串口---------- // ============================================================ // 超时接收(精简版) bool readRs485(uint8_t* buffer, uint16_t maxLen, uint16_t* len, uint32_t timeoutMs = 5) { *len = 0; if (!buffer || maxLen == 0 || !rs485.available()) return false; uint32_t start = millis(); while (*len < maxLen && (millis() - start) < timeoutMs) { if (rs485.available()) { buffer[(*len)++] = rs485.read(); start = millis(); // 收到数据重置超时 } delayMicroseconds(50); } return (*len > 0); } // 超时接收(精简版) bool readTypec(uint8_t* buffer, uint16_t maxLen, uint16_t* len, uint32_t timeoutMs = 5) { *len = 0; if (!buffer || maxLen == 0 || !Serial1.available()) return false; uint32_t start = millis(); while (*len < maxLen && (millis() - start) < timeoutMs) { if (Serial1.available()) { buffer[(*len)++] = Serial1.read(); start = millis(); // 收到数据重置超时 } delayMicroseconds(50); } return (*len > 0); } // ==================== 数据转发 ==================== // void forwardData() { // // 1. RS485 -> 调试口 + 井下口 // while (rs485.available()) { // uint8_t data = rs485.read(); // Serial1.write(data); // JxSerial.write(data); // } // // 2. 调试口 -> RS485 + 井下口 // while (Serial1.available()) { // uint8_t data = Serial1.read(); // rs485Write(data); // JxSerial.write(data); // } // // 3. 井下口 -> RS485 + 调试口 // while (JxSerial.available()) { // uint8_t data = JxSerial.read(); // rs485Write(data); // Serial1.write(data); // } // } // ============================================================ // 5. ----------crc 校验处理---------- // ============================================================ uint16_t Modbus_crc16(uint8_t* snd, uint16_t num) { uint16_t Cal_Crc_i, Cal_Crc_j; uint16_t c, crc = 0xFFFF; for (Cal_Crc_i = 0; Cal_Crc_i < num; Cal_Crc_i++) { c = snd[Cal_Crc_i] & 0x00FF; crc ^= c; for (Cal_Crc_j = 0; Cal_Crc_j < 8; Cal_Crc_j++) { if (crc & 0x0001) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } // ==================== 系统时钟配置 ==================== extern "C" void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {}; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 12; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } SystemCoreClock = 168000000UL; } // ============================================================ // 7. ADC读电压信号 // ============================================================ void readADC(int& ch1, int& ch2, int& ch3) { ch1 = analogRead(ADC1_PIN); ch2 = analogRead(DS_PIN); ch3 = analogRead(CS_PIN); } // ============================================================ // 8. 解码配置 // ============================================================ //全局变量 bool bh = 0; bool fs = 0; bool js = 1; /* 时间常数 */ const unsigned long TIME_UNIT = 310; const unsigned long TIME_MIN = 290; const unsigned long TIME_MAX = 330; /* 上升沿时间戳环形缓冲 */ #define TIMEBUF_SIZE 512 volatile unsigned long timeBuf[TIMEBUF_SIZE]; volatile uint8_t timeHead = 0; volatile uint8_t timeTail = 0; unsigned long lastCaptureTime = 0; bool waitingForFirstBit = true; /* 原始比特缓冲 */ #define MAX_DATA_SIZE 300 bool dataBuffer[MAX_DATA_SIZE]; int dataBufferIndex = 0; /* -------------------- 新增:解码结果缓冲 -------------------- */ #define DECODED_BUFFER_SIZE 128 uint8_t decodedBuffer[DECODED_BUFFER_SIZE]; int decodedBufferIndex = 0; /* ---------------------------------------------------------- */ // 定义超时阈值(3ms) #define TIMEOUT_THRESHOLD 4000 // 3ms in microseconds void processData() { // 等待第一个中断 if (timeTail == timeHead) { return; // 没有数据,直接返回 } unsigned long startTime = micros(); bool dataStarted = false; dataBufferIndex = 0; decodedBufferIndex = 0; waitingForFirstBit = true; lastCaptureTime = 0; while (true) { noInterrupts(); if (timeTail == timeHead) { interrupts(); // 检查是否超时(3ms无新数据) if (dataStarted && (micros() - startTime >= TIMEOUT_THRESHOLD)) { if (dataBufferIndex > 0) { decodeAndStore(dataBuffer, dataBufferIndex); } if (decodedBufferIndex > 0) { rs485WriteBuffer(decodedBuffer, decodedBufferIndex); } break; } continue; } // 有新的中断数据 unsigned long edgeTime = timeBuf[timeTail]; timeTail = (timeTail + 1) & (TIMEBUF_SIZE - 1); interrupts(); // 标记数据开始 if (!dataStarted) { dataStarted = true; startTime = micros(); // 重置超时计时 } unsigned long interval = (lastCaptureTime == 0) ? 0 : (edgeTime - lastCaptureTime); // 无效窄脉冲(仅在已有上次捕获时判断) if (lastCaptureTime != 0 && interval < TIME_MIN) { lastCaptureTime = edgeTime; startTime = micros(); // 重置超时计时 continue; } // 如果是第一个比特或超时后的第一个1 if (waitingForFirstBit) { if (dataBufferIndex < MAX_DATA_SIZE) dataBuffer[dataBufferIndex++] = true; waitingForFirstBit = false; } else { // 处理正常数据间隔 if (interval > TIME_MAX) { float units = (float)interval / TIME_UNIT; int zeros = round(units) - 1; if (zeros < 1) zeros = 0; for (int i = 0; i < zeros; ++i) if (dataBufferIndex < MAX_DATA_SIZE) dataBuffer[dataBufferIndex++] = false; if (dataBufferIndex < MAX_DATA_SIZE) dataBuffer[dataBufferIndex++] = true; } else if (interval >= TIME_MIN) { if (dataBufferIndex < MAX_DATA_SIZE) dataBuffer[dataBufferIndex++] = true; } } lastCaptureTime = edgeTime; startTime = micros(); // 每次中断都重置超时计时 } // 跳出循环后的清理(如果需要) dataBufferIndex = 0; decodedBufferIndex = 0; } /* -------------------- 10-bit 解码 -------------------- */ // 直接解码:enc[0..7] 是 d0..d7(LSB 先发),不做差分 static uint8_t decodeEncodedByte(const bool* enc) { uint8_t val = 0; for (int i = 0; i < 8; ++i) { if (enc[i]) val |= (1u << i); // LSB 先发:enc[0] -> bit0 } return val; } // 扫描 bool 数组,将所有完整 10-bit 帧的解码字节存入 decodedBuffer static void decodeAndStore(const bool* buf, int len) { for (int i = 0; i + 9 < len; ++i) { // 起始位和结束位必须是 1(低电平) if (!buf[i] || !buf[i + 9]) continue; uint8_t val = decodeEncodedByte(buf + i + 1); // 取 i+1 .. i+8 共 8 位 if (decodedBufferIndex < DECODED_BUFFER_SIZE) { decodedBuffer[decodedBufferIndex++] = val; } i += 9; // 跳过这 10 位 } } // ============================================================ // 6. --------- 数据----------处理------------ // ============================================================ void processReceivedData(byte* receivedData, int length) { if (length > 30 || length < 8) return; //数据超长 丢掉 uint16_t receivedCrc = (receivedData[length - 1] << 8) | receivedData[length - 2]; // 检查CRC校验 uint16_t calculatedCrc = Modbus_crc16(receivedData, length - 2); if (receivedCrc != calculatedCrc) { return; } unsigned int crc16; unsigned char DataSend[24]; // 用于存储要发送的数据 if (receivedData[0] == 0xAA) { // CommandExcute(data, length); //处理通讯协议 处理客户给定的协议。 byte address = receivedData[0]; if ((receivedData[1] == 0x03)) { uint16_t regAddress = (receivedData[2] << 8) | receivedData[3]; // 根据地址范围处理 switch (regAddress) { //读控制器电压电流 case 0x0018: { //读长短缆 DataSend[0] = address; DataSend[1] = 0x03; DataSend[2] = (receivedData[4] * 256 + receivedData[5]) * 2; ///读取示例如下 int adc1, ds, cs; readADC(adc1, ds, cs); // 执行后三个变量被赋值 // 将 0-4095 的读数转换为电压值 (假设参考电压 VREF+ 为 3.3V) float voltage5 = adc1 * (3.3 / 4095.0); float voltage6 = ds * 0.3065; //10倍 float voltage7 = cs * 0.1007; //100倍 uint16_t v5 = (uint16_t)(voltage5 + 0.5f); uint16_t v6 = (uint16_t)(voltage6 + 0.5f); uint16_t v7 = (uint16_t)(voltage7 + 0.5f); DataSend[3] = (v6 >> 8) & 0xFF; DataSend[4] = v6 & 0xFF; DataSend[5] = (v7 >> 8) & 0xFF; DataSend[6] = v7 & 0xFF; crc16 = Modbus_crc16(&DataSend[0], 7); DataSend[7] = crc16 % 256; DataSend[8] = crc16 / 256; // Serial1.write(&DataSend[0], 9); rs485WriteBuffer(&DataSend[0], 9); break; } ///////////// 控制器信息 ///////////// case 0x0020: //读长短缆 DataSend[0] = address; DataSend[1] = 0x03; DataSend[2] = (receivedData[4] * 256 + receivedData[5]) * 2; DataSend[3] = cdzyBuf[0]; //ram中 长短缆 的值 DataSend[4] = cdzyBuf[1]; crc16 = Modbus_crc16(&DataSend[0], 5); DataSend[5] = crc16 % 256; DataSend[6] = crc16 / 256; // Serial1.write(&DataSend[0], 7); rs485WriteBuffer(&DataSend[0], 7); break; case 0x0021: //读增益 DataSend[0] = address; DataSend[1] = 0x03; DataSend[2] = (receivedData[4] * 256 + receivedData[5]) * 2; DataSend[3] = cdzyBuf[2]; //ram中 长短缆 的值 DataSend[4] = cdzyBuf[3]; crc16 = Modbus_crc16(&DataSend[0], 5); DataSend[5] = crc16 % 256; DataSend[6] = crc16 / 256; //Serial1.write(&DataSend[0], 7); rs485WriteBuffer(&DataSend[0], 7); break; default: break; } } if ((receivedData[1] == 0x10)) { //写多个寄存器 uint16_t regAddress = (receivedData[2] << 8) | receivedData[3]; // 根据地址范围处理 switch (regAddress) { case 0x0020: //长短缆设置 cdzyBuf[0] = receivedData[7]; cdzyBuf[1] = receivedData[8]; setDecode(cdzyBuf[1]); //获取到数据后 更改gpio 低位有效 // 设置增益: 0x0A (1010) saveData(cdzyBuf, sizeof(cdzyBuf)); // 存储到flash DataSend[0] = receivedData[0]; // 层位 DataSend[1] = receivedData[1]; //功能码 DataSend[2] = receivedData[2]; // 寄存器 高位 DataSend[3] = receivedData[3]; // 寄存器 低位 DataSend[4] = receivedData[4]; // 数据高位 DataSend[5] = receivedData[5]; // crc16 = Modbus_crc16(&DataSend[0], 6); DataSend[6] = crc16 % 256; DataSend[7] = crc16 / 256; //Serial1.write(&DataSend[0], 8); rs485WriteBuffer(&DataSend[0], 8); break; case 0x0021: //增益设置 cdzyBuf[2] = receivedData[7]; cdzyBuf[3] = receivedData[8]; setEncode(cdzyBuf[3]); //获取到数据后 更改gpio 低位有效 saveData(cdzyBuf, sizeof(cdzyBuf)); // 存储到flash DataSend[0] = receivedData[0]; // 层位 DataSend[1] = 0x10; //功能码 DataSend[2] = receivedData[2]; // 寄存器 高位 DataSend[3] = receivedData[3]; // 寄存器 低位 DataSend[4] = receivedData[4]; // 数据高位 DataSend[5] = receivedData[5]; // crc16 = Modbus_crc16(&DataSend[0], 6); DataSend[6] = crc16 % 256; DataSend[7] = crc16 / 256; //Serial1.write(&DataSend[0], 8); rs485WriteBuffer(&DataSend[0], 8); break; default: break; } } } if (receivedData[0] != 0xAA) { // 如果是设置井下的,直接透传 //rs485Write(0x09); // Serial1.write(receivedData, length); JxSerial.write(receivedData, length); //到井下 rs485Write(0x00); //Serial1.write(0x01); //delay(10); } memset(receivedData, 0, length); // 修正:使用length而不是sizeof(data) } // ============================================================ // 0. ==================== 中断 ==================== // ============================================================ // void captureISR() { uint8_t next = (timeHead + 1) & (TIMEBUF_SIZE - 1); if (next != timeTail) { timeBuf[timeHead] = micros(); timeHead = next; } } // ============================================================ // 0. ==================== Setup ==================== // ============================================================ // void setup() { pinMode(RS485_EN, OUTPUT); rs485SetReceive(); JxSerial.begin(300); // 井下口 Serial1.begin(9600); // 调试口 rs485.begin(9600); // RS485 pinMode(RS485_RX, INPUT_PULLUP); pinMode(RS485_TX, OUTPUT); //////////////////////////adc设置//////////////////////////// pinMode(ADC1_PIN, INPUT_ANALOG); pinMode(DS_PIN, INPUT_ANALOG); pinMode(CS_PIN, INPUT_ANALOG); analogReadResolution(12); ///////////长短兰///////////////增益//////////////////////////// pinMode(CL_0, OUTPUT); pinMode(CL_1, OUTPUT); pinMode(ENCODE_PB2, OUTPUT); pinMode(ENCODE_PE7, OUTPUT); pinMode(ENCODE_PE8, OUTPUT); pinMode(ENCODE_PE9, OUTPUT); pinMode(PD6, OUTPUT); //解码灯 // pinMode(KZ, OUTPUT); //控制解码 // digitalWrite(KZ, 1); //控制解码 过滤 pinMode(PA0_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(PA0_PIN), captureISR, FALLING); lastCaptureTime = micros(); loadData(cdzyBuf, sizeof(cdzyBuf)); // 先从flash读,看是否已经写入,已经写入则按写入调用,如果没有设置默认值 if (cdzyBuf[0] == 0xFF) // 第一个字节是0xFF 未写入 { // 默认短缆 setDecode(0x00); // 设置增益: 0x0A (1010) setEncode(0x03); } else { // 设置长短缆: PCODE=1, NCODE=0 setDecode(cdzyBuf[1]); // 设置增益: 0x0A (1010) setEncode(cdzyBuf[3]); } ///////////eprom 写入//////////////////////////// // 准备4字节数据 可以是编号 长短缆 和增益 //uint8_t myData[4] = { 0x0A, 0x01, 0x12, 0x34 }; //saveData(myData, sizeof(myData)); // Serial1.println("Saved!"); } // ==================== Loop ==================== void loop() { digitalWrite(PD6, !digitalRead(PD6)); uint8_t buffer[64]; uint16_t len = 0; //===========485 接收:收到并处理完才开启采集=========== if (js) { if (readRs485(buffer, sizeof(buffer), &len, 5)) { processReceivedData(buffer, len); memset(buffer, 0, len); bh = 1; js = 0; } } if (bh) { noInterrupts(); bool hasData = (timeTail != timeHead); interrupts(); if (hasData) { processData(); } bh = 0; js = 1; } }这个代码可以跑通,看看有优化的没主楼 - 暂无回复