modbus_master.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. #include "modbus_master.h"
  2. #include "modbus_encoder.h"
  3. #include "st_sys.h"
  4. #include <string.h>
  5. //周期通讯指令缓冲池
  6. static modbus_master_period_cmd_t period_cmd_pool[MODBUS_MASTER_PERIOD_POOL_SIZE];
  7. //单次命令缓冲池
  8. static modbus_master_once_cmd_t once_cmd_pool[MODBUS_MASTER_ONCE_POOL_SIZE];
  9. //周期命令池已用计数
  10. static int period_cmd_count = 0;
  11. void modbus_master_init(void)
  12. {
  13. memset(period_cmd_pool, 0, MODBUS_MASTER_PERIOD_POOL_SIZE);
  14. memset(once_cmd_pool, 0, MODBUS_MASTER_PERIOD_POOL_SIZE);
  15. }
  16. /**
  17. * 创建一条周期任务
  18. *
  19. * @author lxz (2019/8/25/周日)
  20. *
  21. * @param group modbus 运行组
  22. * @param id id
  23. * @param cmd 命令
  24. * @param address 地址
  25. * @param number 长度
  26. * @param value
  27. * 要读写的数值,对于写指令,它是数值
  28. * @param respone_timeout 该指令的等待响应时间
  29. * @param period
  30. * 该指令的采集周期,协议会尽可能满足该周期
  31. * @param on_respone 命令响应时的回调函数
  32. *
  33. * @return modbus_master_cmd_t 命令的指针
  34. */
  35. modbus_master_period_cmd_t* modbus_master_add_period_cmd(
  36. modbus_master_t *master,
  37. unsigned char cmdcode,
  38. unsigned char id,
  39. unsigned char cmd,
  40. unsigned short address,
  41. unsigned short number,
  42. unsigned char *value,
  43. int respone_timeout,
  44. void (*on_respone)(modbus_master_cmd_t *, unsigned char *, int),
  45. int period
  46. )
  47. {
  48. if (period_cmd_count < MODBUS_MASTER_PERIOD_POOL_SIZE)
  49. {
  50. modbus_master_period_cmd_t *next = 0;
  51. //获取一个没用过的包
  52. modbus_master_period_cmd_t *period_cmd = &period_cmd_pool[period_cmd_count];
  53. period_cmd_count++;
  54. //初始化命令
  55. period_cmd->cmd.cmdcode=cmdcode;
  56. period_cmd->cmd.id = id;
  57. period_cmd->cmd.cmd = cmd;
  58. period_cmd->cmd.address = address;
  59. period_cmd->cmd.number = number;
  60. period_cmd->cmd.value = value;
  61. period_cmd->cmd.respone_timeout = respone_timeout;
  62. period_cmd->cmd.on_respone = on_respone;
  63. period_cmd->cmd.dir = 0;
  64. period_cmd->period = period;
  65. period_cmd->next = 0;
  66. //将命令挂靠到主站对象到
  67. if (master->period_cmd_list == 0)
  68. {
  69. master->period_cmd_list = period_cmd;
  70. } else
  71. {
  72. next = master->period_cmd_list;
  73. while (next->next != 0)
  74. {
  75. next = (modbus_master_period_cmd_t *)next->next;
  76. }
  77. next->next = (void *)period_cmd;
  78. }
  79. return period_cmd;
  80. }
  81. return 0;
  82. }
  83. /**
  84. * 创建一条单次命令
  85. *
  86. * @author lxz (2019/8/25/周日)
  87. *
  88. * @param group modbus 运行组
  89. * @param id id
  90. * @param cmd 命令
  91. * @param address 地址
  92. * @param number 长度
  93. * @param value
  94. * 要读写的数值,对于写指令,它是数值
  95. * @param respone_timeout 该指令的等待响应时间
  96. * @param period
  97. * 该指令的采集周期,协议会尽可能满足该周期
  98. * @param on_respone 命令响应时的回调函数
  99. * @param dir 方向,0表示读,1表示写
  100. *
  101. * @return modbus_master_cmd_t 命令的指针
  102. */
  103. modbus_master_once_cmd_t* modbus_master_add_once_cmd(
  104. modbus_master_t *master,
  105. unsigned char cmdcode,
  106. unsigned char id,
  107. unsigned char cmd,
  108. unsigned short address,
  109. unsigned short number,
  110. unsigned char *value,
  111. int respone_timeout,
  112. void (*on_respone)(modbus_master_cmd_t *, unsigned char *, int)
  113. )
  114. {
  115. int index = 0;
  116. while (index < MODBUS_MASTER_ONCE_POOL_SIZE)
  117. {
  118. if (once_cmd_pool[index].token == 0)
  119. {
  120. break;
  121. }
  122. index++;
  123. }
  124. if (index < MODBUS_MASTER_ONCE_POOL_SIZE)
  125. {
  126. modbus_master_once_cmd_t *next = 0;
  127. //获取一个没用过的包
  128. once_cmd_pool[index].token=1;
  129. modbus_master_once_cmd_t *once_cmd = &once_cmd_pool[index];
  130. index++;//period_cmd_count++;(修改2024-0703)
  131. //初始化命令
  132. once_cmd->cmd.cmdcode=cmdcode;
  133. once_cmd->cmd.id = id;
  134. once_cmd->cmd.cmd = cmd;
  135. once_cmd->cmd.address = address;
  136. once_cmd->cmd.number = number;
  137. once_cmd->cmd.value = value;
  138. once_cmd->cmd.respone_timeout = respone_timeout;
  139. once_cmd->cmd.on_respone = on_respone;
  140. once_cmd->next = 0;
  141. //将命令挂靠到主站对象到
  142. if (master->once_cmd_list == 0)
  143. {
  144. master->once_cmd_list = once_cmd;
  145. } else
  146. {
  147. next = master->once_cmd_list;
  148. while (next->next != 0)
  149. {
  150. next = (modbus_master_once_cmd_t *)next->next;
  151. }
  152. next->next = (void *)once_cmd;
  153. }
  154. return once_cmd;
  155. }
  156. return 0;
  157. }
  158. /**
  159. * 获取一条指令,
  160. *
  161. * @author lxz (2019/8/25/周日)
  162. *
  163. * @param master modbus master对象,里面有命令缓冲
  164. *
  165. * @return int
  166. * 返回1表示有需要处理的立即指令,返回2表示有需要处理的周期指令
  167. */
  168. static int modbus_master_get_cmd(modbus_master_t *master)
  169. {
  170. //优先读取单次指令发送
  171. if (master->once_cmd_list != 0)
  172. {
  173. //复制命令
  174. master->current_cmd = master->once_cmd_list->cmd;
  175. //命令归还缓冲
  176. master->once_cmd_list->token = 0;
  177. //保存下一条命令
  178. //if (master->once_cmd_list->next != 0)
  179. {
  180. master->once_cmd_list = (modbus_master_once_cmd_t *)master->once_cmd_list->next;
  181. }
  182. return 1;
  183. }
  184. //选取周期指令
  185. else if (master->period_cmd_list != 0)
  186. {
  187. modbus_master_period_cmd_t *next = master->period_cmd_list;
  188. modbus_master_period_cmd_t *pre = master->period_cmd_list;
  189. while (next != 0)
  190. {
  191. //周期时间到
  192. if (sw_timer_expire(&next->timer))
  193. {
  194. //周期时间到了, 需要进行处理
  195. master->current_cmd = next->cmd;
  196. //将该命令时间重置,并放入尾部,从而保证每个命令有平等进行通讯的机会
  197. sw_timer_start(&next->timer, 0, next->period * 1000);
  198. //如果是第一个命令
  199. if (next == master->period_cmd_list)
  200. {
  201. //当不止一个命令的时候
  202. if (next->next != 0)
  203. {
  204. //对象保存的是下一个指令
  205. master->period_cmd_list = (modbus_master_period_cmd_t *)next->next;
  206. //查找最后一个指令,并将它置于链表尾部
  207. pre = next;
  208. while (next->next != 0)
  209. {
  210. next = next->next;
  211. }
  212. if (next != pre)
  213. {
  214. next->next = pre;
  215. }
  216. pre->next = 0;
  217. }
  218. } else
  219. {
  220. //如果不是最后一个指令
  221. if (next->next != 0)
  222. {
  223. //对象保存的是下一个指令
  224. pre->next = (modbus_master_period_cmd_t *)next->next;
  225. //查找最后一个指令,并将它置于链表尾部
  226. pre = next;
  227. while (next->next != 0)
  228. {
  229. next = (modbus_master_period_cmd_t *)next->next;
  230. }
  231. if (next != pre)
  232. {
  233. next->next = pre;
  234. }
  235. pre->next = 0;
  236. }
  237. }
  238. return 2;
  239. }
  240. //因为是单向列表,必须知道自己上一个指令对象;
  241. pre = next;
  242. next = (modbus_master_period_cmd_t *)next->next;
  243. }
  244. }
  245. return 0;
  246. }
  247. /**
  248. * 创建命令
  249. *
  250. * @author lxz (2019/8/25/周日)
  251. *
  252. * @param master
  253. * @param request
  254. *
  255. * @return int
  256. */
  257. static int modbus_master_create_request(modbus_master_t *master, unsigned char *request)
  258. {
  259. int length = 0;
  260. int request_length = 0;
  261. int number = 0;
  262. modbus_master_cmd_t *cmd = &master->current_cmd;
  263. //写指令
  264. master->send_count++;
  265. switch (cmd->cmd)
  266. {
  267. case 0x01:
  268. case 0x02:
  269. case 0x03:
  270. case 0x04:
  271. //读指令进行统一,不负责特殊指令
  272. request[length++] = cmd->id;
  273. request[length++] = cmd->cmd;
  274. request[length++] = (unsigned char)(cmd->address >> 8);
  275. request[length++] = (unsigned char)(cmd->address);
  276. request[length++] = (unsigned char)(cmd->number >> 8);
  277. request[length++] = (unsigned char)(cmd->number);
  278. if (cmd->cmd == 0x02 || cmd->cmd == 0x01)
  279. {
  280. request_length = 3 + (cmd->number + 7) >> 3;
  281. }
  282. else
  283. {
  284. request_length = 3 + cmd->number * 2;
  285. }
  286. break;
  287. case 0x05:
  288. if (cmd->number == 1)
  289. {
  290. //写单个线圈
  291. request[length++] = cmd->id;
  292. request[length++] = 0x05;
  293. request[length++] = (unsigned char)(cmd->address >> 8);
  294. request[length++] = (unsigned char)(cmd->address);
  295. if (cmd->value[0])
  296. {
  297. request[length++] = 0xff;
  298. request[length++] = 0;
  299. } else
  300. {
  301. request[length++] = 0;
  302. request[length++] = 0;
  303. }
  304. } else
  305. {
  306. //写多个线圈
  307. request[length++] = cmd->id;
  308. request[length++] = 0x0F;
  309. cmd->cmd=0x0F;//命令已经改变 20240513 laz
  310. request[length++] = (unsigned char)(cmd->address >> 8);
  311. request[length++] = (unsigned char)(cmd->address);
  312. request[length++] = (unsigned char)(cmd->number >> 8);
  313. request[length++] = (unsigned char)(cmd->number);
  314. request[length++] = (unsigned char)((cmd->number + 7) >> 3);
  315. memcpy(&request[length], cmd->value, request[length - 1]);
  316. length += request[length - 1];
  317. }
  318. request_length = 6;
  319. break;
  320. case 0x0F:
  321. //写多个线圈
  322. request[length++] = cmd->id;
  323. request[length++] = 0x0F;
  324. request[length++] = (unsigned char)(cmd->address >> 8);
  325. request[length++] = (unsigned char)(cmd->address);
  326. request[length++] = (unsigned char)(cmd->number >> 8);
  327. request[length++] = (unsigned char)(cmd->number);
  328. request[length++] = (unsigned char)((cmd->number + 7) >> 1);
  329. memcpy(&request[length], cmd->value, request[length - 1]);
  330. length += request[length - 1];
  331. request_length = 6;
  332. break;
  333. case 0x06:
  334. if (cmd->number == 1)
  335. {
  336. //写单个数值
  337. request[length++] = cmd->id;
  338. request[length++] = 0x06;
  339. request[length++] = (unsigned char)(cmd->address >> 8);
  340. request[length++] = (unsigned char)(cmd->address);
  341. request[length++] = (unsigned char)(cmd->value[1]);
  342. request[length++] = (unsigned char)(cmd->value[0]);
  343. } else
  344. {
  345. //写多个数值
  346. request[length++] = cmd->id;
  347. request[length++] = 0x10;
  348. cmd->cmd=0x10;//命令已经改变 20240513 laz
  349. request[length++] = (unsigned char)(cmd->address >> 8);
  350. request[length++] = (unsigned char)(cmd->address);
  351. request[length++] = (unsigned char)(cmd->number >> 8);
  352. request[length++] = (unsigned char)(cmd->number);
  353. request[length++] = (unsigned char)(cmd->number * 2);
  354. while (number < cmd->number)
  355. {
  356. request[length++] = cmd->value[number * 2 + 1];
  357. request[length++] = cmd->value[number * 2];
  358. number++;
  359. }
  360. }
  361. request_length = 6;
  362. break;
  363. case 0x10:
  364. //写多个数值
  365. request[length++] = cmd->id;
  366. request[length++] = 0x10;
  367. request[length++] = (unsigned char)(cmd->address >> 8);
  368. request[length++] = (unsigned char)(cmd->address);
  369. request[length++] = (unsigned char)(cmd->number >> 8);
  370. request[length++] = (unsigned char)(cmd->number);
  371. request[length++] = (unsigned char)(cmd->number * 2);
  372. while (number < cmd->number)
  373. {
  374. request[length++] = cmd->value[number * 2 + 1];
  375. request[length++] = cmd->value[number * 2];
  376. number++;
  377. }
  378. request_length = 6;
  379. break;
  380. default:
  381. //默认特殊指令,会忽略地址,将number当做value里面的指令长度
  382. request[length++] = cmd->id;
  383. request[length++] = cmd->cmd;
  384. memcpy(&request[length], cmd->value, cmd->number);
  385. length += cmd->number;
  386. request_length = 6;
  387. break;
  388. }
  389. //具有有效命令的时候,进行编码并返回编码后的命令长度
  390. if (length > 0)
  391. {
  392. modbus_encoder_t *encoder = modbus_encoder_get(master->encoder_type);
  393. length = encoder->encode(request, request, length);
  394. //计算命令需要等待的时候,单位是us
  395. request_length = encoder->length_calc(1,request_length);
  396. master->wait_respone_time = request_length * master->one_byte_time + cmd->respone_timeout * 1000;
  397. return length;
  398. }
  399. return 0;
  400. }
  401. /**
  402. * 处理接收命令
  403. *
  404. * @author lxz (2019/8/26/周一)
  405. *
  406. * @param master
  407. * @param respone
  408. * @param length
  409. */
  410. static char modbus_master_deal_respone(modbus_master_t *master, unsigned char *respone, int length)
  411. {
  412. modbus_master_cmd_t *cmd = &master->current_cmd;
  413. int number = 0;
  414. if (respone[0] != cmd->id)
  415. {
  416. //ID不正确
  417. return 1;
  418. } else if (respone[1] & 0x80)
  419. {
  420. //命令报错
  421. //如果设置了回调函数,调用回调函数
  422. if (cmd->on_respone != 0)
  423. {
  424. cmd->on_respone(&master->current_cmd, respone, length);
  425. }
  426. if ((respone[1] & 0x7F) == cmd->cmd)
  427. {
  428. return 0;
  429. }
  430. else
  431. {
  432. return 2;
  433. }
  434. } else if (respone[1] != cmd->cmd)
  435. {
  436. //错误命令
  437. return 3;
  438. }
  439. master->receive_count++;
  440. //只对通用的几个读值指令进行处理
  441. switch (cmd->cmd)
  442. {
  443. case 0x01:
  444. case 0x02:
  445. memcpy(&cmd->value[number], &respone[3], respone[2]);
  446. break;
  447. case 0x03:
  448. case 0x04:
  449. while (number + 1 < respone[2])
  450. {
  451. cmd->value[number] = respone[3 + number + 1];
  452. cmd->value[number + 1] = respone[3 + number];
  453. number += 2;
  454. }
  455. break;
  456. }
  457. //如果设置了回调函数,回调函数
  458. if (cmd->on_respone != 0)
  459. {
  460. cmd->on_respone(&master->current_cmd, respone, length);
  461. }
  462. return 0;
  463. }
  464. /**
  465. * 主站运行函数
  466. *
  467. * @author lxz (2019/8/25/周日)
  468. *
  469. * @param request 请求缓冲
  470. * @param respone 响应的数据缓冲
  471. * @param length 响应数据长度
  472. *
  473. * @return int
  474. * 生成的请求命令长度,当大于0时,说明request里面有需要进行发送的命令
  475. */
  476. int modbus_master_run(modbus_master_t *master, unsigned char *request, unsigned char *respone, int length)
  477. {
  478. switch (master->step)
  479. {
  480. case 0:
  481. //获取一条指令,优先指令
  482. if (modbus_master_get_cmd(master))
  483. {
  484. master->step++;
  485. master->current_try_time = master->retry + 1;
  486. }
  487. break;
  488. case 1:
  489. //可以重次几次
  490. if (master->current_try_time)
  491. {
  492. master->current_try_time--;
  493. master->step++;
  494. return modbus_master_create_request(master, request);
  495. } else
  496. {
  497. master->step = 0;
  498. }
  499. break;
  500. case 2:
  501. //等待响应时间
  502. master->step++;
  503. sw_timer_start(&master->timer, 0, master->wait_respone_time);
  504. break;
  505. case 3:
  506. if (length > 0)
  507. {
  508. //执行校验,如果校验失败,那就重试,校验成功,处理完等待间隔
  509. modbus_encoder_t *encoder = modbus_encoder_get(master->encoder_type);
  510. length = encoder->decode(respone, respone, length);
  511. if (length > 0)
  512. {
  513. //处理协议,如果返回一些错误,需要重试
  514. if (modbus_master_deal_respone(master,respone,length))
  515. {
  516. master->step++;
  517. }
  518. }
  519. sw_timer_start(&master->timer, 0, master->interval);
  520. master->step++;
  521. } else if (sw_timer_expire(&master->timer))
  522. {
  523. //超时没收到命令,重发
  524. master->step = 1;
  525. }
  526. break;
  527. case 4:
  528. //上次通讯完成,准备下一条指令
  529. if (sw_timer_expire(&master->timer))
  530. {
  531. master->step = 0;
  532. }
  533. break;
  534. case 5:
  535. //上次通讯是错误帧,需要重试
  536. if (sw_timer_expire(&master->timer))
  537. {
  538. master->step = 1;
  539. }
  540. break;
  541. }
  542. return 0;
  543. }