comm_car_485(4751).c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #include "comm_car_485.h"
  2. //-------------参数定义
  3. rs485RecDate s_rs485RecDate;
  4. rs485SendDate s_rs485SendDate;
  5. /**
  6. * @brief 计算Modbus RTU消息的CRC校验码
  7. * @param ptr: 数据首地址
  8. * @param len: 数据长度
  9. * @return
  10. */
  11. uint16_t modbusCRC(uint8_t *buf, int len) {
  12. uint16_t crc = 0xFFFF;
  13. for (int pos = 0; pos < len; pos++) {
  14. crc ^= (uint16_t)buf[pos]; // XOR byte into CRC
  15. for (int i = 8; i != 0; i--) { // 循环8次
  16. if ((crc & 0x0001) != 0) { // 如果低位为1
  17. crc >>= 1; // 右移一位
  18. crc ^= 0xA001; // 与多项式码进行XOR
  19. } else {
  20. crc >>= 1; // 仅仅右移一位
  21. }
  22. }
  23. }
  24. return crc;
  25. }
  26. void modbus_recDate(void){
  27. HAL_UART_Receive_DMA(&huart2, (uint8_t*)g_usart2_rx_buf, USART2_REC_LEN); //设置接收缓冲区
  28. }
  29. /**
  30. * @brief 485发送函数
  31. * @param data: 发送的数据
  32. * @param dataLen: 发送的数据长度
  33. * @note
  34. * @retval 无
  35. */
  36. void modbus_sendDate(uint8_t data[], uint32_t dataLen){
  37. //等待发送状态OK
  38. while(HAL_DMA_GetState(&hdma_usart2_tx) == HAL_DMA_STATE_BUSY) osDelay(1);
  39. //发送数据
  40. HAL_UART_Transmit_DMA(&huart2, (uint8_t*)data, dataLen);
  41. // 接收使能
  42. modbus_recDate();
  43. }
  44. // 构造Modbus RTU请求函数 03 功能码
  45. void modbus_read_holding_registers(uint8_t slave_id, uint16_t start_address, uint16_t num_of_registers) {
  46. // 请求数据包的长度为 8 个字节
  47. uint8_t request[8];
  48. // 设置从设备地址
  49. request[0] = slave_id;
  50. // 设置功能码,03 功能码用于读取保持寄存器
  51. request[1] = 0x03;
  52. // 设置寄存器起始地址
  53. request[2] = (start_address >> 8) & 0xFF; // 寄存器起始地址的高字节
  54. request[3] = start_address & 0xFF; // 寄存器起始地址的低字节
  55. // 设置读取寄存器的数量
  56. request[4] = (num_of_registers >> 8) & 0xFF; // 要读取的寄存器数量的高字节
  57. request[5] = num_of_registers & 0xFF; // 要读取的寄存器
  58. // 计算CRC
  59. uint16_t crc = modbusCRC(request, 6);
  60. request[6] = crc & 0xFF; // CRC低位
  61. request[7] = crc >> 8; // CRC高位
  62. // 发送请求
  63. rs485_sendDate(request, 8);
  64. }
  65. // 构建Modbus 10功能码(写多个寄存器)请求帧
  66. // 注意:调用此函数时需要确保`request`数组有足够的空间来存放整个请求帧,此空间应大于或等于 9 + 2 * numRegisters 字节。
  67. void modbus_write_holding_registers(uint8_t slaveID, uint16_t startAddress, uint16_t numRegisters, uint16_t *values) {
  68. uint8_t request[256] = {0}; // 确保有足够的空间
  69. uint16_t requestSize = 0;
  70. int i;
  71. request[0] = slaveID; // 从设备地址
  72. request[1] = 0x10; // 功能码(写多个寄存器)
  73. request[2] = startAddress >> 8; // 起始地址高位
  74. request[3] = startAddress & 0xFF; // 起始地址低位
  75. request[4] = numRegisters >> 8; // 寄存器数量高位
  76. request[5] = numRegisters & 0xFF; // 寄存器数量低位
  77. request[6] = numRegisters * 2; // 字节计数(每个寄存器2字节)
  78. // 循环设置寄存器的值
  79. for (i = 0; i < numRegisters; i++) {
  80. request[7 + 2*i] = values[i] >> 8; // 寄存器值高字节
  81. request[8 + 2*i] = values[i] & 0xFF; // 寄存器值低字节
  82. }
  83. // 计算整个请求帧的大小
  84. requestSize = 7 + 2 * numRegisters;
  85. // 计算CRC
  86. uint16_t crc = modbusCRC(request, *requestSize);
  87. request[requestSize] = crc & 0xFF; // CRC低位
  88. request[requestSize + 1] = crc >> 8; // CRC高位
  89. // 更新请求帧大小以包含CRC
  90. requestSize += 2;
  91. // 发送请求
  92. rs485_sendDate(request, requestSize);
  93. }
  94. // 验证Modbus RTU消息的CRC校验码
  95. int checkModbusCRC(uint8_t *response, uint16_t responseSize) {
  96. if (responseSize < 4) return -1; // 不够长,无法包含CRC
  97. uint16_t receivedCRC = response[responseSize - 2] | response[responseSize - 1] << 8;
  98. uint16_t calculatedCRC = modbusCRC(response, responseSize - 2);
  99. return (receivedCRC == calculatedCRC) ? 0 : -1;
  100. }
  101. // 解析Modbus 03功能码的响应,并带有错误处理和完整性校验
  102. int parseModbus03Response(uint8_t *response, uint16_t responseSize) {
  103. // 最小长度检查
  104. if (responseSize < 5 || response[1] != 0x03) {
  105. printf("Response too short or function code mismatch!\n");
  106. return -1;
  107. }
  108. // 解析响应数据
  109. uint8_t byteCount = response[2];
  110. if (responseSize != (3 + byteCount + 2)) {
  111. printf("Byte count mismatch!\n");
  112. return -1;
  113. }
  114. // 打印结果
  115. printf("Register Values:\n");
  116. for (int i = 0; i < byteCount / 2; i++) {
  117. uint16_t regValue = response[3 + i * 2] << 8 | response[4 + i * 2];
  118. printf("Register %d: %d (0x%X)\n", i, regValue, regValue);
  119. }
  120. return 0; // 成功
  121. }
  122. // 解析Modbus 10功能码的响应,并带有错误处理和完整性校验
  123. int parseModbus10Response(uint8_t *response, uint16_t responseSize) {
  124. // 最小长度检查
  125. if (responseSize != 8 || response[1] != 0x10) {
  126. printf("Response length mismatch or function code mismatch!\n");
  127. return -1;
  128. }
  129. // 解析响应数据
  130. uint16_t startAddress = response[2] << 8 | response[3];
  131. uint16_t numRegisters = response[4] << 8 | response[5];
  132. // 打印结果
  133. printf("Written to start address: %d, number of registers: %d\n", startAddress, numRegisters);
  134. return 0; // 成功
  135. }
  136. /**
  137. * @brief 485接收分析函数
  138. * @param response: 接收到的数据
  139. * @note
  140. * @retval 无
  141. */
  142. void parseModbusResponse(uint8_t *response, uint16_t responseSize){
  143. uint8_t fun_Code = 0;
  144. // 检查CRC
  145. if (checkModbusCRC(response, responseSize) != 0) {
  146. printf("CRC check failed!\n");
  147. return -1;
  148. }
  149. // 确认功能码
  150. fun_Code = response[1];
  151. /* 尝试获取互斥量,等待无限长时间 */
  152. if(osMutexAcquire(mutex_rs485RecDateHandle, osWaitForever) == osOK){
  153. // 数据解析
  154. switch(fun_Code){
  155. case 0x03:
  156. parseModbus03Response(response, responseSize);
  157. break;
  158. case 0x10:
  159. parseModbus10Response(response, responseSize);
  160. break;
  161. default:
  162. break;
  163. }
  164. /* 访问完成,释放互斥量 */
  165. osMutexRelease(mutex_rs485RecDateHandle);
  166. }
  167. }
  168. /**
  169. * @brief 轮询发送接收
  170. * @note 1s请求一次
  171. * @retval 无
  172. */
  173. void rs485_poll_sendReceive(void){
  174. // 读寄存器数据
  175. modbus_read_holding_registers(0x01, 0x00, 13);
  176. }