基于STM32F407+W5500的freemodbus tcp移植与实现

环境:STM32CUBEMX6.6.1  MDK532 freemodbus1.6


首先是创建能驱动W5500的工程

主要用到硬件为SPI UART  

SPI速度建议不要太快,SPI1可以设置到40M,但是没必要这么快。

导出工程

添加fputc重定义


找到W5500官方例子

参考例程WIZnet W5500/W5300-官方代理商

freemodbus tcp需要用到的是TCP server例子

将Ethernet文件夹拷入刚才建好的工程

添加C文件,添加头文件路径

我这里同时用到了HTTP SERVER,没用到可以不加

驱动主要修改是在w5500.conf

添加reset控制引脚,cs控制引脚

还有SPI发送函数

uint8_t SPI_SendByte(uint8_t byte)
{
	uint8_t rxbyte=0;
	uint8_t SPITimeout =200;
	while(__HAL_SPI_GET_FLAG(&hspi1,SPI_FLAG_TXE)==RESET)
	{
		if((SPITimeout--)==0)
        HAL_SPI_ErrorCallback(&hspi1);
			return 0;
	}
	HAL_SPI_TransmitReceive(&hspi1,&byte,&rxbyte,1,100);
	return rxbyte;
}

 将例子中定时器初始化和中断屏蔽掉,没有用到动态IP分配就不需要定时器中断

然后主函数参考例子,添加函数do_tcp_server(); 

照葫芦画瓢就行

然后下载固件,连上自己的电脑或者路由器,这里要保证在同一个网段,就是IP地址前面三个数是一样才行,电脑IP可以用ipconfig查看

直连电脑就看上面的地址,我这里没连上,显示的断开

如果连的路由器,就看下面这个,设置W5500的IP为192.168.8.x 

可以从串口1看到W5500初始化是否完成,初始化正确的话会正常显示MAC和IP为你设置的值

然后随便找一个TCP测试工具,我这里用的是sscom,输入IP和端口,测试一下官方的回环例子即可。


移植freemodbus

GitHub - cwalter-at/freemodbus: BSD licensed MODBUS RTU/ASCII and TCP slave

下载最新软件包,提取出这些文件,新建一个目录freemodbus

 

 修改porttcp.c里的这几个函数,添加各类接口,添加poll处理流程

BOOL
xMBTCPPortInit( USHORT usTCPPort )
{
    BOOL bOkay = FALSE;

    switch(getSn_SR(SOCK_TCPS))                             // 获取socket的状态
    {
        case SOCK_CLOSED:                                     // socket处于关闭状态
        socket(SOCK_TCPS ,Sn_MR_TCP,local_port,Sn_MR_ND);   // 打开socket
        break;     

    case SOCK_INIT:                                       // socket已初始化状态
        listen(SOCK_TCPS);                                  // socket建立监听
        break;
    }
    bOkay = TRUE;
    return bOkay;
}
BOOL
xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
{
	*ppucMBTCPFrame = &aucTCPBuf[0];
    *usTCPLength = usTCPBufLen;
    /* Reset the buffer. */
    usTCPBufLen = 0;
    return TRUE;
}
BOOL
xMBTCPPortSendResponse(const UCHAR * pucMBTCPFrame, USHORT usTCPLength )
{
    send(SOCK_TCPS,(UCHAR*)pucMBTCPFrame,usTCPLength);                         // 向Client发送数据
	//send_tcp_socket_data(SOCK_TCP_PORT,(UCHAR*)pucMBTCPFrame,usTCPLength);	
    return TRUE;
}
BOOL
xMBPortTCPPool( void )
{  

  switch(getSn_SR(SOCK_TCPS))                             // 获取socket的状态
  {
    case SOCK_CLOSED:                                     // socket处于关闭状态
      socket(SOCK_TCPS ,Sn_MR_TCP,local_port,Sn_MR_ND);   // 打开socket
      break;     
    
    case SOCK_INIT:                                       // socket已初始化状态
      listen(SOCK_TCPS);                                  // socket建立监听
      break;
    
    case SOCK_ESTABLISHED:                                // socket处于连接建立状态
    
      if(getSn_IR(SOCK_TCPS) & Sn_IR_CON)
      {
        setSn_IR(SOCK_TCPS, Sn_IR_CON);                   // 清除接收中断标志位
      }
      usTCPBufLen=getSn_RX_RSR(SOCK_TCPS);                        // 定义len为已接收数据的长度
      if(usTCPBufLen>0)
      {
        recv(SOCK_TCPS,aucTCPBuf,usTCPBufLen);                         // 接收来自Client的数据
//        aucTCPBuf[len]=0x00;                                   // 添加字符串结束符
//        printf("%s\r\n",aucTCPBuf);
//        send(SOCK_TCPS,aucTCPBuf,len);                         // 向Client发送数据
          ( void )xMBPortEventPost( EV_FRAME_RECEIVED );			//发送已接收到新数据到Modbus-TCP状态机
      }
      break;
    
    case SOCK_CLOSE_WAIT:                                 // socket处于等待关闭状态
			disconnect(SOCK_TCPS);                              // 断开当前TCP连接
      close(SOCK_TCPS);                                   // 关闭当前所使用socket
      break;
  }
	return TRUE;
}

 然后新建一个modbus_tcp.c文件,加入一些协议实现,就是那些什么线圈,什么保持寄存器,什么输入寄存器之类的, 02 04 06 01这些个东西。

