【RTT-Studio】详细使用教程十一:ETH网口设备--LAN8720A

一、简介

1.TCP协议
  传输控制协议在LWIP协议栈中占据了大半的代码,它是最常见的传输层协议,也是最稳定的传输层协议,很多上层应用都是依赖 TCP 协议进程传输数据,如 SMTP,FTP 等等。

  TCP 与 UDP 一样,都是传输层的协议,但是提供的服务却不相同,UDP 为上层应用提供的是一种不可靠的,无连接的服务,而 TCP 提供一种面向连接,可靠的字节传输服务,TCP 让两个主机建立连接的关系,应用数据以数据流的形式进行传输,先后发出的数据在网络中虽然也是互不干扰的传输,但是这些数据本身携带的信息确实紧密联系的,TCP 协议会给每个传输的字号进行编号,当然了,两个主机方向上的数据编号是彼此独立的,在传输的过程中,发送方把数据的起始编号与长度放在 TCP 报文中,接收方将所有数据按照编号组装起来,然后返回一个确认,当所有的数据接收完成后才将数据递交到应用层。

2.TCP 建立连接

  • 客户端发送含 SYN 标志的报文,表示要建立一个连接, 初始化连接的同步序号 seq;
  • 服务器端接收到含 SYN 的报文后, 也将一条含 SYN 标志报文, 作为 ACK 回复给客户端, 一则表示收到客户端创建连接的请求,二则表示服务端也同时创建与客户端的连接;
  • 客户端收到回复后再发送一条 ACK 报文, 表示收到服务端的创建连接请求。

  不论是客户端还是服务端, 创建连接发送数据的时候数据包中都包含一个初始化序列号 seq, 对方收到数据并回复时会将收到的 seq 加 1 作为 ACK 的数据。 这样发送端就可以判断是不是对本次数据的回复。 以后每次发数据都会在前一次 seq 的基础上 +1 作为本次的 seq (注意, 建立连接和断开连接时 seq 是加 1, 传输应用数据时 seq 加的是上次发送的数据长度 )。
在这里插入图片描述
tcp_connect实现的,大概流程如下:
在这里插入图片描述
服务端连接流程:
在这里插入图片描述
3.TCP断开连接
TCP断开连接时需要 4 个步骤来完成断开, 俗称四次挥手:

  • 已经建立连接的其中一段想要断开连接,称为主动关闭方。其发送一个含 FIN 标志、seq 值(假设为 Q)和 ACK 值(假设为 K, 来自上次收到的 seq)的报文到连接对方,称为被关闭方;
  • 被关闭方收到含 FIN 标志的报文后,将 K 作为 seq 值,Q 加 1 后作为 ACK 值回复给主动关闭方。
  • 被关闭方同时检查自身是否还有处理完的数据包,若没有则开始启动关闭操作,身份转变为主动关闭方,同样发送一个含 FIN、seq(值仍为 K)和 ACK 值(值仍为 Q+1)初始化的报文到连接对方。
  • 原主动关闭方转变为被关闭方,收到报文后,回复一条 seq 为 Q+1,ACK 为 K+1 的报文。至此,完成 TCP 连接的断开。
    在这里插入图片描述
    正常的TCP断开连接一般是从ESTABLISHED状态开始, 就是说两个网络端已经建立了连接, 断开流程如下:
    主动关闭方流程:
    在这里插入图片描述
    被动关闭方流程:
    在这里插入图片描述
    4.TCP 超时重传
      TCP 在发起一次传输时会开启一个定时器,如果在定时器超时时未收到应答就会进行重传。一个 TCP 连接的往返时间为 RTT(Round-Trip Time),然后根据 RTT 来设置重传时间就是 RTO(Retransmission TimeOut)。 如果 RTO 较大,由于等待时间过长会影响 TCP 的效率;较小则可能使系统来不及响应而认为数据传输失败而重传。所以 RTO 是随着网络的状态动态调整的,而网络状态可以通过测量报文段的往返时间来反应。

二、ETH基础配置

1.cubemx配置
配置时钟,一般都使用最大时钟频率即可。
在这里插入图片描述
配置网口引脚设置,具体如下:
在这里插入图片描述
2.board.h配置

/** if you want to use eth you can use the following instructions.
 *
 * STEP 1, define macro related to the eth
 *                 such as    BSP_USING_ETH
 *
 * STEP 2, copy your eth init function from stm32xxxx_hal_msp.c generated by stm32cubemx to the end if board.c file
 *                 such as     void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)
 *
 * STEP 3, modify your stm32xxxx_hal_config.h file to support eth peripherals. define macro related to the peripherals
 *                 such as     #define HAL_ETH_MODULE_ENABLED
 *
 * STEP 4, config your phy type
 *                 such as     #define PHY_USING_LAN8720A
 *                             #define PHY_USING_DM9161CEP
 *                             #define PHY_USING_DP83848C
 * STEP 5, implement your phy reset function in the end of board.c file
 *                 void phy_reset(void)
 *
 * STEP 6, config your lwip or other network stack
 *
 */

#define BSP_USING_ETH
#ifdef BSP_USING_ETH
#define PHY_USING_LAN8720A
/*#define PHY_USING_DM9161CEP*/
/*#define PHY_USING_DP83848C*/
#endif

3.rtthread settings配置
(1)勾选以太网驱动配置
在这里插入图片描述

(2)勾选网络层驱动
在这里插入图片描述
(3)设置控制板IP信息
在这里插入图片描述
4.以太网复位引脚设置

