本文共 9376 字,大约阅读时间需要 31 分钟。
本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件SPI外设与以W5500通信,驱动以太网模块。
1. 准备工作
硬件准备
- 开发板
首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):
- W5500以太网模块
这里我使用常见的以太网模块W5500:
软件准备
- 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
- 准备一个串口调试助手,这里我使用的是
Serial Port Utility
; - 准备一个网络调试助手,这里我使用的是
sockettool
;
2.生成MDK工程
选择芯片型号
打开STM32CubeMX,打开MCU选择器:
搜索并选中芯片STM32L431RCT6
:
配置时钟源
- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以略过;
这里我都使用外部时钟:
配置以太网模块控制GPIO
以太网模块需要额外配置的GPIO有两个:
以太网模块引脚名 | GPIO | 作用 |
---|---|---|
RST | PC9 | 以太网模块硬复位 |
INT | PA0 | 中断引脚 |
复位引脚配置为输出模式即可:
中断引脚需要接收来自以太网模块的中断,所以需要配置EXTI外部中断引脚:配置SPI1接口
本实验中,我将以太网模块接到了SPI1接口,引脚对应表如下:
需要注意,SPI片选引脚不通过硬件SPI外设来控制,而是配置为普通GPIO,手动控制。
以太网模块引脚 | MCU引脚 |
---|---|
MISO | PA6(SPI1_MISO) |
MOSI | PA12(SPI1_MOSI) |
SCS | PA4(SPI1_NSS) |
SCLK | PA1(SPI1_SCK) |
配置SPI接口的时候有三个需要注意的点:
① 分频系数;
② CPOL:CLK空闲时候的电平为高电平或者低电平; ③ CPHA:在第1个时钟边缘采样,还是在第2个时钟边缘采样;接下来开始配置SPI1外设,首先配置SPI1外设的模式和引脚:
因为选择了不使用硬件SPI外设控制片选引脚,所以需要手动配置片选引脚PA4: W5500手册中给出的SPI总线时钟为80Mhz: 但是,需要注意,手册中明确注明了实际至少保证33.3Mhz,所以为了稳妥起见,本实验中配置SPI总线时钟为20Mhz: 对于CPOL,W5500两种模式都支持,选择空闲时为LOW的模式,CPHA手册中给出为第一个时钟沿: 综上所述,时序参数配置如下:配置串口
开发板板载了一个CH340z换串口,连接到USART1。
接下来开始配置USART1
:
配置时钟树
STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz
即可:
生成工程设置
代码生成设置
最后设置生成独立的初始化文件:
生成代码
点击GENERATE CODE
即可生成MDK-V5工程:
3. 重定向printf函数到USART1
参考:。
4. 移植W5500官方驱动库
4.1. 下载官方驱动库
W5500官方提供了ioLibrary v2.0.0,ioLibrary是WIZnet芯片的以太网驱动库,它包括驱动程序和应用程序协议。该驱动程序(ioLibrary)可用于WIZnet TCP / IP芯片的应用设计,如W5500,W5300,W5200,W5100 W5100S。
下载地址有两个:
- github开源仓库地址:
- gitee仓库地址(为了下载速度较快,博主同步到了gitee):
源码目录结构如图:
- Ethernet : 类似BSD的SOCKET API接口,以及WIZCHIP(W5500 / W5300 / W5200 / W5100 / W5100S) 驱动
- Internet : 各种应用层协议栈
- DHCP client
- DNS client
- FTP client
- FTP server
- SNMP agent/trap
- SNTP client
- TFTP client
- HTTP server
- MQTT Client
4.2. 添加驱动库到工程中
在工程目录下新建 Hardware/W5500,将驱动库中的三个文件夹都复制过来:
注意,这其中只有Ethernet下的文件是必需的,其余两个文件夹的文件可选添加,在后面进行测试时会用到。接下来将Ethernet目录下和W5500相关的文件添加到MDK工程中:
添加头文件路径: 确保C99模式开启(STM32Cubemx生成的工程中默认开启):4.3. 配置所使用的芯片型号
打开wizchip_conf.h
文件,在最开始修改宏定义_WIZCHIP_
,该宏定义指明了我们所用的芯片型号,设置为W5500:
5. 适配W5500官方驱动
W5500官方驱动库中通过 _WIZCHIP 结构体中定义的一组函数指针来管理spi驱动,为了防止添加后直接报错,在 wizchip_conf.c 中提供了这些函数指针的默认实现,都为空函数,所以此时编译时不会报错。
这两个适配文件已开源,Github地址:。
5.1. 添加移植适配文件
接下来我们在项目工程中,新建w5500_port_hal.h
文件和w5500_port_hal.c
文件来存放自己的实现,并利用驱动库提供的接口,注册到驱动库中。
5.2. 编写头文件
编写w5500_port_hal.h
文件:
#ifndef _W5500_PORT_HAL_#define _W5500_PORT_HAL_#include "wizchip_conf.h"#include "stm32l4xx.h"#include#include #define W5500_SPI_HANDLE hspi1#define W5500_CS_PORT GPIOA#define W5500_CS_PIN GPIO_PIN_4#define W5500_RST_PORT GPIOC#define W5500_RST_PIN GPIO_PIN_9#define DEFAULT_MAC_ADDR {0x00,0xf1,0xbe,0xc4,0xa1,0x05}#define DEFAULT_IP_ADDR {192,168,0,136}#define DEFAULT_SUB_MASK {255,255,255,0}#define DEFAULT_GW_ADDR {192,168,0,1}#define DEFAULT_DNS_ADDR {8,8,8,8}/* 定义该宏则表示使用自动协商模式,取消则设置为100M全双工模式 */#define USE_AUTONEGO/* 定义该宏则表示在初始化网络信息时设置DHCP *///#define USE_DHCPextern SPI_HandleTypeDef W5500_SPI_HANDLE;void w5500_network_info_show(void);int w5500_init(void);#endif
5.3. 编写c文件
首先包含头文件:
#include "w5500_port_hal.h"
5.3.1. SPI驱动接口实现
接着用HAL库实现W5500驱动所需要的8个SPI函数指针的具体函数:
/** * @brief enter critical section * @param none * @return none */static void w5500_cris_enter(void){ __set_PRIMASK(1);}/** * @brief exit critical section * @param none * @return none */static void w5500_cris_exit(void){ __set_PRIMASK(0);}/** * @brief select chip * @param none * @return none */static void w5500_cs_select(void){ HAL_GPIO_WritePin(W5500_CS_PORT, W5500_CS_PIN, GPIO_PIN_RESET);}/** * @brief deselect chip * @param none * @return none */static void w5500_cs_deselect(void){ HAL_GPIO_WritePin(W5500_CS_PORT, W5500_CS_PIN, GPIO_PIN_SET);}/** * @brief read byte in SPI interface * @param none * @return the value of the byte read */static uint8_t w5500_spi_readbyte(void){ uint8_t value; if (HAL_SPI_Receive(&W5500_SPI_HANDLE, &value, 1, 1000) != HAL_OK) { value = 0; } return value;}/** * @brief write byte in SPI interface * @param wb the value to write * @return none */static void w5500_spi_writebyte(uint8_t wb){ HAL_SPI_Transmit(&W5500_SPI_HANDLE, &wb, 1, 1000);}/** * @brief burst read byte in SPI interface * @param pBuf pointer of data buf * @param len number of bytes to read * @return none */static void w5500_spi_readburst(uint8_t* pBuf, uint16_t len){ if (!pBuf) { return; } HAL_SPI_Receive(&W5500_SPI_HANDLE, pBuf, len, 1000);}/** * @brief burst write byte in SPI interface * @param pBuf pointer of data buf * @param len number of bytes to write * @return none */static void w5500_spi_writeburst(uint8_t* pBuf, uint16_t len){ if (!pBuf) { return; } HAL_SPI_Transmit(&W5500_SPI_HANDLE, pBuf, len, 1000);}/** * @brief hard reset * @param none * @return none */static void w5500_hard_reset(void){ HAL_GPIO_WritePin(W5500_RST_PORT, W5500_RST_PIN, GPIO_PIN_RESET); HAL_Delay(50); HAL_GPIO_WritePin(W5500_RST_PORT, W5500_RST_PIN, GPIO_PIN_SET); HAL_Delay(10);}
5.3.2. 芯片操作实现
基于官方驱动库编写芯片初始化函数,并设置socket的发送和接收缓冲大小(默认2KB):
/** * @brief Initializes WIZCHIP with socket buffer size * @param none * @return errcode * @retval 0 success * @retval -1 fail */static int w5500_chip_init(void){ /* default size is 2KB */ return wizchip_init(NULL, NULL);}
再编写硬件PHY配置函数,比如工作模式、速率,以及是否协商等配置:
自动协商功能需要在上电前连接好网线至路由器,手动配置模式不需要。
/** * @brief set phy config if autonego is disable * @param none * @return none */static void w5500_phy_init(void){ #ifdef USE_AUTONEGO // no thing to do#else wiz_PhyConf conf; conf.by = PHY_CONFBY_SW; conf.mode = PHY_MODE_MANUAL; conf.speed = PHY_SPEED_100; conf.duplex = PHY_DUPLEX_FULL; wizphy_setphyconf(&conf);#endif}
再编写配置和打印网络信息函数:
/** * @brief initializes the network infomation * @param none * @return none */static void w5500_network_info_init(void){ wiz_NetInfo info; uint8_t mac[6] = DEFAULT_MAC_ADDR; uint8_t ip[4] = DEFAULT_IP_ADDR; uint8_t sn[4] = DEFAULT_SUB_MASK; uint8_t gw[4] = DEFAULT_GW_ADDR; uint8_t dns[4] = DEFAULT_DNS_ADDR; memcpy(info.mac, mac, 6); memcpy(info.ip, ip, 4); memcpy(info.sn, sn, 4); memcpy(info.gw, gw, 4); memcpy(info.dns, dns, 4); #ifdef USE_DHCP info.dhcp = NETINFO_DHCP;#else info.dhcp = NETINFO_STATIC;#endif wizchip_setnetinfo(&info);}/** * @brief read and show the network infomation * @param none * @return none */void w5500_network_info_show(void){ wiz_NetInfo info; wizchip_getnetinfo(&info); printf("w5500 network infomation:\r\n"); printf(" -mac:%d:%d:%d:%d:%d:%d\r\n", info.mac[0], info.mac[1], info.mac[2], info.mac[3], info.mac[4], info.mac[5]); printf(" -ip:%d.%d.%d.%d\r\n", info.ip[0], info.ip[1], info.ip[2], info.ip[3]); printf(" -sn:%d.%d.%d.%d\r\n", info.sn[0], info.sn[1], info.sn[2], info.sn[3]); printf(" -gw:%d.%d.%d.%d\r\n", info.gw[0], info.gw[1], info.gw[2], info.gw[3]); printf(" -dns:%d.%d.%d.%d\r\n", info.dns[0], info.dns[1], info.dns[2], info.dns[3]); if (info.dhcp == NETINFO_DHCP) { printf(" -dhcp_mode: dhcp\r\n"); } else { printf(" -dhcp_mode: static\r\n"); }}
最后编写w5500初始化函数:
/** * @brief w5500 init * @param none * @return errcode * @retval 0 success * @retval -1 chip init fail */int w5500_init(void){ /* W5500 hard reset */ w5500_hard_reset(); /* Register spi driver function */ reg_wizchip_cris_cbfunc(w5500_cris_enter, w5500_cris_exit); reg_wizchip_cs_cbfunc(w5500_cs_select, w5500_cs_deselect); reg_wizchip_spi_cbfunc(w5500_spi_readbyte, w5500_spi_writebyte); reg_wizchip_spiburst_cbfunc(w5500_spi_readburst, w5500_spi_writeburst); /* socket buffer size init */ if (w5500_chip_init() != 0) { return -1; } /* phy init */ w5500_phy_init(); /* network infomation init */ w5500_network_info_init(); /* show network infomation */ w5500_network_info_show(); return 0;}
5.3. 测试W5500初始化
在main.c中包含头文件:
#include "w5500_port_hal.h"
在main函数中测试初始化函数:
/* USER CODE BEGIN 2 */ printf("W5500 test on BearPi board by Mculover666\r\n"); int ret; ret = w5500_init(); if (ret != 0) { printf("w5500 init fail, ret is %d\r\n", ret); } else { printf("w5500 init success\r\n"); } /* USER CODE END 2 */
编译,下载,暂不运行。
因为使用的是自动协商模式,确保W5500网线连接至路由器,然后上电运行,串口日志如下:
确保windows主机和开发板连接至同一个路由器(或者同一网段之下),ping一下开发板测试:6. W5500的Socket测试
W5500官方驱动库中实现了标准Socket API,在socket.h和socket.c中,可以直接调用编写TCP或者UDP测试程序。
W5500官方驱动库中也提供了一个Socket的使用案例,其中包括TCP服务端、TCP客户端、UDP服务端的回环测试,在application/loopback文件夹中:
本文接下来将进行TCP客户端的回环测试。6.1. 开启TCP服务器
在电脑上开启网络调试助手,建立一个TCP server,监听本机8000端口:
6.2. 添加loopback测试文件
在MDK中添加c文件:
添加头文件路径:6.3. 调用loopback测试函数
在main函数的开始创建变量:
/* USER CODE BEGIN 1 */int ret;uint8_t destip[4] = { 192, 168, 0, 100};uint16_t destport = 8000;/* USER CODE END 1 */
然后在while循环中调用:
/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){ /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ ret = loopback_tcpc(0, buffer, destip, destport); if (ret != 1) { printf("loopback_tcpc err is %d\r\n", ret); }}/* USER CODE END 3 */
6.4. 测试结果
编译、下载到开发板中运行,串口日志如下:
在网络调试助手向开发板发送消息,会收到开发板发回的消息:若开发板提示连接超时,无法连接TCP服务器,应当检查是否关闭windows网络防火墙。
更多精彩文章及资源,请关注我的微信公众号:『mculover666』。
转载地址:https://mculover666.blog.csdn.net/article/details/114711391 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!