[嵌入式开发模块]用于单线程的W5500控制驱动模块
前言
上一篇中介绍了W5500的官方库中常用的函数的使用。通过这些函数,我们已经可以构建一个正常的TCP/IP应用了。但是上一篇中是一个个函数的讲,虽然是按照使用顺序,难免还是有些混乱。这一篇中,我直接给出了完整的程序框架和自己写的驱动模块,算是把上一篇中讲的整个给串了遍。
另:
- 这个模块使用的全是非阻塞调用,而且只用到了TCP和UDP,所以实际上官方库的代码中有很多都是可以裁剪掉的。我已经尝试过了裁剪并且可以正常运行,所以你会看到里头有两行include是”XXXX_lite.h”,但是已经注释掉而改成官方原版的库,毕竟目标肯定要完全兼容官方库的嘛。
- 模块只负责主要逻辑,不负责注册spi驱动函数等给官方库,需要用户自己注册。
- 官方库真要拿来做产品其实还有很多问题,主要是里头有很多无限循环等待,万一通讯出了一点问题可能程序就永远卡死了。所以实际还要对官方库进行修改,不能有无限循环。
模块代码
W5500Ctr.h
/*
*******************************************************************************************
*
*
* W5500 CONTROLLER FOR SINGLE THREAD
* W5500控制器(单线程)
*
* File : W5500Ctr.h
* By : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date: 2018/09/05
* version: V1.1
* History: 2018/06/01 the prototype of W5500Ctr
* 2018/09/05 V1.1 so many modifications.
* NOTE(s): This module is intended to provide a framework to use W5500 on a single thread
* with offical standard io library, which means all interact with W5500 should be
* processed in on thread.
code example
#include <stdio.h>
#include "SPI.h"
#include "W5500Ctr.h"
......
// for print debug message of this module.
void prinf(const char *str){
printf(str);
}
// implement delay function for module
void Delayms(unsigned int n){
...
}
// implement callback function for io library
uint8_t spi_readbyte(void) {...}
void spi_writebyte(uint8_t wb) {...}
void spi_readburst(uint8_t* pBuf, uint16_t len) {...}
void spi_writeburst(uint8_t* pBuf, uint16_t len) {...}
void cs_select(void) {...}
void cs_deselect(void) {...}
// callback function of this module when TCP connected
void onTCPConnected(uint8_t sn){
...
}
// callback function of this module when TCP/UDP get any data
void onGetData(uint8_t sn){
static uint8_t databuf[DATABUF_SIZE];
uint8_t last_ip[4];
uint16_t last_port;
int32_t len;
if(sn == TCPS_SOCKETNUM || sn == TCPC_SOCKETNUM){
// receive and process all the received data
while((len = recv(sn,databuf,DATABUF_SIZE)) > 0){
...
//send(sn,databuf,(uint16_t)len); // if send back
}
}
if(sn == UDP_SOCKETNUM){
// receive and process all the received data
while((len = recvfrom(sn,databuf,DATABUF_SIZE,last_ip,&last_port)) > 0){
...
//sendto(sn,databuf,(uint16_t)len,last_ip,last_port); // if send back
}
}
}
void main(void){
// init flag for W5500
unsigned char needInit;
// call init functions
...
// register functions to the io library
reg_wizchip_spi_cbfunc(spi_readbyte,spi_writebyte);
reg_wizchip_spiburst_cbfunc(spi_readburst,spi_writeburst);
reg_wizchip_cs_cbfunc(cs_select,cs_deselect);
// register functions to this module
onConnected = onTCPConnected;
onDataReceived = onGetData;
// init at first time
needInit = TRUE;
while(1){
if(needInit){
while(!W5500_Init(NULL,NULL)); // init the W5500 by default configuration
needInit = FALSE;
// Delayms(3000);
}
// drive TCP Server socket
if(Socket_Tick_TCPS(TCPS_SOCKETNUM,TCPS_LPORT) == SOCKETSTATE_UNKNOWN ||
// drive UDP socket
Socket_Tick_UDP(UDP_SOCKETNUM,UDP_LPORT) == SOCKETSTATE_UNKNOWN ||
// drive TCP Client socket
Socket_Tick_TCPC(TCPC_SOCKETNUM,TCPC_LPORT,TCPC_RIP,TCPC_RPORT) == SOCKETSTATE_UNKNOWN){
// if any error, try to initial W5500 again.
needInit = TRUE;
}else{
// some other process if need.
}
}
}
*********************************************************************************************
*/
#ifndef W5500CTR_LITE_H
#define W5500CTR_LITE_H
#ifdef SOCKET_GLOBAL
#define SOCKET_EXT
#else
#define SOCKET_EXT extern
#endif
/*
********************************************************************************************
* MISCELLANEOUS
********************************************************************************************
*/
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/*
*******************************************************************************************
* DEBUG CONFIGURATION 调试配置
*******************************************************************************************
*/
//#define NDEBUG // comment it when is debugging
// user should give the interface to print error message
// prototype: void errmsg_print(char* msg);
extern void prinf(const char *str);
#ifdef NDEBUG
#define errmsg_print(msg)
#define m_assert(cond,errMsg)
#else
#define errmsg_print(msg) prinf(msg)
#define m_assert(cond,errMsg) \
if(!(cond)){ errmsg_print(errMsg); while(1);};
#endif
/*
*******************************************************************************************
* INCLUDES
*******************************************************************************************
*/
//#include "wizchip_conf_lite.h"
//#include "socket_lite.h"
#include "wizchip_conf.h"
#include "socket.h"
/*
*******************************************************************************************
* CONFIGURATION 配置
*******************************************************************************************
*/
// Network default configuration
#define NETCFG_DEFAULT_STATIC_IP_EN TRUE // TRUE: 使用静态IP,否则使用动态分配的IP
#define NETCFG_DEFAULT_LOCAL_IP 192,168,2,135// 本地IP地址,只在NETCFG_STATIC_IP_EN为TRUE时有用
// 格式: XXX,XXX,XXX,XXX
#define NETCFG_DEFAULT_SUBNET_MASK 255,255,255,0// 子网掩码,只在NETCFG_STATIC_IP_EN为TRUE时有用
// 格式: XXX,XXX,XXX,XXX
#define NETCFG_DEFAULT_GATEWAY 192,168,2,1 // 网关地址,只在NETCFG_STATIC_IP_EN为TRUE时有用
// 格式: XXX,XXX,XXX,XXX
#define NETCFG_DEFAULT_DNS_IP 0,0,0,0 // DNS服务器地址,只在NETCFG_STATIC_IP_EN为FALSE时有用
// 格式: XXX,XXX,XXX,XXX
#define NETCFG_DEFAULT_LOCAL_MAC 0x0c,0x29,0xab,0x7c,0x00,0x32 // 本地MAC地址,必填
#define NETCFG_DEFAULT_RETRY_CNT 8 // 超时重传次数(默认8)
#define NETCFG_DEFAULT_RETRY_TIME 2000 // 重试时间(单位:100us,默认2000即200ms)
/*
*******************************************************************************************
* CONSTANT
*******************************************************************************************
*/
#define SOCKETSTATE_UNKNOWN 0 // if unknown or illegal state found, it's suggested
// that reset the W5500
// 发现未知或非法状态,建议重置W5500
#define SOCKETSTATE_CLOSED 1 // if socket closed. The driver func is trying to init
// the socket to the attempted protocol.
// socket关闭状态。驱动函数正试图初始化socket端口为
// 对应协议
#define SOCKETSTATE_NORMAL 2 // if socket is in normal state.
// 一切正常
#define SOCKETSTATE_LINKED 3 // if tcp socket has connected with remote port.
// tcp链接已建立
/*
*******************************************************************************************
* EVENTS
*******************************************************************************************
*/
typedef void (* SOCKET_EVENT)(uint8_t sn);
// 有端口收到数据时触发,用户需要自己到对应端口取数据进行处理,sn为触发这个事件的端口号
SOCKET_EXT SOCKET_EVENT onDataReceived;
// 当socket建立连接时触发,建议如果没有东西发的话也立刻随便发个字符回去,这样才能触发自动心跳检测
// sn为触发这个事件的端口号
SOCKET_EXT SOCKET_EVENT onConnected;
/*
*******************************************************************************************
* FUNCTION PROTOTYPES 函数原型
*******************************************************************************************
*/
// 延迟函数,要求用户在某处实现,n为要延迟多少ms
extern void Delayms(unsigned int n);
// 初始化W5500为指定网络配置
// 返回 TRUE表示成功,FALSE表示失败,在这个成功之后其他操作才有效
// arguments : pNetInfo 指定网络地址配置,NULL使用默认配置
// pNetTo 指定网络重传配置
// return : TRUE if success
// FALSE if fail
uint8_t W5500_Init(wiz_NetInfo const* pNetInfo,wiz_NetTimeout const *pNetTo);
// 获取默认网络参数
// arguments : pNetInfo 返回默认网络地址配置,NULL则不返回
// pNetTo 返回默认网络重传配置, NULL则不返回
// return : void
void getDefaultNetCfg(wiz_NetInfo* pNetInfo,wiz_NetTimeout* pNetTo);
// TCP服务器驱动函数,需要不时调用这个函数以驱动套接字正常运作
// arguments : sn 驱动的套接字号
// lPort 绑定的本地端口
// return : SOCKETSTATE_UNKNOWN 未知状态,建议重置W5500
// SOCKETSTATE_CLOSED socket关闭,重新打开失败
// SOCKETSTATE_NORMAL 一切正常
// SOCKETSTATE_LINKED TCP链接已建立
unsigned char Socket_Tick_TCPS(uint8_t sn,uint16_t lPort);
// TCP客户端驱动函数,需要不时调用这个函数以驱动套接字正常运作
// arguments : sn 驱动的套接字号
// lPort 绑定的本地端口
// dIP 要连接的目标IP
// dPort 要连接的目标端口
// return : SOCKETSTATE_UNKNOWN 未知状态,建议重置W5500
// SOCKETSTATE_CLOSED socket关闭,重新打开失败
// SOCKETSTATE_NORMAL 一切正常
// SOCKETSTATE_LINKED TCP链接已建立
unsigned char Socket_Tick_TCPC(uint8_t sn,uint16_t lPort,uint8_t dIP[4],uint16_t dPort);
// UDP驱动函数,需要不时调用这个函数以驱动套接字正常运作
// arguments : sn 驱动的套接字号
// lPort 绑定的本地端口
// return : SOCKETSTATE_UNKNOWN 未知状态,建议重置W5500
// SOCKETSTATE_CLOSED socket关闭,重新打开失败
// SOCKETSTATE_NORMAL UDP端口已打开
unsigned char Socket_Tick_UDP(uint8_t sn,uint16_t lPort);
#endif
W5500Ctr.c
/*
*******************************************************************************************
*
*
* W5500 CONTROLLER FOR SINGLE THREAD
* W5500控制器(单线程)
*
* File : W5500Ctr.c
* By : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date: 2018/09/05
* version: V1.1
* History:
*
* NOTE(s):
*********************************************************************************************
*/
#define SOCKET_GLOBAL
#include "W5500Ctr.h"
#undef SOCKET_GLOBAL
#include <stddef.h>
#include <stdio.h>
/*
*******************************************************************************************
* LOCAL VARIABLE
*******************************************************************************************
*/
// 默认网络参数
static const wiz_NetInfo _dfNetInfo = {
{NETCFG_DEFAULT_LOCAL_MAC}, // MAC
{NETCFG_DEFAULT_LOCAL_IP}, // IP address
{NETCFG_DEFAULT_SUBNET_MASK}, // Subnet Mask
{NETCFG_DEFAULT_GATEWAY}, // GateWay IP Address
{NETCFG_DEFAULT_DNS_IP}, // DNS server IP Address
#if(NETCFG_DEFAULT_STATIC_IP_EN == TRUE)
NETINFO_STATIC // net type
#else
NETINFO_DHCP
#endif
};
static const wiz_NetTimeout _dfNetTo={
NETCFG_DEFAULT_RETRY_CNT,
NETCFG_DEFAULT_RETRY_TIME
};
/*
*******************************************************************************************
* LOCAL FUNCTION DECLARE
*******************************************************************************************
*/
static void _ISRHander(uint8_t sn);
/*
*********************************************************************************************************
* initialize the W5500 chip
*
* Description : initialize/reset the W5500 chip by given configuration.
*
* Arguments : pNetInfo pointer to the net address configuration, if NULL, use default.
* pNetTo pointer to the net timeout configuration, if NULL, use default.
*
* Return : TRUE if success.
* FALSE if fail(configure fail).
* Note(s) :
*********************************************************************************************************
*/
uint8_t W5500_Init(wiz_NetInfo const* pNetInfo,wiz_NetTimeout const *pNetTo){
uint8_t ip_c[4];
wiz_NetInfo conf;
// 软重启
// wizchip_sw_reset();
setMR(MR_RST); // 对上面函数的简化
// 延迟10ms
Delayms(10);
if(pNetInfo == NULL)
pNetInfo = &_dfNetInfo;
// 设置网络参数
wizchip_setnetinfo((wiz_NetInfo *)pNetInfo);
// 回读确认设置成功
wizchip_getnetinfo(&conf);
#ifndef NDEBUG
{
static char buf[400];
prinf("=== W5500 NET CONF ===\r\n");
(void)sprintf(buf,"MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n"
"MASK:%3u.%3u.%3u.%3u\r\n"
"GATE:%3u.%3u.%3u.%3u\r\n"
"LIP :%3u.%3u.%3u.%3u\r\n"
"地址类型 :静态IP\r\n",
conf.mac[0],conf.mac[1],conf.mac[2],conf.mac[3],conf.mac[4],conf.mac[5],
conf.sn[0],conf.sn[1],conf.sn[2],conf.sn[3],
conf.gw[0],conf.gw[1],conf.gw[2],conf.gw[3],
conf.ip[0],conf.ip[1],conf.ip[2],conf.ip[3]);
prinf(buf);
}
#endif
if(memcmp(&conf,pNetInfo,sizeof(wiz_NetInfo)) != 0)
return FALSE;
// 配置超时
if(pNetTo == NULL)
pNetTo = &_dfNetTo;
if(pNetTo->retry_cnt != 8 || pNetTo->time_100us != 2000)
wizchip_settimeout((wiz_NetTimeout *)pNetTo);
return TRUE;
//设置发送缓冲区和接收缓冲区的大小的方法,默认全部为2K,分别加起来不能超32K,仅供参考
//uint8_t rxsize[8] = {2,2,2,2,2,2,2,2};
//uint8_t txsize[8] = {2,2,2,2,2,2,2,2};
//wizchip_init_size(txsize,rxsize);
}
/*
*********************************************************************************************************
* get default network configuration
*
* Description : get default network configuration.
*
* Arguments : pNetInfo pointer to the space to hold default NetInfo;
* pNetTo pointer to the space to hold default NetTimeout
*
* Return :
* Note(s) :
*********************************************************************************************************
*/
void getDefaultNetCfg(wiz_NetInfo* pNetInfo,wiz_NetTimeout* pNetTo){
if(pNetInfo != NULL)
(void)memcpy((void *)pNetInfo,(void *)&_dfNetInfo,sizeof(wiz_NetInfo));
if(pNetTo != NULL)
(void)memcpy((void *)pNetTo,(void *)&_dfNetTo,sizeof(wiz_NetTimeout));
return;
}
/*
*********************************************************************************************************
* tick for tcp server socket
*
* Description : drive the tcp server socket.
*
* Arguments : sn Socket number
* lPort Port number to be bined locally.
*
* Return : SOCKETSTATE_UNKNOWN if unknown state
* SOCKETSTATE_CLOSED if socket closed and fail opening.
* SOCKETSTATE_NORMAL if every thing is OK.
* SOCKETSTATE_LINKED if linked.
* Note(s) : if return SOCKETSTATE_UNKNOWN, it's recommended to reset the W5500.
*********************************************************************************************************
*/
char errStr1[] = "打开socket 时出现问题\r\n";
char errStr2[] = "socket 开始监听\r\n";
unsigned char Socket_Tick_TCPS(uint8_t sn,uint16_t lPort){
register uint8_t ret = SOCKETSTATE_UNKNOWN;
switch(getSn_SR(sn)){ // 获取socket的状态
case SOCK_CLOSE_WAIT: // Socket处于等待关闭状态
disconnect(sn);
// 故意不break; 与其他状态整合
case SOCK_CLOSED: // 重新Open套接字
if(socket(sn,Sn_MR_TCP,lPort,SF_TCP_NODELAY | SF_IO_NONBLOCK) != sn){
ret = SOCKETSTATE_CLOSED;
#ifndef NDEBUG
errStr1[10] = '0' + sn;
errmsg_print(errStr1);
#endif
break;
}
// 故意不break; 与其他状态整合
case SOCK_INIT: // Socket处于初始化完成(打开)状态
setSn_KPALVTR(sn,2); // 设置心跳包自动发送间隔
(void)listen(sn); // 监听刚刚打开的本地端口,等待客户端连接
ret = SOCKETSTATE_NORMAL;
#ifndef NDEBUG
errStr2[6] = '0' + sn;
errmsg_print(errStr2);
#endif
break;
case SOCK_ESTABLISHED:
ret = SOCKETSTATE_LINKED;
break;
case SOCK_LISTEN:
case SOCK_SYNRECV:
// disconnect-process
case SOCK_TIME_WAIT:
case SOCK_CLOSING:
case SOCK_LAST_ACK:
case SOCK_FIN_WAIT:
ret = SOCKETSTATE_NORMAL;
break;
// 意外的状态返回异常,这种情况一般是通讯上有问题或者W5500没配置成功
//default:
//return SOCKETSTATE_UNKNOWN; // */(初始化值)
}
_ISRHander(sn);
return ret;
}
/*
*********************************************************************************************************
* tick for tcp client socket
*
* Description : drive the tcp client socket.
*
* Arguments : sn Socket number
* lPort Port number to be bined locally.
* dIP the destination ip to connect
* dPort the destination port to connect
*
* Return : SOCKETSTATE_UNKNOWN if unknown state
* SOCKETSTATE_CLOSED if socket closed and fail opening.
* SOCKETSTATE_NORMAL if every thing is OK.
* SOCKETSTATE_LINKED if linked.
* Note(s) : if return SOCKETSTATE_UNKNOWN, it's recommended to reset the W5500.
*********************************************************************************************************
*/
char errStr4[] = "socket 发起连接\r\n";
unsigned char Socket_Tick_TCPC(uint8_t sn,uint16_t lPort,uint8_t dIP[4],uint16_t dPort){
register uint8_t ret = SOCKETSTATE_UNKNOWN;
switch(getSn_SR(sn)){ // 获取socket的状态
case SOCK_CLOSE_WAIT: // Socket处于等待关闭状态
disconnect(sn);
// 故意不break; 与其他状态整合
case SOCK_CLOSED: // 重新Open套接字
if(socket(sn,Sn_MR_TCP,lPort,SF_TCP_NODELAY | SF_IO_NONBLOCK) != sn){
ret = SOCKETSTATE_CLOSED;
#ifndef NDEBUG
errStr1[10] = '0' + sn;
errmsg_print(errStr1);
#endif
break;
}
// 故意不break; 与其他状态整合
case SOCK_INIT: // Socket处于初始化完成(打开)状态
setSn_KPALVTR(sn,2); // 设置心跳包自动发送间隔
(void)connect(sn,dIP,dPort); // 主动连接
ret = SOCKETSTATE_NORMAL;
#ifndef NDEBUG
errStr4[6] = '0' + sn;
errmsg_print(errStr4);
#endif
break;
case SOCK_ESTABLISHED:
ret = SOCKETSTATE_LINKED;
break;
// connect-process
case SOCK_SYNSENT:
// disconnect-process
case SOCK_TIME_WAIT:
case SOCK_CLOSING:
case SOCK_LAST_ACK:
case SOCK_FIN_WAIT:
ret = SOCKETSTATE_NORMAL;
break;
// 意外的状态返回异常,这种情况一般是通讯上有问题或者W5500没配置成功
//default:
//return SOCKETSTATE_UNKNOWN; // */(初始化值)
}
_ISRHander(sn);
return ret;
}
/*
*********************************************************************************************************
* tick for udp server socket
*
* Description : drive the udp socket.
*
* Arguments : sn Socket number
* lPort Port number to be bined locally.
*
* Return : SOCKETSTATE_UNKNOWN if unknown state
* SOCKETSTATE_CLOSED if socket closed and fail opening.
* SOCKETSTATE_NORMAL if udp opend.
* Note(s) : if return 1, it's recommended to reset the W5500.
*********************************************************************************************************
*/
char errStr3[] = "socket 打开UDP\r\n";
unsigned char Socket_Tick_UDP(uint8_t sn,uint16_t lPort){
register uint8_t ret = SOCKETSTATE_UNKNOWN;
switch(getSn_SR(sn)){
case SOCK_CLOSED:
if(socket(sn,Sn_MR_UDP,lPort,SF_IO_NONBLOCK) != sn){
ret = SOCKETSTATE_CLOSED;
#ifndef NDEBUG
errStr1[10] = '0' + sn;
errmsg_print(errStr1);
#endif
break;
};
#ifndef NDEBUG
errStr3[6] = '0' + sn;
errmsg_print(errStr3);
#endif
case SOCK_UDP:
ret = SOCKETSTATE_NORMAL;
break;
// 意外的状态返回异常
//default:
//return SOCKETSTATE_UNKNOWN; (初始化值)
}
_ISRHander(sn);
return ret;
}
/*
*******************************************************************************************
* LOCAL FUNCTION
*******************************************************************************************
*/
static void _ISRHander(uint8_t sn){
uint8_t ir;
ir = getSn_IR(sn);
if(ir & Sn_IR_CON){
setSn_IR(sn, Sn_IR_CON); // Sn_IR的CON位置1,通知W5500连接已建立
if(onConnected != NULL)
onConnected(sn);
}
if(ir & Sn_IR_RECV){
setSn_IR(sn, Sn_IR_RECV); // Sn_IR的RECV位置1,通知接收到了数据
if(onDataReceived != NULL)
onDataReceived(sn);
}
}
简单解释
注释已经注释的很清楚了,函数的用法也在上一篇讲的较清楚了,所以就不细说了。
先去.h文件的配置那里修改一下默认配置。然后到调试配置那里决定一下要不要输出调试信息。
如果需要输出调试信息的话还要实现下prinf函数(没看错,就是没有那个’t’)。
然后实现那个延迟函数,主要是为了裁剪下软重置过程,当然直接使用不裁剪的就可以不用延时函数了。
最后把两个事件给实现了,用于程序的一些业务逻辑。
基本照着下面的示例程序使用就没有问题了。
示例主程序
下面程序是基于MC9S12X的,如果用的其他MCU需要进行相应修改。
用到的两个驱动(SPI、SCI)已经发布在之前的文章中了。
/*
*******************************************************************************************
* example program for W5500Ctr
* This program demonstrate how to implement a TCP/IP application through W5500 by polling
* method with W5500Ctr module.
* You can use this demo as the start point of your application.
* For the purpose of demonstration, I open one tcp client socket, one tcp server socket
* and one udp socket; and all data received are just simply sended back. You can modify it
* as your need.
*********************************************************************************************
*/
#include <hidef.h> /* common defines and macros */
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "derivative.h" /* derivative-specific definitions */
#include "SPI.h"
#include "SCI_def.h"
#include "W5500Ctr.h"
/*
*******************************************************************************************
* CONFIGURATION 配置
*******************************************************************************************
*/
#define BUS_CLOCK 32000000 //总线频率
#define OSC_CLOCK 16000000 //晶振频率
#define HOLD PTJ_PTJ0
#define HOLD_dir DDRJ_DDRJ0
#define CS_dir DDRS_DDRS7
#define W5500_RST PTJ_PTJ0 //定义W5500的RST引脚
#define W5500_SCS PTS_PTS7 //定义W5500的CS引脚
#define RST_dir DDRJ_DDRJ0
#define W5500_SPI SPI0 // 到SPI.h 中的SPI_USED进行设置
#define TCPS_SOCKETNUM 0 // Socket number of tcp server
#define TCPS_LPORT 4000 // local port of tcp server
#define UDP_SOCKETNUM 1 // Socket number of udp server
#define UDP_LPORT 6535 // local port of udp
#define TCPC_SOCKETNUM 2 // Socket number of tcp client
#define TCPC_LPORT 5555 // local port of tcp client
#define TCPC_RIP 192,168,2,50 // remote ip of tcp client to connect
#define TCPC_RPORT 3333 // remote port of tcp client to connect
/*
*******************************************************************************************
* LOCAL FUNCTION DECLARATION
*******************************************************************************************
*/
static void PLL_Init(void);
static void GPIO_Init(void);
/*
*******************************************************************************************
* DEBUG FUNCTION
*******************************************************************************************
*/
// W5500Ctr调试用函数,通过在W5500Ctr.h的配置中uncomment
// #define NDEBUG
// 以取消输出调试信息
void prinf(const char *str){
while(*str){
SCI_PutChar(SCI0,*str);
str++;
}
}
/*
*******************************************************************************************
* DELAY FUNCTION
*******************************************************************************************
*/
void Delayms(unsigned int n){
unsigned int i,j;
for(j=0;j<n;j++)
for(i=0;i<6700;i++)
;
}
/*
*******************************************************************************************
* FUNCTIONS FOR LIBRARY
*******************************************************************************************
*/
uint8_t spi_readbyte(void) {
return SPI_ExchangeChar(W5500_SPI,0xaa);}
void spi_writebyte(uint8_t wb) {SPI_ExchangeChar(W5500_SPI,wb);}
void spi_readburst(uint8_t* pBuf, uint16_t len) {
for(;len > 0;len--, pBuf++){
*pBuf = SPI_ExchangeChar(W5500_SPI,0xaa);
}
}
void spi_writeburst(uint8_t* pBuf, uint16_t len) {
for(;len > 0;len--, pBuf++){
SPI_ExchangeChar(W5500_SPI,*pBuf);
}
}
void cs_select(void) {W5500_SCS = 0;}
void cs_deselect(void) {W5500_SCS = 1;}
/*
*******************************************************************************************
* CALL BACK FUNCTIONS FOR LIBRARY
*******************************************************************************************
*/
// TCP一建立链接就会触发这个事件
void onTCPConnected(uint8_t sn){
// 建议立刻发送点任意数据,这样才能触发W5500自动的超时检测
#define DATABUF_SIZE 400
static uint8_t databuf[DATABUF_SIZE];
#ifndef NDEBUG
uint8_t ip[4];
uint16_t port;
// 记录连接的地址
getSn_DIPR(sn,ip);
port = getSn_DPORT(sn);
sprintf(databuf,"=== Socket %u 连接建立 ===\r\n"
"DIP : %3u.%3u.%3u.%3u\r\n"
"DPort: %u\r\n",
sn,ip[0],ip[1],ip[2],ip[3], port);
prinf(databuf);
#endif
}
// 每次收到数据时触发。
void onGetData(uint8_t sn){
#define DATABUF_SIZE 400
static uint8_t databuf[DATABUF_SIZE];
uint8_t last_ip[4]; // only for UDP
uint16_t last_port; // only for UDP
int32_t len;
if(sn == TCPS_SOCKETNUM || sn == TCPC_SOCKETNUM){
while((len = recv(sn,databuf,DATABUF_SIZE)) > 0){
prinf("收到TCP数据:");
send(sn,databuf,(uint16_t)len); // 回环发送
databuf[len] = '\0';
prinf(databuf); // 串口打印接收到的数据
}
}else if(sn == UDP_SOCKETNUM){
while((len = recvfrom(sn,databuf,DATABUF_SIZE,last_ip,&last_port)) > 0){
prinf("收到UDP数据:");
sendto(sn,databuf,(uint16_t)len,last_ip,last_port); // 回环发送
databuf[len] = '\0';
prinf(databuf); // 串口打印接收到的数据
}
}
}
/*
*******************************************************************************************
* MAIN FUNCTION
*******************************************************************************************
*/
void main(void){
unsigned char needInit;
uint8_t tcp_rip[4] = {TCPC_RIP};
GPIO_Init();
PLL_Init();
SCI_Init(SCI0);
SCI_EnableTrans(SCI0);
SCI_EnableRecv(SCI0);
SPI_Init(SPI0);
SPI_Enable(SPI0);
// 注册驱动函数
reg_wizchip_spi_cbfunc(spi_readbyte,spi_writebyte);
reg_wizchip_spiburst_cbfunc(spi_readburst,spi_writeburst);
reg_wizchip_cs_cbfunc(cs_select,cs_deselect);
onConnected = onTCPConnected;
onDataReceived = onGetData;
needInit = TRUE;
while(1){
if(needInit){
while(!W5500_Init(NULL,NULL)); // 用默认参数初始化,初始化失败则不断重试
needInit = FALSE;
Delayms(3000);
}
// 驱动 TCP Server 端口
if(Socket_Tick_TCPS(TCPS_SOCKETNUM,TCPS_LPORT) == SOCKETSTATE_UNKNOWN ||
// 驱动 UDP 端口
Socket_Tick_UDP(UDP_SOCKETNUM,UDP_LPORT) == SOCKETSTATE_UNKNOWN ||
// 驱动 TCP 端口
Socket_Tick_TCPC(TCPC_SOCKETNUM,TCPC_LPORT,tcp_rip,TCPC_RPORT) == SOCKETSTATE_UNKNOWN){
// 任一次tick发现有问题则需要重新初始化W5500
needInit = TRUE;
}
}
}
/*
*******************************************************************************************
* LOCAL FUNCTION
*******************************************************************************************
*/
// 初始化锁相环
void PLL_Init(void){
CLKSEL &= 0x7f; //set OSCCLK as sysclk
PLLCTL &= 0x8F; //DisaKble PLL circuit
CRGINT &= 0xDF;
#if(BUS_CLOCK == 40000000)
SYNR = 0x44;
#elif(BUS_CLOCK == 32000000)
SYNR = 0x43;
#elif(BUS_CLOCK == 24000000)
SYNR = 0x42;
#endif
REFDV = 0x81; //PLLCLK=2×OSCCLK×(SYNR+1)/(REFDV+1)=64MHz ,fbus=32M
PLLCTL =PLLCTL|0x70; //Enable PLL circuit
asm NOP;
asm NOP;
while(!(CRGFLG&0x08)); //PLLCLK is Locked already
CLKSEL |= 0x80; //set PLLCLK as sysclk
}
// 初始化IO口
void GPIO_Init(void){
HOLD_dir = 1;
HOLD = 1;//复位引脚拉高
CS_dir = 1;
}
还没有评论,来说两句吧...