说明:
1. 本文记录使用 RT-Thread 软件包 WIZNET驱动 W5500 的调试笔记。
2. 采用 RT-Thread Studio 工程 STM32F407VET6 芯片,W5500 PHY芯片,两者之间使用SPI接口链接 。
注意:
1.在按流程建立工程,和移植完 wiznet 软件包后,发现电脑可以 ping 通开发板, 但是开发板不能ping同电脑, 开发板移植的应用代码也连接不上电脑的 tcp 服务端。
调试许久,在网上找到一个帖,提示防火墙问题,发现电脑系统 WIN10 自带的防火墙关闭后就正常了。
链接:https://club.rt-thread.org/ask/question/797654947c126a8a.html
2:通过固定IP ,开发板与电脑直连可以ping通,也可以链接服务发送数据。
3:通过路由器自动获取IP,开发板与电脑直连可以ping通,也可以链接服务发送数据。
1. winnet 软件包应用流程
1.1 创建 RT-Thread Studio 工程
创建一个 STM32F407VET6 的 RT-Thread Studio 工程,系统版本 rt-thread 4.1.0
1.2 开启 SPI 框架,移植STM32CubeMX 生成的 SPI 驱动
打开 board.h 文件,搜索 spi 找到spi框架开启部分的流程介绍
/*-------------------------- SPI CONFIG BEGIN --------------------------*/
/** if you want to use spi bus you can use the following instructions.
*
* STEP 1, open spi driver framework support in the RT-Thread Settings file 在RT设置中打开spi驱动程序框架支持
*
* STEP 2, define macro related to the spi bus 定义与spi总线相关的宏
* such as #define BSP_USING_SPI1
*
* STEP 3, copy your spi init function from stm32xxxx_hal_msp.c generated by stm32cubemx to the end of board.c file
* such as void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) 复制 stm32cubemx 生成的spi驱动到 board.c 文件的末尾处
*
* STEP 4, modify your stm32xxxx_hal_config.h file to support spi peripherals. define macro related to the peripherals
* such as #define HAL_SPI_MODULE_ENABLED 打开 stm32xxxx_hal_config.h 文件中 spi 相关的宏 #define HAL_SPI_MODULE_ENABLED
*/
#define BSP_USING_SPI1
/*#define BSP_USING_SPI2*/
/*#define BSP_USING_SPI3*/
STEP 1:在RT设置中打开spi驱动程序框架支持
STEP 2: 定义与spi总线相关的宏 #define BSP_USING_SPI1
STEP 3: 复制 stm32cubemx 生成的spi驱动到 board.c 文件的末尾处时钟部分设置
SPI部分设置
输出设置
移植生成代码
如下图路径是生成的工程目录 .
1. 时钟部分:
main.c 文件的 void SystemClock_Config(void) 函数里面相关时钟的配置复制到 RT 工程中的 drv_clk.c 文件的 void system_clock_config(int target_freq_mhz)函数内部,替换原函数中的内容。
2. SPI 部分:
把 spi.c 里面的全部代码(不包含头文件)复制到 board.c 文件的末尾。
STEP 4:打开 stm32xxxx_hal_config.h 文件中 spi 相关的宏 #define HAL_SPI_MODULE_ENABLED
在 drivers 文件夹下的 stm32f4xx_hal_conf.h 文件中
1.3 开启 winnet 软件包,并配置相关参数
1.3.1 添加软件包
添加软件包,添加好后保存工程。
右键点击软件包图标,选择 配置项
1.3.2 配置软件包
注意:
1 :SPI device name 部分,如果选择的是SPI1 那就可以设置 SPI1x (x = 0~9) ,其中 SPI1 的1 表示的是硬件的1通道 SPI 总线。spi10 表示挂在在 spi1 总线上的第 0 个设备。
2:Reset PIN number 和 IRQ PIN number 这两个编号对应的是 rt 系统中 IO 的编号,不是硬件封装的管脚号,可以在 drivers / drv_gpio.c 这个文件中查看 IO 对应的编号。如:
tatic const struct pin_index pins[] =
{
#if defined(GPIOA)
__STM32_PIN(0 , A, 0 ),
__STM32_PIN(1 , A, 1 ),
__STM32_PIN(2 , A, 2 ),
__STM32_PIN(3 , A, 3 ),
1.3.3 添加 SPI 设备的挂载代码
以上代码添加好后,winnet 还不能正常启动,因为 上一步设置的 spi10 设备还没挂载到总线上。如下图,这里把代码放到了软件包源码中,也可放到别的地方,但是要保证比 winnet 的初始化代码先运行才行。
rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_4);
1.4 测试代码
如下测试代码是在网上找的,没有被注释掉的两个代码都可以链接电脑的 TCP 服务端,并发送数据。如果一次链接失败可以多尝试几次。测试命令在控制台 MSH ,中发送help 查询如下这两个。
sal_tls_test - SAL TLS function test
demo_tcp - nbiot tcp test
#include "user_cfg.h"
#include "sal_tls.h"
//
#include <stdio.h>
#include <string.h>
#include <rtthread.h>
#include <sys/socket.h>
#include <netdb.h>
//
/* RT-Thread 官网,支持 TLS 功能 */
#define SAL_TLS_HOST "192.168.1.2"
#define SAL_TLS_PORT 4880
#define SAL_TLS_BUFSZ 1024
static const char *send_data = "GET /download/rt-thread.txt HTTP/1.1\r\n"
"Host: www.rt-thread.org\r\n"
"User-Agent: rtthread/4.0.1 rtt\r\n\r\n";
void sal_tls_test(void)
{
int ret, i;
char *recv_data;
struct hostent *host;
int sock = -1, bytes_received;
struct sockaddr_in server_addr;
/* 通过函数入口参数url获得host地址(如果是域名,会做域名解析) */
host = gethostbyname(SAL_TLS_HOST);
recv_data = rt_calloc(1, SAL_TLS_BUFSZ);
if (recv_data == RT_NULL)
{
rt_kprintf("No memory\n");
return;
}
/* 创建一个socket,类型是SOCKET_STREAM,TCP 协议, TLS 类型 */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
rt_kprintf("Socket error\n");
goto __exit;
}
/* 初始化预连接的服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SAL_TLS_PORT);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0)
{
rt_kprintf("Connect fail!\n");
goto __exit;
}
/* 发送数据到 socket 连接 */
ret = send(sock, send_data, strlen(send_data), 0);
if (ret <= 0)
{
rt_kprintf("send error,close the socket.\n");
goto __exit;
}
/* 接收并打印响应的数据,使用加密数据传输 */
bytes_received = recv(sock, recv_data, SAL_TLS_BUFSZ - 1, 0);
if (bytes_received <= 0)
{
rt_kprintf("received error,close the socket.\n");
goto __exit;
}
rt_kprintf("recv data:\n");
for (i = 0; i < bytes_received; i++)
{
rt_kprintf("%c", recv_data[i]);
}
__exit:
if (recv_data)
rt_free(recv_data);
if (sock >= 0)
closesocket(sock);
}
#ifdef FINSH_USING_MSH
#include <finsh.h>
MSH_CMD_EXPORT(sal_tls_test, SAL TLS function test);
#endif /* FINSH_USING_MSH */
//#include "sal_socket.h"
//
//#include <rtthread.h>
//#include <arpa/inet.h>
//#include <netdev.h>
//
//#define SERVER_HOST "192.168.27.17"
//#define SERVER_PORT 4880
//
//static int bing_test(int argc, char **argv)
//{
// struct sockaddr_in client_addr;
// struct sockaddr_in server_addr;
// struct netdev *netdev = RT_NULL;
// int sockfd = -1;
//
// if (argc != 2)
// {
// rt_kprintf("bind_test [netdev_name] --bind network interface device by name.\n");
// return -RT_ERROR;
// }
//
// /* 通过名称获取 netdev 网卡对象 */
// netdev = netdev_get_by_name(argv[1]);
// if (netdev == RT_NULL)
// {
// rt_kprintf("get network interface device(%s) failed.\n", argv[1]);
// return -RT_ERROR;
// }
//
// if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
// {
// rt_kprintf("Socket create failed.\n");
// return -RT_ERROR;
// }
//
// /* 初始化需要绑定的客户端地址 */
// client_addr.sin_family = AF_INET;
// client_addr.sin_port = htons(8080);
// /* 获取网卡对象中 IP 地址信息 */
// client_addr.sin_addr.s_addr = netdev->ip_addr.addr;
// rt_memset(&(client_addr.sin_zero), 0, sizeof(client_addr.sin_zero));
//
// if (bind(sockfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) < 0)
// {
// rt_kprintf("socket bind failed.\n");
// closesocket(sockfd);
// return -RT_ERROR;
// }
// rt_kprintf("socket bind network interface device(%s) success!\n", netdev->name);
//
// /* 初始化预连接的服务端地址 */
// server_addr.sin_family = AF_INET;
// server_addr.sin_port = htons(SERVER_PORT);
// server_addr.sin_addr.s_addr = inet_addr(SERVER_HOST);
// rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
//
// /* 连接到服务端 */
// if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0)
// {
// rt_kprintf("socket connect failed!\n");
// closesocket(sockfd);
// return -RT_ERROR;
// }
// else
// {
// rt_kprintf("socket connect success!\n");
// }
//
// /* 关闭连接 */
// closesocket(sockfd);
// return RT_EOK;
//}
//
//#ifdef FINSH_USING_MSH
//#include <finsh.h>
//MSH_CMD_EXPORT(bing_test, bind network interface device test);
//#endif /* FINSH_USING_MSH */
/*
* Copyright (c) 2006-2019, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-07-09 MurphyZhao first version
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <rtthread.h>
#ifdef RT_USING_SAL
#include <sys/socket.h>
#include <netdb.h>
#include "sal_tls.h"
#include "sys/time.h"
#else
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "lwip/sys.h"
#include "lwip/inet.h"
#endif /* RT_USING_SAL */
#define LOG_TAG "demo.tcp"
#define LOG_LVL LOG_LVL_DBG
#define TCP_TEST_HOST "192.168.1.2"
#define TCP_TEST_PORT (4880u)
#define TEST_BUFSZ (1024u)
static const char *req_data = "GET /service/rt-thread.txt HTTP/1.1\r\n"
"Host: www.rt-thread.com\r\n"
"User-Agent: rtthread/4.0.1 rtt\r\n\r\n";
static char req_uri[128];
static int req_port;
static void nb_tcp_demo(int argc, char** argv)
{
int ret;
int sock = -1;
struct hostent *host;
struct sockaddr_in server_addr;
int bytes_received;
char *recv_data;
char ip_addr_buf[64];
if ((argc != 1) && (argc != 3))
{
LOG_E("In param error");
LOG_I("cmd: demo_tcp [<host> <port>]");
LOG_I("eg: demo_tcp");
LOG_I(" demo_tcp 127.0.0.1 8080");
return;
}
rt_memset(req_uri, 0x0, sizeof(req_uri));
if (argc == 3)
{
rt_strncpy(req_uri, argv[1], rt_strlen(argv[1]));
req_port = atoi(argv[2]);
}
else
{
rt_strncpy(req_uri, TCP_TEST_HOST, rt_strlen(TCP_TEST_HOST));
req_port = TCP_TEST_PORT;
}
LOG_I("TCP demo start");
LOG_I("Host:%s; Port:%d", req_uri, req_port);
LOG_D("will gethostbyname...");
host = gethostbyname(req_uri);
if (!host)
{
LOG_E("gethostbyname failed!");
return;
}
LOG_I("gethostbyname pass. ip addr: %s", inet_ntoa_r(*((struct in_addr *)host->h_addr_list[0]), ip_addr_buf, sizeof(ip_addr_buf)));
recv_data = rt_calloc(1, TEST_BUFSZ);
if (recv_data == RT_NULL)
{
LOG_E("calloc failed. No memory!");
return;
}
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
LOG_E("Create socket failed!");
goto __exit;
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(req_port);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
if ((ret = connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) < 0)
{
LOG_E("Connect <%d> fail! ret:%d", sock, ret);
goto __exit;
}
LOG_I("connect <%s> success", ip_addr_buf);
ret = send(sock, req_data, strlen(req_data), 0);
if (ret <= 0)
{
LOG_E("send error, will close the socket <%d>.", sock);
goto __exit;
}
LOG_I("send success");
bytes_received = recv(sock, recv_data, TEST_BUFSZ - 1, 0);
if (bytes_received <= 0)
{
LOG_E("receive error, will close the socket <%d>.", sock);
goto __exit;
}
LOG_I("received data:\n");
for (int i = 0; i < bytes_received; i++)
{
rt_kprintf("%c", recv_data[i]);
}
rt_kprintf("\r\n");
__exit:
if (recv_data)
rt_free(recv_data);
if (sock >= 0)
{
closesocket(sock);
sock = -1;
}
LOG_I("TCP demo end");
}
#ifdef FINSH_USING_MSH
#include <finsh.h>
MSH_CMD_EXPORT_ALIAS(nb_tcp_demo, demo_tcp, nbiot tcp test);
#endif /* FINSH_USING_MSH */