查看: 66|回复: 0

[教程] STM32F412应用开发——结合W5500实现以太网通讯

[复制链接]

146

主题

0

好友

2686

积分

进士

Rank: 5Rank: 5

  • TA的每日心情
    开心
    2014-4-21 09:44
  • 签到天数: 26 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    发表于 2017-8-10 10:49:41 |显示全部楼层
    因实际使用需求我们测试一下网络通讯,在NUCLEO-F412ZG测试板上没有以太网部分,我们选择外接一个W5500的实验板。W5500支持SPI接口通讯,DC3.3V供源。而NUCLEO-F412ZG测试板已经将SPI1的各引脚SPI1_NSS(CN7_17)、SPI1_SCK(CN7_10)、SPI1_MISO(CN7_12)、SPI1_MOSI(CN7_14)引出到CN7,3.3VDC和GND也引导了CN8,可以方便的实现测试。如下图红框所示:
    564295-20170101145937289-1822231093.jpg

    1、测试的准备工作
    为了实现以太网通讯首先需要下载W5500的驱动源码,可以到WIZnet的官网下载:http://www.iwiznet.cn/
    目前最新版本为V1.1,我选用的是V1.03下载下来的压缩包解压后如下图:
    564295-20170101145958195-303958877.png

    需要将ethernet文件夹拷贝到我们的项目目录中:
    564295-20170101150011695-843170500.png


    并在IAR的项目下添加相关的文件和路径,主要是socket.c、w5500.c、wizchip_.conf.c三个文件。这三个文件分别实现socket、硬件驱动及相关通讯配置功能,具体可以查看相应的源码级手册。
    564295-20170101150026632-1274799197.png


    2、编写测试代码
    在完成以上工作后就可以开始真正地移植工作了。具体步骤如下:
    • 硬件配置及初始化。
    • 以太网通讯配置的初始化。
    • 实现具体的通讯过程。
    2.1、硬件的配置及初始化
    由于W5500通过SPI接口与STM32通讯,所以硬件配置和初始化是非常简单的,与W5500实际上没有关系,使一些通用的操作。事实上就是STM32F412ZG的SPI接口初始化的过程,需要实现RCC、GPIO以及SPI的初始化就可以了。关于这部分可以查看ST的例程。
    2.2、以太网通讯配置的初始化
    以太网通讯配置的初始化主要有三个方面的内容:
    • 注册TCP通讯相关的回调函数  RegisterFunction();
    • 初始化芯片参数  ChipParametersConfiguration();
    • 初始化网络通讯参数  NetworkParameterConfiguration()
    三个函数的具体实现内容如下:
    1. //函数注册,首先,应由用户实现SPI注册回调函数来访问WIZCHIP

    2. void RegisterFunction(void)

    3. {

    4.   //临界区回调函数

    5.   reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit);  //注册临界区函数

    6.   //片选回调函数

    7. #if   _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_

    8.   reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);//注册SPI片选信号函数

    9. #elif _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_FDM_

    10.   reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);  // CS必须为低电平.

    11. #else

    12.    #if (_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SIP_) != _WIZCHIP_IO_MODE_SIP_

    13.       #error "Unknown _WIZCHIP_IO_MODE_"

    14.    #else

    15.       reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);

    16.    #endif

    17. #endif

    18.   //SPI的读写回调函数

    19.   reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte);     //注册读写函数

    20. }
    复制代码
    注册函数实际上就是函数指针的调用,可参考C语言函数指针部分内容。对于以上注册的函数,SPI_WriteByte需要说明一下,无论是用可函数还是直接操作寄存器,在写完之后都需要再读一下(红色部分),否则就会在客户端出现连接TCPServer超时的报警,没明白什么原因。
    1. //写1字节数据到SPI总线

    2. void SPI_WriteByte(uint8_t TxData)

    3. {                       

    4. //  while((SPI2->SR&SPI_I2S_FLAG_TXE)==0);        //等待发送区空              

    5. //  SPI2->DR=TxData;                              //发送一个byte

    6. //  while((SPI2->SR&SPI_I2S_FLAG_RXNE)==0);       //等待接收完一个byte

    7. //  SPI2->DR;

    8.   while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);        //等待发送区空

    9.   SPI_I2S_SendData(SPI2,TxData);                                        //发送一个byte

    10.   while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET);        //等待接收完一个byte

    11.   SPI_I2S_ReceiveData(SPI2);                                            //返回接收的数据

    12. }





    13. //初始化芯片参数

    14. void ChipParametersConfiguration(void)

    15. {

    16.   uint8_t tmp;

    17.   uint8_t memsize[2][8] = {{2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2}};

    18.   //WIZCHIP SOCKET缓存区初始化

    19.   if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -1){

    20.     //printf("WIZCHIP Initialized fail.\r\n");

    21.   while(1);

    22.   }



    23.   //PHY物理层连接状态检查

    24.   do{

    25.     if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1){

    26.       //printf("Unknown PHY Link stauts.\r\n");

    27.     }

    28.   }while(tmp == PHY_LINK_OFF);

    29. }
    复制代码
    以上实现网络物理层的配置。
    1. //初始化WIZCHIP中的网络参数信息

    2. void NetworkParameterConfiguration(void)

    3. {

    4.   uint8_t tmpstr[6];

    5.   ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);

    6.   ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);



    7.   ctlwizchip(CW_GET_ID,(void*)tmpstr);

    8. }
    复制代码
    其中gWIZNETINFO是一个wiz_NetInfo类型的结构体变量,该结构体在wizchip_conf.h中定义,用于设置mac地址、IP地址等网络参数,具体如下:
    1. typedef struct wiz_NetInfo_t

    2. {

    3.    uint8_t mac[6];  ///< Source Mac Address

    4.    uint8_t ip[4];   ///< Source IP Address

    5.    uint8_t sn[4];   ///< Subnet Mask

    6.    uint8_t gw[4];   ///< Gateway IP Address

    7.    uint8_t dns[4];  ///< DNS server IP Address

    8.    dhcp_mode dhcp;  ///< 1 - Static, 2 - DHCP

    9. }wiz_NetInfo;
    复制代码
    至此网络部分的初始化就已完成。
    2.3、具体通讯过程的实现
    经过前面的配置网络已经可以ping通了,下面可以实现具体的应用。对于我这个项目就是可是实现Modbus TCP的编写了。
    编写TCP Server,这部分有很多资料,直接附代码:
    1. //TCP服务器数据通讯

    2. int32_t TCPServer(uint8_t sn, uint16_t port)

    3. {

    4.   int32_t ret;

    5.   uint8_t socketStatus=getSn_SR(sn);



    6.   switch(socketStatus)

    7.   {

    8.     case SOCK_ESTABLISHED :

    9.       {

    10.         if(getSn_IR(sn) & Sn_IR_CON)

    11.         {

    12.           setSn_IR(sn,Sn_IR_CON);

    13.         }

    14.         uint16_t size=0;

    15.         if((size = getSn_RX_RSR(sn)) > 0)

    16.         {

    17.           if(size > DATA_BUFFER_SIZE)

    18.           {

    19.             size = DATA_BUFFER_SIZE;

    20.           }

    21.           uint8_t rxBuffer[DATA_BUFFER_SIZE];

    22.           ret = recv(sn,rxBuffer,size);

    23.           if(ret <= 0)

    24.           {

    25.             return ret;

    26.           }

    27.           //添加数据解析及响应的函数

    28.           uint8_t txBuffer[DATA_BUFFER_SIZE];

    29.           uint16_t length=ReceivedDataParsing(rxBuffer,txBuffer);

    30.          

    31.           uint16_t sentsize=0;

    32.           while(length != sentsize)

    33.           {

    34.             ret = send(sn,txBuffer+sentsize,length-sentsize);

    35.             if(ret < 0)

    36.             {

    37.               close(sn);

    38.               return ret;

    39.             }

    40.             sentsize += ret; // 不用管SOCKERR_BUSY, 因为它是零.

    41.           }

    42.         }

    43.         break;

    44.       }

    45.     case SOCK_CLOSE_WAIT :

    46.       if((ret=disconnect(sn)) != SOCK_OK)

    47.       {

    48.         return ret;

    49.       }

    50.       break;

    51.     case SOCK_INIT :

    52.       if( (ret = listen(sn)) != SOCK_OK)

    53.       {

    54.         return ret;

    55.       }

    56.       break;

    57.     case SOCK_CLOSED:

    58.       if((ret=socket(sn,Sn_MR_TCP,port,0x00)) != sn)

    59.       {

    60.         return ret;

    61.       }

    62.       break;

    63.     default:

    64.       break;

    65.   }

    66.   return 1;

    67. }
    复制代码
    其中ReceivedDataParsing(rxBuffer,txBuffer)实现具体的应用协议,根据具体的需求而定,在此我们实现了简单的Modbus TCP协议。
    3、测试结果
    完成编写,调试无误后,将程序下载到目标板,首先既然是以太网通讯,我们就在上位机上机简单的网络测试。在目标板上我们设定的IP地址为:192.168.1.100,在CMD中使用简单的Ping命令测试如下:
    564295-20170101150115836-952222896.png


    由上图可以看到,网路没有问题。接下来我们用TCP&UDP测试工具进行进一步的测试,并使用Microsoft Network Monitor监视数据包。打开TCP&UDP测试工具,创建一个TCP客户端,目标IP还是目标板IP,端口用502(我们在软件中设定了)。
    564295-20170101150127882-70913725.png

    创建完成后,连接无误,在发送栏中以16进制发送一条指令,可以看到下方的接收栏中收到数据。
    564295-20170101150140461-2143076607.png


    我们再看看Microsoft Network Monitor中捕获到的数据包核对一下数据是否正确。
    564295-20170101150157211-539874151.png


    由于我们实现了简单的Modbus TCP协议,所以我们在使用ModScan32来测试一下通讯是否正确。首先启动ModScan32,并做如下配置:
    564295-20170101150221117-1633871184.png

    连接后数据显示出来,持续更新一段时间后没有问题。
    564295-20170101150238945-1602486776.png


    同样,我们再看看Microsoft Network Monitor中捕获到的数据更新与ModScan32做一对比,数据包完整而且正确。
    564295-20170101150305226-1140581536.png

    完成测试,通讯没有问题STM32F412与W5500实现以太网通讯简单方便。
    转自博客园,博主昵称foxclever
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    关闭

    站长推荐上一条 /4 下一条

    手机版|爱板网 |网站地图  

    GMT+8, 2017-8-24 14:50 , Processed in 0.121023 second(s), 11 queries , Memcache On.

    苏公网安备 32059002001056号

    Powered by Discuz!

    回顶部