这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【瑞萨RA8D1 LVGL/LWIP评测】LWIP进行UDP、TCP、HTTP、

共1条 1/1 1 跳转至

【瑞萨RA8D1 LVGL/LWIP评测】LWIP进行UDP、TCP、HTTP、MQTT功能联合测试

菜鸟
2026-01-28 13:12:01     打赏

      在上一篇文章 《【瑞萨RA8D1 LVGL/LWIP评测】RA8D1部署FreeRTOS+LWIP》 中,我们基于瑞萨 CPKCOR-RA8D1 开发板部署了 FreeRTOS + LWIP 的代码,并进行了 DHCP 与 ICMP 协议的测试。本文我们将更进一步,对 TCP、UDP、HTTP、MQTT 等多个应用场景同时部署,进行联合测试。由于本文测试的是LWIP中间件的通用基础能力,代码同样适用于基于裸机、uCOS、RT-Thread等环境,有任一测试需求的伙伴们都可以参考。那么让我们立即发车~

一、基础环境准备

硬件部分

  1. CPKCOR-RA8D1 核心板

  2. CPKEXP-EKRA8x1 扩展板

  3. 路由器 & 网线

全图.jpg

软件部分

  1. e²Studio 2025-12 (25.12.0)

  2. Renesas FSP 6.3.0

  3. J-Link RTT Viewer V8.92

请基于上篇文章部署好FreeRTOS+LWIP的基础环境。

由于我们本次将同时测试多个协议,请提前增加LWIP的堆栈与内存池资源,下面是测试可行的参考配置。

LWIP堆栈.png

二、TCP echo服务器测试

测试程序

LWIP的TCP功能是默认开启的,因此我们无需额外配置,可以直接开始测试。

下面是测试用的TCP echo服务器测试代码,基于LWIP的Socket接口编写。

/*
 * tcp_echo_server_thread.c
 */
#include <lwip_thread.h>
#include "common_utils.h"

#include "lwip/init.h"
#include "lwip/tcpip.h"
#include "lwip/tcp.h"
#include "lwip/timeouts.h"
#include "lwip/init.h"
#include "lwip/ip4.h"
#include "lwip/dhcp.h"
#include <lwip/sockets.h>

#define LOCAL_PORT          5001
#define RECV_DATA           (1024)

static void tcpecho_thread(void *arg)
{
  int sock = -1,connected;
  char *recv_data;
  struct sockaddr_in server_addr,client_addr;
  socklen_t sin_size;
  size_t recv_data_len;

  (void)arg;
  APP_PRINT("Local port: %d\n\n", LOCAL_PORT);

  recv_data = (char *)pvPortMalloc(RECV_DATA);
  if (recv_data == NULL)
  {
      APP_PRINT("No memory\n");
      goto __exit;
  }

  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
  {
      APP_PRINT("Socket error\n");
      goto __exit;
  }

  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = INADDR_ANY;
  server_addr.sin_port = htons(LOCAL_PORT);
  memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

  if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
  {
      APP_PRINT("Unable to bind\n");
      goto __exit;
  }

  if (listen(sock, 5) == -1)
  {
      APP_PRINT("Listen error\n");
      goto __exit;
  }

  while(1)
  {
    sin_size = sizeof(struct sockaddr_in);

    connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);

    APP_PRINT("new client connected from (%s, %d)\n",
            inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    {
      int flag = 1;

      setsockopt(connected,
                 IPPROTO_TCP,     /* set option at TCP level */
                 TCP_NODELAY,     /* name of option */
                 (void *) &flag,  /* the cast is historical cruft */
                 sizeof(int));    /* length of option value */
    }

    while(1)
    {
      recv_data_len = recv(connected, recv_data, RECV_DATA, 0);

      if (recv_data_len <= 0)
        break;

      APP_PRINT("recv %d len data\n",recv_data_len);

      write(connected, recv_data, recv_data_len);
    }
    if (connected >= 0)
      closesocket(connected);

    connected = -1;
  }
__exit:
  if (sock >= 0) closesocket(sock);
  if (recv_data) free(recv_data);
}

/*-----------------------------------------------------------------------------------*/
void tcp_echo_server_init(void)
{
  sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, 1024, 4);
}

源文件添加完毕后,我们在LWIP初始化代码后加入 tcp_echo_server_init 。

void lwip_thread_entry(void *pvParameters)
{
    ...
    LwIP_Init();
    tcp_echo_server_init();
    ...
}
测试效果

     代码烧录完毕后,我们使用网络调试助手连接开发板,并发送字符串 Hello Renesas! ,可以看到开发板运行的echo服务器成功回复了相同字符串,如图:

