comm_car_485(7004).c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #include "comm_car_485.h"
  2. //-------------define
  3. //-------------参数定义
  4. rs485RecDate s_rs485RecDate;
  5. rs485SendDate s_rs485SendDate;
  6. /**
  7. * @brief 计算Modbus RTU消息的CRC校验码
  8. * @param ptr: 数据首地址
  9. * @param len: 数据长度
  10. * @return
  11. */
  12. uint16_t modbusCRC(uint8_t *buf, int len) {
  13. uint16_t crc = 0xFFFF;
  14. for (int pos = 0; pos < len; pos++) {
  15. crc ^= (uint16_t)buf[pos]; // XOR byte into CRC
  16. for (int i = 8; i != 0; i--) { // 循环8次
  17. if ((crc & 0x0001) != 0) { // 如果低位为1
  18. crc >>= 1; // 右移一位
  19. crc ^= 0xA001; // 与多项式码进行XOR
  20. } else {
  21. crc >>= 1; // 仅仅右移一位
  22. }
  23. }
  24. }
  25. return crc;
  26. }
  27. void modbus_recDate(void){
  28. HAL_UART_Receive_DMA(&huart3, (uint8_t*)g_usart3_rx_buf, USART3_REC_LEN); //设置接收缓冲区
  29. }
  30. /**
  31. * @brief 485发送函数
  32. * @param data: 发送的数据
  33. * @param dataLen: 发送的数据长度
  34. * @note
  35. * @retval 无
  36. */
  37. void modbus_sendDate(uint8_t data[], uint32_t dataLen){
  38. HAL_UART_Transmit(&huart3, data, dataLen, 1000);
  39. // 接收使能
  40. modbus_recDate();
  41. }
  42. // 构造Modbus RTU请求函数 03 功能码
  43. void modbus_read_holding_registers(uint8_t slave_id, uint16_t start_address, uint16_t num_of_registers) {
  44. // 请求数据包的长度为 8 个字节
  45. uint8_t request[8] = {0};
  46. // 设置从设备地址
  47. request[0] = slave_id;
  48. // 设置功能码,03 功能码用于读取保持寄存器
  49. request[1] = 0x03;
  50. // 设置寄存器起始地址
  51. request[2] = (start_address >> 8) & 0xFF; // 寄存器起始地址的高字节
  52. request[3] = start_address & 0xFF; // 寄存器起始地址的低字节
  53. // 设置读取寄存器的数量
  54. request[4] = (num_of_registers >> 8) & 0xFF; // 要读取的寄存器数量的高字节
  55. request[5] = num_of_registers & 0xFF; // 要读取的寄存器
  56. // 计算CRC
  57. uint16_t crc = modbusCRC(request, 6);
  58. request[6] = crc & 0xFF; // CRC低位
  59. request[7] = crc >> 8; // CRC高位
  60. // 发送请求
  61. modbus_sendDate(request, 8);
  62. }
  63. // 构建Modbus 10功能码(写多个寄存器)请求帧
  64. // 注意:调用此函数时需要确保`request`数组有足够的空间来存放整个请求帧,此空间应大于或等于 9 + 2 * numRegisters 字节。
  65. void modbus_write_holding_registers(uint8_t slaveID, uint16_t startAddress, uint16_t numRegisters, uint16_t *values) {
  66. uint8_t request[256] = {0}; // 确保有足够的空间
  67. uint16_t requestSize = 0;
  68. int i;
  69. request[0] = slaveID; // 从设备地址
  70. request[1] = 0x10; // 功能码(写多个寄存器)
  71. request[2] = startAddress >> 8; // 起始地址高位
  72. request[3] = startAddress & 0xFF; // 起始地址低位
  73. request[4] = numRegisters >> 8; // 寄存器数量高位
  74. request[5] = numRegisters & 0xFF; // 寄存器数量低位
  75. request[6] = numRegisters * 2; // 字节计数(每个寄存器2字节)
  76. // 循环设置寄存器的值
  77. for (i = 0; i < numRegisters; i++) {
  78. request[7 + 2*i] = values[i] >> 8; // 寄存器值高字节
  79. request[8 + 2*i] = values[i] & 0xFF; // 寄存器值低字节
  80. }
  81. // 计算整个请求帧的大小
  82. requestSize = 7 + 2 * numRegisters;
  83. // 计算CRC
  84. uint16_t crc = modbusCRC(request, requestSize);
  85. request[requestSize] = crc & 0xFF; // CRC低位
  86. request[requestSize + 1] = crc >> 8; // CRC高位
  87. // 更新请求帧大小以包含CRC
  88. requestSize += 2;
  89. // 发送请求
  90. modbus_sendDate(request, requestSize);
  91. }
  92. // 验证Modbus RTU消息的CRC校验码
  93. int checkModbusCRC(uint8_t *response, uint16_t responseSize) {
  94. if (responseSize < 4) return -1; // 不够长,无法包含CRC
  95. uint16_t receivedCRC = response[responseSize - 2] | response[responseSize - 1] << 8;
  96. uint16_t calculatedCRC = modbusCRC(response, responseSize - 2);
  97. return (receivedCRC == calculatedCRC) ? 0 : -1;
  98. }
  99. // 解析Modbus 03功能码的响应,并带有错误处理和完整性校验
  100. int parseModbus03Response(uint8_t *response, uint16_t responseSize) {
  101. rs485RecDate *p_rs485RecDate = &s_rs485RecDate;
  102. // 最小长度检查
  103. if (responseSize < 5 || response[1] != 0x03) {
  104. // printf("Response too short or function code mismatch!\n");
  105. return -1;
  106. }
  107. // 解析响应数据 从第四个字节开始为第一个数据
  108. p_rs485RecDate->vehicleSpeed = ((uint16_t)response[3] << 8) | response[4];
  109. // p_rs485RecDate->accTotalDrivTime_day = ((uint16_t)response[5] << 8) | response[6];
  110. p_rs485RecDate->dailyDrivTime = ((uint16_t)response[7] << 8) | response[8];
  111. p_rs485RecDate->dailyDrivMileage = ((uint16_t)response[9] << 8) | response[10];
  112. p_rs485RecDate->accTotalDrivTime_day = ((uint16_t)response[11] << 8) | response[12];
  113. p_rs485RecDate->accTotalDrivTime_h_min = ((uint16_t)response[13] << 8) | response[14];
  114. p_rs485RecDate->accTotalMileage_h = ((uint16_t)response[15] << 8) | response[16];
  115. p_rs485RecDate->accTotalMileage_l = ((uint16_t)response[17] << 8) | response[18];
  116. p_rs485RecDate->runTime = ((uint16_t)response[19] << 8) | response[20];
  117. p_rs485RecDate->batCompartmentTemp = ((uint16_t)response[21] << 8) | response[22];
  118. p_rs485RecDate->demandCur = ((uint16_t)response[23] << 8) | response[24];
  119. p_rs485RecDate->demandVol = ((uint16_t)response[25] << 8) | response[26];
  120. p_rs485RecDate->alarmLevel = ((uint16_t)response[27] << 8) | response[28];
  121. p_rs485RecDate->alarmType = ((uint16_t)response[29] << 8) | response[30];
  122. memcpy(p_rs485RecDate->VIN, &response[53], 26);
  123. memcpy(p_rs485RecDate->batSn, &response[79], 40);
  124. memcpy(p_rs485RecDate->Vehicle_Num, &response[119], 20);
  125. s_comData.vinRecSuccess = 1; // 成功获取到车的数据
  126. return 0; // 成功
  127. }
  128. // 解析Modbus 10功能码的响应,并带有错误处理和完整性校验
  129. int parseModbus10Response(uint8_t *response, uint16_t responseSize) {
  130. // 最小长度检查
  131. if (responseSize != 8 || response[1] != 0x10) {
  132. // printf("Response length mismatch or function code mismatch!\n");
  133. return -1;
  134. }
  135. // 解析响应数据
  136. uint16_t startAddress = response[2] << 8 | response[3];
  137. uint16_t numRegisters = response[4] << 8 | response[5];
  138. // 避免警告
  139. startAddress = startAddress;
  140. numRegisters = numRegisters;
  141. // 打印结果
  142. // printf("Written to start address: %d, number of registers: %d\n", startAddress, numRegisters);
  143. return 0; // 成功
  144. }
  145. /**
  146. * @brief 485接收分析函数
  147. * @param response: 接收到的数据
  148. * @note 如果在中断中进行解析,则不能加互斥量,如果是在任务中解析,则加入互斥量的获取
  149. * @retval 无
  150. */
  151. int parseModbusResponse(uint8_t *response, uint16_t responseSize){
  152. uint8_t fun_Code = 0;
  153. // 检查CRC
  154. if (checkModbusCRC(response, responseSize) != 0) {
  155. // printf("CRC check failed!\n");
  156. return -1;
  157. }
  158. // 确认功能码
  159. fun_Code = response[1];
  160. // 数据解析
  161. switch(fun_Code){
  162. case 0x03:
  163. parseModbus03Response(response, responseSize);
  164. break;
  165. case 0x10:
  166. parseModbus10Response(response, responseSize);
  167. break;
  168. default:
  169. break;
  170. }
  171. return 0; // 成功
  172. }
  173. /**
  174. * @brief 轮询发送接收
  175. * @note 1s请求一次
  176. * @retval 无
  177. */
  178. void rs485_poll_sendReceive(uint16_t speed){
  179. static uint8_t step_485 = 0;
  180. global_par *p_global_par = &s_global_par;
  181. switch(step_485){
  182. case 0:
  183. // 读寄存器数据
  184. modbus_read_holding_registers(0x01, 0x0000, 67);
  185. step_485 = 1;
  186. break;
  187. case 1:
  188. // 写寄存器数据
  189. modbus_write_holding_registers(0x01, 0x0001, 1, &speed);
  190. step_485 = 0;
  191. break;
  192. default:
  193. break;
  194. }
  195. p_global_par->timeoutCnt_485++;
  196. if(p_global_par->timeoutCnt_485 == 255){
  197. p_global_par->timeoutCnt_485 = 255;
  198. }
  199. }