• STM32F4_HAL_LWIP_RAM接口UDP实验


    目录

    RAW 的 UDP 接口简介

    RAW 的 UDP 实验

    硬件设计

    例程功能

    软件设计

    UDP 配置步骤

    软件设计流程图


    RAW 的 UDP 接口简介

            UDP 协议的 RAW 的 API 功能函数,我们使用这些函数来完成 UDP 的数据发送和接收功能。

     (1) udp_new 函数

    此函数用来创建一个 UDP 控制块,这个控制块用来描述 IP 地址、端口号和状态等信息,该函数实现源码如下所示:

    1. struct udp_pcb *
    2. udp_new(void)
    3. {
    4. struct udp_pcb *pcb;
    5. /* 申请一个 UDP 内存池 */
    6. pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
    7. /* 申请成功*/
    8. if (pcb != NULL)
    9. {
    10. /* 初始化 PCB 控制块所有零 */
    11. memset(pcb, 0, sizeof(struct udp_pcb));
    12. /* pcb->ttl = 255 */
    13. pcb->ttl = UDP_TTL;
    14. }
    15. return pcb;
    16. }

            可以看到,该控制块的内存由内存池申请,申请成功之后设置该控制块的生存时间。

    (2) udp_remove 函数

            从 PCB 控制块链表中移除一个控制块,并且把移除的控制块释放内存,该函数实现源码如下所示:

    1. void
    2. udp_remove(struct udp_pcb *pcb)
    3. {
    4. struct udp_pcb *pcb2;
    5. mib2_udp_unbind(pcb);
    6. /* 判断 pcb 被删除在列表的第一个 */
    7. if (udp_pcbs == pcb)
    8. {
    9. /* 从第二 pcb 开始制作列表 */
    10. udp_pcbs = udp_pcbs->next;
    11. }
    12. else/* pcb 不在列表的第一个 */
    13. {
    14. /* 遍历 pcb 列表 */
    15. for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next)
    16. {
    17. /* 在 udp_pcbs 列表中查找 pcb */
    18. if (pcb2->next != NULL && pcb2->next == pcb)
    19. {
    20. /* 从列表中删除 pcb */
    21. pcb2->next = pcb->next;
    22. break;
    23. }
    24. }
    25. }
    26. memp_free(MEMP_UDP_PCB, pcb);
    27. }

            以传入的控制块为条件,遍历 PCB 控制块链表,若链表中的控制块等于要移除的控制块,则该控制块移除 PCB 控制块链表,移除完成之后释放该控制块的内存。

    (3) udp_recv 函数

            此函数用来设置接收回调函数及函数参数,若用户使用 RAW 接口实现 UDP,则用户必须调用此函数设置接收回调函数,该函数的源码如下所示:

    1. void
    2. udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
    3. {
    4. /* 调用 recv()回调和用户数据 */
    5. pcb->recv = recv;
    6. pcb->recv_arg = recv_arg;
    7. }

            可以看出,设置的函数和形参都是由 UDP 控制块的字段指向。

    RAW 的 UDP 实验

    硬件设计

    例程功能

            PC 端和开发板通过 UDP 协议连接起来, PC 端使用网络调试助手向开发板发送数据,开发板接收到以后在 LCD 上显示接收到的数据,我们也可以通过开发板上的按键发送数据给 PC。

     * 硬件资源及引脚分配
     * 1 LED灯
         DS0(RED)     : LED0 - PE0
     * 2 串口1 (PA9/PA10连接在板载USB转串口芯片CH340上面)
     * 3 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
     * 4 ETH,YT8521网络芯片
            ETH_MDIO -------------------------> PA2
            ETH_MDC --------------------------> PC1
            ETH_RMII_REF_CLK------------------> PA1
            ETH_RMII_CRS_DV ------------------> PA7
            ETH_RMII_RXD0 --------------------> PC4
            ETH_RMII_RXD1 --------------------> PC5
            ETH_RMII_TX_EN -------------------> PG11
            ETH_RMII_TXD0 --------------------> PG13
            ETH_RMII_TXD1 --------------------> PG14
            ETH_RESET-------------------------> PD3
     * 2 独立按键
         KEY0    - PE2
         KEY1    - PE3
         KEY2    - PE4

    相关硬件驱动参考正点原子探索者开发指南HAL库版本

    软件设计

    UDP 配置步骤

    1) 创建 UDP 控制块

    调用函数 udp_new 创建 UDP 控制块。

    2) 连接指定的 IP 地址和端口号

    调用函数 udp_connect 绑定远程 IP 地址和远程端口号。

    3) 绑定本地 IP 地址与端口号

    调用函数 udp_bind 绑定本地 IP 地址和本地端口号。

    4) 注册接收回调函数

    udp_recv 是注册接收回调函数,该函数需要自己编写。

    5) 发送数据

    调用函数 udp_send 发送数据。

    软件设计流程图

    lwip_demo.h

    1. #ifndef _LWIP_DEMO_H
    2. #define _LWIP_DEMO_H
    3. #include "./SYSTEM/sys/sys.h"
    4. #define LWIP_SEND_DATA 0X80 /* 定义有数据发送 */
    5. extern uint8_t lwip_send_flag; /* UDP数据发送标志位 */
    6. void lwip_demo(void);
    7. #endif /* _CLIENT_H */

     lwip_demo.c

    1. #include <stdint.h>
    2. #include <stdio.h>
    3. #include "./BSP/LCD/lcd.h"
    4. #include "./MALLOC/malloc.h"
    5. #include "./BSP/KEY/key.h"
    6. #include "./SYSTEM/delay/delay.h"
    7. #include "./BSP/LED/led.h"
    8. #include "lwip/pbuf.h"
    9. #include "lwip/udp.h"
    10. #include "lwip/tcp.h"
    11. #include "lwip_demo.h"
    12. #include "lwip_comm.h"
    13. #include "stdio.h"
    14. #include "string.h"
    15. #define LWIP_DEMO_RX_BUFSIZE 2000 /* 定义udp最大接收数据长度 */
    16. #define LWIP_DEMO_PORT 8080 /* 定义udp连接的本地端口号 */
    17. /* 接收数据缓冲区 */
    18. uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE];
    19. /* 发送数据内容 */
    20. char *g_lwip_demo_sendbuf = "ALIENTEK DATA\r\n";
    21. /* 数据发送标志位 */
    22. uint8_t g_lwip_send_flag;
    23. static void lwip_udp_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
    24. void lwip_udp_senddata(struct udp_pcb *upcb);
    25. void lwip_udp_connection_close(struct udp_pcb *upcb);
    26. /**
    27. * @brief 设置远端IP地址
    28. * @param 无
    29. * @retval 无
    30. */
    31. void lwip_udp_set_remoteip(void)
    32. {
    33. char *tbuf;
    34. uint16_t xoff;
    35. uint8_t key;
    36. lcd_clear(WHITE);
    37. g_point_color = RED;
    38. lcd_show_string(5, 30, 200, 16, 16, "STM32", g_point_color);
    39. lcd_show_string(5, 50, 200, 16, 16, "UDP Test", g_point_color);
    40. lcd_show_string(5, 70, 200, 16, 16, "Remote IP Set", g_point_color);
    41. lcd_show_string(5, 90, 200, 16, 16, "KEY0:+ KEY2:-", g_point_color);
    42. lcd_show_string(5, 110, 200, 16, 16, "KEY1:OK", g_point_color);
    43. tbuf = mymalloc(SRAMIN, 100); /* 申请内存 */
    44. if (tbuf == NULL)return;
    45. /* 前三个IP保持和DHCP得到的IP一致 */
    46. g_lwipdev.remoteip[0] = g_lwipdev.ip[0];
    47. g_lwipdev.remoteip[1] = g_lwipdev.ip[1];
    48. g_lwipdev.remoteip[2] = g_lwipdev.ip[2];
    49. sprintf((char *)tbuf, "Remote IP:%d.%d.%d.", g_lwipdev.remoteip[0], g_lwipdev.remoteip[1], g_lwipdev.remoteip[2]); /* 远端IP */
    50. lcd_show_string(5, 150, 210, 16, 16, tbuf, g_point_color);
    51. g_point_color = BLUE;
    52. xoff = strlen((char *)tbuf) * 8 + 5;
    53. lcd_show_xnum(xoff, 150, g_lwipdev.remoteip[3], 3, 16, 0, g_point_color);
    54. while (1)
    55. {
    56. key = key_scan(0);
    57. if (key == KEY1_PRES)break;
    58. else if (key)
    59. {
    60. if (key == KEY0_PRES)g_lwipdev.remoteip[3] ++; /* IP增加 */
    61. if (key == KEY2_PRES)g_lwipdev.remoteip[3] --; /* IP减少 */
    62. lcd_show_xnum(xoff, 150, g_lwipdev.remoteip[3], 3, 16, 0X80, g_point_color); /* 显示新IP */
    63. }
    64. }
    65. myfree(SRAMIN, tbuf);
    66. }
    67. /**
    68. * @brief UDP服务器回调函数
    69. * @param arg :传入参数
    70. * @param upcb:UDP控制块
    71. * @param p : 网络数据包
    72. * @param addr:IP地址
    73. * @param port:端口号
    74. * @retval 无
    75. */
    76. static void lwip_udp_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
    77. {
    78. uint32_t data_len = 0;
    79. struct pbuf *q;
    80. if (p != NULL) /* 接收到不为空的数据时 */
    81. {
    82. memset(g_lwip_demo_recvbuf, 0, LWIP_DEMO_RX_BUFSIZE); /* 数据接收缓冲区清零 */
    83. for (q = p; q != NULL; q = q->next) /* 遍历完整个pbuf链表 */
    84. {
    85. /* 判断要拷贝到LWIP_DEMO_RX_BUFSIZE中的数据是否大于LWIP_DEMO_RX_BUFSIZE的剩余空间,如果大于 */
    86. /* 的话就只拷贝LWIP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据 */
    87. if (q->len > (LWIP_DEMO_RX_BUFSIZE - data_len)) memcpy(g_lwip_demo_recvbuf + data_len, q->payload, (LWIP_DEMO_RX_BUFSIZE - data_len)); /* 拷贝数据 */
    88. else memcpy(g_lwip_demo_recvbuf + data_len, q->payload, q->len);
    89. data_len += q->len;
    90. if (data_len > LWIP_DEMO_RX_BUFSIZE) break; /* 超出UDP客户端接收数组,跳出 */
    91. }
    92. upcb->remote_ip = *addr; /* 记录远程主机的IP地址 */
    93. upcb->remote_port = port; /* 记录远程主机的端口号 */
    94. g_lwipdev.remoteip[0] = upcb->remote_ip.addr & 0xff; /* IADDR4 */
    95. g_lwipdev.remoteip[1] = (upcb->remote_ip.addr >> 8) & 0xff; /* IADDR3 */
    96. g_lwipdev.remoteip[2] = (upcb->remote_ip.addr >> 16) & 0xff; /* IADDR2 */
    97. g_lwipdev.remoteip[3] = (upcb->remote_ip.addr >> 24) & 0xff; /* IADDR1 */
    98. g_lwip_send_flag |= 1 << 6; /* 标记接收到数据了 */
    99. pbuf_free(p); /* 释放内存 */
    100. }
    101. else
    102. {
    103. udp_disconnect(upcb);
    104. lcd_clear(WHITE); /* 清屏 */
    105. lcd_show_string(30, 30, 200, 16, 16, "STM32", g_point_color);
    106. lcd_show_string(30, 50, 200, 16, 16, "UDP Test", g_point_color);
    107. lcd_show_string(30, 70, 200, 16, 16, "ATOM@ALIENTEK", g_point_color);
    108. lcd_show_string(30, 90, 200, 16, 16, "KEY1:Connect", g_point_color);
    109. lcd_show_string(30, 190, 210, 16, 16, "Connect break!", g_point_color);
    110. }
    111. }
    112. /**
    113. * @brief lwip_democ程序入口
    114. * @param 无
    115. * @retval 无
    116. */
    117. void lwip_demo(void)
    118. {
    119. err_t err;
    120. struct udp_pcb *udppcb; /* 定义一个UDP服务器控制块 */
    121. ip_addr_t rmtipaddr; /* 远端ip地址 */
    122. char *tbuf;
    123. uint8_t key;
    124. uint8_t res = 0;
    125. uint8_t t = 0;
    126. lwip_udp_set_remoteip();/* 先选择IP */
    127. lcd_clear(WHITE); /* 清屏 */
    128. g_point_color = RED;
    129. lcd_show_string(5, 30, 200, 16, 16, "STM32", g_point_color);
    130. lcd_show_string(5, 50, 200, 16, 16, "UDP Test", g_point_color);
    131. lcd_show_string(5, 70, 200, 16, 16, "ATOM@ALIENTEK", g_point_color);
    132. lcd_show_string(5, 90, 200, 16, 16, "KEY0:Send data", g_point_color);
    133. lcd_show_string(5, 110, 200, 16, 16, "KEY1:Exit", g_point_color);
    134. tbuf = mymalloc(SRAMIN, 200); /* 申请内存 */
    135. if (tbuf == NULL)return ; /* 内存申请失败了,直接退出 */
    136. sprintf((char *)tbuf, "Local IP:%d.%d.%d.%d", g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]); /* 服务器IP */
    137. lcd_show_string(5, 130, 210, 16, 16, tbuf, g_point_color);
    138. sprintf((char *)tbuf, "Remote IP:%d.%d.%d.%d", g_lwipdev.remoteip[0], g_lwipdev.remoteip[1], g_lwipdev.remoteip[2], g_lwipdev.remoteip[3]); /* 远端IP */
    139. lcd_show_string(5, 150, 210, 16, 16, tbuf, g_point_color);
    140. sprintf((char *)tbuf, "Remote Port:%d", LWIP_DEMO_PORT); /* 客户端端口号 */
    141. lcd_show_string(5, 170, 210, 16, 16, tbuf, g_point_color);
    142. g_point_color = BLUE;
    143. udppcb = udp_new();
    144. if (udppcb) /* 创建成功 */
    145. {
    146. IP4_ADDR(&rmtipaddr, g_lwipdev.remoteip[0], g_lwipdev.remoteip[1], g_lwipdev.remoteip[2], g_lwipdev.remoteip[3]);
    147. err = udp_connect(udppcb, &rmtipaddr, LWIP_DEMO_PORT); /* UDP客户端连接到指定IP地址和端口号的服务器 */
    148. if (err == ERR_OK)
    149. {
    150. err = udp_bind(udppcb, IP_ADDR_ANY, LWIP_DEMO_PORT); /* 绑定本地IP地址与端口号 */
    151. if (err == ERR_OK) /* 绑定完成 */
    152. {
    153. udp_recv(udppcb,lwip_udp_callback, NULL); /* 注册接收回调函数 */
    154. g_point_color = BLUE;
    155. lcd_show_string(5, 190, lcddev.width - 30, lcddev.height - 190, 16, "Receive Data:", g_point_color); /* 提示消息 */
    156. g_point_color = MAGENTA;
    157. }
    158. else res = 1;
    159. }
    160. else res = 1;
    161. }
    162. else res = 1;
    163. while (res == 0)
    164. {
    165. key = key_scan(0);
    166. if (key == KEY1_PRES)break;
    167. if (key == KEY0_PRES) /* KEY0按下了,发送数据 */
    168. {
    169. lwip_udp_senddata(udppcb);
    170. }
    171. if (g_lwip_send_flag & 1 << 6) /* 是否收到数据 */
    172. {
    173. lcd_fill(5, 230, lcddev.width - 1, lcddev.height - 1, WHITE); /* 清上一次数据 */
    174. /* 显示接收到的数据 */
    175. lcd_show_string(6, 230, lcddev.width - 2, lcddev.height - 230, 16, (char *)g_lwip_demo_recvbuf, g_point_color);
    176. g_lwip_send_flag &= ~(1 << 6); /* 标记数据已经被处理了 */
    177. }
    178. lwip_periodic_handle(); /* LWIP轮询任务 */
    179. delay_ms(2);
    180. t++;
    181. if (t == 200)
    182. {
    183. t = 0;
    184. LED0_TOGGLE();
    185. }
    186. }
    187. g_point_color = DARKBLUE;
    188. lwip_udp_connection_close(udppcb);
    189. myfree(SRAMIN, tbuf);
    190. }
    191. /**
    192. * @brief UDP服务器发送数据
    193. * @param upcb: UDP控制块
    194. * @retval 无
    195. */
    196. void lwip_udp_senddata(struct udp_pcb *upcb)
    197. {
    198. struct pbuf *ptr;
    199. ptr = pbuf_alloc(PBUF_TRANSPORT, strlen((char *)g_lwip_demo_sendbuf), PBUF_POOL); /* 申请内存 */
    200. if (ptr)
    201. {
    202. pbuf_take(ptr, (char *)g_lwip_demo_sendbuf, strlen((char *)g_lwip_demo_sendbuf)); /* 将g_lwip_demo_sendbuf中的数据打包进pbuf结构中 */
    203. udp_send(upcb, ptr); /* udp发送数据 */
    204. pbuf_free(ptr); /* 释放内存 */
    205. }
    206. }
    207. /**
    208. * @brief 关闭tcp连接
    209. * @param upcb: UDP控制块
    210. * @retval 无
    211. */
    212. void lwip_udp_connection_close(struct udp_pcb *upcb)
    213. {
    214. udp_disconnect(upcb);
    215. udp_remove(upcb); /* 断开UDP连接 */
    216. g_lwip_send_flag &= ~(1 << 5); /* 标记连接断开 */
    217. lcd_clear(WHITE); /* 清屏 */
    218. lcd_show_string(5, 30, 200, 16, 16, "STM32", g_point_color);
    219. lcd_show_string(5, 50, 200, 16, 16, "UDP Test", g_point_color);
    220. lcd_show_string(5, 70, 200, 16, 16, "ATOM@ALIENTEK", g_point_color);
    221. lcd_show_string(5, 90, 200, 16, 16, "KEY1:Connect", g_point_color);
    222. lcd_show_string(5, 190, 210, 16, 16, "STATUS:Disconnected", g_point_color);
    223. }

    主函数

    1. #include "./SYSTEM/sys/sys.h"
    2. #include "./SYSTEM/usart/usart.h"
    3. #include "./SYSTEM/delay/delay.h"
    4. #include "./BSP/LED/led.h"
    5. #include "./BSP/LCD/lcd.h"
    6. #include "./USMART/usmart.h"
    7. #include "./BSP/KEY/key.h"
    8. #include "./BSP/SRAM/sram.h"
    9. #include "./MALLOC/malloc.h"
    10. #include "lwip_comm.h"
    11. #include "lwipopts.h"
    12. #include "lwip_demo.h"
    13. /**
    14. * @breif 加载UI
    15. * @param mode : bit0:0,不加载;1,加载前半部分UI
    16. * bit1:0,不加载;1,加载后半部分UI
    17. * @retval 无
    18. */
    19. void lwip_test_ui(uint8_t mode)
    20. {
    21. uint8_t speed;
    22. uint8_t buf[30];
    23. if(mode & 1<< 0)
    24. {
    25. lcd_show_string(6, 10, 200, 32, 32, "STM32", DARKBLUE);
    26. lcd_show_string(6, 40, lcddev.width, 24, 24, "lwIP UDP Test", DARKBLUE);
    27. lcd_show_string(6, 70, 200, 16, 16, "ATOM@ALIENTEK", DARKBLUE);
    28. }
    29. if(mode & 1 << 1)
    30. {
    31. lcd_show_string(6, 110, 200, 16, 16, "lwIP Init Successed", MAGENTA);
    32. if(g_lwipdev.dhcpstatus == 2)
    33. {
    34. sprintf((char*)buf,"DHCP IP:%d.%d.%d.%d",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]); /* 显示动态IP地址 */
    35. }
    36. else
    37. {
    38. sprintf((char*)buf,"Static IP:%d.%d.%d.%d",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]); /* 打印静态IP地址 */
    39. }
    40. lcd_show_string(6, 130, 200, 16, 16, (char*)buf, MAGENTA);
    41. speed = ethernet_chip_get_speed(); /* 得到网速 */
    42. if(speed)
    43. {
    44. lcd_show_string(6, 150, 200, 16, 16, "Ethernet Speed:100M", MAGENTA);
    45. }
    46. else
    47. {
    48. lcd_show_string(6, 150, 200, 16, 16, "Ethernet Speed:10M", MAGENTA);
    49. }
    50. }
    51. }
    52. int main(void)
    53. {
    54. uint8_t t = 0;
    55. HAL_Init(); /* 初始化HAL库 */
    56. sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
    57. delay_init(168); /* 延时初始化 */
    58. usart_init(115200); /* 串口初始化为115200 */
    59. usmart_dev.init(84); /* 初始化USMART */
    60. led_init(); /* 初始化LED */
    61. lcd_init(); /* 初始化LCD */
    62. key_init(); /* 初始化按键 */
    63. sram_init(); /* SRAM初始化 */
    64. my_mem_init(SRAMIN); /* 初始化内部SRAM内存池 */
    65. my_mem_init(SRAMEX); /* 初始化外部SRAM内存池 */
    66. my_mem_init(SRAMCCM); /* 初始化内部CCM内存池 */
    67. lwip_test_ui(1); /* 加载前半部分UI */
    68. lcd_show_string(6, 110, 200, 16, 16, "lwIP Init !!", BLUE);
    69. while (lwip_comm_init() != 0)
    70. {
    71. lcd_show_string(6, 110, 200, 16, 16, "lwIP Init failed!!", BLUE);
    72. delay_ms(500);
    73. lcd_fill(6, 50, 200 + 30, 50 + 16, WHITE);
    74. lcd_show_string(6, 110, 200, 16, 16, "Retrying... ", BLUE);
    75. delay_ms(500);
    76. LED1_TOGGLE();
    77. }
    78. while (!ethernet_read_phy(PHY_SR)) /* 检查MCU与PHY芯片是否通信成功 */
    79. {
    80. printf("MCU与PHY芯片通信失败,请检查电路或者源码!!!!\r\n");
    81. }
    82. #if LWIP_DHCP
    83. lcd_show_string(6, 130, 200, 16, 16, "DHCP IP configing... ", BLUE); /* 开始DHCP */
    84. while ((g_lwipdev.dhcpstatus != 2) && (g_lwipdev.dhcpstatus != 0XFF)) /* 等待DHCP获取成功/超时溢出 */
    85. {
    86. lwip_periodic_handle();
    87. delay_ms(1000);
    88. }
    89. #endif
    90. lwip_demo(); /* lwIP程序入口 */
    91. lwip_test_ui(2); /* 加载后半部分UI */
    92. while (1)
    93. {
    94. lwip_periodic_handle(); /* LWIP轮询任务 */
    95. delay_ms(2);
    96. t ++;
    97. if (t >= 200)
    98. {
    99. t = 0;
    100. LED0_TOGGLE();
    101. }
    102. }
    103. }

    完整工程参考正点原子lwip源码RAW_UDP实验

  • 相关阅读:
    StringAOP统一问题处理
    预训练+微调任务
    MyISAM 与 InnoDB 的区别是什么?
    KVM虚拟化介绍和安装使用方法
    软件测试分哪几种?
    MySQL 连接查询(多表查询 二)
    Python3:@property属性装饰器
    MySQL进阶07_存储过程/存储函数
    qt常用控件1
    大型语言模型的语义搜索(二):文本嵌入(Text Embeddings)
  • 原文地址:https://blog.csdn.net/banchengl/article/details/139282819