/**
 * @brief ETH初始化
 * @param ethHandle句柄
 */
void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)
{
   
   
  GPIO_InitTypeDef GPIO_InitStruct = {
   
   0};
  if(heth->Instance==ETH)
  {
   
   
  /* USER CODE BEGIN ETH_MspInit 0 */

  /* USER CODE END ETH_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_ETH_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**ETH GPIO Configuration
    PC1     ------> ETH_MDC
    PA1     ------> ETH_REF_CLK
    PA2     ------> ETH_MDIO
    PA7     ------> ETH_CRS_DV
    PC4     ------> ETH_RXD0
    PC5     ------> ETH_RXD1
    PB11     ------> ETH_TX_EN
    PB12     ------> ETH_TXD0
    PB13     ------> ETH_TXD1
    */
    GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* USER CODE BEGIN ETH_MspInit 1 */

  /* USER CODE END ETH_MspInit 1 */
  }

}

//定义的复位引脚
#define RESET_IO GET_PIN(C, 6)

/**
 * @brief phy复位引脚初始化
 */
void phy_reset(void)
{
   
   
    rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);

    rt_pin_write(RESET_IO, PIN_HIGH);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_LOW);
    rt_thread_mdelay(50);
    rt_pin_write(RESET_IO, PIN_HIGH);
}

5.修改rtthread源码
修改 eth_demo\rt-thread\components\drivers\include\drivers\phy.h文件,注释掉32行。图中 drivers 文件夹下的 drv_eth.c 报错是因为这个变量重定义了。
在这里插入图片描述

6.修改 cubemx 生成的 main 函数
  此时编译,应该是没有报错能通过的。cubemx中的main函数会用到我们删掉的那两个函数,所有会有警告,不想看可以在main函数中也注释或者删掉。
在这里插入图片描述


三、初始化配置

在控制台执行函数中,将广播函数进行调用,从而实现将数据通过网口进行通信。
在这里插入图片描述


四、完整代码

1.tcp_server.c

#if 1
#include <rtthread.h>
#include "board.h"
#ifdef BSP_USING_ETH
#include "tcp_server.h"

int server_state_flag = 0;

/**
 * @brief 广播数据
 * @param buffer 发送的数据
 * @param len    数据长度
 */
void broadcast(char *buffer, int len)
{
   
   
    for(int i=0; i < MAXCLIENT; i++)
    {
   
   
        if(g_client[i] < 0)
            continue;

        send(g_client[i], buffer, len, 0);
    }
}


/**
 * @brief 设置服务器参数
 */
static void set_server_parameter(void)
{
   
   
    struct netdev *netdev = RT_NULL;
    ip_addr_t addr;

    netdev = netdev_get_by_name("e0");
    if (netdev == RT_NULL)
    {
   
   
        rt_kprintf("netdev is null\n");
        return;
    }

    // 设置IP地址
    inet_aton(serverInfo.serverIpAddr, &addr);
    netdev_set_ipaddr(netdev, &addr);

    // 设置网关
    inet_aton(serverInfo.serverGateway, &addr);
    netdev_set_gw(netdev, &addr);

    // 设置子网掩码
    inet_aton(serverInfo.serverMask, &addr);
    netdev_set_netmask(netdev, &addr);

    // 重启控制板
    rt_thread_delay(RT_TICK_PER_SECOND / 100);
    rt_hw_cpu_reset();
}


/**
 * @brief 创建TCP服务器连接的线程
 * @param parameter 传入的参数
 */
static void server_thread_entry(void* parameter)
{
   
   
    int ret, maxFd, i=0;
    int optval = 1;
    char buff[] = {
   
   0};
    int j = 0;
    int z = 0;

    struct sockaddr_in clientAddr;       // 存储连接上来的客户端的IP地址和端口号
    struct sockaddr_in serverAddr;
    int newClientFd = -1;                // 新的套接字
    uint32_t len = sizeof
### 如何在RTT-Studio中读取或查看外设寄存器 #### 使用调试工具访问寄存器 为了在 RTT-Studio 中读取或查看 STM32 的外设寄存器,通常依赖于集成的调试功能。当项目被加载到支持 JTAG 或 SWD 接口的硬件上时,可以通过连接调试器(如 ST-LINK)实现对目标板上的寄存器进行实时监控。 #### 利用MDK自带的功能 如果使用的是基于 MDK (Keil) 平台构建的应用程序,则可以在 Keil μVision IDE 内部利用其提供的“Peripheral”窗口来观察特定外设的状态以及它们各自的控制/状态寄存器值[^1]。 对于 RTT-Studio 用户来说,虽然界面有所不同,但是原理相似: - **启动调试会话**:确保已经正确设置了项目的调试配置,并且能够成功建立与目标设备之间的通信链路。 - **定位至所需外设**:进入 Debug 模式后,在左侧资源管理器找到并展开 "Peripherals" 节点下的对应模块(比如 USART、SPI 等),这里列出了该类外设有多少实例存在。 - **浏览具体寄存器**:点击某个具体的外设实例名称即可看到它所拥有的所有寄存器列表及其当前数值;双击某一项还能进一步编辑它的内容以便即时修改参数设置。 另外一种方法是在代码里加入断点,运行到指定位置暂停下来之后再手动查阅相关联的寄存器信息。这种方式特别适用于想要了解某一时刻下某些关键寄存器确切值得情况。 ```c // 假定我们要检查USART1的相关寄存器 void check_USART1_registers(void){ __asm volatile ("bkpt"); // 设置软件断点 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值