参考资料
- STM32F103参考手册:rm0008-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx
- STM32103ZE数据手册:STM32F103xC, STM32F103xD, STM32F103xE
- 数据手册:W5500 Datasheet v1.1.0 - English
- 官方驱动:https://github.com/Wiznet/ioLibrary_Driver
实验-Ping
硬件电路
(1)W5500-RST:重置硬件,重置(Reset)低电平有效;该引脚需要保持低电平至少 500 us,才能重置 W5500;(正常使用应该高电平,需要重置芯片的时候置为低电平不少500us)。连接的是PG7。
(2)W5500-INT:中断输出(Interrupt output)低电平有效;低电平:W5500的中断生效。高电平:无中断;连接的是PG6。
(3)W5500-CS片选引脚。连接的是PD3
(4)连接的是STM32的SPI2外设。
W5500官方驱动移植
https://github.com/Wiznet/ioLibrary_Driver
移植我们需要的
修改宏定义
修改wizchip_conf.h
找到宏定义_WIZCHIP_
,如果不是W5500,就改成W5500。
#ifndef _WIZCHIP_
#define _WIZCHIP_ W5500 // W5100, W5100S, W5200, W5300, W5500
#endif
修改工作模式为可变数据长度模式(大致在155行,_WIZCHIP_IO_MODE_SPI_FDM_
)
#elif (_WIZCHIP_ == W5500)
#define _WIZCHIP_ID_ "W5500\0"
/**
* @brief Define interface mode. \n
* @todo Should select interface mode as chip.
* - @ref \_WIZCHIP_IO_MODE_SPI_ \n
* -@ref \_WIZCHIP_IO_MODE_SPI_VDM_ : Valid only in @ref \_WIZCHIP_ == W5500 \n
* -@ref \_WIZCHIP_IO_MODE_SPI_FDM_ : Valid only in @ref \_WIZCHIP_ == W5500 \n
* - @ref \_WIZCHIP_IO_MODE_BUS_ \n
* - @ref \_WIZCHIP_IO_MODE_BUS_DIR_ \n
* - @ref \_WIZCHIP_IO_MODE_BUS_INDIR_ \n
* - Others will be defined in future. \n\n
* ex> <code> #define \_WIZCHIP_IO_MODE_ \_WIZCHIP_IO_MODE_SPI_VDM_ </code>
*
*/
#ifndef _WIZCHIP_IO_MODE_
// #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_FDM_
#define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_VDM_
钩子函数 & 注册
wizchip_conf.c
文件中,官方提供了一些接口,待用户补充。
void wizchip_cris_enter(void) {} // 进入临界区(没有上系统,默认即可)
void wizchip_cris_exit(void) {} // 退出临界区(没有上系统,默认即可)
void wizchip_cs_select(void) {} // 片选使能
void wizchip_cs_deselect(void) {} // 片选失能
iodata_t wizchip_bus_readdata(uint32_t AddrSel) { return * ((volatile iodata_t *)((ptrdiff_t) AddrSel)); } // 总线读函数
void wizchip_bus_writedata(uint32_t AddrSel, iodata_t wb) { *((volatile iodata_t*)((ptrdiff_t)AddrSel)) = wb; } // 总线写函数
uint8_t wizchip_spi_readbyte(void) {return 0;} // 读一个字节
void wizchip_spi_writebyte(uint8_t wb) {} // 写一个字节
void wizchip_spi_readburst(uint8_t* pBuf, uint16_t len) {} //按长度读
void wizchip_spi_writeburst(uint8_t* pBuf, uint16_t len) {} // 按长度写
实现上述函数后需要通过如下函数进行注册
//注册进入/退出临界区函数
void reg_wizchip_cris_cbfunc(void(*cris_en)(void), void(*cris_ex)(void))
//注册SPI片选(CS)使能/失能函数
void reg_wizchip_cs_cbfunc(void(*cs_sel)(void), void(*cs_desel)(void))
//注册总线 读/写 函数
void reg_wizchip_bus_cbfunc(iodata_t(*bus_rb)(uint32_t addr), void (*bus_wb)(uint32_t addr, iodata_t wb))
//注册 SPI 按字节 读/写 函数
void reg_wizchip_spi_cbfunc(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb))
//注册 SPI 按长度 读/写函数
void reg_wizchip_spiburst_cbfunc(void (*spi_rb)(uint8_t* pBuf, uint16_t len), void (*spi_wb)(uint8_t* pBuf, uint16_t len))
对接SPI
spi.h
#ifndef __SPI_H__
#define __SPI_H__
#include "stm32f10x.h"
#include "delay.h"
// CS
#define CS_HIGH (GPIOD->ODR |= GPIO_ODR_ODR3)
#define CS_LOW (GPIOD->ODR &= ~GPIO_ODR_ODR3)
void SPI_Init(void);
void SPI_Start(void);
void SPI_Stop(void);
uint8_t SPI_SwapByte(uint8_t dataByte);
#endif /* __SPI_H__ */
wizchip_conf.c
实现钩子函数
void wizchip_cs_select(void) {
CS_LOW;
}
void wizchip_cs_deselect(void) {
CS_HIGH;
}
uint8_t wizchip_spi_readbyte(void) {
return SPI_SwapByte(0);
}
void wizchip_spi_writebyte(uint8_t wb) {
SPI_SwapByte(wb);
}
新增注册接口
void wizchip_register_callbacks(void) {
reg_wizchip_cris_cbfunc(wizchip_cris_enter, wizchip_cris_exit);
reg_wizchip_cs_cbfunc(wizchip_cs_select, wizchip_cs_deselect);
reg_wizchip_spi_cbfunc(wizchip_spi_readbyte, wizchip_spi_writebyte);
}
SPI(STM32F103外设SPI2)
spi.h
#ifndef __SPI_H__
#define __SPI_H__
#include "stm32f10x.h"
#include "delay.h"
// CS
#define CS_HIGH (GPIOD->ODR |= GPIO_ODR_ODR3)
#define CS_LOW (GPIOD->ODR &= ~GPIO_ODR_ODR3)
void SPI_Init(void);
void SPI_Start(void);
void SPI_Stop(void);
uint8_t SPI_SwapByte(uint8_t dataByte);
#endif /* __SPI_H__ */
spi.c
#include "spi.h"
void SPI_Init(void) {
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
// CS-PD3 通用推挽 MODE=11 CNF=00
GPIOD->CRL |= GPIO_CRL_MODE3;
GPIOD->CRL &= ~GPIO_CRL_CNF3;
// SCK-PB13,MOSI-PB15 复用推挽 MODE=11 CNF=10
GPIOB->CRH |= GPIO_CRH_MODE13;
GPIOB->CRH |= GPIO_CRH_CNF13_1;
GPIOB->CRH &= ~GPIO_CRH_CNF13_0;
GPIOB->CRH |= GPIO_CRH_MODE15;
GPIOB->CRH |= GPIO_CRH_CNF15_1;
GPIOB->CRH &= ~GPIO_CRH_CNF15_0;
// MISO-PB14 浮空输入 MODE=00, CNF=01
GPIOB->CRH &= ~GPIO_CRH_MODE14;
GPIOB->CRH |= GPIO_CRH_CNF14_0;
GPIOB->CRH &= ~GPIO_CRH_CNF14_1;
// 波特率配置为PCLK/4 即 72M/4=18M => 001
SPI2->CR1 &= ~SPI_CR1_BR;
SPI2->CR1 |= SPI_CR1_BR_0;
// SPI模式0(时钟极性=0,相位=0)
SPI2->CR1 &= ~SPI_CR1_CPOL;
SPI2->CR1 &= ~SPI_CR1_CPHA;
// 数据帧格式 8bit
SPI2->CR1 &= ~SPI_CR1_DFF;
// 高位优先
SPI2->CR1 &= ~SPI_CR1_LSBFIRST;
// NSS为当前MCU作为从设备时的片选信号输入,主模式下,要么硬件上将该引脚拉高,要么通过SSM和SSI将其强制置1
SPI2->CR1 |= SPI_CR1_SSM; // 将NSS输入配置为软件控制
SPI2->CR1 |= SPI_CR1_SSI; // 通过SSI=1将NSS强制拉高
// 设置主模式
SPI2->CR1 |= SPI_CR1_MSTR;
// 使能SPI外设
SPI2->CR1 |= SPI_CR1_SPE;
// SPI模式0(极性=0,相位=0) 空闲状态 CS拉高(低电平使能),SCK拉低
CS_HIGH;
}
void SPI_Start(void) { CS_LOW; }
void SPI_Stop(void) { CS_HIGH; }
uint8_t SPI_SwapByte(uint8_t dataByte) {
// 等待发送缓冲区为空
while ((SPI2->SR & SPI_SR_TXE) == 0) {
}
// 发送数据
SPI2->DR = dataByte;
// 等待接收缓冲区非空
while ((SPI2->SR & SPI_SR_RXNE) == 0) {
}
// 接收数据
return (uint8_t)SPI2->DR;
}
以太驱动封装
logger.h
[!NOTE]
增加打印IP信息的工具接口
LOG_ARR
#ifndef LOGGER_H
#define LOGGER_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stdio.h"
#include "stm32f10x.h"
#include <string.h>
#ifndef USE_LOG
#define USE_LOG 1
#endif
#ifndef USE_DUMP
#define USE_DUMP 1
#endif
#ifndef USE_BLOCK
#define USE_BLOCK 1
#endif
#define ONLY_FILENAME(x) (strrchr(x, '\\') ? strrchr(x, '\\') + 1 : x)
void log_arr(const uint8_t *data, uint16_t len);
#if USE_BLOCK > 0
#define BLOCK(s, ...) \
do { \
printf("\r\n[BLOCK %s:%d] ", ONLY_FILENAME(__FILE__), __LINE__); \
printf(s, ##__VA_ARGS__); \
printf(",按任意键继续"); \
block_flag = 0; \
while (block_flag == 0) \
; \
printf("\r\n"); \
} while (0)
#else
#define BLOCK(x)
#endif
#if USE_LOG > 0
#define LOG_ARR(info, data, len, ...) \
do { \
printf("\033[1;36m\r\n[DEBUG %s:%d] ", ONLY_FILENAME(__FILE__), \
__LINE__); \
printf(info, ##__VA_ARGS__); \
printf(" => "); \
log_arr(data, len); \
printf("\33[0m\r\n"); \
} while (0);
#define LOG_DEBUG(s, ...) \
do { \
printf("\033[1;36m\r\n[DEBUG %s:%d] ", ONLY_FILENAME(__FILE__), \
__LINE__); \
printf(s, ##__VA_ARGS__); \
printf("\33[0m\r\n"); \
} while (0);
#define LOG_INFO(s, ...) \
do { \
printf("\033[0;32m\r\n[INFO] "); \
printf(s, ##__VA_ARGS__); \
printf("\33[0m\r\n"); \
} while (0);
#define LOG_ERROR(s, ...) \
do { \
printf("\033[0;31m\r\n[ERROR %s:%d] ", ONLY_FILENAME(__FILE__), \
__LINE__); \
printf(s, ##__VA_ARGS__); \
printf("\33[0m\r\n"); \
} while (0);
#define LOG_ASSERT(cond) \
do { \
if (!(cond)) { \
printf("\r\n[ASSERT] File=[%s],Line=[%ld] Failed to vertify thc " \
"condition [\"%s\"]\r\n", \
__FILE__, __LINE__, #cond); \
} \
} while (0);
#else
#define LOG_DEBUG(s, ...)
#define LOG_INFO(s, ...)
#define LOG_ERROR(s, ...)
#define LOG_ERROR2()
#define LOG_ERROR3(cond)
#define LOG_ERROR4(s, ...)
#define LOG_ASSERT(cond)
#endif
#if USE_DUMP > 0
#define LOG_DUMP(info, data, len, ...) \
do { \
printf("\033[1;36m\r\n[DUMP] "); \
printf(info, ##__VA_ARGS__); \
printf("\r\n\r\n"); \
log_dump(data, len); \
printf("\33[0m\r\n"); \
} while (0);
void log_dump(const uint8_t *data, uint16_t len);
#else
#define LOG_DUMP(info, data, len)
#endif
#ifdef __cplusplus
}
#endif
#endif /* LOGGER_H */
logger.c
#include "logger.h"
void log_dump(const uint8_t *data, uint16_t len) {
uint8_t ch, cl;
for (int i = 0; i < len; i++) {
ch = data[i] >> 4;
cl = data[i] & 0x0F;
if (ch < 10) ch += '0';
else
ch += 'A' - 10;
if (cl < 10) cl += '0';
else
cl += 'A' - 10;
putchar(ch);
putchar(cl);
printf(" ");
if ((i + 1) % 16 == 0) {
printf("\r\n");
} else if ((i + 1) % 8 == 0) {
ch = ' ';
putchar(ch);
putchar(ch);
putchar(ch);
putchar(ch);
} else {
ch = ' ';
putchar(ch);
}
}
}
void log_arr(const uint8_t *data, uint16_t len) {
for (int i = 0; i < len; i++) {
printf("%d ", data[i]);
}
}
ethernet.h
#ifndef __ETHERNET_H__
#define __ETHERNET_H__
#include "w5500.h"
#include "logger.h"
#include "delay.h"
#define RST_HIGH() GPIOG->ODR |= GPIO_ODR_ODR7
#define RST_LOW() GPIOG->ODR &= ~GPIO_ODR_ODR7
void Ethernet_Init(void);
void Ethernet_SetMac(void);
void Ethernet_SetIP(void);
void Ethernet_Test(void);
#endif /* __ETHERNET_H__ */
ethernet.c
#include "ethernet.h"
uint8_t mac[6] = {110, 120, 130, 140, 150, 160};
uint8_t ip[4] = {192, 168, 39, 222};
uint8_t gateway[4] = {192, 168, 39, 1};
uint8_t subnetMask[4] = {255, 255, 255, 0};
static void Ethernet_GPIOConfig(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPGEN;
// RST-PG7 推挽输出 MODE=11 CNF=00
GPIOG->CRL |= GPIO_CRL_MODE7;
GPIOG->CRL &= ~GPIO_CRL_CNF7;
// INT-PG6 上拉输入 MODE=00 CNF=10 ODR=1
GPIOG->CRL &= ~GPIO_CRL_MODE6;
GPIOG->CRL |= GPIO_CRL_CNF6_1;
GPIOG->CRL &= ~GPIO_CRL_CNF6_0;
GPIOG->ODR |= GPIO_ODR_ODR6;
}
void Ethernet_Reset(void) {
RST_LOW();
delay_ms(1);
RST_HIGH();
delay_ms(1);
}
void Ethernet_Init(void) {
SPI_Init();
wizchip_register_callbacks();
Ethernet_GPIOConfig();
Ethernet_Reset();
Ethernet_SetMac();
Ethernet_SetIP();
uint8_t macBuf[6] = {0};
getSHAR(macBuf);
LOG_ARR("mac", macBuf, 6);
uint8_t ipBuf[4] = {0};
uint8_t gatewayBuf[4] = {0};
uint8_t subBuf[4] = {0};
getSHAR(macBuf);
getSIPR(ipBuf);
getGAR(gatewayBuf);
getSUBR(subBuf);
LOG_ARR("ip", ipBuf, 4);
LOG_ARR("gateway", gatewayBuf, 4);
LOG_ARR("sub", subBuf, 4);
uint8_t mr = getMR();
LOG_ARR("mr", &mr, 1);
}
void Ethernet_SetMac(void) { setSHAR(mac); }
void Ethernet_SetIP(void) {
setSIPR(ip);
setGAR(gateway);
setSUBR(subnetMask);
}
IP配置注意事项
[!IMPORTANT]
- 要和电脑在同一局域网下,STM32可以通过水晶头网线接入交换机
- MAC地址要保持唯一性
- 静态IP地址:网段、网关要和电脑保持一一致,同时确保IP地址没有被占用(可以换一些IP多试下)
测试Ping功能
#include "logger.h"
#include "stm32f10x.h"
#include "uart.h"
#include "ethernet.h"
void uart1_received_callback(uint8_t buf[], uint8_t size) {}
int main() {
uart_init();
Ethernet_Init();
LOG_DEBUG("main start")
while (1) {
}
}
时序——注意事项
[!IMPORTANT]
- 复位低电平至少保持500us
- 复位拉高后,W5500需要最多1ms来稳定自身内部的PLL时钟,因此我们需要在复位拉高后至少1ms之后才开始与其通信
SPI-W5500逻辑分析插件
实验-TCP建立连接
示例代码
w5500_tcpserver.h
#ifndef __W5500_TCPSERVER_H__
#define __W5500_TCPSERVER_H__
#include "socket.h"
void W5500_TCPServer_Handle(void);
#endif /* __W5500_TCPSERVER_H__ */
w5500_tcpserver.c
#include "w5500_tcpserver.h"
#include "logger.h"
#define SOCK_SN 0
#define TCP_PORT 8080
void W5500_TCPServer_Handle(void) {
uint8_t sockStatus = getSn_SR(SOCK_SN);
int8_t result = 0;
uint8_t clientIP[4] = {0};
uint16_t clientPort = 0;
switch (sockStatus) {
case SOCK_CLOSED:
/*
1. 要打开的socket序列号sn
2. socket协议模式:TCP
3. TCP Nagle算法配置:不延迟ack的发送
*/
result = socket(SOCK_SN, Sn_MR_TCP, TCP_PORT, SF_TCP_NODELAY);
if (result != SOCK_SN) {
LOG_ERROR("socket open failed, result = %d", result)
} else {
LOG_DEBUG("socket open success")
}
break;
case SOCK_INIT:
result = listen(SOCK_SN);
if (result != SOCK_OK) {
LOG_ERROR("socket listen failed, result = %d", result)
} else {
LOG_DEBUG("socket listen success")
}
break;
case SOCK_ESTABLISHED:
getSn_DIPR(SOCK_SN, &clientIP);
clientPort = getSn_DPORT(SOCK_SN);
LOG_ARR("client port = %d, ip", clientIP, 4, clientPort)
while (1)
{
// handle client socket
}
break;
default:
break;
}
}
main.c
#include "stm32f10x.h"
#include "uart.h"
#include "logger.h"
#include "ethernet.h"
#include "w5500_tcpserver.h"
void uart1_received_callback(uint8_t buf[], uint8_t size) {
}
int main() {
uart_init();
LOG_DEBUG("usart test")
Ethernet_Init();
LOG_DEBUG("main start")
while (1) {
W5500_TCPServer_Handle();
}
}
测试
客户端请求连接
服务端socket连接建立
实验-TCPServer收发
驱动依赖
socket.h
示例代码
w5500_tcpserver.c
[!NOTE]
socket
的入参flag
可以配置socket
为非阻塞模式
#include "w5500_tcpserver.h"
#include "logger.h"
#define SOCK_SN 0
#define TCP_PORT 8080
// W5500每个socket有2kb的接收缓冲区、2kb的发送缓冲区
#define SOCK_RX_BUFFER_MAX_SIZE 2048
uint8_t rxbuf[SOCK_RX_BUFFER_MAX_SIZE] = {0};
void W5500_TCPServer_Handle(void) {
uint8_t sockStatus = getSn_SR(SOCK_SN);
int8_t result = 0;
uint8_t clientIP[4] = {0};
uint16_t clientPort = 0;
uint16_t receivedSize = 0;
switch (sockStatus) {
case SOCK_CLOSED:
/*
1. 要打开的socket序列号sn
2. socket协议模式:TCP
3. socket参数配置:不延迟ack的发送
*/
result = socket(SOCK_SN, Sn_MR_TCP, TCP_PORT, SF_TCP_NODELAY);
if (result != SOCK_SN) {
LOG_ERROR("socket open failed, result = %d", result)
} else {
LOG_DEBUG("socket open success")
}
break;
case SOCK_INIT:
result = listen(SOCK_SN);
if (result != SOCK_OK) {
LOG_ERROR("socket listen failed, result = %d", result)
} else {
LOG_DEBUG("socket listen success")
}
break;
case SOCK_ESTABLISHED:
getSn_DIPR(SOCK_SN, clientIP);
clientPort = getSn_DPORT(SOCK_SN);
LOG_ARR("client port = %d, ip", clientIP, 4, clientPort)
while (1) {
// 轮询方式处理接收事件中断标志位,当有数据可读时进行处理
while ((getSn_IR(SOCK_SN) & Sn_IR_RECV) == 0) {
// 轮询过程中,如果客户端断开连接,则关闭socket,走重新监听的流程
if (getSn_SR(SOCK_SN) != SOCK_ESTABLISHED) {
LOG_DEBUG("socket state changed, close socket.");
close(SOCK_SN);
return;
}
}
// 轮询结束,清除中断标志
setSn_IR(SOCK_SN, Sn_IR_RECV);
// 获取socket接收缓冲区中的数据大小
receivedSize = getSn_RX_RSR(SOCK_SN); // received size
recv(SOCK_SN, rxbuf, receivedSize);
LOG_ARR("received msg = %.*s, port = %d, ip", clientIP, 4,
receivedSize, rxbuf, clientPort);
// 回送消息
send(SOCK_SN, rxbuf, receivedSize);
}
default:
break;
}
}
测试
实验-TCPClient收发
驱动依赖
socket.h
示例代码
w5500_tcpclient.h
#ifndef __W5500_TCPCLIENT_H__
#define __W5500_TCPCLIENT_H__
#include "socket.h"
void W5500_TCPClient_Handle(void);
#endif /* __W5500_TCPCLIENT_H__ */
w5500_tcpclient.c
#include "w5500_tcpclient.h"
#include "logger.h"
#define SOCK_SN 0
#define TCP_PORT 8080
// W5500每个socket有2kb的接收缓冲区、2kb的发送缓冲区
#define SOCK_RX_BUFFER_MAX_SIZE 2048
uint8_t serverIP[4] = {192, 168, 39, 57};
uint16_t serverPort = 8080;
uint8_t rxbuf[SOCK_RX_BUFFER_MAX_SIZE] = {0};
void W5500_TCPClient_Handle(void) {
uint8_t sockStatus = getSn_SR(SOCK_SN);
int8_t result = 0;
uint16_t receivedSize = 0;
switch (sockStatus) {
case SOCK_CLOSED:
/*
1. 要打开的socket序列号sn
2. socket协议模式:TCP
3. socket参数配置:不延迟ack的发送
*/
result = socket(SOCK_SN, Sn_MR_TCP, TCP_PORT, SF_TCP_NODELAY);
if (result != SOCK_SN) {
LOG_ERROR("socket open failed, result = %d", result)
} else {
LOG_DEBUG("socket open success")
}
break;
case SOCK_INIT:
result = connect(SOCK_SN, serverIP, serverPort);
if (result != SOCK_OK) {
LOG_ERROR("socket connect failed, result = %d", result)
close(SOCK_SN);
} else {
LOG_DEBUG("socket connect success")
}
break;
case SOCK_ESTABLISHED:
// 发送数据
send(SOCK_SN, "hello server", 12);
// 轮询接收数据
while (1) {
// 轮询方式处理接收事件中断标志位,当有数据可读时进行处理
while ((getSn_IR(SOCK_SN) & Sn_IR_RECV) == 0) {
// 轮询过程中,如果socket状态变化,则关闭socket,走重新连接的流程
if (getSn_SR(SOCK_SN) != SOCK_ESTABLISHED) {
LOG_DEBUG("socket state changed, close socket.");
close(SOCK_SN);
return;
}
}
// 轮询结束,清除中断标志
setSn_IR(SOCK_SN, Sn_IR_RECV);
// 获取socket接收缓冲区中的数据大小
receivedSize = getSn_RX_RSR(SOCK_SN); // received size
recv(SOCK_SN, rxbuf, receivedSize);
LOG_ARR("received msg = %.*s, port = %d, ip", serverIP, 4,
receivedSize, rxbuf, serverPort);
// 回送消息
send(SOCK_SN, rxbuf, receivedSize);
}
default:
LOG_DEBUG("unexpected socket status = %d", sockStatus)
break;
}
}
测试
串口助手TCPServer模式
MCU日志
实验-UDP收发
示例代码
w5500_udp.h
#ifndef __W5500_UDP_H__
#define __W5500_UDP_H__
#include "socket.h"
void W5500_UDP_Handle(void);
#endif /* __W5500_UDP_H__ */
w5500_udp.c
[!NOTE]
通过
recvfrom
获取报文及其来源IP端口
#include "w5500_udp.h"
#include "logger.h"
#define SOCK_SN 0
#define UDP_PORT 8080
// W5500每个socket有2kb的接收缓冲区、2kb的发送缓冲区
#define SOCK_RX_BUFFER_MAX_SIZE 2048
uint8_t rxbuf[SOCK_RX_BUFFER_MAX_SIZE] = {0};
void W5500_UDP_Handle(void) {
uint8_t sockStatus = getSn_SR(SOCK_SN);
int8_t result = 0;
uint16_t receivedSize = 0;
uint8_t sourceIP[4] = {0};
uint16_t sourcePort = 0;
switch (sockStatus) {
case SOCK_CLOSED:
/*
1. 要打开的socket序列号sn
2. socket协议模式:UDP
3. socket参数配置:不配置任何参数
*/
result = socket(SOCK_SN, Sn_MR_UDP, UDP_PORT, 0);
if (result != SOCK_SN) {
LOG_ERROR("socket open failed, result = %d", result)
} else {
LOG_DEBUG("socket open success")
}
break;
case SOCK_UDP:
// 轮询接收数据
while (1) {
// 轮询方式处理接收事件中断标志位,当有数据可读时进行处理
while ((getSn_IR(SOCK_SN) & Sn_IR_RECV) == 0) {
// 轮询过程中,如果socket状态变化,则关闭socket
if (getSn_SR(SOCK_SN) != SOCK_UDP) {
LOG_DEBUG("socket state changed, close socket.");
close(SOCK_SN);
return;
}
}
// 轮询结束,清除中断标志
setSn_IR(SOCK_SN, Sn_IR_RECV);
// 获取报文以及来源IP端口
receivedSize = recvfrom(SOCK_SN, rxbuf, SOCK_RX_BUFFER_MAX_SIZE,
sourceIP, &sourcePort);
LOG_ARR("received msg = %.*s, size = %d, port = %d, ip",
sourceIP, 4, receivedSize, rxbuf, receivedSize,
sourcePort);
// 回送消息
sendto(SOCK_SN, rxbuf, receivedSize, sourceIP, sourcePort);
}
default:
LOG_DEBUG("unexpected socket status = %d", sockStatus)
break;
}
}
测试
串口助手模拟发送方
MCU日志
实验-搭建HTTPServer & WEB控制LED
部署WEB资源
w5500_httpserver.h
#ifndef __W5500_HTTPSERVER_H__
#define __W5500_HTTPSERVER_H__
void W5500_HTTPServer_Init(void);
void W5500_HTTPServer_Start(void);
#endif /* __W5500_HTTPSERVER_H__ */
w5500_httpserver.c
#include "w5500_httpserver.h"
#include "httpServer.h"
#include "logger.h"
#include "socket.h"
#include "led.h"
#include "string.h"
#define SOCKET_MAX_BUFFER_SIZE_TX 2048
#define SOCKET_MAX_BUFFER_SIZE_RX 2048
uint8_t txbuf[SOCKET_MAX_BUFFER_SIZE_TX] = {0};
uint8_t rxbuf[SOCKET_MAX_BUFFER_SIZE_RX] = {0};
// socket序号列表
uint8_t socketList[] = {0, 1, 2, 3, 4, 5, 6, 7};
uint8_t socketSize = sizeof(socketList) / sizeof(socketList[0]);
const uint8_t *contentName = "index.html";
/* 响应的网页的内容 */
const uint8_t content[2048] = "<!doctype html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"GBK\">\n"
" <meta name=\"viewport\"\n"
" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n"
" <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n"
" <title>尚硅谷嵌入式课程</title>\n"
"\n"
" <style type=\"text/css\">\n"
" #open_red{\n"
" color: red;\n"
" width: 100px;\n"
" height: 40px;\n"
"\n"
"\n"
" }\n"
" #close_red{\n"
" color: black;\n"
" width: 100px;\n"
" height: 40px;\n"
" }\n"
" </style>\n"
"</head>\n"
"<body>\n"
"<a href=\"/index.html?action=0\"><button id=\"open_red\" >开灯</button></a>\n"
"<a href=\"/index.html?action=1\"><button id=\"close_red\" >关灯</button></a>\n"
"<a href=\"/index.html?action=2\"><button id=\"close_red\" >翻转</button></a>\n"
"</body>\n"
"</html>";
void W5500_HTTPServer_Init(void) {
// 初始化httpserver, 使用多少个socket来支撑http服务
httpServer_init(txbuf, rxbuf, socketSize, socketList);
// 注册web资源
reg_httpServer_webContent(contentName, content);
}
void W5500_HTTPServer_Start(void) {
while (1) {
// 启动http,监听并处理一次http请求
httpServer_run(0);
}
}
main.c
#include "ethernet.h"
#include "logger.h"
#include "stm32f10x.h"
#include "uart.h"
#include "w5500_httpserver.h"
#include "led.h"
void uart1_received_callback(uint8_t buf[], uint8_t size) {}
int main() {
uart_init();
LOG_DEBUG("usart test")
Ethernet_Init();
LED_Init();
LOG_DEBUG("main start")
W5500_HTTPServer_Init();
W5500_HTTPServer_Start();
while (1) {
}
}
浏览器访问WEB
按钮发出HTTP请求
HTTP请求参数解析
官方驱动httpServer.c
parse_http_request(parsed_http_request, (uint8_t *)http_request);
//LOG_DEBUG("request uri = %s", parsed_http_request->URI)
parse_http_request_post_process(parsed_http_request->URI);
__weak void parse_http_request_post_process(uint8_t *uri) {
}
httpServer.h
void parse_http_request_post_process(uint8_t *uri);
自定义uri处理逻辑
w5500_httpserver.h
static int8_t parse_uri(uint8_t *uri) {
size_t len = strlen(uri);
if (len > 0) {
char *token = strstr(uri, "action=");
if (token != NULL) {
return *(token + 7) - '0';
}
}
return -1;
}
void parse_http_request_post_process(uint8_t *uri) {
LOG_DEBUG("uri = %s", uri);
int8_t actionType = parse_uri(uri);
LOG_DEBUG("actionType = %d", actionType);
switch (actionType) {
case 0:
LED_On(LED1);
break;
case 1:
LED_Off(LED1);
break;
case 2:
LED_Toggle(LED1);
break;
default:
break;
}
}