W5500简介【w5500具有8个Socket,TCPserver模式下最多支持8个客户端同时访问】
https://blog.csdn.net/wiznet2012/article/details/41279113
(此论坛为一个WIZnet用户开设的,论坛积累了大量的W5500相关应用及例程,具有很大的参考价值。)
1. 前言
W5500是一款高性价比的以太网芯片,其全球独一无二的全硬件TCPIP协议栈专利技术,解决了嵌入式以太网的接入问题,简单易用,安全稳定,是物联网设备的首选解决方案。
WIZnet的官方库,官方库写得很好,移植也很简单,功能全面。
https://github.com/Wiznet/ioLibrary_Driver
官方资源:
https://www.iwiznet.cn/
https://www.w5500.com/
分以下几个方面介绍W5500:
W5500芯片简介
库文件组成介绍
库文件移植过程
官方库源码分析
2. W5500芯片简介
W5500 是一款全硬件 TCP/IP 嵌入式以太网控制器,为嵌入式系统提供了更加简易的互联网连接方 案。W5500 集成了 TCP/IP 协议栈,10/100M 以太网数据链路层(MAC) 及物理层(PHY),使得 用户使用单芯片就能够在他们的应用中拓展网络连接。
久经市场考验的 WIZnet 全硬件 TCP/IP 协议栈支持 TCP,UDP,IPv4,ICMP,ARP,IGMP 以及 PPPoE 协议。W5500 内嵌 32K 字节片上缓存以供以太网包处理。如果你使用 W5500, 你只需要一些简单 的 Socket 编程就能实现以太网应用。这将会比其他嵌入式以太网方案 更加快捷、简便。用户可以同 时使用 8 个硬件 Socket 独立通讯。
W5500 提供了 SPI(外设串行接口)从而能够更加容易与外设 MCU 整合。而且, W5500 的使用了新的高效SPI协议支持 80MHz 速率,从而能够更好的实现高速网络通讯。 为了减少系统能耗, W5500 提供了网络唤醒模式(WOL)及掉电模式供客户选择使用。
特点:
支持硬件 TCP/IP 协议:TCP, UDP, ICMP, IPv4, ARP, IGMP, PPPoE
支持 8 个独立端口(Socket)同时通讯
支持掉电模式,支持网络唤醒
支持高速串行外设接口(SPI 模式 0,3)
内部 32K 字节收发缓存
内嵌 10BaseT/100BaseTX 以太网物理层(PHY)
支持自动协商(10/100-Based 全双工/半双工)
不支持 IP 分片
3.3V 工作电压,I/O 信号口 5V 耐压;
LED 状态显示(全双工/半双工,网络连接,网络速度,活动状态)
48 引脚 LQFP 无铅封装(7x7mm, 0.5mm 间距)
适用领域:
家庭网络设备: 机顶盒、个人录像机、数码媒体适配器
串行转以太网: 门禁控制、LED 显示屏、无线 AP 继电器等
并行转以太网: POS/微型打印机、复印机
USB 转以太网: 存储设备、网络打印机
GPIO 转以太网: 家庭网络传感器
安全系统: 数字录像机、网络摄像机、信息亭
工厂和楼宇自动化控制系统
医疗监测设备
嵌入式服务器
3. 库文件组成介绍
W5500官方提供了ioLibrary 4.0.0,ioLibrary是WIZnet芯片的以太网驱动库,它包括驱动程序和应用程序协议。该驱动程序(ioLibrary)可用于WIZnet TCP / IP芯片的应用设计,如W5500,W5300,W5200,W5100 W5100S。
4. 库文件移植过程
软件准备
需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
准备一个串口调试助手,这里我使用的是Serial Port Utility;
准备一个网络调试助手,这里我使用的是sockettool;
链接:https://pan.baidu.com/s/1Cpp4yCPaDDEcdybWZKIsDw
提取码:q14o
4.1 添加驱动库到工程中
在工程目录下新建 Hardware/W5500,将驱动库中的三个文件夹都复制过来:
注意,这其中只有Ethernet下的文件是必需的,其余两个文件夹的文件可选添加,在后面进行测试时会用到。
4.2 接下来将Ethernet目录下和W5500相关的文件添加到MDK工程中
添加头文件路径:
确保C99模式开启(STM32Cubemx生成的工程中默认开启):
4.3 配置所使用的芯片型号
打开wizchip_conf.h文件,在最开始修改宏定义_WIZCHIP_,该宏定义指明了我们所用的芯片型号,设置为W5500:
5. 适配W5500官方驱动
W5500官方驱动库中通过 _WIZCHIP 结构体中定义的一组函数指针来管理spi驱动,为了防止添加后直接报错,在 wizchip_conf.c 中提供了这些函数指针的默认实现,都为空函数,所以此时编译时不会报错。
5.1 添加移植适配文件
接下来我们在项目工程中,新建w5500_port_hal.h文件和w5500_port_hal.c文件来存放自己的实现,并利用驱动库提供的接口,注册到驱动库中。
加入到MDK工程中:
添加头文件路径:
5.2 编写头文件
编写w5500_port_hal.h文件:
#ifndef _W5500_PORT_HAL_
#define _W5500_PORT_HAL_
#include "wizchip_conf.h"
#include "stm32l4xx.h"
#include <string.h>
#include <stdio.h>
#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_DHCP
extern 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.4 测试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一下开发板测试:
5.5 W5500的Socket测试
W5500官方驱动库中实现了标准Socket API,在socket.h和socket.c中,可以直接调用编写TCP或者UDP测试程序。
W5500官方驱动库中也提供了一个Socket的使用案例,其中包括TCP服务端、TCP客户端、UDP服务端的回环测试,在application/loopback文件夹中:
本文接下来将进行TCP客户端的回环测试。
5.6 开启TCP服务器
在电脑上开启网络调试助手,建立一个TCP server,监听本机8000端口:
5.7 添加loopback测试文件
在MDK中添加c文件:
添加头文件路径:
5.8 调用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 */
5.9 测试结果
编译、下载到开发板中运行,串口日志如下:
在网络调试助手向开发板发送消息,会收到开发板发回的消息:
若开发板提示连接超时,无法连接TCP服务器,应当检查是否关闭windows网络防火墙。
6. 官方库源码分析
7 RT1052移植W5500
pin_mux.c内容截取
void BOARD_InitPins(void) {
CLOCK_EnableClock(kCLOCK_Iomuxc);
IOMUXC_SetPinMux(
IOMUXC_GPIO_B0_00_GPIO2_IO00,
0U);
IOMUXC_SetPinMux(
IOMUXC_GPIO_B0_01_GPIO2_IO01,
0U);
IOMUXC_SetPinMux(
IOMUXC_GPIO_B0_02_GPIO2_IO02,
0U);
IOMUXC_SetPinMux(
IOMUXC_GPIO_B0_03_GPIO2_IO03,
0U);
IOMUXC_SetPinMux(
IOMUXC_GPIO_B0_04_GPIO2_IO04,
0U);
IOMUXC_SetPinMux(
IOMUXC_GPIO_AD_B0_04_GPIO1_IO04, /* GPIO_AD_B0_04 is configured as GPIO1_IO04 */
0U);
IOMUXC_SetPinMux(
IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, /* GPIO_AD_B0_09 is configured as GPIO1_IO09 */
0U);
IOMUXC_SetPinMux(
IOMUXC_GPIO_AD_B0_00_LPSPI3_SCK, /* GPIO_AD_B0_00 is configured as LPSPI3_SCK */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_AD_B0_01_LPSPI3_SDO, /* GPIO_AD_B0_01 is configured as LPSPI3_SDO */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_AD_B0_02_LPSPI3_SDI, /* GPIO_AD_B0_02 is configured as LPSPI3_SDI */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_AD_B0_03_LPSPI3_PCS0, /* GPIO_AD_B0_03 is configured as LPSPI3_PCS0 */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_AD_B0_12_LPUART1_TX, /* GPIO_AD_B0_12 is configured as LPUART1_TX */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_AD_B0_13_LPUART1_RX, /* GPIO_AD_B0_13 is configured as LPUART1_RX */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK, /* GPIO_SD_B0_00 is configured as LPSPI1_SCK */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0, /* GPIO_SD_B0_01 is configured as LPSPI1_PCS0 */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO, /* GPIO_SD_B0_02 is configured as LPSPI1_SDO */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinMux(
IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI, /* GPIO_SD_B0_03 is configured as LPSPI1_SDI */
0U); /* Software Input On Field: Input Path is determined by functionality */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, /* GPIO_AD_B0_09 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_AD_B0_04_GPIO1_IO04, /* GPIO_AD_B0_04 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_AD_B0_00_LPSPI3_SCK, /* GPIO_AD_B0_00 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_AD_B0_01_LPSPI3_SDO, /* GPIO_AD_B0_01 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_AD_B0_02_LPSPI3_SDI, /* GPIO_AD_B0_02 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_AD_B0_03_LPSPI3_PCS0, /* GPIO_AD_B0_03 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_AD_B0_12_LPUART1_TX, /* GPIO_AD_B0_12 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_AD_B0_13_LPUART1_RX, /* GPIO_AD_B0_13 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK, /* GPIO_SD_B0_00 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0, /* GPIO_SD_B0_01 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO, /* GPIO_SD_B0_02 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI, /* GPIO_SD_B0_03 PAD functional properties : */
0x10B0u); /* Slew Rate Field: Slow Slew Rate
Drive Strength Field: R0/6
Speed Field: medium(100MHz)
Open Drain Enable Field: Open Drain Disabled
Pull / Keep Enable Field: Pull/Keeper Enabled
Pull / Keep Select Field: Keeper
Pull Up / Down Config. Field: 100K Ohm Pull Down
Hyst. Enable Field: Hysteresis Disabled */
IOMUXC_SetPinConfig(
IOMUXC_GPIO_B0_00_GPIO2_IO00,
0x10B0u);
IOMUXC_SetPinConfig(
IOMUXC_GPIO_B0_01_GPIO2_IO01,
0x10B0u);
IOMUXC_SetPinConfig(
IOMUXC_GPIO_B0_02_GPIO2_IO02,
0x10B0u);
IOMUXC_SetPinConfig(
IOMUXC_GPIO_B0_03_GPIO2_IO03,
0x10B0u);
IOMUXC_SetPinConfig(
IOMUXC_GPIO_B0_04_GPIO2_IO04,
0x10B0u);
}
lpspi_interrupt.c内容截取
#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "fsl_lpspi.h"
#include "fsl_gpio.h"
#include "system_MIMXRT1052.h"
#include "clock_config.h"
#include "board.h"
#include "wizchip_conf.h"
#include "w5500.h"
#include "socket.h"
#include "fsl_common.h"
#include "pin_mux.h"
#if ((defined FSL_FEATURE_SOC_INTMUX_COUNT) && (FSL_FEATURE_SOC_INTMUX_COUNT))
#include "fsl_intmux.h"
#endif
/*******************************************************************************
* Definitions
******************************************************************************/
#define EXAMPLE_LED_GPIO BOARD_USER_LED_GPIO
#define EXAMPLE_LED_GPIO_PIN BOARD_USER_LED_GPIO_PIN
/* Master related */
#define EXAMPLE_LPSPI_MASTER_BASEADDR (LPSPI3)
#define EXAMPLE_LPSPI_MASTER_IRQN (LPSPI3_IRQn)
#define EXAMPLE_LPSPI_MASTER_IRQHandler (LPSPI3_IRQHandler)
#define EXAMPLE_LPSPI_MASTER_PCS_FOR_INIT (kLPSPI_Pcs0)
#define EXAMPLE_LPSPI_MASTER_PCS_FOR_TRANSFER (kLPSPI_MasterPcs0)
/* Slave related */
#define EXAMPLE_LPSPI_SLAVE_BASEADDR (LPSPI1)
#define EXAMPLE_LPSPI_SLAVE_IRQN (LPSPI1_IRQn)
#define EXAMPLE_LPSPI_SLAVE_IRQHandler (LPSPI1_IRQHandler)
#define EXAMPLE_LPSPI_SLAVE_PCS_FOR_INIT (kLPSPI_Pcs0)
#define EXAMPLE_LPSPI_SLAVE_PCS_FOR_TRANSFER (kLPSPI_SlavePcs0)
/* Select USB1 PLL PFD0 (720 MHz) as lpspi clock source */
#define EXAMPLE_LPSPI_CLOCK_SOURCE_SELECT (1U)
/* Clock divider for master lpspi clock source */
#define EXAMPLE_LPSPI_CLOCK_SOURCE_DIVIDER (7U)
#define EXAMPLE_LPSPI_CLOCK_FREQ (CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (EXAMPLE_LPSPI_CLOCK_SOURCE_DIVIDER + 1U))
#define EXAMPLE_LPSPI_MASTER_CLOCK_FREQ EXAMPLE_LPSPI_CLOCK_FREQ
#define EXAMPLE_LPSPI_SLAVE_CLOCK_FREQ EXAMPLE_LPSPI_CLOCK_FREQ
#define TRANSFER_SIZE (512U) /*! Transfer dataSize .*/
#define TRANSFER_BAUDRATE (1000000U) /*! Transfer baudrate - 1000k */
/*******************************************************************************
* Prototypes
******************************************************************************/
/* LPSPI user callback */
void LPSPI_SlaveUserCallback(LPSPI_Type *base, lpspi_slave_handle_t *handle, status_t status, void *userData);
void LPSPI_MasterUserCallback(LPSPI_Type *base, lpspi_master_handle_t *handle, status_t status, void *userData);
uint8_t LPSPI3_ReadWriteByte(uint8_t TxData);
void SysTick_DelayTicks(uint32_t n);
/*******************************************************************************
* W5500移植相关函数
******************************************************************************/
#define W5500_RST_GPIO GPIO2
#define W5500_RST_GPIO_PIN (0U)
#define W5500_CS_GPIO GPIO2
#define W5500_CS_GPIO_PIN (2U)
#define W5500_REST_L GPIO_PinWrite(W5500_RST_GPIO, W5500_RST_GPIO_PIN, 0)
#define W5500_REST_H GPIO_PinWrite(W5500_RST_GPIO, W5500_RST_GPIO_PIN, 1)
#define W5500_CS_L GPIO_PinWrite(W5500_CS_GPIO, W5500_CS_GPIO_PIN, 0)
#define W5500_CS_H GPIO_PinWrite(W5500_CS_GPIO, W5500_CS_GPIO_PIN, 1)
//W5500芯片复位
void W5500_RST(void)
{
W5500_REST_L; //RST置低复位
SysTick_DelayTicks(100U);
W5500_REST_H; //RST置高
SysTick_DelayTicks(100U);
}
//SPI写一字节数据
void SPI_WriteByte(uint8_t data)
{
uint8_t ret;
ret = LPSPI3_ReadWriteByte(data);
SysTick_DelayTicks(1U);
}
//SPI读取一字节数据
uint8_t SPI_ReadByte(void)
{
uint8_t ret;
ret = LPSPI3_ReadWriteByte(0xff);
SysTick_DelayTicks(1U);
return ret;
}
uint8_t W5500_WRITE_REG(uint16_t AddrSel, uint8_t data)
{
uint8_t ret;
SysTick_DelayTicks(1U);
W5500_CS_H;
SysTick_DelayTicks(1U);
W5500_CS_L;
ret = LPSPI3_ReadWriteByte(AddrSel>>8);
SysTick_DelayTicks(1U);
ret = LPSPI3_ReadWriteByte(AddrSel&0x00ff);
SysTick_DelayTicks(1U);
ret = LPSPI3_ReadWriteByte(0x04); //BSB[4:0] = '00000', RWB = '1'
SysTick_DelayTicks(1U);
ret = LPSPI3_ReadWriteByte(data);
SysTick_DelayTicks(1U);
W5500_CS_H;
return ret;
}
uint8_t W5500_READ_REG(uint16_t AddrSel)
{
uint8_t ret = 0;
SysTick_DelayTicks(1U);
W5500_CS_H;
SysTick_DelayTicks(1U);
W5500_CS_L;
ret = LPSPI3_ReadWriteByte(AddrSel>>8);
SysTick_DelayTicks(1U);
ret = LPSPI3_ReadWriteByte(AddrSel&0xff);
SysTick_DelayTicks(1U);
ret = LPSPI3_ReadWriteByte(0x00); //BSB[4:0] = '00000', RWB = '0'
SysTick_DelayTicks(1U);
ret = LPSPI3_ReadWriteByte(0xff);
SysTick_DelayTicks(1U);
W5500_CS_H;
return ret;
}
/*******************************************************************************
* W5500移植4个回调函数定义如下
******************************************************************************/
//进入临界区
void SPI_CrisEnter(void)
{
}
//退出临界区
void SPI_CrisExit(void)
{
}
//W5500片选使能
void SPI_CS_Select(void)
{
SysTick_DelayTicks(1U);
W5500_CS_H;
SysTick_DelayTicks(1U);
W5500_CS_L;
}
//W5500片选取消
void SPI_CS_Deselect(void)
{
SysTick_DelayTicks(1);
W5500_CS_H;
}
//Ethernet通信配置初始化
/*******************************************************************************
* Ethernet通信配置初始化
* 通信配置初始化主要按照顺序依次执行以下3个函数。
* 1. 注册TCP/IP通信相关的回调函数 RegisterFunction();
* 2. 初始化芯片参数 ChipParametersConfig();
* 3. 初始化网络通信参数 NetworkParametersConfig();
******************************************************************************/
//初始化配置定义
wiz_NetInfo gWIZNETINFO = {
.mac = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
.ip = {192, 168, 0, 188},
.sn = {255, 255, 255, 0},
.gw = {192, 168, 0, 1},
.dns = {192, 168, 0, 1},
.dhcp = NETINFO_DHCP
};
//注册TCP/IP通信相关的回调函数 RegisterFunction()
void RegisterFunction(void)
{
reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit); //注册临界区回调函数
reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Select); //注册片选回调函数
reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte); //注册SPI读写字节回调函数
}
//初始化芯片参数 ChipParametersConfig()
void ChipParametersConfig(void)
{
uint8_t tmp;
uint8_t memsize[2][8] = {{2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2}};
if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -1)
{
PRINTF("WIZCHIP Initialized fail.\r\n");
while(1);
}
do{
if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1)
{
PRINTF("Unknown PHY Link stauts.\r\n");
}
}while(tmp == PHY_LINK_OFF);
}
//初始化网络通信参数 NetworkParametersConfig()
void NetworkParametersConfig(void)
{
uint8_t tmpstr[6];
ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);
ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);
ctlwizchip(CW_GET_ID,(void*)tmpstr);
//串口打印出参数
printf("\r\n=== %s NET CONF ===\r\n",(char*)tmpstr);
printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n",gWIZNETINFO.mac[0],gWIZNETINFO.mac[1],gWIZNETINFO.mac[2],
gWIZNETINFO.mac[3],gWIZNETINFO.mac[4],gWIZNETINFO.mac[5]);
printf("SIP: %d.%d.%d.%d\r\n", gWIZNETINFO.ip[0],gWIZNETINFO.ip[1],gWIZNETINFO.ip[2],gWIZNETINFO.ip[3]);
printf("GAR: %d.%d.%d.%d\r\n", gWIZNETINFO.gw[0],gWIZNETINFO.gw[1],gWIZNETINFO.gw[2],gWIZNETINFO.gw[3]);
printf("SUB: %d.%d.%d.%d\r\n", gWIZNETINFO.sn[0],gWIZNETINFO.sn[1],gWIZNETINFO.sn[2],gWIZNETINFO.sn[3]);
printf("DNS: %d.%d.%d.%d\r\n", gWIZNETINFO.dns[0],gWIZNETINFO.dns[1],gWIZNETINFO.dns[2],gWIZNETINFO.dns[3]);
printf("======================\r\n");
}
/*******************************************************************************
* 调用上述3个配置函数进行初始化即可,至此,W5500的移植全部完成,芯片配置完成。
******************************************************************************/
void W5500_ChipInit(void)
{
gpio_pin_config_t sw_config = { \
kGPIO_DigitalOutput, 1, kGPIO_NoIntmode, \
}; \
GPIO_PinInit(W5500_RST_GPIO, W5500_RST_GPIO_PIN, &sw_config);
GPIO_PinInit(W5500_CS_GPIO, W5500_CS_GPIO_PIN, &sw_config);
W5500_RST();
RegisterFunction();
ChipParametersConfig();
NetworkParametersConfig();
}
/*******************************************************************************
* Variables
******************************************************************************/
uint8_t regData = 0xff;
uint16_t regRTD = 0;
float rtdValue = 0.0;
float rtdTemp = 0.0;
uint8_t masterRxData[TRANSFER_SIZE] = {0};
uint8_t masterTxData[TRANSFER_SIZE] = {0};
uint8_t slaveRxData[TRANSFER_SIZE] = {0};
uint8_t slaveTxData[TRANSFER_SIZE] = {0};
volatile uint32_t slaveTxCount;
volatile uint32_t slaveRxCount;
uint8_t g_slaveRxWatermark;
uint8_t g_slaveFifoSize;
volatile uint32_t masterTxCount;
volatile uint32_t masterRxCount;
uint8_t g_masterRxWatermark;
uint8_t g_masterFifoSize;
volatile bool isSlaveTransferCompleted = false;
volatile bool isMasterTransferCompleted = false;
volatile uint32_t g_systickCounter;
/* The PIN status */
volatile bool g_pinSet = false;
uint16_t reg = 0x0005;
uint8_t regRead = 0;
uint8_t regvalue = 192;
/*******************************************************************************
* Code
******************************************************************************/
void SysTick_Handler(void)
{
if (g_systickCounter != 0U)
{
g_systickCounter--;
}
}
void SysTick_DelayTicks(uint32_t n)
{
g_systickCounter = n;
while(g_systickCounter != 0U)
{
}
}
#if 1
void LPSPI_SlaveUserCallback(LPSPI_Type *base, lpspi_slave_handle_t *handle, status_t status, void *userData)
{
if (status == kStatus_Success)
{
PRINTF("This is LPSPI slave transfer completed callback. \r\n");
PRINTF("It's a successful transfer. \r\n\r\n");
}
else if (status == kStatus_LPSPI_Error)
{
PRINTF("This is LPSPI slave transfer completed callback. \r\n");
PRINTF("Error occured in this transfer. \r\n\r\n");
}
else
{
__NOP();
}
isSlaveTransferCompleted = true;
}
void LPSPI_MasterUserCallback(LPSPI_Type *base, lpspi_master_handle_t *handle, status_t status, void *userData)
{
isMasterTransferCompleted = true;
}
void EXAMPLE_LPSPI_SLAVE_IRQHandler(void)
{
if (slaveRxCount < TRANSFER_SIZE)
{
while (LPSPI_GetRxFifoCount(EXAMPLE_LPSPI_SLAVE_BASEADDR))
{
slaveRxData[slaveRxCount] = LPSPI_ReadData(EXAMPLE_LPSPI_SLAVE_BASEADDR);
slaveRxCount++;
if (slaveTxCount < TRANSFER_SIZE)
{
LPSPI_WriteData(EXAMPLE_LPSPI_SLAVE_BASEADDR, slaveTxData[slaveTxCount]);
slaveTxCount++;
}
if (slaveRxCount == TRANSFER_SIZE)
{
break;
}
}
}
/*Update rxWatermark. There isn't RX interrupt for the last datas if the RX count is not greater than rxWatermark.*/
if ((TRANSFER_SIZE - slaveRxCount) <= g_slaveRxWatermark)
{
EXAMPLE_LPSPI_SLAVE_BASEADDR->FCR =
(EXAMPLE_LPSPI_SLAVE_BASEADDR->FCR & (~LPSPI_FCR_RXWATER_MASK)) |
LPSPI_FCR_RXWATER(((TRANSFER_SIZE - slaveRxCount) > 1) ? ((TRANSFER_SIZE - slaveRxCount) - 1U) : (0U));
}
/* Check if remaining receive byte count matches user request */
if ((slaveRxCount == TRANSFER_SIZE) && (slaveTxCount == TRANSFER_SIZE))
{
isSlaveTransferCompleted = true;
/* Disable interrupt requests */
LPSPI_DisableInterrupts(EXAMPLE_LPSPI_SLAVE_BASEADDR, kLPSPI_RxInterruptEnable);
}
#endif
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif
}
void EXAMPLE_LPSPI_MASTER_IRQHandler(void)
{
if (masterRxCount < TRANSFER_SIZE)
{
/* First, disable the interrupts to avoid potentially triggering another interrupt
* while reading out the RX FIFO as more data may be coming into the RX FIFO. We'll
* re-enable the interrupts EXAMPLE_LPSPI_MASTER_BASEADDRd on the LPSPI state after reading out the FIFO.
*/
LPSPI_DisableInterrupts(EXAMPLE_LPSPI_MASTER_BASEADDR, kLPSPI_RxInterruptEnable);
while (LPSPI_GetRxFifoCount(EXAMPLE_LPSPI_MASTER_BASEADDR))
{
/*Read out the data*/
masterRxData[masterRxCount] = LPSPI_ReadData(EXAMPLE_LPSPI_MASTER_BASEADDR);
masterRxCount++;
if (masterRxCount == TRANSFER_SIZE)
{
break;
}
}
/* Re-enable the interrupts only if rxCount indicates there is more data to receive,
* else we may get a spurious interrupt.
* */
if (masterRxCount < TRANSFER_SIZE)
{
/* Set the TDF and RDF interrupt enables simultaneously to avoid race conditions */
LPSPI_EnableInterrupts(EXAMPLE_LPSPI_MASTER_BASEADDR, kLPSPI_RxInterruptEnable);
}
}
/*Update rxWatermark. There isn't RX interrupt for the last datas if the RX count is not greater than rxWatermark.*/
if ((TRANSFER_SIZE - masterRxCount) <= g_masterRxWatermark)
{
EXAMPLE_LPSPI_MASTER_BASEADDR->FCR =
(EXAMPLE_LPSPI_MASTER_BASEADDR->FCR & (~LPSPI_FCR_RXWATER_MASK)) |
LPSPI_FCR_RXWATER(((TRANSFER_SIZE - masterRxCount) > 1) ? ((TRANSFER_SIZE - masterRxCount) - 1U) : (0U));
}
if (masterTxCount < TRANSFER_SIZE)
{
while ((LPSPI_GetTxFifoCount(EXAMPLE_LPSPI_MASTER_BASEADDR) < g_masterFifoSize) &&
(masterTxCount - masterRxCount < g_masterFifoSize))
{
/*Write the word to TX register*/
LPSPI_WriteData(EXAMPLE_LPSPI_MASTER_BASEADDR, masterTxData[masterTxCount]);
++masterTxCount;
if (masterTxCount == TRANSFER_SIZE)
{
break;
}
}
}
/* Check if we're done with this transfer.*/
if ((masterTxCount == TRANSFER_SIZE) && (masterRxCount == TRANSFER_SIZE))
{
isMasterTransferCompleted = true;
/* Complete the transfer and disable the interrupts */
LPSPI_DisableInterrupts(EXAMPLE_LPSPI_MASTER_BASEADDR, kLPSPI_AllInterruptEnable);
}
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif
}
void LPSPI3_Init()
{
uint32_t i;
lpspi_master_config_t masterConfig;
lpspi_slave_config_t slaveConfig;
uint32_t whichPcs;
uint8_t txWatermark;
/*Set up the transfer data*/
for (i = 0; i < TRANSFER_SIZE; i++)
{
masterTxData[i] = i % 256;
masterRxData[i] = 0;
slaveTxData[i] = ~masterTxData[i];
slaveRxData[i] = 0;
}
/*Master config*/
masterConfig.baudRate = TRANSFER_BAUDRATE;
masterConfig.bitsPerFrame = 8;
masterConfig.cpol = kLPSPI_ClockPolarityActiveHigh;
masterConfig.cpha = kLPSPI_ClockPhaseFirstEdge;
masterConfig.direction = kLPSPI_MsbFirst;
masterConfig.pcsToSckDelayInNanoSec = 1000000000 / masterConfig.baudRate;
masterConfig.lastSckToPcsDelayInNanoSec = 1000000000 / masterConfig.baudRate;
masterConfig.betweenTransferDelayInNanoSec = 1000000000 / masterConfig.baudRate;
masterConfig.whichPcs = EXAMPLE_LPSPI_MASTER_PCS_FOR_INIT;
masterConfig.pcsActiveHighOrLow = kLPSPI_PcsActiveLow;
masterConfig.pinCfg = kLPSPI_SdiInSdoOut;
masterConfig.dataOutConfig = kLpspiDataOutRetained;
LPSPI_MasterInit(EXAMPLE_LPSPI_MASTER_BASEADDR, &masterConfig, EXAMPLE_LPSPI_MASTER_CLOCK_FREQ);
/*Slave config*/
slaveConfig.bitsPerFrame = masterConfig.bitsPerFrame;
slaveConfig.cpol = masterConfig.cpol;
slaveConfig.cpha = masterConfig.cpha;
slaveConfig.direction = masterConfig.direction;
slaveConfig.whichPcs = EXAMPLE_LPSPI_SLAVE_PCS_FOR_INIT;
slaveConfig.pcsActiveHighOrLow = masterConfig.pcsActiveHighOrLow;
slaveConfig.pinCfg = kLPSPI_SdiInSdoOut;
slaveConfig.dataOutConfig = kLpspiDataOutRetained;
LPSPI_SlaveInit(EXAMPLE_LPSPI_SLAVE_BASEADDR, &slaveConfig);
/******************Set up slave first ******************/
isSlaveTransferCompleted = false;
slaveTxCount = 0;
slaveRxCount = 0;
whichPcs = EXAMPLE_LPSPI_SLAVE_PCS_FOR_INIT;
/*The TX and RX FIFO sizes are always the same*/
g_slaveFifoSize = LPSPI_GetRxFifoSize(EXAMPLE_LPSPI_SLAVE_BASEADDR);
/*Set the RX and TX watermarks to reduce the ISR times.*/
if (g_slaveFifoSize > 1)
{
txWatermark = 1;
g_slaveRxWatermark = g_slaveFifoSize - 2;
}
else
{
txWatermark = 0;
g_slaveRxWatermark = 0;
}
LPSPI_SetFifoWatermarks(EXAMPLE_LPSPI_SLAVE_BASEADDR, txWatermark, g_slaveRxWatermark);
LPSPI_Enable(EXAMPLE_LPSPI_SLAVE_BASEADDR, false);
EXAMPLE_LPSPI_SLAVE_BASEADDR->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK);
LPSPI_Enable(EXAMPLE_LPSPI_SLAVE_BASEADDR, true);
/*Flush FIFO , clear status , disable all the interrupts.*/
LPSPI_FlushFifo(EXAMPLE_LPSPI_SLAVE_BASEADDR, true, true);
LPSPI_ClearStatusFlags(EXAMPLE_LPSPI_SLAVE_BASEADDR, kLPSPI_AllStatusFlag);
LPSPI_DisableInterrupts(EXAMPLE_LPSPI_SLAVE_BASEADDR, kLPSPI_AllInterruptEnable);
EXAMPLE_LPSPI_SLAVE_BASEADDR->TCR =
(EXAMPLE_LPSPI_SLAVE_BASEADDR->TCR &
~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | LPSPI_TCR_PCS_MASK)) |
LPSPI_TCR_CONT(0) | LPSPI_TCR_CONTC(0) | LPSPI_TCR_RXMSK(0) | LPSPI_TCR_TXMSK(0) | LPSPI_TCR_PCS(whichPcs);
/* Enable the NVIC for LPSPI peripheral. Note that below code is useless if the LPSPI interrupt is in INTMUX ,
* and you should also enable the INTMUX interrupt in your application.
*/
//EnableIRQ(EXAMPLE_LPSPI_SLAVE_IRQN);
}
//LPSPI3 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
uint8_t LPSPI3_ReadWriteByte(uint8_t TxData)
{
uint8_t spirxdata=0;
uint8_t spitxdata=TxData;
lpspi_transfer_t spi_tranxfer;
lpspi_master_handle_t master_handle;
spi_tranxfer.configFlags=kLPSPI_MasterPcs1|kLPSPI_MasterPcsContinuous; //PCS1
spi_tranxfer.txData=&spitxdata; //要发送的数据
spi_tranxfer.rxData=&spirxdata; //接收到的数据
spi_tranxfer.dataSize=1; //数据长度
LPSPI_MasterTransferBlocking(LPSPI3,&spi_tranxfer); //SPI阻塞发送
return spirxdata;
}
/*!
* @brief Main function
*/
int main(void)
{
/* Define the init structure for the output LED pin*/
gpio_pin_config_t led_config = {kGPIO_DigitalOutput, 0, kGPIO_NoIntmode};
uint8_t txData =0x15;
BOARD_ConfigMPU();
BOARD_InitPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
/*Set clock source for LPSPI*/
CLOCK_SetMux(kCLOCK_LpspiMux, EXAMPLE_LPSPI_CLOCK_SOURCE_SELECT);
CLOCK_SetDiv(kCLOCK_LpspiDiv, EXAMPLE_LPSPI_CLOCK_SOURCE_DIVIDER);
/* Set systick reload value to generate 1ms interrupt */
if(SysTick_Config(SystemCoreClock / 1000U))
{
while(1)
{
}
}
PRINTF("LPSPI functional example start.\r\n");
/* Init output LED GPIO. */
GPIO_PinInit(EXAMPLE_LED_GPIO, EXAMPLE_LED_GPIO_PIN, &led_config);
GPIO_PinInit(BOARD_USER_SPI_CS0, BOARD_USER_SPI_CS0_PIN, &led_config);
LPSPI3_Init(); //SPI3初始化
W5500_ChipInit();
while (1)
{
/* Delay 10 ms */
SysTick_DelayTicks(500U);
#if 0
W5500_WRITE_REG(reg, regvalue);
regvalue++;
regRead = W5500_READ_REG(reg);
printf("%d\n", regRead);
#endif
if (g_pinSet)
{
GPIO_PinWrite(EXAMPLE_LED_GPIO, EXAMPLE_LED_GPIO_PIN, 0U);
g_pinSet = false;
}
else
{
GPIO_PinWrite(EXAMPLE_LED_GPIO, EXAMPLE_LED_GPIO_PIN, 1U);
g_pinSet = true;
}
}
}
http://www.wisioe.com/content/163.html
参考链接:
https://mculover666.blog.csdn.net/article/details/114711391?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-17.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-17.no_search_link
https://blog.csdn.net/YinShiJiaW/article/details/102956975
FreeModbus移植到STM32F107(以太网传输方式)
8 freemodbus 在stm32+W5500平台上的移植(w5500具有8个Socket,TCPserver模式下最多支持8个客户端同时访问)
原文链接:https://blog.csdn.net/jordan20052009/article/details/46967451
1、理解freemodbus的运行机制
在W5500平台上移植freemodbus,主要就是要理解eMBpoll()函数的状态机,在理解过程中我主要参考这样几篇文章,甚至可以说是抄袭吧(部分代码),在此表示衷心感谢!http://bbs.eeworld.com.cn/thread-362508-1-1.html,http://bbs.eeworld.com.cn/thread-362508-1-1.html,这两篇文章讲的如如何将freemodbus RTU模式的移植,希望大家能仔细领悟,一篇详细描述了移植过程病给了移植的代码,另一篇对freemodbus的运行机制讲的比较透彻,仔细阅读会对整个移植过程有个比较深刻的理解。
2、理解W5500的运行机制
如果能在理解freemodbus RTU,我们再来看modbus TCP到底在W500平台上如何实现。这里我先首先说明,在此之前还要明白W5500的运行过程,如果这个也不懂请参考http://blog.csdn.net/wiznet2012/article/details/41279113这篇文章,将W5500库函数移植一定要看明白,并且读懂其中int32_t loopback_tcps这个函数,我们移植的平台就是基于这样一个函数的简单改编而已。
3、移植过程
(1)移植环境搭建
在以上两个前提下我们来移植freemodbus—TCP。首先我们按照http://bbs.eeworld.com.cn/thread-362508-1-1.html这篇博文的要求,建立起freemodbus_RTU环境,这几个回调函数写的很好,既然我人家写好了,我就比较懒没有自己重写,直接抄了过来。
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,USHORT usNCoils,eMBRegisterMode eMode);
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete);
(2)编写porttcp.c文件
在此我参考这篇博文http://blog.csdn.net/xukai871105/article/details/21652287,这篇博文讲的是采用uIP协议实现移植,对于modbus的移植有着非常好参考意义,移植前请仔细阅读。主要是建立
//modbus 接收发送寄存器,全局变量
#define MB_TCP_BUF_SIZE 2048
uint8_t ucTCPRequestFrame[MB_TCP_BUF_SIZE]; //接收寄存器
uint16_t ucTCPRequestLen;
uint8_t ucTCPResponseFrame[MB_TCP_BUF_SIZE]; //发送寄存器
uint16_t ucTCPResponseLen;
uint8_t bFrameSent = FALSE; //是否进行发送响应判断
建立porttcp.c文件,实现这样几个函数
BOOL xMBTCPPortInit( USHORT usTCPPort )
{
SOCKET sn;
sn=0;
if(getSn_SR(sn)==SOCK_CLOSED)
{
socket(sn,Sn_MR_TCP,usTCPPort,0x00); //打开socket
}
if (getSn_SR(sn)==SOCK_INIT)
{
listen(sn); //监听
return TRUE;
}
return FALSE;
}
BOOL xMBTCPPortGetRequest( UCHAR **ppucMBTCPFrame, USHORT * usTCPLength )
{
*ppucMBTCPFrame = (uint8_t *) &ucTCPRequestFrame[0];
*usTCPLength = ucTCPRequestLen;
/* Reset the buffer. */
ucTCPRequestLen = 0;
return TRUE;
}
BOOL xMBTCPPortSendResponse( const UCHAR *pucMBTCPFrame, USHORT usTCPLength )
{
memcpy(ucTCPResponseFrame,pucMBTCPFrame , usTCPLength);
ucTCPResponseLen = usTCPLength;
bFrameSent = TRUE; // 通过W5500发送数据
return bFrameSent;
}
void vMBTCPPortClose( void )
{
};
void vMBTCPPortDisable( void )
{
};
这其中 xMBTCPPortInit()函数的内容写不写,貌似影响不大。
(3)编写main.c文件
以上说了半天都是参考别人的,而且和W5500没什么关系。现在是freemodbus 和 w5500 库函数如何结合使用的时候了。看一下main函数
int main(void)
{
uint8_t tmp;
uint8_t sn=0;
uint16_t port=MBTCP_PORT;
eMBErrorCode eStatus;
w5500_SPI_Init();
reg_callback_func();//注册W5500回调函数,移植W5500库函数必须
Socket_buffer_init();//W5500 SOCKET Buffer 初始化
/* PHY link 状态检查*/
if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1){
// printf("Unknown PHY Link stauts.\r\n");
}
/* 网络初始化 */
network_init();
//启动FreeModbus
eStatus = eMBTCPInit(port );
eStatus =eMBEnable();
while(1)
{
modbus_tcps(sn,port);
}
}
w5500_SPI_Init();
reg_callback_func();//注册W5500回调函数,移植W5500库函数必须
Socket_buffer_init();//W5500 SOCKET Buffer 初始化
/* PHY link 状态检查*/
if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp)
}
/* 网络初始化 */
network_init();
等都是W5500初始化的过程,
eStatus = eMBTCPInit(port );
eStatus =eMBEnable();
是freemodbus初始化的过程。
来看一下modbus_tcps(sn,port)函数
//W5500在TCP server模式下,接收、发送modbus-TCP报文
void modbus_tcps(uint8_t sn, uint16_t port)
{
switch(getSn_SR(sn)) //获取socket状态
{
case SOCK_CLOSED: //socket处于关闭状态
socket(sn,Sn_MR_TCP,port,0x00); //打开socket
break;
case SOCK_INIT : //socket处于已经初始化状态
listen(sn); //监听
case SOCK_ESTABLISHED : //socket处于连接状态
if(getSn_IR(sn) & Sn_IR_CON)
{
setSn_IR(sn,Sn_IR_CON);
}
ucTCPRequestLen = getSn_RX_RSR(sn); //获取接收数据长度
if(ucTCPRequestLen>0)
{
recv(sn,ucTCPRequestFrame, ucTCPRequestLen); //W5500接收数据
xMBPortEventPost(EV_FRAME_RECEIVED); //发送EV_FRAME_RECEIVED事件,以驱动eMBpoll()函数中的状态机
eMBPoll(); //处理EV_FRAME_RECEIVED事件
eMBPoll(); //处理EV_EXECUTE事件
if(bFrameSent)
{
bFrameSent = FALSE;
//W5500发送Modbus应答数据包
send(sn,ucTCPResponseFrame,ucTCPResponseLen);
}
}
break;
case SOCK_CLOSE_WAIT : //socket处于等待关闭状态
disconnect(sn); //关闭连接
break;
default:
break;
}
}
这个函数就是不断检查W5500的状态,如果检查到有收到数据包之后开始接受数据,并启动freemodbus状态机。具体流程是,W5500接受数据包存入ucTCPRequestFrame数组中 → 发送EV_FRAME_RECEIVED事件 → 两次调用freemodbus状态机,完成对接受报文的解析,并生成响应的报文存入ucTCPRequestFrame数组中 → 最后W5500将数组中的报文中发送出去。
即每次收到数据包之后就执行这样一个流程即可。ucTCPRequestFrame,ucTCPResponseFrame正是W5500和freemodbus结合的媒介。
5、多客户端支持
主函数这样修改
while(1)
{
for(sn=1;sn<8;sn++)
modbus_tcps(sn,port);
}
w5500具有8个Socket,TCPserver模式下最多支持8个客户端同时访问。