修复软件包
This commit is contained in:
1 parent
e99dd7a6c3
commit
ccd0acd07b
52 files changed
+229298
-886
No files matched your search
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
#include <packages/qmodbus/inc/modbus.h>
|
||||
#include <packages/qmodbus/inc/modbus_cvt.h>
|
||||
#include <packages/qmodbus/inc/modbus_port.h>
|
||||
* modbus.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-02 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef MB_USING_RAW_PRT
|
||||
static void mb_raw_prt(bool is_send, const u8 *pdata, int dlen)//原始数据打印
|
||||
{
|
||||
MB_PRINTF("%s", is_send ? ">>" : "<<");
|
||||
for (int i=0; i<dlen; i++)
|
||||
{
|
||||
MB_PRINTF("%02X ", pdata[i]);
|
||||
}
|
||||
MB_PRINTF("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
//创建modbus实例, 成功返回实例指针, 失败返回NULL
|
||||
mb_inst_t * mb_create(mb_backend_type_t type, const mb_backend_param_t *param)
|
||||
{
|
||||
MB_ASSERT(param != NULL);
|
||||
|
||||
mb_backend_t *backend = mb_backend_create(type, param);
|
||||
if (backend == NULL)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
mb_inst_t *hinst = malloc(sizeof(mb_inst_t));
|
||||
if (hinst == NULL)
|
||||
{
|
||||
mb_backend_destory(backend);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
hinst->saddr = MB_RTU_ADDR_DEF;
|
||||
hinst->prot = (type == MB_BACKEND_TYPE_RTU) ? MB_PROT_RTU : MB_PROT_TCP;
|
||||
hinst->tid = 0;
|
||||
hinst->backend = backend;
|
||||
#ifdef MB_USING_SLAVE
|
||||
extern const mb_cb_table_t mb_cb_table;
|
||||
hinst->cb = (mb_cb_table_t *)&mb_cb_table;
|
||||
#else
|
||||
hinst->cb = NULL;
|
||||
#endif
|
||||
|
||||
return(hinst);
|
||||
}
|
||||
|
||||
//销毁modbus实例
|
||||
void mb_destory(mb_inst_t *hinst)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
if (hinst->backend != NULL)
|
||||
{
|
||||
mb_backend_destory(hinst->backend);
|
||||
hinst->backend = NULL;
|
||||
}
|
||||
|
||||
free(hinst);
|
||||
}
|
||||
|
||||
//修改从机地址, 默认地址为1
|
||||
void mb_set_slave(mb_inst_t *hinst, u8 saddr)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
hinst->saddr = saddr;
|
||||
}
|
||||
//修改协议, 默认使用与后端类型一致的协议类型
|
||||
void mb_set_prot(mb_inst_t *hinst, mb_prot_t prot)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
hinst->prot = prot;
|
||||
}
|
||||
|
||||
//修改超时时间, 默认应答超时300ms, 字节超时32ms
|
||||
void mb_set_tmo(mb_inst_t *hinst, int ack_tmo_ms, int byte_tmo_ms)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(hinst->backend != NULL);
|
||||
|
||||
mb_backend_config(hinst->backend, ack_tmo_ms, byte_tmo_ms);
|
||||
}
|
||||
|
||||
//建立连接, 成功返回0, 失败返回-1
|
||||
int mb_connect(mb_inst_t *hinst)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(hinst->backend != NULL);
|
||||
|
||||
return(mb_backend_open(hinst->backend));
|
||||
}
|
||||
|
||||
//断开连接, 成功返回0, 失败返回-1
|
||||
int mb_disconn(mb_inst_t *hinst)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(hinst->backend != NULL);
|
||||
|
||||
return(mb_backend_close(hinst->backend));
|
||||
}
|
||||
|
||||
int mb_recv(mb_inst_t *hinst, u8 *buf, int bufsize)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(hinst->backend != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
MB_ASSERT(bufsize > 0);
|
||||
|
||||
int len = mb_backend_read(hinst->backend, buf, bufsize);
|
||||
if (len < 0)//发生错误, 关闭后端
|
||||
{
|
||||
mb_backend_close(hinst->backend);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RAW_PRT
|
||||
if (len > 0)
|
||||
{
|
||||
mb_raw_prt(false, buf, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
int mb_send(mb_inst_t *hinst, u8 *buf, int size)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(hinst->backend != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
MB_ASSERT(size > 0);
|
||||
|
||||
int len = mb_backend_write(hinst->backend, buf, size);
|
||||
if (len < 0)//发生错误, 关闭后端
|
||||
{
|
||||
mb_backend_close(hinst->backend);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RAW_PRT
|
||||
if (len > 0)
|
||||
{
|
||||
mb_raw_prt(true, buf, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
//清空接收缓存, 成功返回0, 失败返回-1
|
||||
int mb_flush(mb_inst_t *hinst)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(hinst->backend != NULL);
|
||||
|
||||
return(mb_backend_flush(hinst->backend));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
#include <packages/qmodbus/inc/modbus_backend.h>
|
||||
#include <packages/qmodbus/inc/modbus_port.h>
|
||||
* modbus_backend.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-02 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#ifdef MB_USING_RTU_BACKEND
|
||||
static const mb_backend_ops_t mb_port_rtu_ops =
|
||||
{
|
||||
mb_port_rtu_open,
|
||||
mb_port_rtu_close,
|
||||
mb_port_rtu_read,
|
||||
mb_port_rtu_write,
|
||||
mb_port_rtu_flush
|
||||
};
|
||||
|
||||
static mb_backend_t * mb_backend_create_rtu(const mb_backend_param_rtu_t *rtu)//创建rtu后端
|
||||
{
|
||||
extern char *strdup(const char *str);
|
||||
mb_backend_t *backend = malloc(sizeof(mb_backend_t));
|
||||
if (backend)
|
||||
{
|
||||
backend->type = MB_BACKEND_TYPE_RTU;
|
||||
backend->param.rtu = *rtu;
|
||||
backend->param.rtu.dev = strdup(rtu->dev);
|
||||
backend->ops = &mb_port_rtu_ops;
|
||||
backend->ack_tmo_ms = MB_BKD_ACK_TMO_MS_DEF;
|
||||
backend->byte_tmo_ms = MB_BKD_BYTE_TMO_MS_DEF;
|
||||
backend->hinst = NULL;
|
||||
}
|
||||
|
||||
return(backend);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_BACKEND
|
||||
static const mb_backend_ops_t mb_port_tcp_ops =
|
||||
{
|
||||
mb_port_tcp_open,
|
||||
mb_port_tcp_close,
|
||||
mb_port_tcp_read,
|
||||
mb_port_tcp_write,
|
||||
mb_port_tcp_flush
|
||||
};
|
||||
|
||||
static mb_backend_t *mb_backend_create_tcp(const mb_backend_param_tcp_t *tcp)//创建tcp后端
|
||||
{
|
||||
extern char *strdup(const char *str);
|
||||
mb_backend_t *backend = calloc(1, sizeof(mb_backend_t));
|
||||
if (backend)
|
||||
{
|
||||
backend->type = MB_BACKEND_TYPE_TCP;
|
||||
backend->param.tcp.host = strdup(tcp->host);
|
||||
backend->param.tcp.port = tcp->port;
|
||||
backend->ops = &mb_port_tcp_ops;
|
||||
backend->ack_tmo_ms = MB_BKD_ACK_TMO_MS_DEF;
|
||||
backend->byte_tmo_ms = MB_BKD_BYTE_TMO_MS_DEF;
|
||||
backend->hinst = NULL;
|
||||
}
|
||||
|
||||
return(backend);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_SOCK_BACKEND
|
||||
static const mb_backend_ops_t mb_port_sock_ops =
|
||||
{
|
||||
NULL,
|
||||
mb_port_tcp_close,
|
||||
mb_port_tcp_read,
|
||||
mb_port_tcp_write,
|
||||
mb_port_tcp_flush
|
||||
};
|
||||
|
||||
static mb_backend_t *mb_backend_create_sock(const mb_backend_param_sock_t *sock)//创建sock后端
|
||||
{
|
||||
mb_backend_t *backend = calloc(1, sizeof(mb_backend_t));
|
||||
if (backend)
|
||||
{
|
||||
backend->type = MB_BACKEND_TYPE_SOCK;
|
||||
backend->param.sock.fd = sock->fd;
|
||||
backend->ops = &mb_port_sock_ops;
|
||||
backend->ack_tmo_ms = MB_BKD_ACK_TMO_MS_DEF;
|
||||
backend->byte_tmo_ms = MB_BKD_BYTE_TMO_MS_DEF;
|
||||
backend->hinst = (void *)(sock->fd);
|
||||
}
|
||||
|
||||
return(backend);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
mb_backend_t *mb_backend_create(mb_backend_type_t type, const mb_backend_param_t *param)//创建后端, 成功返回后端指针, 失败返回NULL
|
||||
{
|
||||
mb_backend_t *backend = NULL;
|
||||
switch(type)
|
||||
{
|
||||
#ifdef MB_USING_RTU_BACKEND
|
||||
case MB_BACKEND_TYPE_RTU :
|
||||
backend = mb_backend_create_rtu(&(param->rtu));
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_BACKEND
|
||||
case MB_BACKEND_TYPE_TCP :
|
||||
backend = mb_backend_create_tcp(&(param->tcp));
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_SOCK_BACKEND
|
||||
case MB_BACKEND_TYPE_SOCK :
|
||||
backend = mb_backend_create_sock(&(param->sock));
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(backend);
|
||||
}
|
||||
|
||||
void mb_backend_destory(mb_backend_t *backend)//销毁后端
|
||||
{
|
||||
if (backend == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mb_backend_close(backend);
|
||||
|
||||
switch(backend->type)
|
||||
{
|
||||
#ifdef MB_USING_RTU_BACKEND
|
||||
case MB_BACKEND_TYPE_RTU :
|
||||
if (backend->param.rtu.dev)
|
||||
{
|
||||
free(backend->param.rtu.dev);
|
||||
backend->param.rtu.dev = NULL;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_BACKEND
|
||||
case MB_BACKEND_TYPE_TCP :
|
||||
if (backend->param.tcp.host)
|
||||
{
|
||||
free(backend->param.tcp.host);
|
||||
backend->param.tcp.host = NULL;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(backend);
|
||||
}
|
||||
|
||||
int mb_backend_open(mb_backend_t *backend)//打开后端, 成功返回0, 错误返回-1
|
||||
{
|
||||
if (backend == NULL)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (backend->hinst != NULL)//已打开
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
if ((backend->ops == NULL) || (backend->ops->open == NULL))
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
backend->hinst = backend->ops->open((void *)&(backend->param));
|
||||
if (backend->hinst == NULL)//打开失败
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int mb_backend_close(mb_backend_t *backend)//关闭后端, 成功返回0, 错误返回-1
|
||||
{
|
||||
if (backend == NULL)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (backend->hinst == NULL)//已关闭
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
if ((backend->ops == NULL) || (backend->ops->close == NULL))
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (backend->ops->close(backend->hinst) != 0)//关闭失败
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
backend->hinst = NULL;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int mb_backend_config(mb_backend_t *backend, int ack_tmo_ms, int byte_tmo_ms)//配置后端超时参数, 成功返回0, 错误返回-1
|
||||
{
|
||||
if (backend == NULL)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
backend->ack_tmo_ms = ack_tmo_ms;
|
||||
backend->byte_tmo_ms = byte_tmo_ms;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int mb_backend_read(mb_backend_t *backend, u8 *buf, int bufsize)//从后端读数据, 返回读取到数据长度, 0表示超时, 错误返回-1
|
||||
{
|
||||
if ((backend == NULL) || (buf == NULL) || (bufsize <= 0))
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (backend->hinst == NULL)//未打开
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if ((backend->ops == NULL) || (backend->ops->read == NULL))
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
int pos = 0;
|
||||
long long told_ms = mb_port_get_ms();
|
||||
while(pos < bufsize)
|
||||
{
|
||||
int len = backend->ops->read(backend->hinst, buf + pos, bufsize - pos);
|
||||
if (len < 0)//发生错误
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (len > 0)//读到数据
|
||||
{
|
||||
told_ms = mb_port_get_ms();
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
int tmo_ms = mb_port_get_ms() - told_ms;
|
||||
if (pos)//已有数据接收到, 则检查字节超时
|
||||
{
|
||||
if (tmo_ms > backend->byte_tmo_ms)//字节超时了
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else//未收到过数据, 则检查应答超时
|
||||
{
|
||||
if (tmo_ms > backend->ack_tmo_ms)//应答超时了
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
mb_port_delay_ms(2);
|
||||
}
|
||||
return(pos);
|
||||
}
|
||||
|
||||
int mb_backend_write(mb_backend_t *backend, u8 *buf, int size)//向后端写数据, 返回已发送数据长度, 错误返回-1
|
||||
{
|
||||
if ((backend == NULL) || (buf == NULL) || (size <= 0))
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (backend->hinst == NULL)//未打开
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if ((backend->ops == NULL) || (backend->ops->write == NULL))
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
return(backend->ops->write(backend->hinst, buf, size));
|
||||
}
|
||||
|
||||
int mb_backend_flush(mb_backend_t *backend)//清空后端接收缓存, 成功返回0, 错误返回-1
|
||||
{
|
||||
if (backend == NULL)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (backend->hinst == NULL)//未打开
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if ((backend->ops == NULL) || (backend->ops->flush == NULL))
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
return(backend->ops->flush(backend->hinst));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* modbus_crc.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-02 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <packages/qmodbus/inc/modbus_crc.h>
|
||||
|
||||
static const u16 mb_crc_table[] = {
|
||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
|
||||
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
|
||||
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
|
||||
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
|
||||
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
|
||||
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
|
||||
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
|
||||
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
|
||||
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
|
||||
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
|
||||
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
|
||||
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
|
||||
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
|
||||
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
|
||||
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
|
||||
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
|
||||
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
|
||||
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
|
||||
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
|
||||
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
|
||||
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
|
||||
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
|
||||
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
|
||||
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
|
||||
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
|
||||
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
|
||||
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
|
||||
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
|
||||
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
|
||||
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
|
||||
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
|
||||
};
|
||||
|
||||
u16 mb_crc_cyc_cal(u16 init, const u8 *pdata, int len)
|
||||
{
|
||||
u16 crc = init;
|
||||
for(int i=0; i<len; i++)
|
||||
{
|
||||
int idx = ((u8)crc) ^ (*pdata++);
|
||||
crc = (crc>>8) ^ mb_crc_table[idx];
|
||||
}
|
||||
|
||||
return(crc);
|
||||
}
|
||||
|
||||
u16 mb_crc_cal(const u8 *pdata, int len)
|
||||
{
|
||||
return(mb_crc_cyc_cal(MB_CRC_INIT_VOL, pdata, len));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* modbus_cvt.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-01 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <packages/qmodbus/inc/modbus_cvt.h>
|
||||
|
||||
int mb_cvt_u8_put(u8 *buf, u8 val)
|
||||
{
|
||||
*buf = val;
|
||||
return(1);
|
||||
}
|
||||
|
||||
int mb_cvt_u8_get(const u8 *buf, u8 *pval)
|
||||
{
|
||||
*pval = *buf;
|
||||
return(1);
|
||||
}
|
||||
|
||||
int mb_cvt_u16_put(u8 *buf, u16 val)
|
||||
{
|
||||
u8 *p = buf;
|
||||
*p++ = (val >> 8);
|
||||
*p++ = val;
|
||||
return(2);
|
||||
}
|
||||
|
||||
int mb_cvt_u16_get(const u8 *buf, u16 *pval)
|
||||
{
|
||||
u8 *p = (u8 *)buf;
|
||||
u16 uval = *p++;
|
||||
uval <<= 8;
|
||||
uval += *p++;
|
||||
*pval = uval;
|
||||
return(2);
|
||||
}
|
||||
|
||||
int mb_cvt_u32_put(u8 *buf, u32 val)
|
||||
{
|
||||
u8 *p = buf;
|
||||
*p++ = (val >> 24);
|
||||
*p++ = (val >> 16);
|
||||
*p++ = (val >> 8);
|
||||
*p++ = val;
|
||||
return(4);
|
||||
}
|
||||
|
||||
int mb_cvt_u32_get(const u8 *buf, u32 *pval)
|
||||
{
|
||||
u8 *p = (u8 *)buf;
|
||||
u32 uval = *p++;
|
||||
uval <<= 8;
|
||||
uval += *p++;
|
||||
uval <<= 8;
|
||||
uval += *p++;
|
||||
uval <<= 8;
|
||||
uval += *p++;
|
||||
*pval = uval;
|
||||
return(4);
|
||||
}
|
||||
|
||||
int mb_cvt_f32_put(u8 *buf, f32 val)
|
||||
{
|
||||
float fval = val;
|
||||
u32 *puv = (u32 *)&fval;
|
||||
return(mb_cvt_u32_put(buf, *puv));
|
||||
}
|
||||
|
||||
int mb_cvt_f32_get(const u8 *buf, f32 *pval)
|
||||
{
|
||||
return(mb_cvt_u32_get(buf, (u32 *)pval));
|
||||
}
|
||||
|
||||
u8 mb_bitmap_get(const u8 *pbits, int idx)//从位表中读指定索引的位
|
||||
{
|
||||
return(((pbits[idx/8] & (1 << (idx % 8))) != 0) ? 1 : 0);
|
||||
}
|
||||
|
||||
void mb_bitmap_set(u8 *pbits, int idx, u8 bit)//向位表中写指定索引的位
|
||||
{
|
||||
if (bit)
|
||||
{
|
||||
pbits[idx/8] |= (1 << (idx % 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
pbits[idx/8] &= ~(1 << (idx % 8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,802 @@
|
||||
/*
|
||||
#include <packages/qmodbus/inc/modbus.h>
|
||||
#include <packages/qmodbus/inc/modbus_cvt.h>
|
||||
#include <packages/qmodbus/inc/modbus_port.h>
|
||||
* modbus_master.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-02 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef MB_USING_MASTER
|
||||
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
static int mb_read_req_rtu(mb_inst_t *hinst, u8 func, u16 addr, int nb, u8 *pdata)
|
||||
{
|
||||
mb_rtu_frm_t frm;
|
||||
frm.saddr = hinst->saddr;
|
||||
frm.pdu.rd_req.fc = func;
|
||||
frm.pdu.rd_req.addr = addr;
|
||||
frm.pdu.rd_req.nb = nb;
|
||||
|
||||
int flen = mb_rtu_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_rtu_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.saddr != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
int dlen = frm.pdu.rd_rsp.dlen;
|
||||
memcpy(pdata, frm.pdu.rd_rsp.pdata, dlen);
|
||||
|
||||
return(dlen);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
static int mb_read_req_tcp(mb_inst_t *hinst, u8 func, u16 addr, int nb, u8 *pdata)
|
||||
{
|
||||
mb_tcp_frm_t frm;
|
||||
hinst->tid++;
|
||||
frm.mbap.tid = hinst->tid;
|
||||
frm.mbap.pid = MB_TCP_MBAP_PID;
|
||||
frm.mbap.did = hinst->saddr;
|
||||
frm.pdu.rd_req.fc = func;
|
||||
frm.pdu.rd_req.addr = addr;
|
||||
frm.pdu.rd_req.nb = nb;
|
||||
|
||||
int flen = mb_tcp_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_tcp_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len < 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.mbap.did != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_MBAP_CHK
|
||||
if ((frm.mbap.tid != hinst->tid) || (frm.mbap.pid != MB_TCP_MBAP_PID) || (frm.mbap.dlen != (pdu_len + 1)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
int dlen = frm.pdu.rd_rsp.dlen;
|
||||
memcpy(pdata, frm.pdu.rd_rsp.pdata, dlen);
|
||||
|
||||
return(dlen);
|
||||
}
|
||||
#endif
|
||||
|
||||
//读请求, 功能码和数据由用户确定, 成功返回应答数据长度, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_read_req(mb_inst_t *hinst, u8 func, u16 addr, int nb, u8 *pdata)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(pdata != NULL);
|
||||
MB_ASSERT(nb > 0);
|
||||
|
||||
switch (hinst->prot)
|
||||
{
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
case MB_PROT_RTU :
|
||||
return(mb_read_req_rtu(hinst, func, addr, nb, pdata));
|
||||
#endif
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
case MB_PROT_TCP :
|
||||
return(mb_read_req_tcp(hinst, func, addr, nb, pdata));
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
static int mb_write_req_rtu(mb_inst_t *hinst, u8 func, u16 addr, int nb, const u8 *pdata, int dlen)
|
||||
{
|
||||
mb_rtu_frm_t frm;
|
||||
frm.saddr = hinst->saddr;
|
||||
frm.pdu.wr_req.fc = func;
|
||||
frm.pdu.wr_req.addr = addr;
|
||||
frm.pdu.wr_req.nb = nb;
|
||||
frm.pdu.wr_req.dlen = dlen;
|
||||
frm.pdu.wr_req.pdata = (u8 *)pdata;
|
||||
|
||||
int flen = mb_rtu_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_rtu_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.saddr != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
return(frm.pdu.wr_rsp.nb);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
static int mb_write_req_tcp(mb_inst_t *hinst, u8 func, u16 addr, int nb, const u8 *pdata, int dlen)
|
||||
{
|
||||
mb_tcp_frm_t frm;
|
||||
hinst->tid++;
|
||||
frm.mbap.tid = hinst->tid;
|
||||
frm.mbap.pid = MB_TCP_MBAP_PID;
|
||||
frm.mbap.did = hinst->saddr;
|
||||
frm.pdu.wr_req.fc = func;
|
||||
frm.pdu.wr_req.addr = addr;
|
||||
frm.pdu.wr_req.nb = nb;
|
||||
frm.pdu.wr_req.dlen = dlen;
|
||||
frm.pdu.wr_req.pdata = (u8 *)pdata;
|
||||
|
||||
int flen = mb_tcp_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_tcp_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len < 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.mbap.did != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_MBAP_CHK
|
||||
if ((frm.mbap.tid != hinst->tid) || (frm.mbap.pid != MB_TCP_MBAP_PID) || (frm.mbap.dlen != (pdu_len + 1)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
return(frm.pdu.wr_rsp.nb);
|
||||
}
|
||||
#endif
|
||||
|
||||
//写请求, 功能码和数据由用户确定, 成功返回写入数量, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_write_req(mb_inst_t *hinst, u8 func, u16 addr, int nb, const u8 *pdata, int dlen)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(pdata != NULL);
|
||||
MB_ASSERT(nb > 0);
|
||||
MB_ASSERT(dlen > 0);
|
||||
|
||||
switch (hinst->prot)
|
||||
{
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
case MB_PROT_RTU :
|
||||
return(mb_write_req_rtu(hinst, func, addr, nb, pdata, dlen));
|
||||
#endif
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
case MB_PROT_TCP :
|
||||
return(mb_write_req_tcp(hinst, func, addr, nb, pdata, dlen));
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
//读多个线圈, 功能码-0x01, 成功返回读取位数量, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_read_bits(mb_inst_t *hinst, u16 addr, int nb, u8 *pbits)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(pbits != NULL);
|
||||
MB_ASSERT(nb > 0);
|
||||
|
||||
int dlen = mb_read_req(hinst, MODBUS_FC_READ_COILS, addr, nb, pbits);
|
||||
return((dlen != (nb + 7) / 8) ? dlen : nb);
|
||||
}
|
||||
|
||||
//读多个离散量输入, 功能码-0x02, 成功返回读取位数量, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_read_input_bits(mb_inst_t *hinst, u16 addr, int nb, u8 *pbits)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(pbits != NULL);
|
||||
MB_ASSERT(nb > 0);
|
||||
|
||||
int dlen = mb_read_req(hinst, MODBUS_FC_READ_DISCRETE_INPUTS, addr, nb, pbits);
|
||||
return((dlen != (nb + 7) / 8) ? dlen : nb);
|
||||
}
|
||||
|
||||
//读多个保持寄存器, 功能码-0x03, 成功返回读取寄存器数量, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_read_regs(mb_inst_t *hinst, u16 addr, int nb, u16 *pregs)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(pregs != NULL);
|
||||
MB_ASSERT(nb > 0);
|
||||
|
||||
int dlen = mb_read_req(hinst, MODBUS_FC_READ_HOLDING_REGISTERS, addr, nb, hinst->datas);
|
||||
if (dlen <= 0)
|
||||
{
|
||||
return(dlen);
|
||||
}
|
||||
|
||||
if (dlen != (nb * 2))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = hinst->datas;
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
p += mb_cvt_u16_get(p, pregs + i);
|
||||
}
|
||||
|
||||
return(nb);
|
||||
}
|
||||
|
||||
//读多个输入寄存器, 功能码-0x04, 成功返回读取寄存器数量, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_read_input_regs(mb_inst_t *hinst, u16 addr, int nb, u16 *pregs)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(pregs != NULL);
|
||||
MB_ASSERT(nb > 0);
|
||||
|
||||
int dlen = mb_read_req(hinst, MODBUS_FC_READ_INPUT_REGISTERS, addr, nb, hinst->datas);
|
||||
if (dlen <= 0)
|
||||
{
|
||||
return(dlen);
|
||||
}
|
||||
|
||||
if (dlen != (nb * 2))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = hinst->datas;
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
p += mb_cvt_u16_get(p, pregs + i);
|
||||
}
|
||||
|
||||
return(nb);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
static int mb_write_single_rtu(mb_inst_t *hinst, u8 func, u16 addr, u16 val)
|
||||
{
|
||||
mb_rtu_frm_t frm;
|
||||
frm.saddr = hinst->saddr;
|
||||
frm.pdu.wr_single.fc = func;
|
||||
frm.pdu.wr_single.addr = addr;
|
||||
frm.pdu.wr_single.val = val;
|
||||
|
||||
int flen = mb_rtu_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_rtu_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.saddr != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
static int mb_write_single_tcp(mb_inst_t *hinst, u8 func, u16 addr, u16 val)
|
||||
{
|
||||
mb_tcp_frm_t frm;
|
||||
hinst->tid++;
|
||||
frm.mbap.tid = hinst->tid;
|
||||
frm.mbap.pid = MB_TCP_MBAP_PID;
|
||||
frm.mbap.did = hinst->saddr;
|
||||
frm.pdu.wr_single.fc = func;
|
||||
frm.pdu.wr_single.addr = addr;
|
||||
frm.pdu.wr_single.val = val;
|
||||
|
||||
int flen = mb_tcp_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_tcp_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len < 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.mbap.did != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_MBAP_CHK
|
||||
if ((frm.mbap.tid != hinst->tid) || (frm.mbap.pid != MB_TCP_MBAP_PID) || (frm.mbap.dlen != (pdu_len + 1)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mb_write_single(mb_inst_t *hinst, u8 func, u16 addr, u16 val)
|
||||
{
|
||||
switch (hinst->prot)
|
||||
{
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
case MB_PROT_RTU :
|
||||
return(mb_write_single_rtu(hinst, func, addr, val));
|
||||
#endif
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
case MB_PROT_TCP :
|
||||
return(mb_write_single_tcp(hinst, func, addr, val));
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
//写单个线圈, 功能码-0x05, 成功返回1, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_write_bit(mb_inst_t *hinst, u16 addr, u8 bit)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
u16 val = bit ? 0xFF00 : 0x0000;
|
||||
return(mb_write_single(hinst, MODBUS_FC_WRITE_SINGLE_COIL, addr, val));
|
||||
}
|
||||
|
||||
//写单个寄存器, 功能码-0x06, 成功返回1, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_write_reg(mb_inst_t *hinst, u16 addr, u16 val)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
return(mb_write_single(hinst, MODBUS_FC_WRITE_SINGLE_REGISTER, addr, val));
|
||||
}
|
||||
|
||||
//写多个线圈, 功能码-0x0F, 成功返回写位数量, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_write_bits(mb_inst_t *hinst, u16 addr, int nb, const u8 *pbits)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(pbits != NULL);
|
||||
MB_ASSERT(nb > 0);
|
||||
|
||||
int dlen = (nb + 7) / 8;
|
||||
return(mb_write_req(hinst, MODBUS_FC_WRITE_MULTIPLE_COILS, addr, nb, pbits, dlen));
|
||||
}
|
||||
|
||||
//写多个寄存器, 功能码-0x10, 成功返回写寄存器数量, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_write_regs(mb_inst_t *hinst, u16 addr, int nb, const u16 *pregs)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(pregs != NULL);
|
||||
MB_ASSERT(nb > 0);
|
||||
|
||||
u8 *p = hinst->datas;
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
p += mb_cvt_u16_put(p, pregs[i]);
|
||||
}
|
||||
int dlen = nb * 2;
|
||||
return(mb_write_req(hinst, MODBUS_FC_WRITE_MULTIPLE_REGISTERS, addr, nb, hinst->datas, dlen));
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
static int mb_mask_write_rtu(mb_inst_t *hinst, u16 addr, u16 val_and, u16 val_or)
|
||||
{
|
||||
mb_rtu_frm_t frm;
|
||||
frm.saddr = hinst->saddr;
|
||||
frm.pdu.mask_wr.fc = MODBUS_FC_MASK_WRITE_REGISTER;
|
||||
frm.pdu.mask_wr.addr = addr;
|
||||
frm.pdu.mask_wr.val_and = val_and;
|
||||
frm.pdu.mask_wr.val_or = val_or;
|
||||
|
||||
int flen = mb_rtu_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_rtu_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.saddr != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
static int mb_mask_write_tcp(mb_inst_t *hinst, u16 addr, u16 val_and, u16 val_or)
|
||||
{
|
||||
mb_tcp_frm_t frm;
|
||||
hinst->tid++;
|
||||
frm.mbap.tid = hinst->tid;
|
||||
frm.mbap.pid = MB_TCP_MBAP_PID;
|
||||
frm.mbap.did = hinst->saddr;
|
||||
frm.pdu.mask_wr.fc = MODBUS_FC_MASK_WRITE_REGISTER;
|
||||
frm.pdu.mask_wr.addr = addr;
|
||||
frm.pdu.mask_wr.val_and = val_and;
|
||||
frm.pdu.mask_wr.val_or = val_or;
|
||||
|
||||
int flen = mb_tcp_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_tcp_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len < 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.mbap.did != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_MBAP_CHK
|
||||
if ((frm.mbap.tid != hinst->tid) || (frm.mbap.pid != MB_TCP_MBAP_PID) || (frm.mbap.dlen != (pdu_len + 1)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
//屏蔽写寄存器, 功能码-0x16, 成功返回1, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_mask_write_reg(mb_inst_t *hinst, u16 addr, u16 mask_and, u16 mask_or)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
switch (hinst->prot)
|
||||
{
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
case MB_PROT_RTU :
|
||||
return(mb_mask_write_rtu(hinst, addr, mask_and, mask_or));
|
||||
#endif
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
case MB_PROT_TCP :
|
||||
return(mb_mask_write_tcp(hinst, addr, mask_and, mask_or));
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
static int mb_write_and_read_regs_rtu(mb_inst_t *hinst, u16 wr_addr, int wr_nb, const u16 *p_wr_regs,
|
||||
u16 rd_addr, int rd_nb, u16 *p_rd_regs)
|
||||
{
|
||||
u8 *p = hinst->datas;
|
||||
for (int i=0; i<wr_nb; i++)
|
||||
{
|
||||
p += mb_cvt_u16_put(p, p_wr_regs[i]);
|
||||
}
|
||||
|
||||
mb_rtu_frm_t frm;
|
||||
frm.saddr = hinst->saddr;
|
||||
frm.pdu.wr_rd_req.fc = MODBUS_FC_WRITE_AND_READ_REGISTERS;
|
||||
frm.pdu.wr_rd_req.rd_addr = rd_addr;
|
||||
frm.pdu.wr_rd_req.rd_nb = rd_nb;
|
||||
frm.pdu.wr_rd_req.wr_addr = wr_addr;
|
||||
frm.pdu.wr_rd_req.wr_nb = wr_nb;
|
||||
frm.pdu.wr_rd_req.dlen = wr_nb * 2;
|
||||
frm.pdu.wr_rd_req.pdata = hinst->datas;
|
||||
|
||||
int flen = mb_rtu_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_rtu_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.saddr != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
int dlen = frm.pdu.rd_rsp.dlen;
|
||||
if (dlen != (rd_nb * 2))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
p = frm.pdu.rd_rsp.pdata;
|
||||
for (int i=0; i<rd_nb; i++)
|
||||
{
|
||||
p += mb_cvt_u16_get(p, p_rd_regs + i);
|
||||
}
|
||||
|
||||
return(rd_nb);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
static int mb_write_and_read_regs_tcp(mb_inst_t *hinst, u16 wr_addr, int wr_nb, const u16 *p_wr_regs,
|
||||
u16 rd_addr, int rd_nb, u16 *p_rd_regs)
|
||||
{
|
||||
u8 *p = hinst->datas;
|
||||
for (int i=0; i<wr_nb; i++)
|
||||
{
|
||||
p += mb_cvt_u16_put(p, p_wr_regs[i]);
|
||||
}
|
||||
|
||||
mb_tcp_frm_t frm;
|
||||
hinst->tid++;
|
||||
frm.mbap.tid = hinst->tid;
|
||||
frm.mbap.pid = MB_TCP_MBAP_PID;
|
||||
frm.mbap.did = hinst->saddr;
|
||||
frm.pdu.wr_rd_req.fc = MODBUS_FC_WRITE_AND_READ_REGISTERS;
|
||||
frm.pdu.wr_rd_req.rd_addr = rd_addr;
|
||||
frm.pdu.wr_rd_req.rd_nb = rd_nb;
|
||||
frm.pdu.wr_rd_req.wr_addr = wr_addr;
|
||||
frm.pdu.wr_rd_req.wr_nb = wr_nb;
|
||||
frm.pdu.wr_rd_req.dlen = wr_nb * 2;
|
||||
frm.pdu.wr_rd_req.pdata = hinst->datas;
|
||||
|
||||
int flen = mb_tcp_frm_make(hinst->buf, (void *)&frm, MB_PDU_TYPE_REQ);
|
||||
int slen = mb_send(hinst, hinst->buf, flen);
|
||||
if (slen != flen)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int pdu_len = mb_tcp_frm_parse(hinst->buf, rlen, &frm, MB_PDU_TYPE_RSP);
|
||||
if (pdu_len < 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.mbap.did != hinst->saddr)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_MBAP_CHK
|
||||
if ((frm.mbap.tid != hinst->tid) || (frm.mbap.pid != MB_TCP_MBAP_PID) || (frm.mbap.dlen != (pdu_len + 1)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MODBUS_FC_EXCEPT_CHK(frm.pdu.fc))//是异常应答
|
||||
{
|
||||
return(-(int)frm.pdu.exc.ec);
|
||||
}
|
||||
|
||||
int dlen = frm.pdu.rd_rsp.dlen;
|
||||
if (dlen != (rd_nb * 2))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
p = frm.pdu.rd_rsp.pdata;
|
||||
for (int i=0; i<rd_nb; i++)
|
||||
{
|
||||
p += mb_cvt_u16_get(p, p_rd_regs + i);
|
||||
}
|
||||
|
||||
return(rd_nb);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
//读/写多个寄存器, 功能码-0x17, 成功返回读取寄存器数量, 异常应答返回负值错误码, 其它错误返回0
|
||||
int mb_write_and_read_regs(mb_inst_t *hinst, u16 wr_addr, int wr_nb, const u16 *p_wr_regs,
|
||||
u16 rd_addr, int rd_nb, u16 *p_rd_regs)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(p_wr_regs != NULL);
|
||||
MB_ASSERT(p_rd_regs != NULL);
|
||||
MB_ASSERT(wr_nb > 0);
|
||||
MB_ASSERT(rd_nb > 0);
|
||||
|
||||
switch (hinst->prot)
|
||||
{
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
case MB_PROT_RTU :
|
||||
return(mb_write_and_read_regs_rtu(hinst, wr_addr, wr_nb, p_wr_regs, rd_addr, rd_nb, p_rd_regs));
|
||||
#endif
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
case MB_PROT_TCP :
|
||||
return(mb_write_and_read_regs_tcp(hinst, wr_addr, wr_nb, p_wr_regs, rd_addr, rd_nb, p_rd_regs));
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
#include <packages/qmodbus/inc/modbus_cvt.h>
|
||||
#include <packages/qmodbus/inc/modbus_pdu.h>
|
||||
* modbus_pdu.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-01 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static int mb_pdu_except_make(u8 *buf, const mb_pdu_except_t *exc)
|
||||
{
|
||||
u8 *p = buf;
|
||||
|
||||
p += mb_cvt_u8_put(p, exc->fc);
|
||||
p += mb_cvt_u8_put(p, exc->ec);
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_except_parse(const u8 *buf, int len, mb_pdu_except_t *exc)
|
||||
{
|
||||
if (len < 2)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
|
||||
p += mb_cvt_u8_get(p, &(exc->fc));
|
||||
p += mb_cvt_u8_get(p, &(exc->ec));
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_rd_req_make(u8 *buf, const mb_pdu_rd_req_t *rd_req)
|
||||
{
|
||||
u8 *p = buf;
|
||||
|
||||
p += mb_cvt_u8_put(p, rd_req->fc);
|
||||
p += mb_cvt_u16_put(p, rd_req->addr);
|
||||
p += mb_cvt_u16_put(p, rd_req->nb);
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_rd_req_parse(const u8 *buf, int len, mb_pdu_rd_req_t *rd_req)
|
||||
{
|
||||
if (len < 5)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
|
||||
p += mb_cvt_u8_get(p, &(rd_req->fc));
|
||||
p += mb_cvt_u16_get(p, &(rd_req->addr));
|
||||
p += mb_cvt_u16_get(p, &(rd_req->nb));
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_rd_rsp_make(u8 *buf, const mb_pdu_rd_rsp_t *rd_rsp)
|
||||
{
|
||||
u8 *p = buf;
|
||||
|
||||
p += mb_cvt_u8_put(p, rd_rsp->fc);
|
||||
p += mb_cvt_u8_put(p, rd_rsp->dlen);
|
||||
memcpy(p, rd_rsp->pdata, rd_rsp->dlen);
|
||||
p += rd_rsp->dlen;
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_rd_rsp_parse(const u8 *buf, int len, mb_pdu_rd_rsp_t *rd_rsp)
|
||||
{
|
||||
if (len < 3)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
|
||||
p += mb_cvt_u8_get(p, &(rd_rsp->fc));
|
||||
p += mb_cvt_u8_get(p, &(rd_rsp->dlen));
|
||||
rd_rsp->pdata = p;
|
||||
p += rd_rsp->dlen;
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_wr_single_make(u8 *buf, const mb_pdu_wr_single_t *wr_single)
|
||||
{
|
||||
u8 *p = buf;
|
||||
|
||||
p += mb_cvt_u8_put(p, wr_single->fc);
|
||||
p += mb_cvt_u16_put(p, wr_single->addr);
|
||||
p += mb_cvt_u16_put(p, wr_single->val);
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_wr_single_parse(const u8 *buf, int len, mb_pdu_wr_single_t *wr_single)
|
||||
{
|
||||
if (len < 5)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
|
||||
p += mb_cvt_u8_get(p, &(wr_single->fc));
|
||||
p += mb_cvt_u16_get(p, &(wr_single->addr));
|
||||
p += mb_cvt_u16_get(p, &(wr_single->val));
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_wr_req_make(u8 *buf, const mb_pdu_wr_req_t *wr_req)
|
||||
{
|
||||
u8 *p = buf;
|
||||
|
||||
p += mb_cvt_u8_put(p, wr_req->fc);
|
||||
p += mb_cvt_u16_put(p, wr_req->addr);
|
||||
p += mb_cvt_u16_put(p, wr_req->nb);
|
||||
p += mb_cvt_u8_put(p, wr_req->dlen);
|
||||
memcpy(p, wr_req->pdata, wr_req->dlen);
|
||||
p += wr_req->dlen;
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_wr_req_parse(const u8 *buf, int len, mb_pdu_wr_req_t *wr_req)
|
||||
{
|
||||
if (len < 7)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
|
||||
p += mb_cvt_u8_get(p, &(wr_req->fc));
|
||||
p += mb_cvt_u16_get(p, &(wr_req->addr));
|
||||
p += mb_cvt_u16_get(p, &(wr_req->nb));
|
||||
p += mb_cvt_u8_get(p, &(wr_req->dlen));
|
||||
wr_req->pdata = p;
|
||||
p += wr_req->dlen;
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_wr_rsp_make(u8 *buf, const mb_pdu_wr_rsp_t *wr_rsp)
|
||||
{
|
||||
u8 *p = buf;
|
||||
|
||||
p += mb_cvt_u8_put(p, wr_rsp->fc);
|
||||
p += mb_cvt_u16_put(p, wr_rsp->addr);
|
||||
p += mb_cvt_u16_put(p, wr_rsp->nb);
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_wr_rsp_parse(const u8 *buf, int len, mb_pdu_wr_rsp_t *wr_rsp)
|
||||
{
|
||||
if (len < 5)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
|
||||
p += mb_cvt_u8_get(p, &(wr_rsp->fc));
|
||||
p += mb_cvt_u16_get(p, &(wr_rsp->addr));
|
||||
p += mb_cvt_u16_get(p, &(wr_rsp->nb));
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_mask_wr_make(u8 *buf, const mb_pdu_mask_wr_t *mask_wr)
|
||||
{
|
||||
u8 *p = buf;
|
||||
|
||||
p += mb_cvt_u8_put(p, mask_wr->fc);
|
||||
p += mb_cvt_u16_put(p, mask_wr->addr);
|
||||
p += mb_cvt_u16_put(p, mask_wr->val_and);
|
||||
p += mb_cvt_u16_put(p, mask_wr->val_or);
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_mask_wr_parse(const u8 *buf, int len, mb_pdu_mask_wr_t *mask_wr)
|
||||
{
|
||||
if (len < 7)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
|
||||
p += mb_cvt_u8_get(p, &(mask_wr->fc));
|
||||
p += mb_cvt_u16_get(p, &(mask_wr->addr));
|
||||
p += mb_cvt_u16_get(p, &(mask_wr->val_and));
|
||||
p += mb_cvt_u16_get(p, &(mask_wr->val_or));
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_wr_rd_req_make(u8 *buf, const mb_pdu_wr_rd_req_t *wr_rd_req)
|
||||
{
|
||||
u8 *p = buf;
|
||||
|
||||
p += mb_cvt_u8_put(p, wr_rd_req->fc);
|
||||
p += mb_cvt_u16_put(p, wr_rd_req->rd_addr);
|
||||
p += mb_cvt_u16_put(p, wr_rd_req->rd_nb);
|
||||
p += mb_cvt_u16_put(p, wr_rd_req->wr_addr);
|
||||
p += mb_cvt_u16_put(p, wr_rd_req->wr_nb);
|
||||
p += mb_cvt_u8_put(p, wr_rd_req->dlen);
|
||||
memcpy(p, wr_rd_req->pdata, wr_rd_req->dlen);
|
||||
p += wr_rd_req->dlen;
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_wr_rd_req_parse(const u8 *buf, int len, mb_pdu_wr_rd_req_t *wr_rd_req)
|
||||
{
|
||||
if (len < 11)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
|
||||
p += mb_cvt_u8_get(p, &(wr_rd_req->fc));
|
||||
p += mb_cvt_u16_get(p, &(wr_rd_req->rd_addr));
|
||||
p += mb_cvt_u16_get(p, &(wr_rd_req->rd_nb));
|
||||
p += mb_cvt_u16_get(p, &(wr_rd_req->wr_addr));
|
||||
p += mb_cvt_u16_get(p, &(wr_rd_req->wr_nb));
|
||||
p += mb_cvt_u8_get(p, &(wr_rd_req->dlen));
|
||||
wr_rd_req->pdata = p;
|
||||
p += wr_rd_req->dlen;
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
static int mb_pdu_req_make(u8 *buf, const mb_pdu_t *pdu)//生成pdu请求帧, 返回帧长度, 失败返回0
|
||||
{
|
||||
switch(pdu->fc)
|
||||
{
|
||||
case MODBUS_FC_READ_COILS :
|
||||
return(mb_pdu_rd_req_make(buf, (mb_pdu_rd_req_t *)pdu));
|
||||
case MODBUS_FC_READ_DISCRETE_INPUTS :
|
||||
return(mb_pdu_rd_req_make(buf, (mb_pdu_rd_req_t *)pdu));
|
||||
case MODBUS_FC_READ_HOLDING_REGISTERS :
|
||||
return(mb_pdu_rd_req_make(buf, (mb_pdu_rd_req_t *)pdu));
|
||||
case MODBUS_FC_READ_INPUT_REGISTERS :
|
||||
return(mb_pdu_rd_req_make(buf, (mb_pdu_rd_req_t *)pdu));
|
||||
case MODBUS_FC_WRITE_SINGLE_COIL :
|
||||
return(mb_pdu_wr_single_make(buf, (mb_pdu_wr_single_t *)pdu));
|
||||
case MODBUS_FC_WRITE_SINGLE_REGISTER :
|
||||
return(mb_pdu_wr_single_make(buf, (mb_pdu_wr_single_t *)pdu));
|
||||
case MODBUS_FC_READ_EXCEPTION_STATUS :
|
||||
break;
|
||||
case MODBUS_FC_WRITE_MULTIPLE_COILS :
|
||||
return(mb_pdu_wr_req_make(buf, (mb_pdu_wr_req_t *)pdu));
|
||||
case MODBUS_FC_WRITE_MULTIPLE_REGISTERS :
|
||||
return(mb_pdu_wr_req_make(buf, (mb_pdu_wr_req_t *)pdu));
|
||||
case MODBUS_FC_REPORT_SLAVE_ID :
|
||||
break;
|
||||
case MODBUS_FC_MASK_WRITE_REGISTER :
|
||||
return(mb_pdu_mask_wr_make(buf, (mb_pdu_mask_wr_t *)pdu));
|
||||
case MODBUS_FC_WRITE_AND_READ_REGISTERS :
|
||||
return(mb_pdu_wr_rd_req_make(buf, (mb_pdu_wr_rd_req_t *)pdu));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int mb_pdu_req_parse(const u8 *buf, int len, mb_pdu_t *pdu)//解析pdu请求帧, 成功返回帧长度, 失败返回-1
|
||||
{
|
||||
u8 fc = *buf;
|
||||
switch(fc)
|
||||
{
|
||||
case MODBUS_FC_READ_COILS :
|
||||
return(mb_pdu_rd_req_parse(buf, len, (mb_pdu_rd_req_t *)pdu));
|
||||
case MODBUS_FC_READ_DISCRETE_INPUTS :
|
||||
return(mb_pdu_rd_req_parse(buf, len, (mb_pdu_rd_req_t *)pdu));
|
||||
case MODBUS_FC_READ_HOLDING_REGISTERS :
|
||||
return(mb_pdu_rd_req_parse(buf, len, (mb_pdu_rd_req_t *)pdu));
|
||||
case MODBUS_FC_READ_INPUT_REGISTERS :
|
||||
return(mb_pdu_rd_req_parse(buf, len, (mb_pdu_rd_req_t *)pdu));
|
||||
case MODBUS_FC_WRITE_SINGLE_COIL :
|
||||
return(mb_pdu_wr_single_parse(buf, len, (mb_pdu_wr_single_t *)pdu));
|
||||
case MODBUS_FC_WRITE_SINGLE_REGISTER :
|
||||
return(mb_pdu_wr_single_parse(buf, len, (mb_pdu_wr_single_t *)pdu));
|
||||
case MODBUS_FC_READ_EXCEPTION_STATUS :
|
||||
break;
|
||||
case MODBUS_FC_WRITE_MULTIPLE_COILS :
|
||||
return(mb_pdu_wr_req_parse(buf, len, (mb_pdu_wr_req_t *)pdu));
|
||||
case MODBUS_FC_WRITE_MULTIPLE_REGISTERS :
|
||||
return(mb_pdu_wr_req_parse(buf, len, (mb_pdu_wr_req_t *)pdu));
|
||||
case MODBUS_FC_REPORT_SLAVE_ID :
|
||||
break;
|
||||
case MODBUS_FC_MASK_WRITE_REGISTER :
|
||||
return(mb_pdu_mask_wr_parse(buf, len, (mb_pdu_mask_wr_t *)pdu));
|
||||
case MODBUS_FC_WRITE_AND_READ_REGISTERS :
|
||||
return(mb_pdu_wr_rd_req_parse(buf, len, (mb_pdu_wr_rd_req_t *)pdu));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
static int mb_pdu_rsp_make(u8 *buf, const mb_pdu_t *pdu)//生成pdu响应帧, 返回帧长度, 失败返回0
|
||||
{
|
||||
if (MODBUS_FC_EXCEPT_CHK(pdu->fc))
|
||||
{
|
||||
return(mb_pdu_except_make(buf, (mb_pdu_except_t *)pdu));
|
||||
}
|
||||
|
||||
switch(pdu->fc)
|
||||
{
|
||||
case MODBUS_FC_READ_COILS :
|
||||
return(mb_pdu_rd_rsp_make(buf, (mb_pdu_rd_rsp_t *)pdu));
|
||||
case MODBUS_FC_READ_DISCRETE_INPUTS :
|
||||
return(mb_pdu_rd_rsp_make(buf, (mb_pdu_rd_rsp_t *)pdu));
|
||||
case MODBUS_FC_READ_HOLDING_REGISTERS :
|
||||
return(mb_pdu_rd_rsp_make(buf, (mb_pdu_rd_rsp_t *)pdu));
|
||||
case MODBUS_FC_READ_INPUT_REGISTERS :
|
||||
return(mb_pdu_rd_rsp_make(buf, (mb_pdu_rd_rsp_t *)pdu));
|
||||
case MODBUS_FC_WRITE_SINGLE_COIL :
|
||||
return(mb_pdu_wr_single_make(buf, (mb_pdu_wr_single_t *)pdu));
|
||||
case MODBUS_FC_WRITE_SINGLE_REGISTER :
|
||||
return(mb_pdu_wr_single_make(buf, (mb_pdu_wr_single_t *)pdu));
|
||||
case MODBUS_FC_READ_EXCEPTION_STATUS :
|
||||
break;
|
||||
case MODBUS_FC_WRITE_MULTIPLE_COILS :
|
||||
return(mb_pdu_wr_rsp_make(buf, (mb_pdu_wr_rsp_t *)pdu));
|
||||
case MODBUS_FC_WRITE_MULTIPLE_REGISTERS :
|
||||
return(mb_pdu_wr_rsp_make(buf, (mb_pdu_wr_rsp_t *)pdu));
|
||||
case MODBUS_FC_REPORT_SLAVE_ID :
|
||||
break;
|
||||
case MODBUS_FC_MASK_WRITE_REGISTER :
|
||||
return(mb_pdu_mask_wr_make(buf, (mb_pdu_mask_wr_t *)pdu));
|
||||
case MODBUS_FC_WRITE_AND_READ_REGISTERS :
|
||||
return(mb_pdu_rd_rsp_make(buf, (mb_pdu_rd_rsp_t *)pdu));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int mb_pdu_rsp_parse(const u8 *buf, int len, mb_pdu_t *pdu)//解析pdu响应帧, 成功返回帧长度, 失败返回-1
|
||||
{
|
||||
u8 fc = *buf;
|
||||
if (MODBUS_FC_EXCEPT_CHK(fc))
|
||||
{
|
||||
return(mb_pdu_except_parse(buf, len, (mb_pdu_except_t *)pdu));
|
||||
}
|
||||
|
||||
switch(fc)
|
||||
{
|
||||
case MODBUS_FC_READ_COILS :
|
||||
return(mb_pdu_rd_rsp_parse(buf, len, (mb_pdu_rd_rsp_t *)pdu));
|
||||
case MODBUS_FC_READ_DISCRETE_INPUTS :
|
||||
return(mb_pdu_rd_rsp_parse(buf, len, (mb_pdu_rd_rsp_t *)pdu));
|
||||
case MODBUS_FC_READ_HOLDING_REGISTERS :
|
||||
return(mb_pdu_rd_rsp_parse(buf, len, (mb_pdu_rd_rsp_t *)pdu));
|
||||
case MODBUS_FC_READ_INPUT_REGISTERS :
|
||||
return(mb_pdu_rd_rsp_parse(buf, len, (mb_pdu_rd_rsp_t *)pdu));
|
||||
case MODBUS_FC_WRITE_SINGLE_COIL :
|
||||
return(mb_pdu_wr_single_parse(buf, len, (mb_pdu_wr_single_t *)pdu));
|
||||
case MODBUS_FC_WRITE_SINGLE_REGISTER :
|
||||
return(mb_pdu_wr_single_parse(buf, len, (mb_pdu_wr_single_t *)pdu));
|
||||
case MODBUS_FC_READ_EXCEPTION_STATUS :
|
||||
break;
|
||||
case MODBUS_FC_WRITE_MULTIPLE_COILS :
|
||||
return(mb_pdu_wr_rsp_parse(buf, len, (mb_pdu_wr_rsp_t *)pdu));
|
||||
case MODBUS_FC_WRITE_MULTIPLE_REGISTERS :
|
||||
return(mb_pdu_wr_rsp_parse(buf, len, (mb_pdu_wr_rsp_t *)pdu));
|
||||
case MODBUS_FC_REPORT_SLAVE_ID :
|
||||
break;
|
||||
case MODBUS_FC_MASK_WRITE_REGISTER :
|
||||
return(mb_pdu_mask_wr_parse(buf, len, (mb_pdu_mask_wr_t *)pdu));
|
||||
case MODBUS_FC_WRITE_AND_READ_REGISTERS :
|
||||
return(mb_pdu_rd_rsp_parse(buf, len, (mb_pdu_rd_rsp_t *)pdu));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int mb_pdu_make(u8 *buf, const mb_pdu_t *pdu, mb_pdu_type_t type)//生成pdu帧, 返回帧长度, 错误返回0
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case MB_PDU_TYPE_REQ:
|
||||
return(mb_pdu_req_make(buf, pdu));
|
||||
case MB_PDU_TYPE_RSP:
|
||||
return(mb_pdu_rsp_make(buf, pdu));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int mb_pdu_parse(const u8 *buf, int len, mb_pdu_t *pdu, mb_pdu_type_t type)//解析pdu帧, 成功返回帧长度, 帧错误返回0, 功能码不支持返回-1
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case MB_PDU_TYPE_REQ:
|
||||
return(mb_pdu_req_parse(buf, len, pdu));
|
||||
case MB_PDU_TYPE_RSP:
|
||||
return(mb_pdu_rsp_parse(buf, len, pdu));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* modbus_port_linux.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-02 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <packages/qmodbus/inc/modbus_port.h>
|
||||
|
||||
#ifdef MB_USING_PORT_LINUX
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
|
||||
#define QLOG_TAG "modbus.port.linux"
|
||||
#define QLOG_LVL QLOG_LVL_LOG //QLOG_LVL_INFO //
|
||||
#include "qlog.h"
|
||||
|
||||
MB_WEAK long long mb_port_get_ms(void)//获取毫秒时间
|
||||
{
|
||||
long long ms;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
ms = tv.tv_sec;
|
||||
ms = ms * 1000;
|
||||
ms = ms + (tv.tv_usec / 1000);
|
||||
return(ms);
|
||||
}
|
||||
|
||||
MB_WEAK void mb_port_delay_ms(int tmo_ms)
|
||||
{
|
||||
usleep((long long)tmo_ms * 1000);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RTU_BACKEND
|
||||
static int mb_port_uart_config(int fd, int baudrate, int databit, int parity, int stopbit)//配置串口, 成功返回0, 失败返回-1
|
||||
{
|
||||
struct termios options;
|
||||
|
||||
//获取串口相关参数,该函数还可以测试配置是否正确,该串口是否可用等。
|
||||
//若调用成功,函数返回值为0,若调用失败,函数返回值为1.
|
||||
if( tcgetattr(fd, &options) != 0)
|
||||
{
|
||||
//zlog_error(g_zc, "uart(fd=%d) get attr fail.", fd);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
//设置串口输入波特率和输出波特率
|
||||
int speed = B115200;
|
||||
switch(baudrate)
|
||||
{
|
||||
case 1200:
|
||||
speed = B1200;
|
||||
break;
|
||||
case 2400:
|
||||
speed = B2400;
|
||||
break;
|
||||
case 4800:
|
||||
speed = B4800;
|
||||
break;
|
||||
case 9600:
|
||||
speed = B9600;
|
||||
break;
|
||||
case 19200:
|
||||
speed = B19200;
|
||||
break;
|
||||
case 38400:
|
||||
speed = B38400;
|
||||
break;
|
||||
case 57600:
|
||||
speed = B57600;
|
||||
break;
|
||||
case 115200:
|
||||
speed = B115200;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cfsetispeed(&options, speed);
|
||||
cfsetospeed(&options, speed);
|
||||
|
||||
options.c_cflag |= CLOCAL;//修改控制模式,保证程序不会占用串口
|
||||
options.c_cflag |= CREAD;//修改控制模式,使得能够从串口中读取输入数据
|
||||
//options.c_cflag &= ~CRTSCTS;//不使用流控制
|
||||
|
||||
options.c_iflag = 0;
|
||||
//options.c_iflag &= ~(IGNCR | ICRNL | INLCR);//不使用回车换行转换
|
||||
//options.c_iflag &= ~(IXON | IXOFF | IXANY);//不使用软件流控制
|
||||
//options.c_iflag |= IXON | IXOFF | IXANY;//使用软件流控制
|
||||
|
||||
options.c_cflag &= ~CSIZE;//屏蔽其他标志位
|
||||
switch (databit)//设置数据位
|
||||
{
|
||||
case 5:
|
||||
options.c_cflag |= CS5;
|
||||
break;
|
||||
case 6:
|
||||
options.c_cflag |= CS6;
|
||||
break;
|
||||
case 7:
|
||||
options.c_cflag |= CS7;
|
||||
break;
|
||||
case 8:
|
||||
options.c_cflag |= CS8;
|
||||
break;
|
||||
default:
|
||||
options.c_cflag |= CS8;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (parity)//设置校验位
|
||||
{
|
||||
case 0: //无奇偶校验位。
|
||||
options.c_cflag &= ~PARENB;
|
||||
options.c_iflag &= ~INPCK;
|
||||
break;
|
||||
case 1://设置为奇校验
|
||||
options.c_cflag |= (PARODD | PARENB);
|
||||
options.c_iflag |= INPCK;
|
||||
break;
|
||||
case 2://设置为偶校验
|
||||
options.c_cflag |= PARENB;
|
||||
options.c_cflag &= ~PARODD;
|
||||
options.c_iflag |= INPCK;
|
||||
break;
|
||||
default://无奇偶校验位。
|
||||
options.c_cflag &= ~PARENB;
|
||||
options.c_iflag &= ~INPCK;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (stopbit)// 设置停止位
|
||||
{
|
||||
case 1:
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
break;
|
||||
case 2:
|
||||
options.c_cflag |= CSTOPB;
|
||||
break;
|
||||
default:
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
break;
|
||||
}
|
||||
|
||||
options.c_oflag &= ~OPOST;//关闭输出处理
|
||||
options.c_lflag &= ~(ISIG | TCSADRAIN | ICANON | ECHO | ECHOE);//关闭特殊字符信号发送, 关闭规范输入, 关闭回显
|
||||
//options.c_lflag &= ~(ISIG | ICANON);//关闭特殊字符信号发送, 关闭规范输入
|
||||
|
||||
//设置等待时间和最小接收字符
|
||||
options.c_cc[VTIME] = 0; /* 读取一个字符等待1*(1/10)s */
|
||||
options.c_cc[VMIN] = 0; /* 读取字符的最少个数为1 */
|
||||
|
||||
//如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读
|
||||
//tcflush(fd, TCIFLUSH);
|
||||
|
||||
//激活配置 (将修改后的termios数据设置到串口中)
|
||||
//opt : TCSANOW--立即生效, TCSADRAIN--发送完成后生效, TCSAFLUSH--发送完成后生效并清除输入缓冲
|
||||
if (tcsetattr(fd, TCSADRAIN, &options) != 0)//发送完成后生效
|
||||
{
|
||||
LOG_E("uart set attr error!");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
MB_WEAK void * mb_port_rtu_open(const mb_backend_param_t *param)//打开, 成功返回实例指针或文件标识, 错误返回NULL
|
||||
{
|
||||
MB_ASSERT(param != NULL);
|
||||
MB_ASSERT(param->rtu.dev != NULL);
|
||||
|
||||
char *path = param->rtu.dev;
|
||||
int fd = open(path, O_RDWR|O_NOCTTY|O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
{
|
||||
LOG_E("device (%s) open fail.", path);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
if(fcntl(fd, F_SETFL, 0) < 0)//恢复串口为阻塞状态
|
||||
{
|
||||
close(fd);
|
||||
LOG_E("device (%s) fcntl fail.", path);
|
||||
return(NULL);
|
||||
}
|
||||
if (mb_port_uart_config(fd, param->rtu.baudrate, 8, param->rtu.parity, 1) < 0)
|
||||
{
|
||||
close(fd);
|
||||
LOG_E("device (%s) config fail.", path);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
LOG_D("device (%s, fd=%d) open suceess.", path, fd);
|
||||
|
||||
return((void *)fd);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_rtu_close(void *hinst)//关闭, 成功返回0, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
int fd = (int)hinst;
|
||||
return(close(fd));
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_rtu_read(void *hinst, u8 *buf, int bufsize)//接收数据, 返回接收到的数据长度, 0表示超时, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
|
||||
int fd = (int)hinst;
|
||||
int len = read(fd, buf, bufsize);
|
||||
if (len < 0)
|
||||
{
|
||||
LOG_E("device (fd = %d) read error!", fd);
|
||||
return(-1);
|
||||
}
|
||||
return(len);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_rtu_write(void *hinst, u8 *buf, int size)//发送数据, , 返回成功发送的数据长度, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
|
||||
int fd = (int)hinst;
|
||||
int len = write(fd, buf, size);
|
||||
if (len <= 0)
|
||||
{
|
||||
LOG_E("device (fd = %d) write error!", fd);
|
||||
return(-1);
|
||||
}
|
||||
return(len);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_rtu_flush(void *hinst)//清空接收缓存, 成功返回0, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
int fd = (int)hinst;
|
||||
return(tcflush(fd, TCIFLUSH));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(MB_USING_TCP_BACKEND) || defined(MB_USING_SOCK_BACKEND))
|
||||
MB_WEAK void * mb_port_tcp_open(const mb_backend_param_t *param)//打开, 成功返回实例指针或文件标识, 错误返回NULL
|
||||
{
|
||||
MB_ASSERT(param != NULL);
|
||||
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
{
|
||||
LOG_E("socket create fail.");
|
||||
return(NULL);
|
||||
}
|
||||
LOG_D("socket create success, fd = %d.", sock);
|
||||
|
||||
struct hostent *host = gethostbyname(param->tcp.host);
|
||||
if (host == NULL)
|
||||
{
|
||||
close(sock);
|
||||
LOG_E("host get error.");
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
struct sockaddr_in srv_addr;
|
||||
memset(&srv_addr, 0, sizeof(srv_addr));
|
||||
srv_addr.sin_family = AF_INET;
|
||||
srv_addr.sin_port = htons(param->tcp.port);
|
||||
srv_addr.sin_addr = *((struct in_addr *)host->h_addr);
|
||||
if (connect(sock, (struct sockaddr *)&srv_addr, sizeof(struct sockaddr)) < 0)
|
||||
{
|
||||
close(sock);
|
||||
LOG_E("socket connect fail.");
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
return((void *)sock);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_tcp_close(void *hinst)//关闭, 成功返回0, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
int sock = (int)hinst;
|
||||
return((close(sock) == 0) ? 0 : -1);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_tcp_read(void *hinst, u8 *buf, int bufsize)//接收数据, 返回接收到的数据长度, 0表示超时, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
|
||||
int sock = (int)hinst;
|
||||
int len = recv(sock, buf, bufsize, MSG_DONTWAIT);
|
||||
if (len == 0)//socket已关闭
|
||||
{
|
||||
LOG_E("tcp read error, fd = %d", sock);
|
||||
return(-1);
|
||||
}
|
||||
if (len < 0)//超时或其它错误
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_tcp_write(void *hinst, u8 *buf, int size)//发送数据, , 返回成功发送的数据长度, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
|
||||
int sock = (int)hinst;
|
||||
int len = send(sock, buf, size, 0);
|
||||
if (len <= 0)//socket已关闭
|
||||
{
|
||||
LOG_E("tcp write error, fd = %d", sock);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_tcp_flush(void *hinst)//清空接收缓存, 成功返回0, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
u8 c;
|
||||
int sock = (int)hinst;
|
||||
while(1)
|
||||
{
|
||||
int len = recv(sock, &c, 1, MSG_DONTWAIT);
|
||||
if (len == 0)//socket已关闭
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (len < 0)//超时或其它错误
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* modbus_port_rtt.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-02 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <packages/qmodbus/inc/modbus_port.h>
|
||||
|
||||
#ifdef MB_USING_PORT_RTT
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "rtdevice.h"
|
||||
|
||||
#define DBG_TAG "modbus.port.rtt"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
MB_WEAK long long mb_port_get_ms(void)//获取毫秒时间
|
||||
{
|
||||
return(rt_tick_get_millisecond());
|
||||
}
|
||||
|
||||
MB_WEAK void mb_port_delay_ms(int tmo_ms)
|
||||
{
|
||||
rt_thread_mdelay(tmo_ms);
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RTU_BACKEND
|
||||
MB_WEAK void * mb_port_rtu_open(const mb_backend_param_t *param)//打开, 成功返回实例指针或文件标识, 错误返回NULL
|
||||
{
|
||||
MB_ASSERT(param != NULL);
|
||||
MB_ASSERT(param->rtu.dev != NULL);
|
||||
|
||||
char *name = param->rtu.dev;
|
||||
rt_device_t dev = rt_device_find(name);
|
||||
if (dev == RT_NULL)
|
||||
{
|
||||
LOG_E("device (%s) not found.", name);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
struct serial_configure cfg = RT_SERIAL_CONFIG_DEFAULT;
|
||||
cfg.baud_rate = param->rtu.baudrate;
|
||||
cfg.parity = param->rtu.parity;
|
||||
if (rt_device_control(dev, RT_DEVICE_CTRL_CONFIG, &cfg) < 0)
|
||||
{
|
||||
LOG_E("device (%s) config fail.", name);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
if ( rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX) < 0)
|
||||
{
|
||||
LOG_E("device (%s) open fail.", name);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
int pin = param->rtu.pin;
|
||||
int lvl = param->rtu.lvl;
|
||||
if (pin >= 0)
|
||||
{
|
||||
u32 ud = 0xABCD0000 + ((pin << 1) & 0xFFFF) + (lvl ? 1 : 0);
|
||||
dev->user_data = (void *)ud;
|
||||
rt_pin_mode(pin, PIN_MODE_OUTPUT);
|
||||
rt_pin_write(pin, ! lvl);
|
||||
}
|
||||
|
||||
LOG_D("device (%s) open suceess.", name);
|
||||
|
||||
return((void *)dev);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_rtu_close(void *hinst)//关闭, 成功返回0, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
rt_device_t dev = (rt_device_t)hinst;
|
||||
return(rt_device_close(dev));
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_rtu_read(void *hinst, u8 *buf, int bufsize)//接收数据, 返回接收到的数据长度, 0表示超时, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
|
||||
rt_device_t dev = (rt_device_t)hinst;
|
||||
int len = rt_device_read(dev, -1, buf, bufsize);
|
||||
if (len < 0)
|
||||
{
|
||||
LOG_E("device read error.");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_rtu_write(void *hinst, u8 *buf, int size)//发送数据, , 返回成功发送的数据长度, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
|
||||
rt_device_t dev = (rt_device_t)hinst;
|
||||
u32 ud = (u32)(dev->user_data);
|
||||
int pin = ((ud & 0xFFFF0000) == 0xABCD0000) ? ((ud & 0xFFFF) >> 1) : -1;
|
||||
int lvl = (ud & 0x01);
|
||||
if (pin >= 0) rt_pin_write(pin, lvl);
|
||||
int len = rt_device_write(dev, -1, buf, size);
|
||||
if (pin >= 0) rt_pin_write(pin, ! lvl);
|
||||
if (len < 0)
|
||||
{
|
||||
LOG_E("device read error.");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_rtu_flush(void *hinst)//清空接收缓存, 成功返回0, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
u8 c;
|
||||
rt_device_t dev = (rt_device_t)hinst;
|
||||
while(1)
|
||||
{
|
||||
int len = rt_device_read(dev, -1, &c, 1);
|
||||
if (len < 0)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (len == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(MB_USING_TCP_BACKEND) || defined(MB_USING_SOCK_BACKEND))
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
MB_WEAK void * mb_port_tcp_open(const mb_backend_param_t *param)//打开, 成功返回实例指针或文件标识, 错误返回NULL
|
||||
{
|
||||
MB_ASSERT(param != NULL);
|
||||
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
{
|
||||
LOG_E("socket create fail.");
|
||||
return(NULL);
|
||||
}
|
||||
LOG_D("socket create success, fd = %d.", sock);
|
||||
|
||||
struct hostent *host = gethostbyname(param->tcp.host);
|
||||
if (host == NULL)
|
||||
{
|
||||
closesocket(sock);
|
||||
LOG_E("host get error.");
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
struct sockaddr_in srv_addr;
|
||||
memset(&srv_addr, 0, sizeof(srv_addr));
|
||||
srv_addr.sin_family = AF_INET;
|
||||
srv_addr.sin_port = htons(param->tcp.port);
|
||||
srv_addr.sin_addr = *((struct in_addr *)host->h_addr);
|
||||
if (connect(sock, (struct sockaddr *)&srv_addr, sizeof(struct sockaddr)) < 0)
|
||||
{
|
||||
closesocket(sock);
|
||||
LOG_E("socket connect fail.");
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
return((void *)sock);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_tcp_close(void *hinst)//关闭, 成功返回0, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
int sock = (int)hinst;
|
||||
return((closesocket(sock) == 0) ? 0 : -1);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_tcp_read(void *hinst, u8 *buf, int bufsize)//接收数据, 返回接收到的数据长度, 0表示超时, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
|
||||
int sock = (int)hinst;
|
||||
int len = recv(sock, buf, bufsize, MSG_DONTWAIT);
|
||||
if (len == 0)//socket已关闭
|
||||
{
|
||||
LOG_E("TCP read error.");
|
||||
return(-1);
|
||||
}
|
||||
if (len < 0)//超时或其它错误
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_tcp_write(void *hinst, u8 *buf, int size)//发送数据, , 返回成功发送的数据长度, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(buf != NULL);
|
||||
|
||||
int sock = (int)hinst;
|
||||
int len = send(sock, buf, size, 0);
|
||||
if (len <= 0)//socket已关闭
|
||||
{
|
||||
LOG_E("TCP write error.");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_tcp_flush(void *hinst)//清空接收缓存, 成功返回0, 错误返回-1
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
u8 c;
|
||||
int sock = (int)hinst;
|
||||
while(1)
|
||||
{
|
||||
int len = recv(sock, &c, 1, MSG_DONTWAIT);
|
||||
if (len == 0)//socket已关闭
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
if (len < 0)//超时或其它错误
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* modbus_port_slave.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-02 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <packages/qmodbus/inc/modbus_port.h>
|
||||
|
||||
#ifdef MB_USING_SLAVE
|
||||
MB_WEAK int mb_port_read_disc(u16 addr, u8 *pbit)//读离散量输入, 返回 : 0-成功, -2-地址错误
|
||||
{
|
||||
MB_ASSERT(pbit != NULL);
|
||||
|
||||
return(-2);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_read_coil(u16 addr, u8 *pbit)//读线圈, 返回 : 0-成功, -2-地址错误
|
||||
{
|
||||
MB_ASSERT(pbit != NULL);
|
||||
|
||||
return(-2);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_write_coil(u16 addr, u8 bit)//写线圈, 返回 : 0-成功, -2-地址错误, -4-设备故障
|
||||
{
|
||||
return(-2);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_read_input(u16 addr, u16 *preg)//读输入寄存器, 返回 : 0-成功, -2-地址错误
|
||||
{
|
||||
MB_ASSERT(preg != NULL);
|
||||
|
||||
return(-2);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_read_hold(u16 addr, u16 *preg)//读保持寄存器, 返回 : 0-成功, -2-地址错误
|
||||
{
|
||||
MB_ASSERT(preg != NULL);
|
||||
|
||||
return(-2);
|
||||
}
|
||||
|
||||
MB_WEAK int mb_port_write_hold(u16 addr, u16 reg)//写保持寄存器, 返回 : 0-成功, -2-地址错误, -3-值非法, -4-设备故障
|
||||
{
|
||||
return(-2);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* modbus_rtu.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-01 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <packages/qmodbus/inc/modbus_crc.h>
|
||||
#include <packages/qmodbus/inc/modbus_cvt.h>
|
||||
#include <packages/qmodbus/inc/modbus_rtu.h>
|
||||
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
|
||||
int mb_rtu_frm_make(u8 *buf, const mb_rtu_frm_t *frm, mb_pdu_type_t type)//生成rtu帧, 返回帧长度
|
||||
{
|
||||
u8 *p = buf;
|
||||
p += mb_cvt_u8_put(p, frm->saddr);
|
||||
p += mb_pdu_make(p, &(frm->pdu), type);
|
||||
u16 crc = mb_crc_cal(buf, (int)(p - buf));
|
||||
*p++ = crc;
|
||||
*p++ = (crc >> 8);
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
int mb_rtu_frm_parse(const u8 *buf, int len, mb_rtu_frm_t *frm, mb_pdu_type_t type)//解析rtu帧, 返回pdu数据长度, 解析失败返回0, 功能码不支持返回-1
|
||||
{
|
||||
if (len < MB_RTU_FRM_MIN)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
frm->saddr = *buf;
|
||||
int remain = len - (MB_RTU_SADDR_SIZE + MB_RTU_CRC_SIZE);
|
||||
int pdu_len = mb_pdu_parse(buf + 1, remain, &(frm->pdu), type);
|
||||
if (pdu_len <= 0)
|
||||
{
|
||||
return(pdu_len);
|
||||
}
|
||||
|
||||
if (remain < pdu_len)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int flen = pdu_len + (MB_RTU_SADDR_SIZE + MB_RTU_CRC_SIZE);
|
||||
u16 cal_crc = mb_crc_cal(buf, flen);
|
||||
if (cal_crc != 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(pdu_len);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
#include <packages/qmodbus/inc/modbus.h>
|
||||
#include <packages/qmodbus/inc/modbus_cvt.h>
|
||||
#include <packages/qmodbus/inc/modbus_port.h>
|
||||
* modbus_slave.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-02 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef MB_USING_SLAVE
|
||||
|
||||
static void mb_slave_pdu_deal_read_coils(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->read_coil == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->rd_req.addr;
|
||||
int nb = pdu->rd_req.nb;
|
||||
memset(hinst->datas, 0, sizeof(hinst->datas));
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
u8 bit;
|
||||
int rst = hinst->cb->read_coil(addr + i, &bit);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
mb_bitmap_set(hinst->datas, i, bit);
|
||||
}
|
||||
|
||||
pdu->rd_rsp.dlen = (nb + 7) / 8;
|
||||
pdu->rd_rsp.pdata = hinst->datas;
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_read_discs(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->read_disc == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->rd_req.addr;
|
||||
int nb = pdu->rd_req.nb;
|
||||
memset(hinst->datas, 0, sizeof(hinst->datas));
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
u8 bit;
|
||||
int rst = hinst->cb->read_disc(addr + i, &bit);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
mb_bitmap_set(hinst->datas, i, bit);
|
||||
}
|
||||
|
||||
pdu->rd_rsp.dlen = (nb + 7) / 8;
|
||||
pdu->rd_rsp.pdata = hinst->datas;
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_read_holds(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->read_hold == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->rd_req.addr;
|
||||
int nb = pdu->rd_req.nb;
|
||||
u8 *p = hinst->datas;
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
u16 val;
|
||||
int rst = hinst->cb->read_hold(addr + i, &val);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
p += mb_cvt_u16_put(p, val);
|
||||
}
|
||||
|
||||
pdu->rd_rsp.dlen = 2 * nb;
|
||||
pdu->rd_rsp.pdata = hinst->datas;
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_read_inputs(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->read_input == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->rd_req.addr;
|
||||
int nb = pdu->rd_req.nb;
|
||||
u8 *p = hinst->datas;
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
u16 val;
|
||||
int rst = hinst->cb->read_input(addr + i, &val);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
p += mb_cvt_u16_put(p, val);
|
||||
}
|
||||
|
||||
pdu->rd_rsp.dlen = 2 * nb;
|
||||
pdu->rd_rsp.pdata = hinst->datas;
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_write_coil(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->write_coil == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->wr_single.addr;
|
||||
u16 val = pdu->wr_single.val;
|
||||
if ((val != 0xFF00) && (val != 0x0000))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_ILLEGAL_DATA_VALUE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
int rst = hinst->cb->write_coil(addr, (val ? 1 : 0));
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_write_reg(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->write_hold == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->wr_single.addr;
|
||||
u16 val = pdu->wr_single.val;
|
||||
int rst = hinst->cb->write_hold(addr, val);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_write_coils(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->write_coil == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->wr_req.addr;
|
||||
int nb = pdu->wr_req.nb;
|
||||
u8 *pbits = pdu->wr_req.pdata;
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
u8 bit = mb_bitmap_get(pbits, i);
|
||||
int rst = hinst->cb->write_coil(addr + i, bit);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_write_regs(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->write_hold == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->wr_req.addr;
|
||||
int nb = pdu->wr_req.nb;
|
||||
u8 *p = pdu->wr_req.pdata;
|
||||
for (int i=0; i<nb; i++)
|
||||
{
|
||||
u16 val;
|
||||
p += mb_cvt_u16_get(p, &val);
|
||||
int rst = hinst->cb->write_hold(addr + i, val);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_mask_write_reg(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->read_hold == NULL) || (hinst->cb->write_hold == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 addr = pdu->mask_wr.addr;
|
||||
u16 val_and = pdu->mask_wr.val_and;
|
||||
u16 val_or = pdu->mask_wr.val_or;
|
||||
u16 val;
|
||||
int rst = hinst->cb->read_hold(addr, &val);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
val = ((val & val_and) | (val_or & ~val_and));
|
||||
rst = hinst->cb->write_hold(addr, val);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal_write_and_read_regs(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
if ((hinst->cb == NULL) || (hinst->cb->read_hold == NULL) || (hinst->cb->write_hold == NULL))
|
||||
{
|
||||
pdu->exc.ec = MODBUS_EC_SLAVE_OR_SERVER_FAILURE;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
|
||||
u16 rd_addr = pdu->wr_rd_req.rd_addr;
|
||||
int rd_nb = pdu->wr_rd_req.rd_nb;
|
||||
u16 wr_addr = pdu->wr_rd_req.wr_addr;
|
||||
int wr_nb = pdu->wr_rd_req.wr_nb;
|
||||
u8 *p = pdu->wr_rd_req.pdata;
|
||||
for (int i=0; i<wr_nb; i++)
|
||||
{
|
||||
u16 val;
|
||||
p += mb_cvt_u16_get(p, &val);
|
||||
int rst = hinst->cb->write_hold(wr_addr + i, val);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
p = hinst->datas;
|
||||
for (int i=0; i<rd_nb; i++)
|
||||
{
|
||||
u16 val;
|
||||
int rst = hinst->cb->read_hold(rd_addr + i, &val);
|
||||
if (rst < 0)
|
||||
{
|
||||
pdu->exc.ec = -rst;
|
||||
pdu->exc.fc = MODBUS_FC_EXCEPT_MAKE(pdu->exc.fc);
|
||||
return;
|
||||
}
|
||||
p += mb_cvt_u16_put(p, val);
|
||||
}
|
||||
|
||||
pdu->rd_rsp.dlen = rd_nb * 2;
|
||||
pdu->rd_rsp.pdata = hinst->datas;
|
||||
}
|
||||
|
||||
static void mb_slave_pdu_deal(mb_inst_t *hinst, mb_pdu_t *pdu)
|
||||
{
|
||||
switch(pdu->fc)
|
||||
{
|
||||
case MODBUS_FC_READ_COILS :
|
||||
mb_slave_pdu_deal_read_coils(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_READ_DISCRETE_INPUTS :
|
||||
mb_slave_pdu_deal_read_discs(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_READ_HOLDING_REGISTERS :
|
||||
mb_slave_pdu_deal_read_holds(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_READ_INPUT_REGISTERS :
|
||||
mb_slave_pdu_deal_read_inputs(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_WRITE_SINGLE_COIL :
|
||||
mb_slave_pdu_deal_write_coil(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_WRITE_SINGLE_REGISTER :
|
||||
mb_slave_pdu_deal_write_reg(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_READ_EXCEPTION_STATUS :
|
||||
break;
|
||||
case MODBUS_FC_WRITE_MULTIPLE_COILS :
|
||||
mb_slave_pdu_deal_write_coils(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_WRITE_MULTIPLE_REGISTERS :
|
||||
mb_slave_pdu_deal_write_regs(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_REPORT_SLAVE_ID :
|
||||
break;
|
||||
case MODBUS_FC_MASK_WRITE_REGISTER :
|
||||
mb_slave_pdu_deal_mask_write_reg(hinst, pdu);
|
||||
break;
|
||||
case MODBUS_FC_WRITE_AND_READ_REGISTERS :
|
||||
mb_slave_pdu_deal_write_and_read_regs(hinst, pdu);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
static void mb_slave_recv_deal_rtu(mb_inst_t *hinst, u8 *buf, int len)
|
||||
{
|
||||
mb_rtu_frm_t frm;
|
||||
int pdu_len = mb_rtu_frm_parse(buf, len, &frm, MB_PDU_TYPE_REQ);
|
||||
if (pdu_len == 0)//帧错误, 不处理
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if (frm.saddr != hinst->saddr)//地址不同, 不处理
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pdu_len < 0)//功能码不支持, 响应异常帧
|
||||
{
|
||||
frm.pdu.exc.ec = MODBUS_EC_ILLEGAL_FUNCTION;
|
||||
frm.pdu.exc.fc = MODBUS_FC_EXCEPT_MAKE(frm.pdu.exc.fc);
|
||||
}
|
||||
else
|
||||
{
|
||||
mb_slave_pdu_deal(hinst, &(frm.pdu));
|
||||
}
|
||||
|
||||
int flen = mb_rtu_frm_make(hinst->buf, &frm, MB_PDU_TYPE_RSP);
|
||||
mb_send(hinst, hinst->buf, flen);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
static void mb_slave_recv_deal_tcp(mb_inst_t *hinst, u8 *buf, int len)
|
||||
{
|
||||
mb_tcp_frm_t frm;
|
||||
int pdu_len = mb_tcp_frm_parse(buf, len, &frm, MB_PDU_TYPE_REQ);
|
||||
if (pdu_len == 0)//帧错误, 不处理
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MB_USING_ADDR_CHK
|
||||
if ((frm.mbap.did != 0xFF) && (frm.mbap.did != hinst->saddr))//使用地址且地址不同, 不处理
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MB_USING_MBAP_CHK
|
||||
if (frm.mbap.pid != MB_TCP_MBAP_PID)//协议标识错误, 不处理
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pdu_len < 0)//功能码不支持, 响应异常帧
|
||||
{
|
||||
frm.pdu.exc.ec = MODBUS_EC_ILLEGAL_FUNCTION;
|
||||
frm.pdu.exc.fc = MODBUS_FC_EXCEPT_MAKE(frm.pdu.exc.fc);
|
||||
}
|
||||
else
|
||||
{
|
||||
mb_slave_pdu_deal(hinst, &(frm.pdu));
|
||||
}
|
||||
|
||||
int flen = mb_tcp_frm_make(hinst->buf, &frm, MB_PDU_TYPE_RSP);
|
||||
mb_send(hinst, hinst->buf, flen);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void mb_slave_recv_deal(mb_inst_t *hinst, u8 *buf, int len)
|
||||
{
|
||||
switch(hinst->prot)
|
||||
{
|
||||
#ifdef MB_USING_RTU_PROTOCOL
|
||||
case MB_PROT_RTU :
|
||||
mb_slave_recv_deal_rtu(hinst, buf, len);
|
||||
break;
|
||||
#endif
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
case MB_PROT_TCP :
|
||||
mb_slave_recv_deal_tcp(hinst, buf, len);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const mb_cb_table_t mb_cb_table = {
|
||||
.read_disc = mb_port_read_disc, //读离散量输入
|
||||
.read_coil = mb_port_read_coil, //读线圈
|
||||
.write_coil = mb_port_write_coil, //写线圈
|
||||
.read_input = mb_port_read_input, //读输入寄存器
|
||||
.read_hold = mb_port_read_hold, //读保持寄存器
|
||||
.write_hold = mb_port_write_hold, //写保持寄存器
|
||||
};
|
||||
|
||||
//修改从机回调函数表, 默认使用modbus_port中接口函数做回调函数
|
||||
void mb_set_cb_table(mb_inst_t *hinst, const mb_cb_table_t *cb)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
MB_ASSERT(cb != NULL);
|
||||
|
||||
hinst->cb = (mb_cb_table_t *)cb;
|
||||
}
|
||||
|
||||
//从机状态机处理, 在线程中循环调用即可
|
||||
void mb_slave_fsm(mb_inst_t *hinst)
|
||||
{
|
||||
MB_ASSERT(hinst != NULL);
|
||||
|
||||
if (mb_connect(hinst) < 0)//连接失败, 延时返回
|
||||
{
|
||||
mb_port_delay_ms(1000);
|
||||
return;
|
||||
}
|
||||
|
||||
int rlen = mb_recv(hinst, hinst->buf, sizeof(hinst->buf));
|
||||
if (rlen <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mb_slave_recv_deal(hinst, hinst->buf, rlen);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* modbus_tcp.c
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2024-04-01 qiyongzhong first version
|
||||
*/
|
||||
|
||||
#include <packages/qmodbus/inc/modbus_cvt.h>
|
||||
#include <packages/qmodbus/inc/modbus_tcp.h>
|
||||
|
||||
#ifdef MB_USING_TCP_PROTOCOL
|
||||
|
||||
int mb_tcp_frm_make(u8 *buf, const mb_tcp_frm_t *frm, mb_pdu_type_t type)//生成tcp帧, 返回帧长度
|
||||
{
|
||||
int pdu_len = mb_pdu_make(buf + MB_TCP_MBAP_SIZE, &(frm->pdu), type);
|
||||
|
||||
u8 *p = buf;
|
||||
p += mb_cvt_u16_put(p, frm->mbap.tid);
|
||||
p += mb_cvt_u16_put(p, frm->mbap.pid);
|
||||
p += mb_cvt_u16_put(p, pdu_len + 1);
|
||||
p += mb_cvt_u8_put(p, frm->mbap.did);
|
||||
p += pdu_len;
|
||||
|
||||
return((int)(p - buf));
|
||||
}
|
||||
|
||||
int mb_tcp_frm_parse(const u8 *buf, int len, mb_tcp_frm_t *frm, mb_pdu_type_t type)//解析tcp帧, 返回pdu数据长度, 解析失败返回0, 功能码不支持返回-1
|
||||
{
|
||||
if (len < MB_TCP_FRM_MIN)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 *p = (u8 *)buf;
|
||||
p += mb_cvt_u16_get(p, &(frm->mbap.tid));
|
||||
p += mb_cvt_u16_get(p, &(frm->mbap.pid));
|
||||
p += mb_cvt_u16_get(p, &(frm->mbap.dlen));
|
||||
p += mb_cvt_u8_get(p, &(frm->mbap.did));
|
||||
|
||||
int remain = len - (int)(p - buf);
|
||||
int pdu_len = mb_pdu_parse(p, remain, &(frm->pdu), type);
|
||||
if (pdu_len <= 0)
|
||||
{
|
||||
return(pdu_len);
|
||||
}
|
||||
|
||||
if (remain < pdu_len)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(pdu_len);
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user