#include "comm_car_485.h" //-------------参数定义 rs485RecDate s_rs485RecDate; rs485SendDate s_rs485SendDate; /** * @brief 计算Modbus RTU消息的CRC校验码 * @param ptr: 数据首地址 * @param len: 数据长度 * @return */ uint16_t modbusCRC(uint8_t *buf, int len) { uint16_t crc = 0xFFFF; for (int pos = 0; pos < len; pos++) { crc ^= (uint16_t)buf[pos]; // XOR byte into CRC for (int i = 8; i != 0; i--) { // 循环8次 if ((crc & 0x0001) != 0) { // 如果低位为1 crc >>= 1; // 右移一位 crc ^= 0xA001; // 与多项式码进行XOR } else { crc >>= 1; // 仅仅右移一位 } } } return crc; } void modbus_recDate(void){ HAL_UART_Receive_DMA(&huart2, (uint8_t*)g_usart2_rx_buf, USART2_REC_LEN); //设置接收缓冲区 } /** * @brief 485发送函数 * @param data: 发送的数据 * @param dataLen: 发送的数据长度 * @note * @retval 无 */ void modbus_sendDate(uint8_t data[], uint32_t dataLen){ HAL_UART_Transmit(&huart2, data, dataLen, 1000); // 接收使能 modbus_recDate(); } // 构造Modbus RTU请求函数 03 功能码 void modbus_read_holding_registers(uint8_t slave_id, uint16_t start_address, uint16_t num_of_registers) { // 请求数据包的长度为 8 个字节 uint8_t request[8] = {0}; // 设置从设备地址 request[0] = slave_id; // 设置功能码,03 功能码用于读取保持寄存器 request[1] = 0x03; // 设置寄存器起始地址 request[2] = (start_address >> 8) & 0xFF; // 寄存器起始地址的高字节 request[3] = start_address & 0xFF; // 寄存器起始地址的低字节 // 设置读取寄存器的数量 request[4] = (num_of_registers >> 8) & 0xFF; // 要读取的寄存器数量的高字节 request[5] = num_of_registers & 0xFF; // 要读取的寄存器 // 计算CRC uint16_t crc = modbusCRC(request, 6); request[6] = crc & 0xFF; // CRC低位 request[7] = crc >> 8; // CRC高位 // 发送请求 modbus_sendDate(request, 8); } // 构建Modbus 10功能码(写多个寄存器)请求帧 // 注意:调用此函数时需要确保`request`数组有足够的空间来存放整个请求帧,此空间应大于或等于 9 + 2 * numRegisters 字节。 void modbus_write_holding_registers(uint8_t slaveID, uint16_t startAddress, uint16_t numRegisters, uint16_t *values) { uint8_t request[256] = {0}; // 确保有足够的空间 uint16_t requestSize = 0; int i; request[0] = slaveID; // 从设备地址 request[1] = 0x10; // 功能码(写多个寄存器) request[2] = startAddress >> 8; // 起始地址高位 request[3] = startAddress & 0xFF; // 起始地址低位 request[4] = numRegisters >> 8; // 寄存器数量高位 request[5] = numRegisters & 0xFF; // 寄存器数量低位 request[6] = numRegisters * 2; // 字节计数(每个寄存器2字节) // 循环设置寄存器的值 for (i = 0; i < numRegisters; i++) { request[7 + 2*i] = values[i] >> 8; // 寄存器值高字节 request[8 + 2*i] = values[i] & 0xFF; // 寄存器值低字节 } // 计算整个请求帧的大小 requestSize = 7 + 2 * numRegisters; // 计算CRC uint16_t crc = modbusCRC(request, requestSize); request[requestSize] = crc & 0xFF; // CRC低位 request[requestSize + 1] = crc >> 8; // CRC高位 // 更新请求帧大小以包含CRC requestSize += 2; // 发送请求 modbus_sendDate(request, requestSize); } // 验证Modbus RTU消息的CRC校验码 int checkModbusCRC(uint8_t *response, uint16_t responseSize) { if (responseSize < 4) return -1; // 不够长,无法包含CRC uint16_t receivedCRC = response[responseSize - 2] | response[responseSize - 1] << 8; uint16_t calculatedCRC = modbusCRC(response, responseSize - 2); return (receivedCRC == calculatedCRC) ? 0 : -1; } // 解析Modbus 03功能码的响应,并带有错误处理和完整性校验 int parseModbus03Response(uint8_t *response, uint16_t responseSize) { rs485RecDate *p_rs485RecDate = &s_rs485RecDate; // 最小长度检查 if (responseSize < 5 || response[1] != 0x03) { printf("Response too short or function code mismatch!\n"); return -1; } // 解析响应数据 从第四个字节开始为第一个数据 p_rs485RecDate->vehicleSpeed = ((uint16_t)response[3] << 8) | response[4]; return 0; // 成功 } // 解析Modbus 10功能码的响应,并带有错误处理和完整性校验 int parseModbus10Response(uint8_t *response, uint16_t responseSize) { // 最小长度检查 if (responseSize != 8 || response[1] != 0x10) { printf("Response length mismatch or function code mismatch!\n"); return -1; } // 解析响应数据 uint16_t startAddress = response[2] << 8 | response[3]; uint16_t numRegisters = response[4] << 8 | response[5]; // 打印结果 printf("Written to start address: %d, number of registers: %d\n", startAddress, numRegisters); return 0; // 成功 } /** * @brief 485接收分析函数 * @param response: 接收到的数据 * @note 如果在中断中进行解析,则不能加互斥量,如果是在任务中解析,则加入互斥量的获取 * @retval 无 */ int parseModbusResponse(uint8_t *response, uint16_t responseSize){ uint8_t fun_Code = 0; // 检查CRC if (checkModbusCRC(response, responseSize) != 0) { printf("CRC check failed!\n"); return -1; } // 确认功能码 fun_Code = response[1]; /* 尝试获取互斥量,等待无限长时间 */ // if(osMutexAcquire(mutex_rs485RecDateHandle, osWaitForever) == osOK){ // 数据解析 switch(fun_Code){ case 0x03: parseModbus03Response(response, responseSize); break; case 0x10: parseModbus10Response(response, responseSize); break; default: break; } // /* 访问完成,释放互斥量 */ // osMutexRelease(mutex_rs485RecDateHandle); // } return 0; // 成功 } /** * @brief 轮询发送接收 * @note 1s请求一次 * @retval 无 */ void rs485_poll_sendReceive(void){ // 读寄存器数据 modbus_read_holding_registers(0x01, 0x0000, 1); }