网络测试.JPG

三、UDP echo服务器测试

测试程序

同样地,LWIP的UDP功能也是默认开启的,我们无需额外配置,直接开始测试。

下面是测试用的UDP echo服务器测试代码,基于LWIP的Socket接口编写。

/*
 * udp_echo_server_thread.c
 */
#include "FreeRTOS.h"
#include "task.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"

#define UDP_ECHO_PORT 7          // 标准 Echo 端口
#define RECV_BUF_SIZE 1500       // 适配以太网 MTU

void udp_echo_thread(void *arg) {
    int sock;
    int err;
    struct sockaddr_in server_addr, client_addr;
    char *recv_buf;
    socklen_t addr_len = sizeof(client_addr);

    (void)arg;

    // 1. 为接收缓冲区分配内存
    recv_buf = (char *)pvPortMalloc(RECV_BUF_SIZE);
    if (recv_buf == NULL) {
        vTaskDelete(NULL);
        return;
    }

    // 2. 创建 UDP Socket
    // AF_INET: IPv4, SOCK_DGRAM: 数据报(UDP)
    sock = lwip_socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        vPortFree(recv_buf);
        vTaskDelete(NULL);
        return;
    }

    // 3. 准备本地监听地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = lwip_htons(UDP_ECHO_PORT);
    server_addr.sin_addr.s_addr = lwip_htonl(IPADDR_ANY); // 监听所有网卡 IP

    // 4. 绑定端口
    err = lwip_bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (err < 0) {
        lwip_close(sock);
        vPortFree(recv_buf);
        vTaskDelete(NULL);
        return;
    }

    while (1) {
        // 5. 等待接收数据
        // recvfrom 是阻塞的,FreeRTOS 此时会挂起该任务不占用 CPU
        int n = lwip_recvfrom(sock, recv_buf, RECV_BUF_SIZE, 0,
                              (struct sockaddr *)&client_addr, &addr_len);

        if (n > 0) {
            // 6. 将收到的数据原样发回给客户端
            lwip_sendto(sock, recv_buf, (size_t)n, 0,
                        (struct sockaddr *)&client_addr, addr_len);
        } else if (n < 0) {
            // 处理错误,例如由于资源耗尽导致的接收失败
            vTaskDelay(pdMS_TO_TICKS(10));
        }
    }
}

// 任务创建入口
void udp_echo_server_init(void) {
    xTaskCreate(udp_echo_thread, "udp_echo", 1024 / sizeof(StackType_t), NULL, 4, NULL);
}

源文件添加完毕后,我们同样在LWIP初始化代码后加入 udp_echo_server_init 。

void lwip_thread_entry(void *pvParameters)
{
    ...
    LwIP_Init();
    tcp_echo_server_init();
    udp_echo_server_init();
    ...
}
测试效果

     网络调试助手不支持作为udp客户端连接,我们使用 netcat 进行测试。

     代码烧录完毕后,发送字符串 hello Renesas! ,可以看到开发板运行的UDP echo服务器成功回复了相同字符串,如图:

UDP test.png

四、HTTP服务器测试

功能配置

     LWIP的HTTP功能是属于附加APP,需要我们手动添加相应的功能。在Stack页面,添加Networking -> LwIP HTTP Server。框图如下:

HTTP Server 模块.JPG

     配置好框图后,LWIP不需要额外增加业务代码。但要在初始化方法中加入httpd服务的初始化。

void lwip_thread_entry(void *pvParameters)
{
    ...
    LwIP_Init();
    tcp_echo_server_init();
    udp_echo_server_init();
    httpd_init();
    ...
}

     HTTP模块不需要修改配置项,使用默认配置即可。

http配置.JPG

测试效果

     官方会提供一个默认的页面。我们打开浏览器,直接访问开发板的IP地址,默认80端口不用填写。效果如下:

httpd.JPG


五、MQTT服务器测试

功能配置

     LWIP的MQTT功能同样属于附加APP,需要我们手动添加相应的功能。在Stack页面,添加Networking -> LwIP MQTT Client。框图如下:

mqtt框图.JPG

测试代码

     接下来我们添加MQTT客户端的测试代码,如下:

#include "bsp_api.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "hal_data.h"
#include "lwip/init.h"
#include "lwip/tcpip.h"
#include "lwip/tcp.h"
#include "lwip/timeouts.h"
#include "lwip/ip4.h"
#include "lwip/apps/mqtt.h"
#include "common_utils.h"

//#define HOST  "broker.emqx.io"
#define PORT  1883
#define TOPIC "/test"

