工程源码下载:基于裸机和Freertos的W5500网络通信工程
目录
1. 移植W5500步骤
2. Cobemx配置步骤
2.1 时钟配置Clock Configuration
2.2 Trace and Debug配置
2.3 使能CPU ICache与DCache
2.4 USART配置
2.5 硬件SPI配置
2.6 GPIO的LED配置
2.7 烧录程序时注意
2.8 FREERTOS配置
2.9 下载程序后不能运行
2.10 提升编译速度
2.11 下载完程序自动启动
3. W5500硬件连接
3.1 初始化SPI
3.2 初始化GPIO
4. W5500程序移植
4.1 修改wizchip_conf.h文件
5. 我们要实现的函数bsp_spi_w5500.c
5.1 W5500芯片复位
5.2 将SPI函数进行与W5500进行注册
5.3 收发缓存设置
5.4 获取芯片ID
5.5 设置网络配置
5.6 读取网络配置
5.7 配置超时
5.8 打印配置信息
6. 实验现象
1. 移植W5500步骤
- 初始化SPI引脚
- 初始化SCS引脚(片选)
- 下载官方库文件
- 添加官方库到工程目录
- 实现SPI接口和库文件的对接
- 调用官方库提供的函数实现连接
2. Cobemx配置步骤
2.1 时钟配置Clock Configuration
所使用的单片机是正点原子的阿波罗底板STM32H743IIT6。
我们看原理图是再29.30引脚接的25MHz的无源晶振。
RCC 配置界面。以外部时钟为例,STM32CubeMX中外部时钟配置可选类型为
Disable
BYPASS Clock Source(旁路时钟源)
Crystal/Ceramic Resonator(石英/陶瓷 晶振)
三种类型,参考手册也将链接电路展示出来了,我们这里用的是无源晶振,故选择第三个方案。
H743凭借外部晶振的加持,最高主频可以达到480MHz。
呢么我们就通过配置时钟树使H743发挥出它最佳状态。
STM32H7 主频在 400~480MHz 下,SPI1,SPI2 和 SPI3 的最高时钟是 200MHz,而 SPI4,5,6 是 100MHz。这里特别注意一点,SPI 工作时最少选择二分频,也就是说 SPI1,2,3 实际通信时钟是100MHz
2.2 Trace and Debug配置
2.3 使能CPU ICache与DCache
STM32F7使用CubeMX生产代码时,可以在Cortex-M7设置里选择是否开启ART ACCLERATOR,ICache,DCache等,如下图
2.4 USART配置
配置调试串口,确定物理引脚PA10\PA9。
时钟配置完成以后,可以在 Connectivity 栏开启UART功能,USART1可以开启的模式有Asynchronous——异步通讯、synchronous——同步通讯、Single Wire(Half-Duplex)——单线(半双工)通讯,此处配置为异步通讯。
2.5 硬件SPI配置
2.6 GPIO的LED配置
PB0和PB1对应开发板LED0和LED1。
2.7 烧录程序时注意
为什么SWD烧录STM32时BOOT0脚要接高电平,否则SWD下载失败。
对配置的程序进行测试测试成功。
2.8 FREERTOS配置
2.9 下载程序后不能运行
进行软件仿真的时候寻踪按键需要执行三次程序测能启动,每次仿真程序都停留在:
解决办法:
记得将Uis Micro LIB勾选。
2.10 提升编译速度
在CobeMX生成的freertos之后,如果我们选择V6编译器会出现很多编译错误,解决办法是:
替换这两个文件,替换的路径如下:
Middlewares\Third_Party\FreeRTOS\Source\portable\RVDS\ARM_CM4F
替换之后编译就不会报错,而且在Browse information勾选的情况下快速编译。
链接在我提供的程序源文件里面:
2.11 下载完程序自动启动
3. W5500硬件连接
10个引脚中,真正使用的也就只有vcc gnd miso mosi sclk scs 这几个脚,RST和int引脚如果是引用官方的库的话,是不需要的(RST是复位引脚,INT是中断触发引脚)。我们这次也需要用到RST引脚。
3.1 初始化SPI
因为W5500模块内部自己实现了TCP/IP协议,在外部只需要通过提供的接口利用SPI把相关的参数传递进去就行了,比如物理地址,本机IP,本机端口,目标端口,目标IP等等。
3.2 初始化GPIO
除去SPI口之外还有一个片选引脚SCS,这个引脚很重要,在官方协议实现的4个函数中,起到的很关键的作用。同时也初始化了复位引脚。
#define CSPin(n) (n? HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,GPIO_PIN_SET):HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,GPIO_PIN_RESET))
#define RSTNPin(n) (n? HAL_GPIO_WritePin(SPI1_RST_GPIO_Port,SPI1_RST_Pin,GPIO_PIN_SET):HAL_GPIO_WritePin(SPI1_RST_GPIO_Port,SPI1_RST_Pin,GPIO_PIN_RESET))
4. W5500程序移植
最新程序下载网址如下:
https://github.com/Wiznet/ioLibrary_Driver
我们解压后需要下面几个
下面介绍以下主要的功能:
application/loopback是一个回环测试的示例程序。
Internet里则是上层多种协议的驱动程序。socket.c 文件夹里面主要是socket的一些函数,比如close connect socket等等函数,实现了一些应用层的封装
w5500.c 文件主要实现的是对W5500模块寄存器的读写和操作,包括读写那些寄存器,地址是多少等等,
wizchip_conf.c 就是一个公共文件主要包括和SPI的连接以及连接W5500读写寄存器函数和socket函数。有用户用于注册函数的几个api以及配置W5X00的一些驱动函数。
DHCP.c 主要就是初始化DHCP,启动DHCP,获取ip,端口等函数
4.1 修改wizchip_conf.h文件
5. 我们要实现的函数bsp_spi_w5500.c
首先根据提供的结构体,将我们要定义的网络配置信息进行赋值:
static wiz_NetInfo NetConf = {.mac = {0x0c, 0x29, 0xab, 0x7c, 0x04, 0x02}, //mac地址.ip = {192, 168, 0, 215}, //本机IP地址.sn = {255, 255, 255, 0}, //子网掩码.gw = {192, 168, 0, 1}, //网关地址.dns = {0, 0, 0, 0}, //DNS服务器地址.dhcp = NETINFO_STATIC //使用静态IP
};
这个结构体存在于wizchip_conf.c,其中NetConf是一个wiz_NetInfo类型的结构体变量,该结构体在wizchip_conf.h中定义,用于设置mac地址、IP地址等网络参数,具体如下:
typedef struct wiz_NetInfo_t
{uint8_t mac[6]; ///< Source Mac Addressuint8_t ip[4]; ///< Source IP Addressuint8_t sn[4]; ///< Subnet Mask uint8_t gw[4]; ///< Gateway IP Addressuint8_t dns[4]; ///< DNS server IP Addressdhcp_mode dhcp; ///< 1 - Static, 2 - DHCP
}wiz_NetInfo;
接下来我们就要编写网络初始化的配置函数,先把程序贴出来,然后再慢慢分解:
void set_w5500_conf(void)
{reset();//芯片复位//注册函数,用户通过实现SPI注册回调函数来访问WIZCHPreg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit); //注册临界区函数 reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect); //注册SPI片选信号函数reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte); //注册读写函数
/***********************物理层配置*************************///初始化芯片参数ctlwizchip(CW_RESET_WIZCHIP,(void *)0);//软件重置uint8_t memsize[16] = {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};uint8_t tmp;//WIZCHIP SOCKET缓存区初始化 ctlwizchip(CW_INIT_WIZCHIP, &memsize);//PHY物理层连接状态检查ctlwizchip(CW_GET_PHYLINK, (void*)&tmp);
/*******************获取芯片ID********************/uint8_t id[6];//获取芯片IDctlwizchip(CW_GET_ID,(void*)id);//获取ID/*******************设置网络配置********************/ ctlnetwork(CN_SET_NETINFO, (void*)&NetConf);/*******************读取网络配置********************/wiz_NetInfo conf;ctlnetwork(CN_GET_NETINFO, (void*)&conf); //读取设置网络状态 if(memcmp(&conf,&NetConf,sizeof(wiz_NetInfo)) == 0){printf("set network parameters success");// 配置成功}else{printf("set network parameters fail"); // 配置失败}
/*******************配置超时********************/ wiz_NetTimeout timeout;timeout.retry_cnt = 8; // 重试次数,默认8次timeout.time_100us = 2000; // 超时时间,默认2000*100us = 200mswizchip_settimeout(&timeout);/*******************打印配置信息********************/ printf("w5500 network infomation:\r\n");printf("w5500 -mac:%d:%d:%d:%d:%d:%d\r\n", conf.mac[0], conf.mac[1], conf.mac[2], conf.mac[3], conf.mac[4], conf.mac[5]);printf("w5500 -ip:%d.%d.%d.%d\r\n", conf.ip[0], conf.ip[1], conf.ip[2], conf.ip[3]);printf("w5500 -sn:%d.%d.%d.%d\r\n", conf.sn[0], conf.sn[1], conf.sn[2], conf.sn[3]);printf("w5500 -gw:%d.%d.%d.%d\r\n", conf.gw[0], conf.gw[1], conf.gw[2], conf.gw[3]);printf("w5500 -dns:%d.%d.%d.%d\r\n", conf.dns[0], conf.dns[1], conf.dns[2], conf.dns[3]);}
5.1 W5500芯片复位
reset();//芯片复位
void reset()
{RSTNPin(0);HAL_Delay(100);RSTNPin(1);HAL_Delay(1500);
}
5.2 将SPI函数进行与W5500进行注册
//注册函数,用户通过实现SPI注册回调函数来访问WIZCHPreg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit); //注册临界区函数 reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect); //注册SPI片选信号函数reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte); //注册读写函数
在spi里实现一些函数,然后把函数名赋值给wizchip_conf.c里面的一些函数指针,这样W5500.c里面的函数实现读写的时候,就能够直接使用SPI接口了,具体实现的函数有以下6个。
void SPI1_WriteByte(u8 TxData)
uint8_t SPI1_ReadByte()
void SPI_CrisEnter(void)
void SPI_CrisExit(void)
void SPI_CS_Select(void)
void SPI_CS_Deselect(void)
/*** @brief 写1字节数据到SPI总线* @param TxData 写到总线的数据* @retval None*/
void SPI_WriteByte(uint8_t TxData)
{ HAL_SPI_Transmit(&SPI_HANDLE, &TxData, 1, 0xFF);
}
/*** @brief 从SPI总线读取1字节数据* @retval 读到的数据*/
uint8_t SPI_ReadByte(void)
{ uint8_t send = 0xff;uint8_t recv;HAL_SPI_TransmitReceive(&SPI_HANDLE, &send, &recv, 1, 0xff);return recv;
}
/*** @brief 进入临界区* @retval None*/
void SPI_CrisEnter(void)
{__set_PRIMASK(1);
}
/*** @brief 退出临界区* @retval None*/
void SPI_CrisExit(void)
{__set_PRIMASK(0);
}
/*** @brief 片选信号输出低电平* @retval None*/
void SPI_CS_Select(void)
{CSPin(0); //低电平有效
}
/*** @brief 片选信号输出高电平* @retval None*/
void SPI_CS_Deselect(void)
{CSPin(1);
}
实现了这几个函数之后,需要把他们注册到库函数里面,利用函数指针对上述函数进行调用。
前面说了库文件wizchip_conf.c 里面就包括了和SPI口的对接实现指针函数。例如:
void reg_wizchip_spi_cbfunc(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb))
{while(!(WIZCHIP.if_mode & _WIZCHIP_IO_MODE_SPI_));if(!spi_rb || !spi_wb){WIZCHIP.IF.SPI._read_byte = wizchip_spi_readbyte;WIZCHIP.IF.SPI._write_byte = wizchip_spi_writebyte;}else{WIZCHIP.IF.SPI._read_byte = spi_rb;WIZCHIP.IF.SPI._write_byte = spi_wb;}
}
5.3 收发缓存设置
分别指定16个端口上收和发缓冲区的大小(KB),收和发分别加起来不能超过16K。默认每个端口的收发端口分别为2K。
/***********************物理层配置*************************///初始化芯片参数ctlwizchip(CW_RESET_WIZCHIP,(void *)0);//软件重置uint8_t memsize[16] = {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};uint8_t tmp;//WIZCHIP SOCKET缓存区初始化 ctlwizchip(CW_INIT_WIZCHIP, &memsize);//PHY物理层连接状态检查ctlwizchip(CW_GET_PHYLINK, (void*)&tmp);
5.4 获取芯片ID
id里是固定的 “W5500”。
/*******************获取芯片ID********************/uint8_t id[6];//获取芯片IDctlwizchip(CW_GET_ID,(void*)id);//获取ID
5.5 设置网络配置
/*******************设置网络配置********************/ ctlnetwork(CN_SET_NETINFO, (void*)&NetConf);
5.6 读取网络配置
/*******************读取网络配置********************/wiz_NetInfo conf;ctlnetwork(CN_GET_NETINFO, (void*)&conf); //读取设置网络状态 if(memcmp(&conf,&NetConf,sizeof(wiz_NetInfo)) == 0){printf("set network parameters success");// 配置成功}else{printf("set network parameters fail"); // 配置失败}
配置成功会串口打印
5.7 配置超时
如发起tcp链接、发送数据等的时候,如果一段时间没有答复(即超过超时时间),W5500会重发,直到超过重试次数还没有得到答复,就会触发超时中断。
/*******************配置超时********************/ wiz_NetTimeout timeout;timeout.retry_cnt = 8; // 重试次数,默认8次timeout.time_100us = 2000; // 超时时间,默认2000*100us = 200mswizchip_settimeout(&timeout);
5.8 打印配置信息
加在配置网络函数内void set_w5500_ip(void),配置完成即可通过串口进行打印。
/*******************打印配置信息********************/ printf("w5500 network infomation:\r\n");printf(" -mac:%d:%d:%d:%d:%d:%d\r\n", conf.mac[0], conf.mac[1], conf.mac[2], conf.mac[3], conf.mac[4], conf.mac[5]);printf(" -ip:%d.%d.%d.%d\r\n", conf.ip[0], conf.ip[1], conf.ip[2], conf.ip[3]);printf(" -sn:%d.%d.%d.%d\r\n", conf.sn[0], conf.sn[1], conf.sn[2], conf.sn[3]);printf(" -gw:%d.%d.%d.%d\r\n", conf.gw[0], conf.gw[1], conf.gw[2], conf.gw[3]);printf(" -dns:%d.%d.%d.%d\r\n", conf.dns[0], conf.dns[1], conf.dns[2], conf.dns[3]);
6. 实验现象
网络配置到这里就算完成了,将这个函数进行初始化后就可以ping通了。