#include "modbus_tcp.h"
#include "mb.h"

//输入寄存器内容
uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x0};
//保持寄存器内容		
uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x0};					
//线圈状态
uint8_t ucRegCoilsBuf[REG_COILS_SIZE] = {0x00};
//离散寄存器内容
uint8_t ucRegDiscreteBuf[REG_DISCRETE_SIZE]; //= {0x01,0x01,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x01}; 




/****************************************************************************
* 名	  称:eMBRegInputCB 
* 功    能:读取输入寄存器,对应功能码是 04 eMBFuncReadInputRegister
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机   
*						usAddress: 寄存器地址
*						usNRegs: 要读取的寄存器个数
* 出口参数:
* 注	  意:上位机发来的 帧格式是: SlaveAddr(1 Byte)+FuncCode(1 Byte)
*								+StartAddrHiByte(1 Byte)+StartAddrLoByte(1 Byte)
*								+LenAddrHiByte(1 Byte)+LenAddrLoByte(1 Byte)+
*								+CRCAddrHiByte(1 Byte)+CRCAddrLoByte(1 Byte)
*							3 区
****************************************************************************/
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - REG_INPUT_START );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}

/****************************************************************************
* 名	  称:eMBRegHoldingCB 
* 功    能:对应功能码有:06 写保持寄存器 eMBFuncWriteHoldingRegister 
*													16 写多个保持寄存器 eMBFuncWriteMultipleHoldingRegister
*													03 读保持寄存器 eMBFuncReadHoldingRegister
*													23 读写多个保持寄存器 eMBFuncReadWriteMultipleHoldingRegister
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机   
*						usAddress: 寄存器地址
*						usNRegs: 要读写的寄存器个数
*						eMode: 功能码
* 出口参数:
* 注	  意:4 区
****************************************************************************/
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
	eMBErrorCode    eStatus = MB_ENOERR;
	int             iRegIndex;


	if((usAddress >= REG_HOLDING_START)&&\
		((usAddress+usNRegs) <= (REG_HOLDING_START + REG_HOLDING_NREGS)))
	{
		iRegIndex = (int)(usAddress - REG_HOLDING_START);
		switch(eMode)
		{                                       
			case MB_REG_READ://读 MB_REG_READ = 0
				while(usNRegs > 0)
				{
					*pucRegBuffer++ = (u8)(usRegHoldingBuf[iRegIndex] >> 8);            
					*pucRegBuffer++ = (u8)(usRegHoldingBuf[iRegIndex] & 0xFF); 
					iRegIndex++;
					usNRegs--;					
				}                            
			break;
			case MB_REG_WRITE://写 MB_REG_WRITE = 0
				while(usNRegs > 0)
				{         
					usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
					usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
					iRegIndex++;
					usNRegs--;
				}
			break;				
		}
	}
	else//错误
	{
		eStatus = MB_ENOREG;
	}	
	
	return eStatus;
}

