123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- #include "modbus_master.h"
- #include "modbus_encoder.h"
- #include "st_sys.h"
- #include <string.h>
- //周期通讯指令缓冲池
- static modbus_master_period_cmd_t period_cmd_pool[MODBUS_MASTER_PERIOD_POOL_SIZE];
- //单次命令缓冲池
- static modbus_master_once_cmd_t once_cmd_pool[MODBUS_MASTER_ONCE_POOL_SIZE];
- //周期命令池已用计数
- static int period_cmd_count = 0;
- void modbus_master_init(void)
- {
- memset(period_cmd_pool, 0, MODBUS_MASTER_PERIOD_POOL_SIZE);
- memset(once_cmd_pool, 0, MODBUS_MASTER_PERIOD_POOL_SIZE);
- }
- /**
- * 创建一条周期任务
- *
- * @author lxz (2019/8/25/周日)
- *
- * @param group modbus 运行组
- * @param id id
- * @param cmd 命令
- * @param address 地址
- * @param number 长度
- * @param value
- * 要读写的数值,对于写指令,它是数值
- * @param respone_timeout 该指令的等待响应时间
- * @param period
- * 该指令的采集周期,协议会尽可能满足该周期
- * @param on_respone 命令响应时的回调函数
- *
- * @return modbus_master_cmd_t 命令的指针
- */
- modbus_master_period_cmd_t* modbus_master_add_period_cmd(
- modbus_master_t *master,
- unsigned char cmdcode,
- unsigned char id,
- unsigned char cmd,
- unsigned short address,
- unsigned short number,
- unsigned char *value,
- int respone_timeout,
- void (*on_respone)(modbus_master_cmd_t *, unsigned char *, int),
- int period
- )
- {
- if (period_cmd_count < MODBUS_MASTER_PERIOD_POOL_SIZE)
- {
- modbus_master_period_cmd_t *next = 0;
- //获取一个没用过的包
- modbus_master_period_cmd_t *period_cmd = &period_cmd_pool[period_cmd_count];
- period_cmd_count++;
- //初始化命令
- period_cmd->cmd.cmdcode=cmdcode;
- period_cmd->cmd.id = id;
- period_cmd->cmd.cmd = cmd;
- period_cmd->cmd.address = address;
- period_cmd->cmd.number = number;
- period_cmd->cmd.value = value;
- period_cmd->cmd.respone_timeout = respone_timeout;
- period_cmd->cmd.on_respone = on_respone;
- period_cmd->cmd.dir = 0;
- period_cmd->period = period;
- period_cmd->next = 0;
- //将命令挂靠到主站对象到
- if (master->period_cmd_list == 0)
- {
- master->period_cmd_list = period_cmd;
- } else
- {
- next = master->period_cmd_list;
- while (next->next != 0)
- {
- next = (modbus_master_period_cmd_t *)next->next;
- }
- next->next = (void *)period_cmd;
- }
- return period_cmd;
- }
- return 0;
- }
- /**
- * 创建一条单次命令
- *
- * @author lxz (2019/8/25/周日)
- *
- * @param group modbus 运行组
- * @param id id
- * @param cmd 命令
- * @param address 地址
- * @param number 长度
- * @param value
- * 要读写的数值,对于写指令,它是数值
- * @param respone_timeout 该指令的等待响应时间
- * @param period
- * 该指令的采集周期,协议会尽可能满足该周期
- * @param on_respone 命令响应时的回调函数
- * @param dir 方向,0表示读,1表示写
- *
- * @return modbus_master_cmd_t 命令的指针
- */
- modbus_master_once_cmd_t* modbus_master_add_once_cmd(
- modbus_master_t *master,
- unsigned char cmdcode,
- unsigned char id,
- unsigned char cmd,
- unsigned short address,
- unsigned short number,
- unsigned char *value,
- int respone_timeout,
- void (*on_respone)(modbus_master_cmd_t *, unsigned char *, int)
- )
- {
- int index = 0;
- while (index < MODBUS_MASTER_ONCE_POOL_SIZE)
- {
- if (once_cmd_pool[index].token == 0)
- {
- break;
- }
- index++;
- }
- if (index < MODBUS_MASTER_ONCE_POOL_SIZE)
- {
- modbus_master_once_cmd_t *next = 0;
- //获取一个没用过的包
- once_cmd_pool[index].token=1;
- modbus_master_once_cmd_t *once_cmd = &once_cmd_pool[index];
- index++;//period_cmd_count++;(修改2024-0703)
- //初始化命令
- once_cmd->cmd.cmdcode=cmdcode;
- once_cmd->cmd.id = id;
- once_cmd->cmd.cmd = cmd;
- once_cmd->cmd.address = address;
- once_cmd->cmd.number = number;
- once_cmd->cmd.value = value;
- once_cmd->cmd.respone_timeout = respone_timeout;
- once_cmd->cmd.on_respone = on_respone;
-
- once_cmd->next = 0;
- //将命令挂靠到主站对象到
- if (master->once_cmd_list == 0)
- {
- master->once_cmd_list = once_cmd;
- } else
- {
- next = master->once_cmd_list;
- while (next->next != 0)
- {
- next = (modbus_master_once_cmd_t *)next->next;
- }
- next->next = (void *)once_cmd;
- }
- return once_cmd;
- }
- return 0;
- }
- /**
- * 获取一条指令,
- *
- * @author lxz (2019/8/25/周日)
- *
- * @param master modbus master对象,里面有命令缓冲
- *
- * @return int
- * 返回1表示有需要处理的立即指令,返回2表示有需要处理的周期指令
- */
- static int modbus_master_get_cmd(modbus_master_t *master)
- {
- //优先读取单次指令发送
- if (master->once_cmd_list != 0)
- {
- //复制命令
- master->current_cmd = master->once_cmd_list->cmd;
- //命令归还缓冲
- master->once_cmd_list->token = 0;
- //保存下一条命令
- //if (master->once_cmd_list->next != 0)
- {
- master->once_cmd_list = (modbus_master_once_cmd_t *)master->once_cmd_list->next;
- }
- return 1;
- }
- //选取周期指令
- else if (master->period_cmd_list != 0)
- {
- modbus_master_period_cmd_t *next = master->period_cmd_list;
- modbus_master_period_cmd_t *pre = master->period_cmd_list;
- while (next != 0)
- {
- //周期时间到
- if (sw_timer_expire(&next->timer))
- {
- //周期时间到了, 需要进行处理
- master->current_cmd = next->cmd;
- //将该命令时间重置,并放入尾部,从而保证每个命令有平等进行通讯的机会
- sw_timer_start(&next->timer, 0, next->period * 1000);
- //如果是第一个命令
- if (next == master->period_cmd_list)
- {
- //当不止一个命令的时候
- if (next->next != 0)
- {
- //对象保存的是下一个指令
- master->period_cmd_list = (modbus_master_period_cmd_t *)next->next;
- //查找最后一个指令,并将它置于链表尾部
- pre = next;
- while (next->next != 0)
- {
- next = next->next;
- }
- if (next != pre)
- {
- next->next = pre;
- }
- pre->next = 0;
- }
- } else
- {
- //如果不是最后一个指令
- if (next->next != 0)
- {
- //对象保存的是下一个指令
- pre->next = (modbus_master_period_cmd_t *)next->next;
- //查找最后一个指令,并将它置于链表尾部
- pre = next;
- while (next->next != 0)
- {
- next = (modbus_master_period_cmd_t *)next->next;
- }
- if (next != pre)
- {
- next->next = pre;
- }
- pre->next = 0;
- }
- }
- return 2;
- }
- //因为是单向列表,必须知道自己上一个指令对象;
- pre = next;
- next = (modbus_master_period_cmd_t *)next->next;
- }
- }
- return 0;
- }
- /**
- * 创建命令
- *
- * @author lxz (2019/8/25/周日)
- *
- * @param master
- * @param request
- *
- * @return int
- */
- static int modbus_master_create_request(modbus_master_t *master, unsigned char *request)
- {
- int length = 0;
- int request_length = 0;
- int number = 0;
- modbus_master_cmd_t *cmd = &master->current_cmd;
- //写指令
- master->send_count++;
- switch (cmd->cmd)
- {
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- //读指令进行统一,不负责特殊指令
- request[length++] = cmd->id;
- request[length++] = cmd->cmd;
- request[length++] = (unsigned char)(cmd->address >> 8);
- request[length++] = (unsigned char)(cmd->address);
- request[length++] = (unsigned char)(cmd->number >> 8);
- request[length++] = (unsigned char)(cmd->number);
- if (cmd->cmd == 0x02 || cmd->cmd == 0x01)
- {
- request_length = 3 + (cmd->number + 7) >> 3;
- }
- else
- {
- request_length = 3 + cmd->number * 2;
- }
- break;
- case 0x05:
- if (cmd->number == 1)
- {
- //写单个线圈
- request[length++] = cmd->id;
- request[length++] = 0x05;
- request[length++] = (unsigned char)(cmd->address >> 8);
- request[length++] = (unsigned char)(cmd->address);
- if (cmd->value[0])
- {
- request[length++] = 0xff;
- request[length++] = 0;
- } else
- {
- request[length++] = 0;
- request[length++] = 0;
- }
- } else
- {
- //写多个线圈
- request[length++] = cmd->id;
- request[length++] = 0x0F;
- cmd->cmd=0x0F;//命令已经改变 20240513 laz
- request[length++] = (unsigned char)(cmd->address >> 8);
- request[length++] = (unsigned char)(cmd->address);
- request[length++] = (unsigned char)(cmd->number >> 8);
- request[length++] = (unsigned char)(cmd->number);
- request[length++] = (unsigned char)((cmd->number + 7) >> 3);
- memcpy(&request[length], cmd->value, request[length - 1]);
- length += request[length - 1];
- }
- request_length = 6;
- break;
- case 0x0F:
- //写多个线圈
- request[length++] = cmd->id;
- request[length++] = 0x0F;
- request[length++] = (unsigned char)(cmd->address >> 8);
- request[length++] = (unsigned char)(cmd->address);
- request[length++] = (unsigned char)(cmd->number >> 8);
- request[length++] = (unsigned char)(cmd->number);
- request[length++] = (unsigned char)((cmd->number + 7) >> 1);
- memcpy(&request[length], cmd->value, request[length - 1]);
- length += request[length - 1];
- request_length = 6;
- break;
- case 0x06:
- if (cmd->number == 1)
- {
- //写单个数值
- request[length++] = cmd->id;
- request[length++] = 0x06;
- request[length++] = (unsigned char)(cmd->address >> 8);
- request[length++] = (unsigned char)(cmd->address);
- request[length++] = (unsigned char)(cmd->value[1]);
- request[length++] = (unsigned char)(cmd->value[0]);
- } else
- {
- //写多个数值
- request[length++] = cmd->id;
- request[length++] = 0x10;
- cmd->cmd=0x10;//命令已经改变 20240513 laz
- request[length++] = (unsigned char)(cmd->address >> 8);
- request[length++] = (unsigned char)(cmd->address);
- request[length++] = (unsigned char)(cmd->number >> 8);
- request[length++] = (unsigned char)(cmd->number);
- request[length++] = (unsigned char)(cmd->number * 2);
- while (number < cmd->number)
- {
- request[length++] = cmd->value[number * 2 + 1];
- request[length++] = cmd->value[number * 2];
- number++;
- }
- }
- request_length = 6;
- break;
- case 0x10:
- //写多个数值
- request[length++] = cmd->id;
- request[length++] = 0x10;
- request[length++] = (unsigned char)(cmd->address >> 8);
- request[length++] = (unsigned char)(cmd->address);
- request[length++] = (unsigned char)(cmd->number >> 8);
- request[length++] = (unsigned char)(cmd->number);
- request[length++] = (unsigned char)(cmd->number * 2);
- while (number < cmd->number)
- {
- request[length++] = cmd->value[number * 2 + 1];
- request[length++] = cmd->value[number * 2];
- number++;
- }
- request_length = 6;
- break;
- default:
- //默认特殊指令,会忽略地址,将number当做value里面的指令长度
- request[length++] = cmd->id;
- request[length++] = cmd->cmd;
- memcpy(&request[length], cmd->value, cmd->number);
- length += cmd->number;
- request_length = 6;
- break;
- }
- //具有有效命令的时候,进行编码并返回编码后的命令长度
- if (length > 0)
- {
- modbus_encoder_t *encoder = modbus_encoder_get(master->encoder_type);
- length = encoder->encode(request, request, length);
- //计算命令需要等待的时候,单位是us
- request_length = encoder->length_calc(1,request_length);
- master->wait_respone_time = request_length * master->one_byte_time + cmd->respone_timeout * 1000;
- return length;
- }
- return 0;
- }
- /**
- * 处理接收命令
- *
- * @author lxz (2019/8/26/周一)
- *
- * @param master
- * @param respone
- * @param length
- */
- static char modbus_master_deal_respone(modbus_master_t *master, unsigned char *respone, int length)
- {
- modbus_master_cmd_t *cmd = &master->current_cmd;
- int number = 0;
- if (respone[0] != cmd->id)
- {
- //ID不正确
- return 1;
- } else if (respone[1] & 0x80)
- {
- //命令报错
-
- //如果设置了回调函数,调用回调函数
- if (cmd->on_respone != 0)
- {
- cmd->on_respone(&master->current_cmd, respone, length);
- }
- if ((respone[1] & 0x7F) == cmd->cmd)
- {
- return 0;
- }
- else
- {
- return 2;
- }
- } else if (respone[1] != cmd->cmd)
- {
- //错误命令
- return 3;
- }
- master->receive_count++;
- //只对通用的几个读值指令进行处理
- switch (cmd->cmd)
- {
- case 0x01:
- case 0x02:
- memcpy(&cmd->value[number], &respone[3], respone[2]);
- break;
- case 0x03:
- case 0x04:
- while (number + 1 < respone[2])
- {
- cmd->value[number] = respone[3 + number + 1];
- cmd->value[number + 1] = respone[3 + number];
- number += 2;
- }
- break;
- }
- //如果设置了回调函数,回调函数
- if (cmd->on_respone != 0)
- {
- cmd->on_respone(&master->current_cmd, respone, length);
- }
- return 0;
- }
- /**
- * 主站运行函数
- *
- * @author lxz (2019/8/25/周日)
- *
- * @param request 请求缓冲
- * @param respone 响应的数据缓冲
- * @param length 响应数据长度
- *
- * @return int
- * 生成的请求命令长度,当大于0时,说明request里面有需要进行发送的命令
- */
- int modbus_master_run(modbus_master_t *master, unsigned char *request, unsigned char *respone, int length)
- {
- switch (master->step)
- {
- case 0:
- //获取一条指令,优先指令
- if (modbus_master_get_cmd(master))
- {
- master->step++;
- master->current_try_time = master->retry + 1;
- }
- break;
- case 1:
- //可以重次几次
- if (master->current_try_time)
- {
- master->current_try_time--;
- master->step++;
- return modbus_master_create_request(master, request);
- } else
- {
- master->step = 0;
- }
- break;
- case 2:
- //等待响应时间
- master->step++;
- sw_timer_start(&master->timer, 0, master->wait_respone_time);
- break;
- case 3:
- if (length > 0)
- {
- //执行校验,如果校验失败,那就重试,校验成功,处理完等待间隔
- modbus_encoder_t *encoder = modbus_encoder_get(master->encoder_type);
- length = encoder->decode(respone, respone, length);
- if (length > 0)
- {
- //处理协议,如果返回一些错误,需要重试
- if (modbus_master_deal_respone(master,respone,length))
- {
- master->step++;
- }
- }
- sw_timer_start(&master->timer, 0, master->interval);
- master->step++;
- } else if (sw_timer_expire(&master->timer))
- {
- //超时没收到命令,重发
- master->step = 1;
- }
- break;
- case 4:
- //上次通讯完成,准备下一条指令
- if (sw_timer_expire(&master->timer))
- {
- master->step = 0;
- }
- break;
- case 5:
- //上次通讯是错误帧,需要重试
- if (sw_timer_expire(&master->timer))
- {
- master->step = 1;
- }
- break;
- }
- return 0;
- }
|