#define SIGNAL_CONNECTED 0x01

// 使用 FreeRTOS 原生句柄
static SemaphoreHandle_t publish_semaphore_id = NULL;
static TaskHandle_t      mqtt_thread_id      = NULL;
static ip_addr_t         host_ip;

void start_mqtt_task(void *argument);

void mqtt_client_init(void) {
    // FreeRTOS v11 任务创建
    BaseType_t ret = xTaskCreate(start_mqtt_task,
                "mqtt_task",
                4096 / sizeof(StackType_t), // 瑞萨平台注意:此处通常为字数,RA8建议给足堆栈
                NULL,
                configMAX_PRIORITIES - 2,   // 优先级调整为系统常用优先级
                &mqtt_thread_id);

    if (ret == pdTRUE) {
        APP_PRINT("lwip mqtt init success...\n\n");
    } else {
        APP_PRINT("lwip mqtt init fail...\n\n");
    }
}

static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
    (void)client; (void)arg;
    if(status == MQTT_CONNECT_ACCEPTED) {
        APP_PRINT("MQTT connected\n");
        // 使用任务通知代替 osSignalSet
        if (mqtt_thread_id != NULL) {
            xTaskNotify(mqtt_thread_id, SIGNAL_CONNECTED, eSetBits);
        }
    }
}

static void mqtt_pub_request_cb(void *arg, err_t result)
{
    (void)arg; (void)result;
    APP_PRINT("MQTT published\n");
    // 释放计数信号量
    if (publish_semaphore_id != NULL) {
        xSemaphoreGive(publish_semaphore_id);
    }
}

void start_mqtt_task(void *argument)
{
    (void)argument;
    uint32_t notified_value;

    mqtt_client_t *client = mqtt_client_new();

    struct mqtt_connect_client_info_t ci;
    memset(&ci, 0, sizeof(ci));
    ci.client_id = "lwip_mqtt_test";

    // 创建计数信号量:最大值 MQTT_REQ_MAX_IN_FLIGHT,初始值相同
    publish_semaphore_id = xSemaphoreCreateCounting(MQTT_REQ_MAX_IN_FLIGHT, MQTT_REQ_MAX_IN_FLIGHT);

    IP4_ADDR(&host_ip, 192, 168, 1, 9);
    // 假设此处已通过 DNS 获取了 host_ip,或手动填入 IP
    mqtt_client_connect(client, &host_ip, PORT, mqtt_connection_cb, NULL, &ci);

    char payload[100];
    u8_t qos = 1;
    u8_t retain = 0;

    // 等待任务通知
    // 参数:进入前不清除位,退出时清除所有位,指向通知值的指针,永久等待
    if (xTaskNotifyWait(0, 0xFFFFFFFF, &notified_value, portMAX_DELAY) == pdTRUE) {
        if (notified_value & SIGNAL_CONNECTED) {
            for(int i = 0; ; i++) {
                // 获取信号量 (代替 osSemaphoreWait)
                if (xSemaphoreTake(publish_semaphore_id, portMAX_DELAY) == pdPASS) {
                    snprintf(payload, sizeof(payload), "Hello Renesas! cnt:%d", i);
                    mqtt_publish(client, TOPIC, payload, (u16_t)strlen(payload), qos, retain, mqtt_pub_request_cb, NULL);
                }
                vTaskDelay(pdMS_TO_TICKS(1000));
            }
        }
    }
}

     最后在LWIP初始化方法中加入MQTT服务的初始化。

void lwip_thread_entry(void *pvParameters)
{
    ...
    LwIP_Init();
    tcp_echo_server_init();
    udp_echo_server_init();
    httpd_init();
    mqtt_client_init();
    ...
}
测试效果

     首先启动 MQTT 服务器。

MQTT Server.JPG

     随后连接开发板,打开客户端软件 MQTTX,可以看到开发板每秒发布一次的 Hello Renesas! 消息:

mqtt.JPG

六、测试总结

taskList.JPG

usage.JPG

     至此,我们已经在 RA8D1 上同时运行了 TCP、UDP、HTTP、MQTT 多个应用。看起来很繁重的样子,但实际上这对于 RA8D1 强大的 480MHz Cortex-M85 核心来说简直小菜一碟,内存也仅仅占用了28%。DTCM、ITCM 和 32M SDRAM 还未投入使用。我后续会联合LVGL和LWIP编写一个电脑副屏项目,敬请期待~


本文工程源码:

lwip_rtos-udp+tcp+http+mqtt.zip





关键词: 瑞萨     RA8D1     FreeRTOS     LWIP    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]