/****************************************************************************
* 名	  称:eMBRegCoilsCB 
* 功    能:对应功能码有:01 读线圈 eMBFuncReadCoils
*						  05 写线圈 eMBFuncWriteCoil
*						  15 写多个线圈 eMBFuncWriteMultipleCoils
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机   
*						usAddress: 线圈地址
*						usNCoils: 要读写的线圈个数
*						eMode: 功能码
* 出口参数:
* 注	  意:如继电器 
*						0 区
****************************************************************************/
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
	eMBErrorCode    eStatus = MB_ENOERR;
	int             iRegIndex;
	u8 i;
	USHORT readNumber=usNCoils;
	USHORT coilValue=0x0000;
	if((usAddress >= REG_COILS_START)&&\
	((usAddress+usNCoils) <= (REG_COILS_START + REG_COILS_SIZE)))
	{
		iRegIndex = (int)(usAddress + usNCoils-REG_COILS_START);
		switch(eMode)
		{                                       
			case MB_REG_READ://读 MB_REG_READ = 0
				for(i=0;i<usNCoils;i++)
				{
					readNumber--;
					iRegIndex--;
					coilValue|=ucRegCoilsBuf[iRegIndex]<<readNumber;
				}
				if(usNCoils<=8)
				{
					* pucRegBuffer=coilValue;
				}
				else
				{
					* pucRegBuffer++ = (coilValue)&0x00ff;
					* pucRegBuffer++ = (coilValue>>8)&0x00ff;
				}
			break;                            
			case MB_REG_WRITE://写 MB_REG_WRITE = 1
				while(usNCoils > 0)
				{         
					// 			usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
					//           usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
						iRegIndex++;
						usNCoils--;
				}
			break;
		}
	}
	else//错误
	{
		eStatus = MB_ENOREG;
	}	

	return eStatus;
}
/****************************************************************************
* 名	  称:eMBRegDiscreteCB 
* 功    能:读取离散寄存器,对应功能码有:02 读离散寄存器 eMBFuncReadDiscreteInputs
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机   
*						usAddress: 寄存器地址
*						usNDiscrete: 要读取的寄存器个数
* 出口参数:
* 注	  意:1 区
****************************************************************************/
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
	eMBErrorCode    eStatus = MB_ENOERR;
	int             iRegIndex;
	u8 i;
	USHORT readNumber=usNDiscrete;
	USHORT coilValue=0x0000;
	iRegIndex = (int)(usAddress + usNDiscrete-REG_DISCRETE_START);
	if((usAddress >= REG_DISCRETE_START)&&\
	((usAddress+usNDiscrete) <= (REG_DISCRETE_START + REG_DISCRETE_SIZE)))
	{
		for(i=0;i<usNDiscrete;i++)
		{
			readNumber--;
			iRegIndex--;
			coilValue|=ucRegDiscreteBuf[iRegIndex]<<readNumber;
		}
		if(usNDiscrete<=8)
		{
			* pucRegBuffer=coilValue;
		}
		else
		{
			* pucRegBuffer++ = (coilValue)&0x00ff;
			* pucRegBuffer++ = (coilValue>>8)&0x00ff;
		}
	}
	else
	{
		eStatus = MB_ENOREG;
	}
    return eStatus;
}


 随便塞点数据

主函数里添加

eMBPoll();

替换原来的

do_tcp_server();

然后烧写,找一个MODBUS tcp测试软件,我这里用的是Qmodbus

设置好IP PORT  function  点send

结果如下

我用这个modbus的软件链接
https://github.com/ed-chemnitz/qmodbus/releases/tag/v0.3.0

  • 4
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值