- 管理员1
/* * STM32F407 串口双向透传 * ------------------------------------------------------------ * 485主机接口 : PB6(RX)/PB7(TX)/PB5(EN) 9600 * 井下数据口 : PA2(TX)/PA3(RX) 300 (软件 bit-bang 发送) * 调试口 : PA9/PA10(USART1) 9600 * * 调试开关: DBG 改 0 关闭全部调试打印 */ #include <Arduino.h> #include <SoftwareSerial.h> // ==================== 调试开关 ==================== #define DBG 1 #if DBG #define DBG_PRINT(x) Serial1.print(x) #define DBG_PRINTLN(x) Serial1.println(x) #define DBG_PRINTHEX(x) do { if ((x) < 0x10) Serial1.print('0'); Serial1.print((x), HEX); } while (0) #else #define DBG_PRINT(x) do { } while (0) #define DBG_PRINTLN(x) do { } while (0) #define DBG_PRINTHEX(x) do { } while (0) #endif // ==================== 硬件配置 ==================== #define RS485_RX PB7 #define RS485_TX PB6 #define RS485_EN PB5 #define EEPROM_BASE 0x080E0000 #define EEPROM_SECTOR 11 // ==================== 引脚定义 ==================== #define CL_0 PB0 #define CL_1 PB1 #define KZ PE10 #define ENCODE_PB2 PB2 #define ENCODE_PE7 PE7 #define ENCODE_PE8 PE8 #define ENCODE_PE9 PE9 #define ADC1_PIN PA5 #define DS_PIN PA6 #define CS_PIN PA7 #define PCODE_PIN PA1 #define NCODE_PIN PA0 #define LED_PIN PD6 // 井下口 (软件 bit-bang) #define JX_TX_PIN PA2 #define JX_RX_PIN PA3 #define JX_BAUD 300UL // ==================== 串口对象 ==================== Uart Serial1(USART1); // 调试口 SoftwareSerial rs485(RS485_RX, RS485_TX); // RS485 // ==================== 协议常量 ==================== static const uint8_t PROTO_ADDR_AA = 0xAA; static const uint8_t FUNC_READ_HOLDING = 0x03; static const uint8_t FUNC_WRITE_MULTI = 0x10; static const uint16_t REG_VOLTAGE_CURRENT = 0x0018; static const uint16_t REG_CABLE_LENGTH = 0x0020; static const uint16_t REG_GAIN = 0x0021; static const int MAX_FRAME_LEN = 30; static const int MIN_FRAME_LEN = 8; // ==================== 全局配置缓存 ==================== uint8_t cdzyBuf[4]; // ==================== 调试计数器 ==================== static uint32_t g_frameCnt = 0; static uint32_t g_jxTxCnt = 0; static uint32_t g_rsTxCnt = 0; // ==================== 前置声明 ==================== void processReceivedData(uint8_t* data, int length); // ============================================================ // RS485 方向控制 // ============================================================ static inline void rs485SetReceive() { digitalWrite(RS485_EN, LOW); delayMicroseconds(10); } static inline void rs485SetTransmit() { digitalWrite(RS485_EN, HIGH); delayMicroseconds(10); } // ============================================================ // RS485 发送 // ============================================================ void rs485WriteBuffer(const uint8_t* buf, uint16_t len) { g_rsTxCnt++; DBG_PRINT("[485] TX cnt="); DBG_PRINT(g_rsTxCnt); DBG_PRINT(" len="); DBG_PRINTLN(len); rs485SetTransmit(); rs485.write(buf, len); rs485.flush(); delay(1); rs485SetReceive(); DBG_PRINTLN("[485] TX done"); } // ============================================================ // PA2 软件 bit-bang 发送 (300bps) // ============================================================ static void jxSendByte(uint8_t b) { uint32_t bitUs = 1000000UL / JX_BAUD; // 3333us // 起始位 digitalWrite(JX_TX_PIN, LOW); delayMicroseconds(bitUs); // 8 个数据位 (LSB first) for (int i = 0; i < 8; i++) { digitalWrite(JX_TX_PIN, (b >> i) & 1); delayMicroseconds(bitUs); } // 停止位 digitalWrite(JX_TX_PIN, HIGH); delayMicroseconds(bitUs); } static void jxSendBuffer(const uint8_t* data, int length) { for (int i = 0; i < length; i++) { jxSendByte(data[i]); } } // ============================================================ // 透传到井下口 // ============================================================ static void forwardToDownhole(uint8_t* data, int length) { g_jxTxCnt++; DBG_PRINT("[JX] forward #"); DBG_PRINT(g_jxTxCnt); DBG_PRINT(" len="); DBG_PRINTLN(length); pinMode(JX_TX_PIN, OUTPUT); digitalWrite(JX_TX_PIN, HIGH); delayMicroseconds(10); jxSendBuffer(data, length); // 发送完毕, 让 PA2 空闲为高电平 (空闲态) digitalWrite(JX_TX_PIN, HIGH); } // ============================================================ // Flash 读写 // ============================================================ void saveData(const uint8_t* data, uint16_t len) { HAL_FLASH_Unlock(); FLASH_EraseInitTypeDef e = {}; e.TypeErase = FLASH_TYPEERASE_SECTORS; e.Sector = EEPROM_SECTOR; e.NbSectors = 1; e.VoltageRange = FLASH_VOLTAGE_RANGE_3; uint32_t err; HAL_FLASHEx_Erase(&e, &err); for (uint16_t i = 0; i < len; i += 2) { uint16_t 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 (uint16_t i = 0; i < len; i++) { buf[i] = *(volatile uint8_t*)(EEPROM_BASE + i); } } // ============================================================ // GPIO 控制 // ============================================================ void setCableLength(uint8_t val) { digitalWrite(CL_0, (val >> 0) & 0x01); digitalWrite(CL_1, (val >> 1) & 0x01); } void setGain(uint8_t val) { digitalWrite(ENCODE_PE9, (val >> 0) & 0x01); digitalWrite(ENCODE_PE8, (val >> 1) & 0x01); digitalWrite(ENCODE_PE7, (val >> 2) & 0x01); digitalWrite(ENCODE_PB2, (val >> 3) & 0x01); } // ============================================================ // 超时接收 // ============================================================ bool readWithTimeout(Stream& port, uint8_t* buffer, uint16_t maxLen, uint16_t* len, uint32_t timeoutMs) { *len = 0; if (!buffer || maxLen == 0) return false; if (!port.available()) return false; uint32_t start = millis(); while (*len < maxLen && (millis() - start) < timeoutMs) { if (port.available()) { buffer[(*len)++] = port.read(); start = millis(); } delayMicroseconds(50); } return (*len > 0); } // ============================================================ // Modbus CRC16 // ============================================================ uint16_t modbusCRC16(const uint8_t* data, uint16_t len) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } static void appendCRC(uint8_t* buf, uint16_t payloadLen) { uint16_t crc = modbusCRC16(buf, payloadLen); buf[payloadLen] = crc & 0xFF; buf[payloadLen + 1] = (crc >> 8) & 0xFF; } // ============================================================ // ADC // ============================================================ void readADC(int& cableAdc, int& voltageAdc, int& currentAdc) { cableAdc = analogRead(ADC1_PIN); voltageAdc = analogRead(DS_PIN); currentAdc = analogRead(CS_PIN); } // ============================================================ // Modbus 响应构造 // ============================================================ static uint16_t buildReadResponse(uint8_t* out, uint8_t addr, const uint8_t* payload, uint8_t payloadLen) { out[0] = addr; out[1] = FUNC_READ_HOLDING; out[2] = payloadLen; memcpy(&out[3], payload, payloadLen); appendCRC(out, 3 + payloadLen); return 3 + payloadLen + 2; } static uint16_t buildWriteResponse(uint8_t* out, const uint8_t* req) { memcpy(out, req, 6); appendCRC(out, 6); return 8; } // ============================================================ // 寄存器读处理 // ============================================================ static void handleReadVoltageCurrent(uint8_t* req, uint8_t* resp) { uint8_t addr = req[0]; int cableAdc, voltageAdc, currentAdc; readADC(cableAdc, voltageAdc, currentAdc); uint16_t voltage = (uint16_t)(voltageAdc * 0.3065f + 0.5f); uint16_t current = (uint16_t)(currentAdc * 0.1007f + 0.5f); uint8_t payload[4]; payload[0] = (voltage >> 8) & 0xFF; payload[1] = voltage & 0xFF; payload[2] = (current >> 8) & 0xFF; payload[3] = current & 0xFF; uint16_t frameLen = buildReadResponse(resp, addr, payload, 4); rs485WriteBuffer(resp, frameLen); } static void handleReadCableLength(uint8_t* req, uint8_t* resp) { uint8_t addr = req[0]; uint8_t payload[2] = { cdzyBuf[0], cdzyBuf[1] }; uint16_t frameLen = buildReadResponse(resp, addr, payload, 2); rs485WriteBuffer(resp, frameLen); } static void handleReadGain(uint8_t* req, uint8_t* resp) { uint8_t addr = req[0]; uint8_t payload[2] = { cdzyBuf[2], cdzyBuf[3] }; uint16_t frameLen = buildReadResponse(resp, addr, payload, 2); rs485WriteBuffer(resp, frameLen); } // ============================================================ // 寄存器写处理 // ============================================================ static void handleWriteCableLength(uint8_t* req, uint8_t* resp) { cdzyBuf[0] = req[7]; cdzyBuf[1] = req[8]; setCableLength(cdzyBuf[1]); saveData(cdzyBuf, sizeof(cdzyBuf)); uint16_t frameLen = buildWriteResponse(resp, req); rs485WriteBuffer(resp, frameLen); } static void handleWriteGain(uint8_t* req, uint8_t* resp) { cdzyBuf[2] = req[7]; cdzyBuf[3] = req[8]; setGain(cdzyBuf[3]); saveData(cdzyBuf, sizeof(cdzyBuf)); uint16_t frameLen = buildWriteResponse(resp, req); rs485WriteBuffer(resp, frameLen); } // ============================================================ // Modbus 帧处理 // ============================================================ static bool handleModbusFrame(uint8_t* data, int length) { if (length < MIN_FRAME_LEN || length > MAX_FRAME_LEN) return false; uint16_t recvCRC = (data[length - 1] << 8) | data[length - 2]; uint16_t calcCRC = modbusCRC16(data, length - 2); if (recvCRC != calcCRC) return false; uint8_t func = data[1]; uint16_t regAddress = (data[2] << 8) | data[3]; uint8_t resp[24]; if (func == FUNC_READ_HOLDING) { switch (regAddress) { case REG_VOLTAGE_CURRENT: handleReadVoltageCurrent(data, resp); return true; case REG_CABLE_LENGTH: handleReadCableLength(data, resp); return true; case REG_GAIN: handleReadGain(data, resp); return true; default: return false; } } if (func == FUNC_WRITE_MULTI) { switch (regAddress) { case REG_CABLE_LENGTH: handleWriteCableLength(data, resp); return true; case REG_GAIN: handleWriteGain(data, resp); return true; default: return false; } } return false; } // ============================================================ // 主处理入口 // ============================================================ void processReceivedData(uint8_t* data, int length) { if (length <= 0) return; if (data[0] == PROTO_ADDR_AA) { DBG_PRINTLN("[PROC] Modbus 0xAA"); handleModbusFrame(data, length); } else { DBG_PRINTLN("[PROC] forward to downhole"); forwardToDownhole(data, length); rs485SetReceive(); } } // ============================================================ // 系统时钟 // ============================================================ 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; } // ============================================================ // 初始化 // ============================================================ static void initPins() { pinMode(RS485_EN, OUTPUT); pinMode(RS485_RX, INPUT_PULLUP); pinMode(RS485_TX, OUTPUT); rs485SetReceive(); 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(LED_PIN, OUTPUT); pinMode(ADC1_PIN, INPUT_ANALOG); pinMode(DS_PIN, INPUT_ANALOG); pinMode(CS_PIN, INPUT_ANALOG); analogReadResolution(12); // 井下口 PA2: 初始化为输出高 (空闲态) pinMode(JX_TX_PIN, OUTPUT); digitalWrite(JX_TX_PIN, HIGH); pinMode(JX_RX_PIN, INPUT); } static void initSerial() { Serial1.begin(9600); // 调试口 rs485.begin(9600); // RS485 } static void loadConfigOrDefault() { loadData(cdzyBuf, sizeof(cdzyBuf)); if (cdzyBuf[0] == 0xFF) { cdzyBuf[0] = 0x00; cdzyBuf[1] = 0x00; cdzyBuf[2] = 0x00; cdzyBuf[3] = 0x03; } setCableLength(cdzyBuf[1]); setGain(cdzyBuf[3]); } void setup() { initPins(); initSerial(); loadConfigOrDefault(); DBG_PRINTLN("=== BOOT ==="); } // ============================================================ // 主循环 // ============================================================ void loop() { uint8_t buffer[64]; uint16_t len = 0; // RS485 接收 → 透传到井下 if (readWithTimeout(rs485, buffer, sizeof(buffer), &len, 5)) { g_frameCnt++; DBG_PRINT("[RX] frame#"); DBG_PRINT(g_frameCnt); DBG_PRINT(" len="); DBG_PRINT(len); DBG_PRINT(" data="); for (uint16_t i = 0; i < len; i++) { DBG_PRINTHEX(buffer[i]); DBG_PRINT(' '); } DBG_PRINTLN(""); processReceivedData(buffer, len); memset(buffer, 0, len); } // 心跳灯 + 心跳打印 static uint32_t lastBlink = 0; if (millis() - lastBlink > 500) { lastBlink = millis(); digitalWrite(LED_PIN, !digitalRead(LED_PIN)); DBG_PRINT("[HB] frameCnt="); DBG_PRINT(g_frameCnt); DBG_PRINT(" jxTxCnt="); DBG_PRINT(g_jxTxCnt); DBG_PRINT(" rsTxCnt="); DBG_PRINTLN(g_rsTxCnt); } }主楼 - 暂无回复