硬汉嵌入式论坛

 找回密码
 立即注册
查看: 2287|回复: 8
收起左侧

[RL-TCPnet教程] 【RL-TCPnet网络教程】第17章 RL-TCPnet之UDP通信

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2017-11-7 15:17:59 | 显示全部楼层 |阅读模式
第17章      RL-TCPnet之UDP通信



    本章节为大家讲解RL-TCPnet的UDP通信实现,学习本章节前,务必要优先学习第16章UDP用户数据报协议基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。
    本章教程含STM32F407开发板和STM32F429开发板。
17.1 初学者重要提示
17.2 UDP的API函数
17.3 特别注意UDP个数配置
17.4 UDP配置说明(Net_Config.c)
17.5 UDP调试说明(Net_Debug.c)
17.6 UDP通信的丢包问题说明
17.7 UDP通信的实现方法
17.8 网络调试助手和板子的操作步骤
17.9 实验例程说明(裸机)
17.10 实验例程说明(RTX)
17.11    总结



17.1  初学者重要提示

1、学习本章节前,务必保证已经学习了第16章的基础知识。
2、本章要掌握的函数稍多,可以先学会基本的使用,然后再深入了解这些函数使用时的注意事项,争取达到熟练运用。
3、对于UDP通讯时的丢包问题在本章节的17.6小节有特别说明。
4、本章节使用野人网络调试助手,前面几个章节使用的网络调试助手在UDP测试方面丢包稍微高一点。具体看本章节的17.8小节。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-7 15:24:28 | 显示全部楼层
17.2 UDP的API函数


    使用如下7个函数可以实现RL-TCPnet的UDP通信:
(1)udp_get_socket
(2)udp_open
(3)udp_close
(4)udp_release_socket
(5)udp_get_buf
(6)udp_send
(7)udp_mcast_ttl
关于这7个函数的讲解及其使用方法可以看教程第 3 章 3.4 小节里面说的参考资料 rlarm.chm 文件:
17.1.png


这里我们重点的说以下4个函数,因为本章节配套的例子使用的是这4个函数:
(1)udp_get_socket
(2)udp_open
(3)udp_get_buf
(4)udp_send
关于这些函数注意以下两点:
1、这些函数都不支持重入,也就是不支持多任务调用。
2、UDP接口函数通过UDP Socket做数据传输,主要用于不把数据可靠传输作为首选的场合。因为UDP没有确认机制,会有丢包问题。

17.2.1 函数udp_get_socket


函数原型:
  1. U8 udp_get_socket (
  2.     U8   tos,           /* UDP Socket服务类型 */
  3.     U8   opt,           /* 校验和选项 */
  4.     U16 (*listener)(    /* 回调函数 */
  5.         U8  socket,     /* UDP Socket句柄 */
  6.         U8* remip,      /* 远程设备的IP地址 */
  7.         U16 port,       /* 远程设备的端口号. */
  8.         U8* buf,        /* 接收到的数据地址 */
  9.         U16 len ));     /* 接收到的数据长度 */          [size=3][font=新宋体]         [/font][/size]
复制代码

函数描述:
函数udp_get_socket用于获取一个UDP Socket。
(1)第1个参数用于指定服务类型,默认取零即可。
(2)第2个参数是校验和选项,有如下两种可选。
17.2.png


    用户可以通过或操作将发送和接收校验和都选上UDP_OPT_CHK_CS | UDP_OPT_SEND_CS。如果这两个选项都不使用的话,设置此参数为0即可,这样一定程度上可以加快系统响应时间。
(3)第3个参数是回调函数,用于事件监听。
    a. 回调函数第1个参数,UDP Socket的句柄,也就是函数tcp_get_socket的返回值。
    b. 回调函数第2个参数,远程设备的IP地址。
    c. 回调函数第3个参数,远程设备的端口号。
    d. 回调函数第4个参数,接收到的数据地址。
    e. 回调函数第5个参数,接收到的数据长度。
(4)返回值,如果获取成功,返回TCP Socket句柄,如果获取失败,返回0
使用这个函数要注意以下问题:
1.     调用UDPSocket任何其它函数前,务必要调用此函数udp_get_socket。
2.     以太网数据包受到以太网CRC的保护。
3.     传输的数据包通过路由器、代理服务器、网关等,数据包是可以被修改的。
4.     使用函数udp_get_socket,第3个参数的回调函数务必要设置。
使用举例:
  1. /*
  2. *********************************************************************************************************
  3. *                                         变量
  4. *********************************************************************************************************
  5. */
  6. uint8_t udp_soc;
  7. /*
  8. *********************************************************************************************************
  9. *    函 数 名: tcp_callback
  10. *    功能说明: TCP Socket的回调函数
  11. *    形    参: socket   UDP Socket类型
  12. *             remip    远程设备的IP地址
  13. *             remport  远程设备的端口号
  14. *             buf      远程设备发来的数据地址
  15. *             len      远程设备发来的数据长度,单位字节
  16. *    返 回 值: 默认返回0即可,一般用不上
  17. *********************************************************************************************************
  18. */
  19. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  20. {
  21.      char buffer[50];
  22.      U16 i;
  23.      /* 确保是udp_soc的回调 */
  24.      if (socket != udp_soc)
  25.      {
  26.          return (0);
  27.      }
  28.      /* 发消息的远程IP,打印IP和端口号 */
  29.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  30.      printf_debug("IP:%s  port:%d\\r\\n", buffer, remport);
  31.      /* 接收到的数据长度,单位字节 */
  32.      printf_debug("Data length = %d\\r\\n", len);
  33.      /* 打印接收到的消息 */
  34.      for(i = 0; i < len; i++)
  35.      {
  36.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  37.      }
  38.      return (0);
  39. }
  40. /*
  41. *********************************************************************************************************
  42. *    函 数 名: TCPnetTest
  43. *    功能说明: TCPnet应用
  44. *    形    参: 无
  45. *    返 回 值: 无
  46. *********************************************************************************************************
  47. */
  48. void TCPnetTest(void)
  49. {
  50.      int32_t iCount;
  51.      uint8_t *sendbuf;
  52.      uint8_t res;
  53.      uint8_t ucKeyCode;
  54.    
  55.    
  56.      /* 初始化网络协议栈 */
  57.      init_TcpNet ();
  58.    
  59.      /* 获取一个UDP Socket  */
  60.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  61.      if (udp_soc != 0)
  62.      {
  63.          /* 打开UDP端口号 */
  64.           udp_open (udp_soc, LocalPort_NUM);
  65.      }
  66.     /* 省略 */
  67. }
复制代码


17.2.2 函数udp_open


函数原型:
  1. BOOL udp_open (
  2.     U8  socket,      /* UDP Socket句柄 */
  3.     U16 locport);    /* 端口号 */       [size=3][font=新宋体]         [/font][/size]
复制代码

函数描述:
函数udp_open用于打开UDP通信。
(1)第1个参数是设置要监听的UDP Socket句柄。
(2)第2个参数是UDP端口号。
(3)返回值,打开成功返回__TRUE,打开失败返回__FALSE
使用这个函数要注意以下问题:
1.     如果第二个参数填0的话,系统将为其自动分配一个未使用的UDP端口号。
使用举例:
  1. /*
  2. *********************************************************************************************************
  3. *                                         变量
  4. *********************************************************************************************************
  5. */
  6. uint8_t udp_soc;
  7. /*
  8. *********************************************************************************************************
  9. *    函 数 名: tcp_callback
  10. *    功能说明: TCP Socket的回调函数
  11. *    形    参: socket   UDP Socket类型
  12. *             remip    远程设备的IP地址
  13. *             remport  远程设备的端口号
  14. *             buf      远程设备发来的数据地址
  15. *             len      远程设备发来的数据长度,单位字节
  16. *    返 回 值: 默认返回0即可,一般用不上
  17. *********************************************************************************************************
  18. */
  19. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  20. {
  21.      char buffer[50];
  22.      U16 i;
  23.      /* 确保是udp_soc的回调 */
  24.      if (socket != udp_soc)
  25.      {
  26.          return (0);
  27.      }
  28.      /* 发消息的远程IP,打印IP和端口号 */
  29.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  30.      printf_debug("IP:%s  port:%d\\r\\n", buffer, remport);
  31.      /* 接收到的数据长度,单位字节 */
  32.      printf_debug("Data length = %d\\r\\n", len);
  33.      /* 打印接收到的消息 */
  34.      for(i = 0; i < len; i++)
  35.      {
  36.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  37.      }
  38.      return (0);
  39. }
  40. /*
  41. *********************************************************************************************************
  42. *    函 数 名: TCPnetTest
  43. *    功能说明: TCPnet应用
  44. *    形    参: 无
  45. *    返 回 值: 无
  46. *********************************************************************************************************
  47. */
  48. void TCPnetTest(void)
  49. {
  50.      int32_t iCount;
  51.      uint8_t *sendbuf;
  52.      uint8_t res;
  53.      uint8_t ucKeyCode;
  54.    
  55.    
  56.      /* 初始化网络协议栈 */
  57.      init_TcpNet ();
  58.    
  59.      /* 获取一个UDP Socket  */
  60.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  61.      if (udp_soc != 0)
  62.      {
  63.          /* 打开UDP端口号 */
  64.          udp_open (udp_soc, LocalPort_NUM);
  65.      }
  66.     /* 省略 */
  67. }
复制代码


17.2.3 函数udp_get_buf


函数原型:
  1. U8* udp_get_buf (
  2.     U16 size);    /* 申请缓冲区大小,单位字节 */
复制代码

函数描述:
函数udp_get_buf用于获取UDP发送缓冲区,用户将要发送的数据存到这个缓冲区中,然后通过函数udp_send发送。发送完毕后会释放申请的发送缓冲区。
(1)第1个参数是要申请的缓冲区大小,单位字节。
(2)返回值,返回获取的缓冲区地址。如果缓冲区申请失败,RL-TCPnet会调用函数sys_error,并触发里面的错误类型ERR_MEM_ALLOC。对于RL-TCPnet V4.60及其以上版本,如果用户将此函数的形参与0x8000进行或操作,即最高位设置为1,那么此函数申请失败的话会返回空指针,即数值0,并触发函数sys_error的调用。
使用这个函数要注意以下问题:
1.     每次发送都需要调用此函数获取发送缓冲区地址。
2.     申请的发送缓冲区大小不可超过最大数据包大小UDP Maximum Packet Size,即1472字节。
3.     操作缓冲区的时候,切不可超过申请的缓冲区大小,否则会造成RL-TCPnet崩溃。
使用举例:
  1. /*
  2. *********************************************************************************************************
  3. *                                      用于本文件的调试
  4. *********************************************************************************************************
  5. */
  6. #define PORT_NUM       1001    /* TCP服务器监听端口号 */
  7. /*
  8. *********************************************************************************************************
  9. *                                         变量
  10. *********************************************************************************************************
  11. */
  12. uint8_t udp_soc;
  13. /*
  14. *********************************************************************************************************
  15. *    函 数 名: tcp_callback
  16. *    功能说明: TCP Socket的回调函数
  17. *    形    参: socket   UDP Socket类型
  18. *             remip    远程设备的IP地址
  19. *             remport  远程设备的端口号
  20. *             buf      远程设备发来的数据地址
  21. *             len      远程设备发来的数据长度,单位字节
  22. *    返 回 值: 默认返回0即可,一般用不上
  23. *********************************************************************************************************
  24. */
  25. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  26. {
  27.      char buffer[50];
  28.      U16 i;
  29.      /* 确保是udp_soc的回调 */
  30.      if (socket != udp_soc)
  31.      {
  32.          return (0);
  33.      }
  34.      /* 发消息的远程IP,打印IP和端口号 */
  35.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  36.      printf_debug("IP:%s  port:%d\\r\\n", buffer, remport);
  37.      /* 接收到的数据长度,单位字节 */
  38.      printf_debug("Data length = %d\\r\\n", len);
  39.      /* 打印接收到的消息 */
  40.      for(i = 0; i < len; i++)
  41.      {
  42.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  43.      }
  44.      return (0);
  45. }
  46. /*
  47. *********************************************************************************************************
  48. *    函 数 名: TCPnetTest
  49. *    功能说明: TCPnet应用
  50. *    形    参: 无
  51. *    返 回 值: 无
  52. *********************************************************************************************************
  53. */
  54. void TCPnetTest(void)
  55. {
  56.      int32_t iCount;
  57.      uint8_t *sendbuf;
  58.      uint8_t res;
  59.      uint8_t ucKeyCode;
  60.    
  61.    
  62.      /* 初始化网络协议栈 */
  63.      init_TcpNet ();
  64.    
  65.      /* 获取一个UDP Socket  */
  66.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  67.      if (udp_soc != 0)
  68.      {
  69.          /* 打开UDP端口号 */
  70.          udp_open (udp_soc, LocalPort_NUM);
  71.      }
  72.      /* 创建一个周期是100ms的软定时器 */
  73.     bsp_StartAutoTimer(0, 100);
  74.    
  75.      while (1)
  76.      {
  77.          /* TCP轮询 */
  78.          tcpnet_poll();
  79.          /* 按键消息的处理 */
  80.          ucKeyCode = bsp_GetKey();
  81.          if (ucKeyCode != KEY_NONE)
  82.          {
  83.               switch (ucKeyCode)
  84.               {
  85.                    /* K1键按下,给远程UDP设备发送8字节数据 */
  86.                    case KEY_DOWN_K1:
  87.                        /* 用于设置发送次数 */
  88.                        iCount = 1;
  89.                        do
  90.                        {
  91.                             tcpnet_poll();
  92.                             /* 申请8字节的空间 */
  93.                             sendbuf = udp_get_buf (8);
  94.                      
  95.                             if(sendbuf != NULL)
  96.                             {
  97.                                  /* 初始化8个字节变量 */
  98.                                  sendbuf[0] = '1';
  99.                                  sendbuf[1] = '2';
  100.                                  sendbuf[2] = '3';
  101.                                  sendbuf[3] = '4';
  102.                                  sendbuf[4] = '5';
  103.                                  sendbuf[5] = '6';
  104.                                  sendbuf[6] = '7';
  105.                                  sendbuf[7] = '8';
  106.                                 
  107.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 8);
  108.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  109.                                  if(res == __TRUE )
  110.                                  {
  111.                                      iCount--;
  112.                                  }
  113.                             }
  114.                            
  115.                             /*
  116.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  117.                                保证远程设备可以接受到数据
  118.                             */
  119.                             bsp_DelayMS(10);  
  120.                        }while(iCount > 0);
  121.                        break;
  122.                   
  123.                     /* 其他的键值不处理 */
  124.                    default:                    
  125.                        break;
  126.               }
  127.          }
  128.      }
  129. }
复制代码


17.2.4 函数udp_send


函数原型:
  1. BOOL udp_send (
  2.     U8  socket,     /* UDP socket句柄 */
  3.     U8* remip,      /* 远程设备的IP地址 */
  4.     U16 remport,    /* 远程设备的端口号 */
  5.     U8* buf,        /* 要发送数据的地址 */
  6.     U16 dlen );     /* 要发送数据的大小,单位字节 */
复制代码

函数描述:
函数udp_send用于发送数据包给远程设备。
(1)第1个参数是UDP Socket句柄。
(2)第2个参数是远程设备的IP地址。
(3)第3个参数是远程设备的端口号。
(4)第4个参数是要发送数据的地址。
(5)第5个参数是要发送数据的大小,单位字节。
(6)返回值,发送成功返回__TRUE,发送失败返回__FALSE
使用这个函数要注意以下问题:
1.     调用函数udp_send前务必要调用函数udp_get_buf获得缓冲区。
2.     数据通信前,务必要通过函数udp_open打开。
3.     同一个端口号,同一个UDP Socket可以与多个远程设备通信,但需要用户管理好多个设备通信时的数据发送和接收。
使用举例:
  1. /*
  2. *********************************************************************************************************
  3. *                                      用于本文件的调试
  4. *********************************************************************************************************
  5. */
  6. #define PORT_NUM       1001    /* TCP服务器监听端口号 */
  7. /*
  8. *********************************************************************************************************
  9. *                                         变量
  10. *********************************************************************************************************
  11. */
  12. uint8_t udp_soc;
  13. /*
  14. *********************************************************************************************************
  15. *    函 数 名: tcp_callback
  16. *    功能说明: TCP Socket的回调函数
  17. *    形    参: socket   UDP Socket类型
  18. *             remip    远程设备的IP地址
  19. *             remport  远程设备的端口号
  20. *             buf      远程设备发来的数据地址
  21. *             len      远程设备发来的数据长度,单位字节
  22. *    返 回 值: 默认返回0即可,一般用不上
  23. *********************************************************************************************************
  24. */
  25. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  26. {
  27.      char buffer[50];
  28.      U16 i;
  29.      /* 确保是udp_soc的回调 */
  30.      if (socket != udp_soc)
  31.      {
  32.          return (0);
  33.      }
  34.      /* 发消息的远程IP,打印IP和端口号 */
  35.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  36.      printf_debug("IP:%s  port:%d\\r\\n", buffer, remport);
  37.      /* 接收到的数据长度,单位字节 */
  38.      printf_debug("Data length = %d\\r\\n", len);
  39.      /* 打印接收到的消息 */
  40.      for(i = 0; i < len; i++)
  41.      {
  42.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  43.      }
  44.      return (0);
  45. }
  46. /*
  47. *********************************************************************************************************
  48. *    函 数 名: TCPnetTest
  49. *    功能说明: TCPnet应用
  50. *    形    参: 无
  51. *    返 回 值: 无
  52. *********************************************************************************************************
  53. */
  54. void TCPnetTest(void)
  55. {
  56.      int32_t iCount;
  57.      uint8_t *sendbuf;
  58.      uint8_t res;
  59.      uint8_t ucKeyCode;
  60.    
  61.    
  62.      /* 初始化网络协议栈 */
  63.      init_TcpNet ();
  64.    
  65.      /* 获取一个UDP Socket  */
  66.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  67.      if (udp_soc != 0)
  68.      {
  69.          /* 打开UDP端口号 */
  70.          udp_open (udp_soc, LocalPort_NUM);
  71.      }
  72.      /* 创建一个周期是100ms的软定时器 */
  73.     bsp_StartAutoTimer(0, 100);
  74.    
  75.      while (1)
  76.      {
  77.          /* TCP轮询 */
  78.          tcpnet_poll();
  79.          /* 按键消息的处理 */
  80.          ucKeyCode = bsp_GetKey();
  81.          if (ucKeyCode != KEY_NONE)
  82.          {
  83.               switch (ucKeyCode)
  84.               {
  85.                    /* K1键按下,给远程UDP设备发送8字节数据 */
  86.                    case KEY_DOWN_K1:
  87.                        /* 用于设置发送次数 */
  88.                        iCount = 1;
  89.                        do
  90.                        {
  91.                             tcpnet_poll();
  92.                             /* 申请8字节的空间 */
  93.                             sendbuf = udp_get_buf (8);
  94.                      
  95.                             if(sendbuf != NULL)
  96.                             {
  97.                                  /* 初始化8个字节变量 */
  98.                                  sendbuf[0] = '1';
  99.                                  sendbuf[1] = '2';
  100.                                  sendbuf[2] = '3';
  101.                                  sendbuf[3] = '4';
  102.                                  sendbuf[4] = '5';
  103.                                  sendbuf[5] = '6';
  104.                                  sendbuf[6] = '7';
  105.                                  sendbuf[7] = '8';
  106.                                 
  107.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 8);
  108.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  109.                                  if(res == __TRUE )
  110.                                  {
  111.                                      iCount--;
  112.                                  }
  113.                             }
  114.                            
  115.                             /*
  116.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  117.                                保证远程设备可以接受到数据
  118.                             */
  119.                             bsp_DelayMS(10);  
  120.                        }while(iCount > 0);
  121.                        break;
  122.                   
  123.                     /* 其他的键值不处理 */
  124.                    default:                    
  125.                        break;
  126.               }
  127.          }
  128.      }
  129. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-7 15:25:29 | 显示全部楼层
17.3 特别注意UDP个数配置


    使用UDP要特别注意UDP的个数配置,因为除了用户自己使用的UDP,还要考虑BSD Socket,TFTP,TFTPC,DNS,SNTP,SNMP,DHCP和NetBIOS也都要使用UDPSocket(下面的代码在Net_lib.c文件中定义):
  1. /* Check number of UDP sockets available. */
  2. #define __UDPNS    ((BSD_ENABLE  * BSD_NUMSOCKS)   + \\
  3.                     (TFTP_ENABLE * __TFTP_NSOCKS)  + \\
  4.                     (TFTPC_ENABLE* 1)              + \\
  5.                     (DNS_ENABLE  * 1)              + \\
  6.                     (SNMP_ENABLE * 1)              + \\
  7.                     (SNTP_ENABLE * 1)              + \\
  8.                     (DHCP_ENABLE * ETH_ENABLE)     + \\
  9.                     (NBNS_ENABLE * ETH_ENABLE))
  10. #if (__UDPNS > UDP_NUMSOCKS)
  11. #error Number of UDP Sockets too small
  12. #endif
复制代码
    本章节配套的例子就使用了DHCP和NetBIOS了,所以这两个就得占用两个UDP Socket,外接测试UDP通信也需要占用一个,所以至少要在Net_Config.c文件中配置3个UDPSocket供使用。本章配套的例子是配置了5个UDP Socket。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-7 15:29:34 | 显示全部楼层
17.4 UDP配置说明(Net_Config.c)


    (本章节配套例子的配置与本小节的说明相同)
    RL-TCPnet的配置工作是通过配置文件Net_Config.c实现。在MDK工程中打开文件Net_Config.c,可以看到下图所示的工程配置向导:
17.3.png

RL-TCPnet要配置的选项非常多,我们这里把几个主要的配置选项简单介绍下。
17.4.png

System Definitions
(1)Local Host Name
    局域网域名。
    这里起名为armfly,使用局域网域名限制为15个字符。
(2)Memory Pool size
    参数范围1536-262144字节。
    内存池大小配置,单位字节。另外注意一点,配置向导这里显示的单位是字节,如果看原始定义,MDK会做一个自动的4字节倍数转换,比如我们这里配置的是8192字节,那么原始定义是#define MEM_SIZE  2048,也就是8192/4 = 2048。
(3)Tick Timer interval
    可取10,20,25,40,50,100,200,单位ms。
    系统滴答时钟间隔,也就是网络协议栈的系统时间基准,默认情况下,取值100ms。
17.5.png

Ethernet Network Interface
    以太网接口配置,勾选了此选项就可以配置了,如果没有使能DHCP的话,将使用这里配置的固定IP
(1)MAC Address
    局域网内可以随意配置,只要不跟局域网内其它设备的MAC地址冲突即可。
(2)IP Address
    IP地址。
(3)Subnet mask
    子网掩码。
(4)Default Gateway
    默认网关。
17.6.png

Ethernet Network Interface
    以太网接口配置,这个配置里面还有如下两项比较重要的配置需要说明。
(1)NetBIOS Name Service
    NetBIOS局域网域名服务,这里打上对勾就使能了。这样我们就可以通过前面配置的Local Host Name局域网域名进行访问,而不需要通过IP地址访问了。
(2)Dynaminc Host Configuration
    即DHCP,这里打上对勾就使能了。使能了DHCP后,RL-TCPnet就可以从外接的路由器上获得动态IP地址。
17.7.png

UDP Sockets
    UDP Sockets配置,打上对勾就使能了此项功能
(1)Number of UDP Sockets
    用于配置可创建的UDP Sockets数量,这里配置了5个,特别注意本章节前面17.3小节的说明。
    范围1 – 20。
17.8.png

TCP Sockets
    TCP Sockets配置,打上对勾就使能了此项功能
(1)Number of TCP Sockets
    用于配置可创建的TCP Sockets数量。
(2)Number of Retries
    范围0-20。
    用于配置重试次数,TCP数据传输时,如果在设置的重试时间内得不到应答,算一次重试失败,这里就是配置的最大重试次数。
(3)Retry Timeout in seconds
    范围1-10,单位秒。
    重试时间。如果发送的数据在重试时间内得不到应答,将重新发送数据。
(4)Default Connect Timeout in seconds
    范围1-600,单位秒。
    用于配置默认的保持连接时间,即我们常说的Keep Alive时间,如果时间到了将断开连接。常用于HTTP Server,Telnet Server等。
(5)Maximum Segment Size
    范围536-1460,单位字节。
    MSS定义了TCP数据包能够传输的最大数据分段。
(6)Receive Window Size
    范围536-65535,单位字节。
    TCP接收窗口大小。

17.5 UDP调试说明(Net_Debug.c)


    (重要说明,RL-TCPnet的调试是通过串口打印出来的)
    RL-TCPnet的调试功能是通过配置文件Net_Debug.c实现。在MDK工程中打开文件Net_Debug.c,可以看到下图所示的工程配置向导:
17.9.png

Print Time Stamp
    勾选了此选项的话,打印消息时,前面会附带时间信息。
其它所有的选项
    默认情况下,所有的调试选项都关闭了,每个选项有三个调试级别可选择,这里我们以Memory Management Debug为例,点击下拉列表,可以看到里面有Off,Errors only和Full debug三个调试级别可供选择,每个调试选项里面都是这三个级别。
17.10.png

    Off:表示关闭此选项的调试功能。
    Errorsonly:表示仅在此选项出错时,将其错误打印出来。
    Fulldebug:表示此选项的全功能调试。
具体测试,我们这里就不做了,大家可以按照第11章讲解的调试方法进行测试。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-7 15:33:40 | 显示全部楼层
17.6 UDP通信的丢包问题说明


    使用本章节配套的例子做大批量数据量通信的时候,出现UDP丢包是正常的,因为UDP通信不需要建立连接就可以直接把数据包丢出去,而且没有应答机制,发送失败了也没有重传,所以出现丢包是正常的。如此一来,可靠性机制只能用户自己在应用层去实现。
    对于本章节配套的例子,我们是使用板子跟电脑端的网络调试助手通信,没法做应用层的可靠性机制,所以在大批量数据包发送的时候,简单的在每个数据包发送之间加个延迟,从而保证网络调试助手可以接收到。

17.7 UDP通信的实现方法


    有了本章节17.4小节的配置后,剩下的问题就是UDP的创建和UDP数据收发的实现。

17.7.1 DHCP和ARP状态获取


    不像TCP,UDP不需要建立连接就可以收发数据,而且也没有重发、应答、流控制等保证数据可靠发送的机制,对于这种情况,程序中做了一个特别处理,在创建了UDP Socket后就检测DHCP是否获取了IP地址(如果使能了DHCP)以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。这样就保证板子已经获得了IP地址并且要访问的远程设备也存在。此时用户就可以做UDP通信了。
    具体实现代码如下(具体DHCP和ARP的知识会在后面章节为大家讲解):
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: DCHP_ARP_Check
  4. *    功能说明: 检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void DCHP_ARP_Check(void)
  10. {
  11.      if(DHCP_Status == __FALSE)
  12.      {
  13.          if(mem_test(localm[NETIF_ETH].IpAdr, 0, 4) == __FALSE)
  14.          {
  15.               DHCP_Status = __TRUE;
  16.               printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");         
  17.          }
  18.      }
  19.      if(CacheARP_Status == __FALSE)  
  20.      {
  21.          if(arp_cache_ip (Rem_IP, ARP_FIXED_IP) == __FALSE)
  22.          {
  23.               CacheARP_Status = __TRUE;
  24.               printf_debug("通过IP地址可以解析出MAC\\r\\n");                  
  25.          }
  26.      }   
  27. }
复制代码

17.7.2 创建UDP Socket


    UDP通信的创建比较简单,调用函数udp_get_socket即可,此函数的使用方法和注意事项在本章的17.2.1小节有讲解:
  1. /*
  2. *********************************************************************************************************
  3. *                                         变量
  4. *********************************************************************************************************
  5. */
  6. uint8_t udp_soc;
  7. /*
  8. *********************************************************************************************************
  9. *    函 数 名: tcp_callback
  10. *    功能说明: TCP Socket的回调函数
  11. *    形    参: socket   UDP Socket类型
  12. *             remip    远程设备的IP地址
  13. *             remport  远程设备的端口号
  14. *             buf      远程设备发来的数据地址
  15. *             len      远程设备发来的数据长度,单位字节
  16. *    返 回 值: 默认返回0即可,一般用不上
  17. *********************************************************************************************************
  18. */
  19. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  20. {
  21.      char buffer[50];
  22.      U16 i;
  23.      /* 确保是udp_soc的回调 */
  24.      if (socket != udp_soc)
  25.      {
  26.          return (0);
  27.      }
  28.      /* 发消息的远程IP,打印IP和端口号 */
  29.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  30.      printf_debug("IP:%s  port:%d\\r\\n", buffer, remport);
  31.      /* 接收到的数据长度,单位字节 */
  32.      printf_debug("Data length = %d\\r\\n", len);
  33.      /* 打印接收到的消息 */
  34.      for(i = 0; i < len; i++)
  35.      {
  36.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  37.      }
  38.      return (0);
  39. }
  40. /*
  41. *********************************************************************************************************
  42. *    函 数 名: TCPnetTest
  43. *    功能说明: TCPnet应用
  44. *    形    参: 无
  45. *    返 回 值: 无
  46. *********************************************************************************************************
  47. */
  48. void TCPnetTest(void)
  49. {
  50.      int32_t iCount;
  51.      uint8_t *sendbuf;
  52.      uint8_t res;
  53.      uint8_t ucKeyCode;
  54.    
  55.    
  56.      /* 初始化网络协议栈 */
  57.      init_TcpNet ();
  58.    
  59.      /* 获取一个UDP Socket  */
  60.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  61.      if (udp_soc != 0)
  62.      {
  63.          /* 打开UDP端口号 */
  64.          udp_open (udp_soc, LocalPort_NUM);
  65.      }
  66.     /* 省略 */
  67. }
复制代码

17.7.3 UDP数据发送


    UDP Socket的数据发送一定要注意各个函数调用顺序和使用方法,非常重要!否则,数据发送很容易失败。数据发送所用到函数的使用方法和注意事项在本章节的17.2小节有讲解。下面的代码中对数据发送专门做了处理,支持任意字节大小的数据发送,仅需修改计数变量iCount的初始值即可,初始值是多少,就发送多少次数据包,具体每次发送的数据包大小由函数udp_get_buf和udp_send决定。下面的代码是裸机方式的,测试发送8字节,10240字节和2MB:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: tcpnet_poll
  4. *    功能说明: 使用TCPnet必须要一直调用的函数
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void tcpnet_poll(void)
  10. {
  11.      if(bsp_CheckTimer(0))
  12.      {
  13.          bsp_LedToggle(2);
  14.          /* 此函数坚决不可以放在中断里面跑 */
  15.          timer_tick ();//--------------(1)
  16.          DCHP_ARP_Check();
  17.      }
  18.    
  19.      main_TcpNet ();//--------------(2)
  20. }
  21. /*
  22. *********************************************************************************************************
  23. *    函 数 名: TCPnetTest
  24. *    功能说明: TCPnet应用
  25. *    形    参: 无
  26. *    返 回 值: 无
  27. *********************************************************************************************************
  28. */
  29. void TCPnetTest(void)
  30. {
  31.      int32_t iCount;
  32.      uint8_t *sendbuf;
  33.      uint8_t res;
  34.      uint8_t ucKeyCode;
  35.    
  36.    
  37.      /* 初始化网络协议栈 */
  38.      init_TcpNet ();
  39.    
  40.      /* 获取一个UDP Socket  */
  41.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  42.      if (udp_soc != 0)
  43.      {
  44.          /* 打开UDP端口号 */
  45.          udp_open (udp_soc, LocalPort_NUM);
  46.      }
  47.      /* 创建一个周期是100ms的软定时器 */
  48.     bsp_StartAutoTimer(0, 100);
  49.    
  50.      while (1)
  51.      {
  52.          /* TCP轮询 */
  53.          tcpnet_poll();
  54.          /* 按键消息的处理 */
  55.          ucKeyCode = bsp_GetKey();
  56.          if (ucKeyCode != KEY_NONE)
  57.          {
  58.               switch (ucKeyCode)
  59.               {
  60.                    /* K1键按下,给远程UDP设备发送8字节数据 */
  61.                    case KEY_DOWN_K1:
  62.                        /* 用于设置发送次数 */
  63.                        iCount = 1; //--------------(3)
  64.                        do //--------------(4)
  65.                        {
  66.                             tcpnet_poll();
  67.                             /* 申请8字节的空间 */
  68.                             sendbuf = udp_get_buf (8);
  69.                      
  70.                             if(sendbuf != NULL)
  71.                             {
  72.                                  /* 初始化8个字节变量 */
  73.                                  sendbuf[0] = '1';
  74.                                  sendbuf[1] = '2';
  75.                                  sendbuf[2] = '3';
  76.                                  sendbuf[3] = '4';
  77.                                  sendbuf[4] = '5';
  78.                                 sendbuf[5] = '6';
  79.                                  sendbuf[6] = '7';
  80.                                  sendbuf[7] = '8';
  81.                                 
  82.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 8);
  83.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  84.                                  if(res == __TRUE )  //--------------(5)
  85.                                  {
  86.                                       iCount--;
  87.                                  }
  88.                             }
  89.                            
  90.                             /*
  91.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  92.                                保证远程设备可以接受到数据
  93.                             */
  94.                             bsp_DelayMS(10);   //--------------(6)
  95.                        }while(iCount > 0);
  96.                        break;
  97.                    /* K2键按下,给远程UDP设备发送10240字节数据 */
  98.                    case KEY_DOWN_K2:
  99.                         /* 用于设置发送次数,每次1024字节 */                  
  100.                        iCount = 10; //--------------(7)
  101.                        do
  102.                        {
  103.                             tcpnet_poll();
  104.                            
  105.                             /* 申请1024字节的空间 */
  106.                             sendbuf = udp_get_buf (1024);
  107.                      
  108.                             if(sendbuf != NULL)
  109.                             {
  110.                                  /* 将申请到的1024字节全部清零 */
  111.                                  memset(sendbuf, 0, 1024); //--------------(8)
  112.                                 
  113.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  114.                                  sendbuf[0] = 'a';
  115.                                  sendbuf[1] = 'b';
  116.                                  sendbuf[2] = 'c';
  117.                                  sendbuf[3] = 'd';
  118.                                  sendbuf[4] = 'e';
  119.                                  sendbuf[5] = 'f';
  120.                                  sendbuf[6] = 'g';
  121.                                  sendbuf[7] = 'h';
  122.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);  
  123.                                 
  124.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  125.                                  if(res == __TRUE )
  126.                                  {
  127.                                      iCount--;
  128.                                  }
  129.                             }
  130.                            
  131.                             /*
  132.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  133.                                保证远程设备可以接受到数据
  134.                             */                        
  135.                             bsp_DelayMS(10);
  136.                            
  137.                        }while(iCount > 0);
  138.                        break;
  139.                      
  140.                    /* K3键按下,给远程UDP设备发送2MB数据 */
  141.                    case KEY_DOWN_K3:
  142.                        /* 用于设置发送次数,每次发送1024字节 */                  
  143.                        iCount = 2048; //--------------(9)
  144.                        do
  145.                        {
  146.                             tcpnet_poll();
  147.                             /* 申请1024字节的空间 */
  148.                             sendbuf = udp_get_buf (1024);
  149.                      
  150.                             if(sendbuf != NULL)
  151.                             {
  152.                                  /* 将申请到的1024字节全部清零 */
  153.                                  memset(sendbuf, 0, 1024);
  154.                                 
  155.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  156.                                  sendbuf[0] = 'a';
  157.                                  sendbuf[1] = 'b';
  158.                                  sendbuf[2] = 'c';
  159.                                  sendbuf[3] = 'd';
  160.                                  sendbuf[4] = 'e';
  161.                                  sendbuf[5] = 'f';
  162.                                  sendbuf[6] = 'g';
  163.                                  sendbuf[7] = 'h';
  164.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);  
  165.                                 
  166.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  167.                                  if(res == __TRUE )
  168.                                  {
  169.                                     
  170.                                      iCount--;
  171.                                  }
  172.                             }
  173.                             /*
  174.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  175.                                保证远程设备可以接受到数据
  176.                             */
  177.                             bsp_DelayMS(15);
  178.                            
  179.                        }while(iCount > 0);
  180.                        break;
  181.                   
  182.                     /* 其他的键值不处理 */
  183.                    default:                    
  184.                        break;
  185.               }
  186.          }
  187.      }
  188. }
复制代码
1.     函数timer_tick用于实现网络时间基准,必须要周期性调用,周期大小是由配置向导文件中参数Tick Timer interval决定的。默认情况下,我们都取100ms。
2.     函数main_TcpNet必须要一直调用着,协议栈的执行,主要靠它。
3.     通过变量iCount设置要发送的次数,这里是发送1次。
4.     do while语句中的流程很重要:
    (1)    函数tcpnet_poll一定要实时调用着。
    (2)    函数udp_get_buf和udp_send务必要依次调用,一个都不能少。
5.     一定要保证发送成功了,发送次数才可以减一,但这里发送成功并不保证远程设备接收成功。
6.     由于UDP没有重发、应答、流控制等机制,这里简单的做个延迟,保证远程设备可以接收到数据。
7.     通过变量iCount设置要发送的次数,这里是发送10次,每次发送1024字节。
8.     将申请到的1024字节数据全部清零,因为后面的代码仅初始化了前8个字节,RL-TCPnet不负责对申请的空间清零,申请的空间依然保存着上次数据包或者其它应用时的数值。
9.     通过变量iCount设置要发送的次数,这里是发送2048次,每次发送1024字节。

说完了裸机方式,下面说说RTOS方式的数据发送,这里我们以RTX操作系统为例进行说明(其它的uCOS-III和FreeRTOS的思路是一样的)。RTX操作系统与裸机方式的主要不同是为RL-TCPnet专门配套了两个任务,一个是RL-TCPnet主任务,另一个是网络系统时间基准更新任务。
网络系统时间更新任务:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskStart
  4. *    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *   优 先 级: 5
  8. *********************************************************************************************************
  9. */
  10. __task void AppTaskStart(void)
  11. {
  12.      /* 初始化RL-TCPnet */
  13.      init_TcpNet ();
  14.    
  15.      /* 创建任务 */
  16.      AppTaskCreate();
  17.    
  18.      os_itv_set (100);
  19.    
  20.     while(1)
  21.     {
  22.          os_itv_wait ();
  23.         
  24.          /* RL-TCPnet时间基准更新函数 */
  25.          timer_tick ();
  26.     }
  27. }
复制代码
特别注意,这里的网络时间基准函数timer_tick,必须要周期性调用,周期大小是由配置向导文件中参数TickTimer interval决定的。默认情况下,我们都取100ms,所以这里的延迟一定要匹配。
RL-TCPnet主任务,TCP数据收发在这个任务里面实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskTCPMain
  4. *    功能说明: RL-TCPnet测试任务
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *   优 先 级: 4
  8. *********************************************************************************************************
  9. */
  10. __task void AppTaskTCPMain(void)
  11. {
  12.      while (1)
  13.      {
  14.          TCPnetTest();
  15.      }
  16. }
复制代码
函数TCPnetTest的具体实现如下:
  1. /*
  2. *********************************************************************************************************
  3. *                                      宏定义
  4. *********************************************************************************************************
  5. */
  6. #define PORT_NUM       1001    /* TCP服务器监听端口号 */
  7. /*
  8. *********************************************************************************************************
  9. *                                         变量
  10. *********************************************************************************************************
  11. */
  12. uint8_t socket_tcp;
  13. /*
  14. *********************************************************************************************************
  15. *    函 数 名: TCPnetTest
  16. *    功能说明: TCPnet应用
  17. *    形    参: 无
  18. *    返 回 值: 无
  19. *********************************************************************************************************
  20. */
  21. void TCPnetTest(void)
  22. {
  23.      int32_t iCount;
  24.      uint8_t *sendbuf;
  25.      uint8_t tcp_status;
  26.      uint16_t maxlen;
  27.      uint8_t res;
  28.      OS_RESULT xResult;
  29.      const uint16_t usMaxBlockTime = 2; /* 延迟周期 */
  30.      /*
  31.         创建TCP Socket并创建监听,客户端连接服务器后,10秒内无数据通信将断开连接。
  32.         但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。
  33.      */
  34.     socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);
  35.      if(socket_tcp != 0)
  36.      {
  37.          res = tcp_listen (socket_tcp, PORT_NUM);
  38.          printf_debug("tcp listen res = %d\\r\\n", res);
  39.      }
  40.    
  41.      while (1)
  42.      {
  43.          /* RL-TCPnet处理函数 */
  44.          main_TcpNet();  //--------------(1)
  45.         
  46.          /* 用于网线插拔的处理 */
  47.          tcp_status = TCP_StatusCheck();
  48.         
  49.          /* 按键消息的处理 */
  50.          if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE)) //------(2)
  51.          {
  52.               xResult = os_evt_get ();
  53.               switch (xResult)
  54.               {
  55.                    /* 接收到K1键按下,给远程TCP客户端发送8字节数据 */
  56.                    case KEY1_BIT0:              
  57.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  58.                        iCount = 8; //--------------(3)
  59.                        do
  60.                        {
  61.                             main_TcpNet();//--------------(4)
  62.                             if (tcp_check_send (socket_tcp) == __TRUE)  
  63.                             {
  64.                                  maxlen = tcp_max_dsize (socket_tcp);
  65.                                  iCount -= maxlen;
  66.                                 
  67.                                  if(iCount < 0)
  68.                                  {
  69.                                      /* 这么计算没问题的 */
  70.                                      maxlen = iCount + maxlen;
  71.                                  }
  72.                                 
  73.                                  sendbuf = tcp_get_buf(maxlen);
  74.                                  sendbuf[0] = '1';
  75.                                  sendbuf[1] = '2';
  76.                                  sendbuf[2] = '3';
  77.                                  sendbuf[3] = '4';
  78.                                  sendbuf[4] = '5';
  79.                                  sendbuf[5] = '6';
  80.                                  sendbuf[6] = '7';
  81.                                  sendbuf[7] = '8';
  82.                                 
  83.                                  /* 测试发现只能使用获取的内存 */
  84.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  85.                             }
  86.                            
  87.                        }while(iCount > 0);
  88.                        break;
  89.                    /* 接收到K2键按下,给远程TCP客户端发送1024字节的数据 */
  90.                    case KEY2_BIT1:      
  91.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  92.                        iCount = 1024; //--------------(5)
  93.                        do
  94.                        {
  95.                             main_TcpNet();
  96.                             if (tcp_check_send (socket_tcp) == __TRUE)
  97.                             {
  98.                                  maxlen = tcp_max_dsize (socket_tcp);
  99.                                  iCount -= maxlen;
  100.                                 
  101.                                  if(iCount < 0)
  102.                                  {
  103.                                      /* 这么计算没问题的 */
  104.                                      maxlen = iCount + maxlen;
  105.                                  }
  106.                                 
  107.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  108.                                  sendbuf = tcp_get_buf(maxlen);
  109.                                  sendbuf[0] = 'a';
  110.                                  sendbuf[1] = 'b';
  111.                                  sendbuf[2] = 'c';
  112.                                  sendbuf[3] = 'd';
  113.                                  sendbuf[4] = 'e';
  114.                                  sendbuf[5] = 'f';
  115.                                  sendbuf[6] = 'g';
  116.                                  sendbuf[7] = 'h';
  117.                                 
  118.                                  /* 测试发现只能使用获取的内存 */
  119.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  120.                             }
  121.                            
  122.                        }while(iCount > 0);                     
  123.                        break;
  124.                      
  125.                    /* 接收到K3键按下,给远程TCP客户端发送5MB数据 */
  126.                    case KEY3_BIT2:              
  127.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  128.                        iCount = 5*1024*1024; //--------------(6)
  129.                        do
  130.                        {
  131.                             main_TcpNet();
  132.                             if (tcp_check_send (socket_tcp) == __TRUE)
  133.                             {
  134.                                  maxlen = tcp_max_dsize (socket_tcp);
  135.                                  iCount -= maxlen;
  136.                                 
  137.                                  if(iCount < 0)
  138.                                  {
  139.                                      /* 这么计算没问题的 */
  140.                                      maxlen = iCount + maxlen;
  141.                                  }
  142.                                 
  143.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  144.                                  sendbuf = tcp_get_buf(maxlen);
  145.                                  sendbuf[0] = 'a';
  146.                                  sendbuf[1] = 'b';
  147.                                  sendbuf[2] = 'c';
  148.                                  sendbuf[3] = 'd';
  149.                                  sendbuf[4] = 'e';
  150.                                  sendbuf[5] = 'f';
  151.                                  sendbuf[6] = 'g';
  152.                                  sendbuf[7] = 'h';
  153.                                 
  154.                                  /* 测试发现只能使用获取的内存 */
  155.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  156.                             }
  157.                            
  158.                        }while(iCount > 0);
  159.                        break;
  160.                   
  161.                     /* 其他的键值不处理 */
  162.                    default:                    
  163.                        break;
  164.               }
  165.          }
  166.      }
  167. }
复制代码
1.     函数main_TcpNet必须要一直调用着,协议栈的执行,主要靠它。
2.     这里使用了事件标志组,溢出时间设置为了2毫秒。这样一方面保证了函数main_TcpNet的周期性执行,另一方面用来等待按键发送事件标志消息。
3.     通过变量iCount设置要发送的字节数,这里是发送8字节数据。
4.     do while语句中的流程很重要:
    (1)    函数main_TcpNet一定要实时调用着。
    (2)    发送前务必要调用函数tcp_check_send查看发送是否就绪。
    (3)    函数tcp_max_dsize,tcp_get_buf和tcp_send务必要依次调用,一个都不能少。
5.     通过变量iCount设置要发送的字节数,这里是发送1024字节数据。
6.     通过变量iCount设置要发送的字节数,这里是发送5MB数据。

17.7.4 UDP数据接收


    TCP数据接收主要是通过函数udp_get_socket的回调函数实现(裸机,RTX,uCOS-III和FreeRTOS是一样的):
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: tcp_callback
  4. *    功能说明: TCP Socket的回调函数
  5. *    形    参: socket   UDP Socket类型
  6. *             remip    远程设备的IP地址
  7. *             remport  远程设备的端口号
  8. *             buf      远程设备发来的数据地址
  9. *             len      远程设备发来的数据长度,单位字节
  10. *    返 回 值: 默认返回0即可,一般用不上
  11. *********************************************************************************************************
  12. */
  13. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  14. {
  15.      char buffer[50];
  16.      U16 i;
  17.      /* 确保是udp_soc的回调 */
  18.      if (socket != udp_soc)
  19.      {
  20.          return (0);
  21.      }
  22.      /* 发消息的远程IP,打印IP和端口号 */
  23.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  24.      printf_debug("IP:%s  port:%d\\r\\n", buffer, remport);
  25.      /* 接收到的数据长度,单位字节 */
  26.      printf_debug("Data length = %d\\r\\n", len);
  27.      /* 打印接收到的消息 */
  28.      for(i = 0; i < len; i++)
  29.      {
  30.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  31.      }
  32.      return (0);
  33. }
复制代码
相比TCP Socket中函数tcp_get_socket的回调函数,UDP Socket的回调就简单很多了。接收到数据后,都会进入到这个回调函数中。在回调函数中可以获得数据来源IP地址和端口号,以及数据和数据大小。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-7 15:38:50 | 显示全部楼层
17.8 网络调试助手和板子的操作步骤


    由于前面TCP通信章节使用的网络调试助手做UDP测试效果不好,丢包稍微高一些,所以本章节UDP通信使用“野人网络调试助手”:http://www.armbbs.cn/forum.php?mod=viewthread&tid=30223

17.8.1 获取板子IP地址


    首先,强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址,而且在前面的配置中使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址。测试方法如下:
(1)WIN+R组合键打开“运行”窗口,输入cmd。
17.11.png

(2)弹出的命令窗口中,输入ping armfly
17.12.png

(3)输入ping armfly后,回车。
17.13.png


    获得IP地址是192.168.1.5。

17.8.2 获取电脑的IP地址


    获取电脑IP地址的方法很多,可以在网上邻居获取,也可以通过输入命令ipconfig获取,方法跟上面17.8.1小节中的方式一样:
(1)WIN+R组合键打开“运行”窗口,输入cmd。
17.14.png

(2)弹出的命令窗口中,输入ipconfig
17.15.png

(3)输入ipconfig后,回车。
17.16.png

获得电脑的IP地址是192.168.1.2。

17.8.3 网络调试助手打开UDP端口

1、打开调试助手,可以自动识别出电脑的IP地址,我们这里仅需配置成UDP模式,端口号1001并且勾选“显示接收时间”功能:
17.17.png

2、点击“打开”按钮后,就可以使用了。“打开”按钮会变成如下状态:
17.18.png


17.8.4 程序中配置远程IP地址和端口


    据前面17.8.2小节获取的电脑端IP地址和17.8.3小节给网络调试助手设置的端口号,需要大家配置程序中app_tcpnet_lib.c文件开头的宏定义,其中IP地址填前面获取的192.168.1.2,大家要根据电脑实际的IP地址填写。而端口号,配置为1001,跟网络调试助手中设置的端口号要统一:
  1. /*
  2. *********************************************************************************************************
  3. *                              宏定义,远程服务器的IP和端口
  4. *********************************************************************************************************
  5. */
  6. /* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */
  7. #define IP1            192
  8. #define IP2            168
  9. #define IP3            1
  10. #define IP4            2
  11. #define PORT_NUM         1001
复制代码

17.8.5 DHCP和ARP状态


    对于UDP通信,程序中创建了UDP Socket后就检测DHCP 是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。这样就保证板子已经获得了IP地址并且要访问的远程设备也存在。此时用户就可以做UDP通信了。正常情况下,板子上电后,程序会打印出如下信息(波特率115200,数据位8,奇偶校验位无,停止位1):
17.19.png


17.8.6 UDP发送数据


    将板子上电,并且网络调试助手的UDP通信也打开后就可以相互收发数据了。对于发送数据,程序中创建了三种数据大小的数据发送测试。
1、K1按键按下,发送了8个字符,从1到8。
17.20.png

2、K2按键按下,发送了10次数据包,每次发1024字节,每个数据包的前8个字节设置了字符a到字符h,后面都未做设置。
17.21.png

3、K3按键按下,发送了2048次,每次1024字节,共计发送2048*1024 = 2097152字节,即2MB。这里仅设置了每个数据包的前8个字节为字符a到字符h,后面都未做设置。
17.22.png


17.8.7 UDP接收数据


    UDP接收数据的测试也比较方便,我们这里通过网络调试助手给板子发送0到9,共10个字符:
17.23.png

点击发送后,可以看到串口软件打印出发送此消息的远程设备IP地址和端口号以及接收到的10个字符:
17.24.png

字符0对应的ASCII值就是48,其它字符数值依次增加。测试也是没问题的。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-7 16:08:24 | 显示全部楼层
17.9 实验例程说明(裸机)



17.9.1 STM32F407开发板实验


配套例子:
    V5-1020_RL-TCPnet实验_UDP通信(裸机)
实验目的:
     1.     学习RL-TCPnet的UDP通信创建和数据收发。
实验内容:
    1.      强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
    2.      测试此例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要跟实际情况重新配置,如果不会配置,看本例程对应的教程即可):
        #define IP1            192
        #define IP2            168
        #define IP3            1
        #define IP4            2                 
        #define PORT_NUM       1001
    3.      创建了一个UDP通信,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,本地端口被设置为1024
    4.      对于UDP通信,UDP Socket是不区分客户端和服务器端的,板子和电脑端的网络助手都开启UDP后,可以直接互发数据。
    5.      由于UDP不需要建立连接就可以收发数据,而且也没有重复、应答、流控制等保证数据可靠发送的机制,程序在创建了UDP Socket后就检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。这样就保证板子已经获得了IP地址并且要访问的远程设备也存在。此时用户就可以做UDP通信了。正常情况下,板子上电后,程序会打印出对应的成功消息。
       (1)printf_debug("通过IP地址可以解析出MAC\\r\\n");   
       (2)printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");     
    6.      按键K1按下,发送8字节的数据给电脑端UDP端口。
    7.      按键K2按下,发送10240字节的数据给电脑端UDP端口。
    8.      按键K3按下,发送2MB字节的数据给电脑端UDP端口。
实验操作:
    详见本章节17.8小节。
配置向导文件设置(Net_Config.c):
    详见本章节17.4小节。
调试文件设置(Net_Debug.c):
    详见本章节17.5小节。
程序设计:
u  主函数初始化
    在main.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main (void)
  10. {   
  11.      /* 初始化外设 */
  12.      bsp_Init();
  13.      /* 进入RL-TCPnet测试函数 */
  14.      TCPnetTest();
  15. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在 bsp.c 文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitUart();    /* 初始化串口 */
  19.      bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitTimer();   /* 初始化滴答定时器 */
  22. }
复制代码
u  RL-TCPnet功能测试
    这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,这里是创建了一个UDP Socket。
  1. /*
  2. *********************************************************************************************************
  3. *                                      用于本文件的调试
  4. *********************************************************************************************************
  5. */
  6. #if 1
  7.      #define printf_debug printf
  8. #else
  9.      #define printf_debug(...)
  10. #endif
  11. /*
  12. *********************************************************************************************************
  13. *                              宏定义,远程服务器的IP和端口
  14. *********************************************************************************************************
  15. */
  16. /* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */
  17. #define IP1            192
  18. #define IP2            168
  19. #define IP3            1
  20. #define IP4            2
  21. #define PORT_NUM         1001
  22. /* 这个是本地端口 */
  23. #define LocalPort_NUM    1024
  24. /*
  25. *********************************************************************************************************
  26. *                                         变量
  27. *********************************************************************************************************
  28. */
  29. extern LOCALM localm[];
  30. uint8_t udp_soc;
  31. uint8_t DHCP_Status = __FALSE;
  32. uint8_t CacheARP_Status = __FALSE;
  33. uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};
  34. /*
  35. *********************************************************************************************************
  36. *    函 数 名: DCHP_ARP_Check
  37. *    功能说明: 使用TCPnet必须要一直调用的函数
  38. *    形    参: 无
  39. *    返 回 值: 无
  40. *********************************************************************************************************
  41. */
  42. void DCHP_ARP_Check(void)
  43. {
  44.      if(DHCP_Status == __FALSE)
  45.      {
  46.          if(mem_test(localm[NETIF_ETH].IpAdr, 0, 4) == __FALSE)
  47.          {
  48.               DHCP_Status = __TRUE;
  49.               printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");         
  50.          }
  51.      }
  52.      if(CacheARP_Status == __FALSE)  
  53.      {
  54.          if(arp_cache_ip (Rem_IP, ARP_FIXED_IP) == __FALSE)
  55.          {
  56.               CacheARP_Status = __TRUE;
  57.               printf_debug("通过IP地址可以解析出MAC\\r\\n");                  
  58.          }
  59.      }   
  60. }
  61. /*
  62. *********************************************************************************************************
  63. *    函 数 名: tcpnet_poll
  64. *    功能说明: 使用TCPnet必须要一直调用的函数
  65. *    形    参: 无
  66. *    返 回 值: 无
  67. *********************************************************************************************************
  68. */
  69. void tcpnet_poll(void)
  70. {
  71.      if(bsp_CheckTimer(0))
  72.      {
  73.          bsp_LedToggle(2);
  74.          /* 此函数坚决不可以放在中断里面跑 */
  75.          timer_tick ();
  76.          DCHP_ARP_Check();
  77.      }
  78.    
  79.      main_TcpNet ();
  80. }
  81. /*
  82. *********************************************************************************************************
  83. *    函 数 名: tcp_callback
  84. *    功能说明: TCP Socket的回调函数
  85. *    形    参: socket   UDP Socket类型
  86. *             remip    远程设备的IP地址
  87. *             remport  远程设备的端口号
  88. *             buf      远程设备发来的数据地址
  89. *             len      远程设备发来的数据长度,单位字节
  90. *    返 回 值: 默认返回0即可,一般用不上
  91. *********************************************************************************************************
  92. */
  93. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  94. {
  95.      char buffer[50];
  96.      U16 i;
  97.      /* 确保是udp_soc的回调 */
  98.      if (socket != udp_soc)
  99.      {
  100.          return (0);
  101.      }
  102.      /* 发消息的远程IP,打印IP和端口号 */
  103.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  104.      printf_debug("%s  port:%d\\r\\n", buffer, remport);
  105.      /* 接收到的数据长度,单位字节 */
  106.      printf_debug("Data length = %d\\r\\n", len);
  107.      /* 打印接收到的消息 */
  108.      for(i = 0; i < len; i++)
  109.      {
  110.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  111.      }
  112.      return (0);
  113. }
  114. /*
  115. *********************************************************************************************************
  116. *    函 数 名: TCPnetTest
  117. *    功能说明: TCPnet应用
  118. *    形    参: 无
  119. *    返 回 值: 无
  120. *********************************************************************************************************
  121. */
  122. void TCPnetTest(void)
  123. {
  124.      int32_t iCount;
  125.      uint8_t *sendbuf;
  126.      uint8_t res;
  127.      uint8_t ucKeyCode;
  128.    
  129.    
  130.      /* 初始化网络协议栈 */
  131.      init_TcpNet ();
  132.    
  133.      /* 获取一个UDP Socket  */
  134.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  135.      if (udp_soc != 0)
  136.      {
  137.          /* 打开UDP端口号 */
  138.          udp_open (udp_soc, LocalPort_NUM);
  139.      }
  140.      /* 创建一个周期是100ms的软定时器 */
  141.     bsp_StartAutoTimer(0, 100);
  142.    
  143.      while (1)
  144.      {
  145.           /* TCP轮询 */
  146.          tcpnet_poll();
  147.          /* 按键消息的处理 */
  148.          ucKeyCode = bsp_GetKey();
  149.          if (ucKeyCode != KEY_NONE)
  150.          {
  151.               switch (ucKeyCode)
  152.               {
  153.                    /* K1键按下,给远程UDP设备发送8字节数据 */
  154.                    case KEY_DOWN_K1:
  155.                        /* 用于设置发送次数 */
  156.                        iCount = 1;
  157.                        do
  158.                        {
  159.                             tcpnet_poll();
  160.                             /* 申请8字节的空间 */
  161.                             sendbuf = udp_get_buf (8);
  162.                      
  163.                             if(sendbuf != NULL)
  164.                             {
  165.                                  /* 初始化8个字节变量 */
  166.                                  sendbuf[0] = '1';
  167.                                  sendbuf[1] = '2';
  168.                                  sendbuf[2] = '3';
  169.                                  sendbuf[3] = '4';
  170.                                  sendbuf[4] = '5';
  171.                                  sendbuf[5] = '6';
  172.                                  sendbuf[6] = '7';
  173.                                  sendbuf[7] = '8';
  174.                                 
  175.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 8);
  176.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  177.                                  if(res == __TRUE )
  178.                                  {
  179.                                      iCount--;
  180.                                  }
  181.                             }
  182.                            
  183.                             /*
  184.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  185.                                保证远程设备可以接受到数据
  186.                             */
  187.                             bsp_DelayMS(10);  
  188.                        }while(iCount > 0);
  189.                        break;
  190.                    /* K2键按下,给远程UDP设备发送10240字节数据 */
  191.                    case KEY_DOWN_K2:
  192.                         /* 用于设置发送次数,每次1024字节 */                  
  193.                        iCount = 10;
  194.                        do
  195.                        {
  196.                             tcpnet_poll();
  197.                            
  198.                             /* 申请1024字节的空间 */
  199.                             sendbuf = udp_get_buf (1024);
  200.                      
  201.                             if(sendbuf != NULL)
  202.                             {
  203.                                  /* 将申请到的1024字节全部清零 */
  204.                                  memset(sendbuf, 0, 1024);
  205.                                 
  206.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  207.                                  sendbuf[0] = 'a';
  208.                                  sendbuf[1] = 'b';
  209.                                  sendbuf[2] = 'c';
  210.                                  sendbuf[3] = 'd';
  211.                                  sendbuf[4] = 'e';
  212.                                  sendbuf[5] = 'f';
  213.                                  sendbuf[6] = 'g';
  214.                                  sendbuf[7] = 'h';
  215.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);  
  216.                                 
  217.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  218.                                  if(res == __TRUE )
  219.                                  {
  220.                                      iCount--;
  221.                                  }
  222.                             }
  223.                            
  224.                             /*
  225.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  226.                                保证远程设备可以接受到数据
  227.                             */                        
  228.                             bsp_DelayMS(10);
  229.                            
  230.                        }while(iCount > 0);
  231.                        break;
  232.                      
  233.                    /* K3键按下,给远程UDP设备发送2MB数据 */
  234.                    case KEY_DOWN_K3:
  235.                        /* 用于设置发送次数,每次发送1024字节 */                  
  236.                        iCount = 2048;
  237.                        do
  238.                        {
  239.                             tcpnet_poll();
  240.                             /* 申请1024字节的空间 */
  241.                             sendbuf = udp_get_buf (1024);
  242.                      
  243.                             if(sendbuf != NULL)
  244.                             {
  245.                                  /* 将申请到的1024字节全部清零 */
  246.                                  memset(sendbuf, 0, 1024);
  247.                                 
  248.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  249.                                  sendbuf[0] = 'a';
  250.                                  sendbuf[1] = 'b';
  251.                                  sendbuf[2] = 'c';
  252.                                  sendbuf[3] = 'd';
  253.                                  sendbuf[4] = 'e';
  254.                                  sendbuf[5] = 'f';
  255.                                  sendbuf[6] = 'g';
  256.                                  sendbuf[7] = 'h';
  257.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);  
  258.                                 
  259.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  260.                                  if(res == __TRUE )
  261.                                  {
  262.                                     
  263.                                      iCount--;
  264.                                  }
  265.                             }
  266.                             /*
  267.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  268.                                保证远程设备可以接受到数据
  269.                             */
  270.                             bsp_DelayMS(15);
  271.                            
  272.                         }while(iCount > 0);
  273.                        break;
  274.                   
  275.                     /* 其他的键值不处理 */
  276.                    default:                    
  277.                        break;
  278.               }
  279.          }
  280.      }
  281. }
复制代码

17.9.2 STM32F429开发板实验


配套例子:
    V6-1020_RL-TCPnet实验_UDP通信(裸机)
实验目的:
    1.     学习RL-TCPnet的UDP通信创建和数据收发。
实验内容:
    1.      强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
    2.      测试此例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要跟实际情况重新配置,如果不会配置,看本例程对应的教程即可):
        #define IP1            192
        #define IP2            168
        #define IP3            1
        #define IP4            2                 
        #define PORT_NUM       1001
    3.      创建了一个UDP通信,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,本地端口被设置为1024
    4.      对于UDP通信,UDP Socket是不区分客户端和服务器端的,板子和电脑端的网络助手都开启UDP后,可以直接互发数据。
    5.      由于UDP不需要建立连接就可以收发数据,而且也没有重复、应答、流控制等保证数据可靠发送的机制,程序在创建了UDP Socket后就检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。这样就保证板子已经获得了IP地址并且要访问的远程设备也存在。此时用户就可以做UDP通信了。正常情况下,板子上电后,程序会打印出对应的成功消息。
        (1)printf_debug("通过IP地址可以解析出MAC\\r\\n");   
        (2)printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");     
    6.      按键K1按下,发送8字节的数据给电脑端UDP端口。
    7.      按键K2按下,发送10240字节的数据给电脑端UDP端口。
    8.      按键K3按下,发送2MB字节的数据给电脑端UDP端口。
实验操作:
    详见本章节17.8小节。
配置向导文件设置(Net_Config.c):
    详见本章节17.4小节。
调试文件设置(Net_Debug.c):
    详见本章节17.5小节。
程序设计:
u  主函数初始化
    在main.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main (void)
  10. {   
  11.      /* 初始化外设 */
  12.      bsp_Init();
  13.      /* 进入RL-TCPnet测试函数 */
  14.      TCPnetTest();
  15. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在 bsp.c 文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitUart();    /* 初始化串口 */
  19.      bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitTimer();   /* 初始化滴答定时器 */
  22. }
复制代码
u  RL-TCPnet功能测试
    这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,这里是创建了一个UDP Socket。
  1. /*
  2. *********************************************************************************************************
  3. *                                      用于本文件的调试
  4. *********************************************************************************************************
  5. */
  6. #if 1
  7.      #define printf_debug printf
  8. #else
  9.      #define printf_debug(...)
  10. #endif
  11. /*
  12. *********************************************************************************************************
  13. *                             宏定义,远程服务器的IP和端口
  14. *********************************************************************************************************
  15. */
  16. /* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */
  17. #define IP1            192
  18. #define IP2            168
  19. #define IP3            1
  20. #define IP4            2
  21. #define PORT_NUM         1001
  22. /* 这个是本地端口 */
  23. #define LocalPort_NUM    1024
  24. /*
  25. *********************************************************************************************************
  26. *                                         变量
  27. *********************************************************************************************************
  28. */
  29. extern LOCALM localm[];
  30. uint8_t udp_soc;
  31. uint8_t DHCP_Status = __FALSE;
  32. uint8_t CacheARP_Status = __FALSE;
  33. uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};
  34. /*
  35. *********************************************************************************************************
  36. *    函 数 名: DCHP_ARP_Check
  37. *    功能说明: 使用TCPnet必须要一直调用的函数
  38. *    形    参: 无
  39. *    返 回 值: 无
  40. *********************************************************************************************************
  41. */
  42. void DCHP_ARP_Check(void)
  43. {
  44.      if(DHCP_Status == __FALSE)
  45.      {
  46.          if(mem_test(localm[NETIF_ETH].IpAdr, 0, 4) == __FALSE)
  47.          {
  48.               DHCP_Status = __TRUE;
  49.               printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");         
  50.          }
  51.      }
  52.      if(CacheARP_Status == __FALSE)  
  53.      {
  54.          if(arp_cache_ip (Rem_IP, ARP_FIXED_IP) == __FALSE)
  55.          {
  56.               CacheARP_Status = __TRUE;
  57.               printf_debug("通过IP地址可以解析出MAC\\r\\n");                  
  58.          }
  59.      }   
  60. }
  61. /*
  62. *********************************************************************************************************
  63. *    函 数 名: tcpnet_poll
  64. *    功能说明: 使用TCPnet必须要一直调用的函数
  65. *    形    参: 无
  66. *    返 回 值: 无
  67. *********************************************************************************************************
  68. */
  69. void tcpnet_poll(void)
  70. {
  71.      if(bsp_CheckTimer(0))
  72.      {
  73.          bsp_LedToggle(2);
  74.          /* 此函数坚决不可以放在中断里面跑 */
  75.          timer_tick ();
  76.          DCHP_ARP_Check();
  77.      }
  78.    
  79.      main_TcpNet ();
  80. }
  81. /*
  82. *********************************************************************************************************
  83. *    函 数 名: tcp_callback
  84. *    功能说明: TCP Socket的回调函数
  85. *    形    参: socket   UDP Socket类型
  86. *             remip    远程设备的IP地址
  87. *             remport  远程设备的端口号
  88. *             buf      远程设备发来的数据地址
  89. *             len      远程设备发来的数据长度,单位字节
  90. *    返 回 值: 默认返回0即可,一般用不上
  91. *********************************************************************************************************
  92. */
  93. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  94. {
  95.      char buffer[50];
  96.      U16 i;
  97.      /* 确保是udp_soc的回调 */
  98.      if (socket != udp_soc)
  99.      {
  100.          return (0);
  101.      }
  102.      /* 发消息的远程IP,打印IP和端口号 */
  103.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  104.      printf_debug("%s  port:%d\\r\\n", buffer, remport);
  105.      /* 接收到的数据长度,单位字节 */
  106.      printf_debug("Data length = %d\\r\\n", len);
  107.      /* 打印接收到的消息 */
  108.      for(i = 0; i < len; i++)
  109.      {
  110.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  111.      }
  112.      return (0);
  113. }
  114. /*
  115. *********************************************************************************************************
  116. *    函 数 名: TCPnetTest
  117. *    功能说明: TCPnet应用
  118. *    形    参: 无
  119. *    返 回 值: 无
  120. *********************************************************************************************************
  121. */
  122. void TCPnetTest(void)
  123. {
  124.      int32_t iCount;
  125.      uint8_t *sendbuf;
  126.      uint8_t res;
  127.      uint8_t ucKeyCode;
  128.    
  129.    
  130.      /* 初始化网络协议栈 */
  131.      init_TcpNet ();
  132.    
  133.      /* 获取一个UDP Socket  */
  134.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  135.      if (udp_soc != 0)
  136.      {
  137.          /* 打开UDP端口号 */
  138.          udp_open (udp_soc, LocalPort_NUM);
  139.      }
  140.      /* 创建一个周期是100ms的软定时器 */
  141.     bsp_StartAutoTimer(0, 100);
  142.    
  143.      while (1)
  144.      {
  145.           /* TCP轮询 */
  146.          tcpnet_poll();
  147.          /* 按键消息的处理 */
  148.          ucKeyCode = bsp_GetKey();
  149.          if (ucKeyCode != KEY_NONE)
  150.          {
  151.               switch (ucKeyCode)
  152.               {
  153.                    /* K1键按下,给远程UDP设备发送8字节数据 */
  154.                    case KEY_DOWN_K1:
  155.                        /* 用于设置发送次数 */
  156.                        iCount = 1;
  157.                        do
  158.                        {
  159.                             tcpnet_poll();
  160.                             /* 申请8字节的空间 */
  161.                             sendbuf = udp_get_buf (8);
  162.                      
  163.                             if(sendbuf != NULL)
  164.                             {
  165.                                  /* 初始化8个字节变量 */
  166.                                  sendbuf[0] = '1';
  167.                                  sendbuf[1] = '2';
  168.                                  sendbuf[2] = '3';
  169.                                  sendbuf[3] = '4';
  170.                                  sendbuf[4] = '5';
  171.                                  sendbuf[5] = '6';
  172.                                  sendbuf[6] = '7';
  173.                                  sendbuf[7] = '8';
  174.                                 
  175.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 8);
  176.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  177.                                  if(res == __TRUE )
  178.                                  {
  179.                                      iCount--;
  180.                                  }
  181.                             }
  182.                            
  183.                             /*
  184.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  185.                                保证远程设备可以接受到数据
  186.                             */
  187.                             bsp_DelayMS(10);  
  188.                        }while(iCount > 0);
  189.                        break;
  190.                    /* K2键按下,给远程UDP设备发送10240字节数据 */
  191.                    case KEY_DOWN_K2:
  192.                         /* 用于设置发送次数,每次1024字节 */                  
  193.                        iCount = 10;
  194.                        do
  195.                        {
  196.                             tcpnet_poll();
  197.                            
  198.                             /* 申请1024字节的空间 */
  199.                             sendbuf = udp_get_buf (1024);
  200.                      
  201.                             if(sendbuf != NULL)
  202.                             {
  203.                                  /* 将申请到的1024字节全部清零 */
  204.                                  memset(sendbuf, 0, 1024);
  205.                                 
  206.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  207.                                  sendbuf[0] = 'a';
  208.                                  sendbuf[1] = 'b';
  209.                                  sendbuf[2] = 'c';
  210.                                  sendbuf[3] = 'd';
  211.                                  sendbuf[4] = 'e';
  212.                                  sendbuf[5] = 'f';
  213.                                  sendbuf[6] = 'g';
  214.                                  sendbuf[7] = 'h';
  215.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);  
  216.                                 
  217.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  218.                                  if(res == __TRUE )
  219.                                  {
  220.                                      iCount--;
  221.                                  }
  222.                             }
  223.                            
  224.                             /*
  225.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  226.                                保证远程设备可以接受到数据
  227.                             */                        
  228.                             bsp_DelayMS(10);
  229.                            
  230.                        }while(iCount > 0);
  231.                         break;
  232.                      
  233.                    /* K3键按下,给远程UDP设备发送2MB数据 */
  234.                    case KEY_DOWN_K3:
  235.                        /* 用于设置发送次数,每次发送1024字节 */                  
  236.                        iCount = 2048;
  237.                        do
  238.                        {
  239.                             tcpnet_poll();
  240.                             /* 申请1024字节的空间 */
  241.                             sendbuf = udp_get_buf (1024);
  242.                      
  243.                             if(sendbuf != NULL)
  244.                             {
  245.                                  /* 将申请到的1024字节全部清零 */
  246.                                  memset(sendbuf, 0, 1024);
  247.                                 
  248.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  249.                                  sendbuf[0] = 'a';
  250.                                  sendbuf[1] = 'b';
  251.                                  sendbuf[2] = 'c';
  252.                                  sendbuf[3] = 'd';
  253.                                  sendbuf[4] = 'e';
  254.                                  sendbuf[5] = 'f';
  255.                                  sendbuf[6] = 'g';
  256.                                  sendbuf[7] = 'h';
  257.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);  
  258.                                 
  259.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  260.                                  if(res == __TRUE )
  261.                                  {
  262.                                     
  263.                                      iCount--;
  264.                                  }
  265.                             }
  266.                             /*
  267.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  268.                                保证远程设备可以接受到数据
  269.                             */
  270.                             bsp_DelayMS(15);
  271.                            
  272.                        }while(iCount > 0);
  273.                        break;
  274.                   
  275.                     /* 其他的键值不处理 */
  276.                    default:                    
  277.                        break;
  278.               }
  279.          }
  280.      }
  281. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-7 16:22:55 | 显示全部楼层
17.10 实验例程说明(RTX)



17.10.1 STM32F407开发板实验


配套例子:
    V5-1021_RL-TCPnet实验_UDP通信(RTX)
实验目的:
    1.     学习RL-TCPnet的UDP通信创建和数据收发。
实验内容:
    1.      强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
    2.      测试此例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要跟实际情况重新配置,如果不会配置,看本例程对应的教程即可):
        #define IP1            192
        #define IP2            168
        #define IP3            1
        #define IP4            2                 
        #define PORT_NUM       1001
    3.      创建了一个UDP通信,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,本地端口被设置为1024
    4.      对于UDP通信,UDP Socket是不区分客户端和服务器端的,板子和电脑端的网络助手都开启UDP后,可以直接互发数据。
    5.      由于UDP不需要建立连接就可以收发数据,而且也没有重复、应答、流控制等保证数据可靠发送的机制,程序在创建了UDP Socket后就检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。这样就保证板子已经获得了IP地址并且要访问的远程设备也存在。此时用户就可以做UDP通信了。正常情况下,板子上电后,程序会打印出对应的成功消息。
        (1)printf_debug("通过IP地址可以解析出MAC\\r\\n");   
        (2)printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");     
    6.      按键K1按下,发送8字节的数据给电脑端UDP端口。
    7.      按键K2按下,发送10240字节的数据给电脑端UDP端口。
    8.      按键K3按下,发送2MB字节的数据给电脑端UDP端口。
实验操作:
    详见本章节17.8小节。
配置向导文件设置(Net_Config.c):
    详见本章节17.4小节。
调试文件设置(Net_Debug.c):
    详见本章节17.5小节。
RTX配置:
    RTX配置向导详情如下:
17.25.png

Task Configuration
(1)Number of concurrent running tasks
    允许创建6个任务,实际创建了如下5个任务:
    AppTaskUserIF任务 :按键消息处理。
    AppTaskLED任务    :LED闪烁。
    AppTaskMsgPro任务 :按键检测。
    AppTaskTCPMain任务:RL-TCPnet测试任务。
    AppTaskStart任务  :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。
(2)Number of tasks with user-provided stack
    创建的5个任务都是采用自定义堆栈方式。
(3)Run in privileged mode
    设置任务运行在非特权级模式。
RTX任务调试信息:
17.26.png

程序设计:
u  任务栈大小分配:
    staticuint64_t AppTaskUserIFStk[1024/8];   /* 任务栈 */
    staticuint64_t AppTaskLEDStk[1024/8];      /* 任务栈 */
    staticuint64_t AppTaskMsgProStk[1024/8];  /* 任务栈 */
    staticuint64_t AppTaskTCPMainStk[2048/8]; /* 任务栈 */
    staticuint64_t AppTaskStartStk[1024/8];     /* 任务栈 */
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。
u  系统栈大小分配:
17.27.png

u  RTX初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main (void)
  10. {   
  11.      /* 初始化外设 */
  12.      bsp_Init();
  13.    
  14.      /* 创建启动任务 */
  15.      os_sys_init_user (AppTaskStart,              /* 任务函数 */
  16.                        5,                         /* 任务优先级 */
  17.                        &AppTaskStartStk,          /* 任务栈 */
  18.                        sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */
  19.      while(1);
  20. }
复制代码
u  硬件外设初始化
    硬件外设的初始化是在 bsp.c 文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitDWT();     /* 初始化DWT */
  19.      bsp_InitUart();    /* 初始化串口 */
  20.      bsp_InitKey();    /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */
  21.      bsp_InitLed();    /* 初始LED指示灯端口 */
  22. }
复制代码
u  RTX任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.      HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
  12.                                            1,                         /* 任务优先级 */
  13.                                            &AppTaskUserIFStk,         /* 任务栈 */
  14.                                            sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
  15.    
  16.      HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */
  17.                                         2,                       /* 任务优先级 */
  18.                                         &AppTaskLEDStk,          /* 任务栈 */
  19.                                         sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */
  20.    
  21.      HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
  22.                                            3,                         /* 任务优先级 */
  23.                                            &AppTaskMsgProStk,         /* 任务栈 */
  24.                                            sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
  25.    
  26.     HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任务函数 */
  27.                                            4,                         /* 任务优先级 */
  28.                                            &AppTaskTCPMainStk,         /* 任务栈 */
  29.                                            sizeof(AppTaskTCPMainStk)); /* 任务栈大小,单位字节数 */
  30. }
复制代码
u  五个RTX任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskUserIF
  4. *    功能说明: 按键消息处理     
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. __task void AppTaskUserIF(void)
  11. {
  12.      uint8_t ucKeyCode;
  13.     while(1)
  14.     {
  15.          ucKeyCode = bsp_GetKey();
  16.         
  17.          if (ucKeyCode != KEY_NONE)
  18.          {
  19.               switch (ucKeyCode)
  20.               {
  21.                    /* K1键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit0 */
  22.                    case KEY_DOWN_K1:
  23.                        printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\\r\\n");
  24.                        os_evt_set (KEY1_BIT0, HandleTaskTCPMain);                     
  25.                        break;  
  26.                    /* K2键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit1 */
  27.                    case KEY_DOWN_K2:
  28.                        printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\\r\\n");
  29.                        os_evt_set (KEY2_BIT1, HandleTaskTCPMain);
  30.                        break;
  31.                   
  32.                    /* K3键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit2 */
  33.                    case KEY_DOWN_K3:
  34.                        printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\\r\\n");
  35.                        os_evt_set (KEY3_BIT2, HandleTaskTCPMain);
  36.                        break;
  37.                    /* 其他的键值不处理 */
  38.                    default:                    
  39.                        break;
  40.               }
  41.          }
  42.         
  43.          os_dly_wait(20);
  44.      }
  45. }
  46. /*
  47. *********************************************************************************************************
  48. *    函 数 名: AppTaskLED
  49. *    功能说明: LED闪烁。
  50. *    形    参: 无
  51. *    返 回 值: 无
  52. *   优 先 级: 2
  53. *********************************************************************************************************
  54. */
  55. __task void AppTaskLED(void)
  56. {
  57.      const uint16_t usFrequency = 500; /* 延迟周期 */
  58.    
  59.      /* 设置延迟周期 */
  60.      os_itv_set(usFrequency);
  61.    
  62.     while(1)
  63.     {
  64.          bsp_LedToggle(2);
  65.          /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
  66.          os_itv_wait();
  67.     }
  68. }
  69. /*
  70. *********************************************************************************************************
  71. *    函 数 名: AppTaskMsgPro
  72. *    功能说明: 按键检测
  73. *    形    参: 无
  74. *    返 回 值: 无
  75. *   优 先 级: 3
  76. *********************************************************************************************************
  77. */
  78. __task void AppTaskMsgPro(void)
  79. {
  80.     while(1)
  81.     {
  82.          bsp_KeyScan();
  83.          os_dly_wait(10);
  84.     }
  85. }
  86. /*
  87. *********************************************************************************************************
  88. *    函 数 名: AppTaskTCPMain
  89. *    功能说明: RL-TCPnet测试任务
  90. *    形    参: 无
  91. *    返 回 值: 无
  92. *   优 先 级: 4
  93. *********************************************************************************************************
  94. */
  95. __task void AppTaskTCPMain(void)
  96. {
  97.      while (1)
  98.      {
  99.          TCPnetTest();
  100.      }
  101. }
  102. /*
  103. *********************************************************************************************************
  104. *    函 数 名: AppTaskStart
  105. *    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。
  106. *    形    参: 无
  107. *    返 回 值: 无
  108. *   优 先 级: 5
  109. *********************************************************************************************************
  110. */
  111. __task void AppTaskStart(void)
  112. {
  113.      /* 初始化RL-TCPnet */
  114.      init_TcpNet ();
  115.    
  116.      /* 创建任务 */
  117.      AppTaskCreate();
  118.    
  119.      os_itv_set (100);
  120.    
  121.     while(1)
  122.     {
  123.          os_itv_wait ();
  124.         
  125.          /* RL-TCPnet时间基准更新函数 */
  126.          timer_tick ();
  127.     }
  128. }
复制代码
u  RL-TCPnet功能测试
    这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,这里是创建了一个UDP Socket。
  1. #include "includes.h"
  2. /*
  3. *********************************************************************************************************
  4. *                                      用于本文件的调试
  5. *********************************************************************************************************
  6. */
  7. #if 1
  8.      #define printf_debug printf
  9. #else
  10.      #define printf_debug(...)
  11. #endif
  12. /*
  13. *********************************************************************************************************
  14. *                              宏定义,远程服务器的IP和端口
  15. *********************************************************************************************************
  16. */
  17. /* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */
  18. #define IP1            192
  19. #define IP2            168
  20. #define IP3            1
  21. #define IP4            2
  22. #define PORT_NUM         1001
  23. /* 这个是本地端口 */
  24. #define LocalPort_NUM    1024
  25. /*
  26. *********************************************************************************************************
  27. *                                         变量
  28. *********************************************************************************************************
  29. */
  30. extern LOCALM localm[];
  31. uint8_t udp_soc;
  32. uint8_t DHCP_Status = __FALSE;
  33. uint8_t CacheARP_Status = __FALSE;
  34. uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};
  35. /*
  36. *********************************************************************************************************
  37. *    函 数 名: DCHP_ARP_Check
  38. *    功能说明: 检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。
  39. *    形    参: 无
  40. *    返 回 值: 无
  41. *********************************************************************************************************
  42. */
  43. void DCHP_ARP_Check(void)
  44. {
  45.      if(DHCP_Status == __FALSE)
  46.      {
  47.          if(mem_test(localm[NETIF_ETH].IpAdr, 0, 4) == __FALSE)
  48.          {
  49.               DHCP_Status = __TRUE;
  50.               printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");         
  51.          }
  52.      }
  53.      if(CacheARP_Status == __FALSE)  
  54.      {
  55.          if(arp_cache_ip (Rem_IP, ARP_FIXED_IP) == __FALSE)
  56.          {
  57.               CacheARP_Status = __TRUE;
  58.               printf_debug("通过IP地址可以解析出MAC\\r\\n");                  
  59.          }
  60.      }   
  61. }
  62. /*
  63. *********************************************************************************************************
  64. *    函 数 名: tcp_callback
  65. *    功能说明: TCP Socket的回调函数
  66. *    形    参: socket   UDP Socket类型
  67. *             remip    远程设备的IP地址
  68. *             remport  远程设备的端口号
  69. *             buf      远程设备发来的数据地址
  70. *             len      远程设备发来的数据长度,单位字节
  71. *    返 回 值: 默认返回0即可,一般用不上
  72. *********************************************************************************************************
  73. */
  74. U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)
  75. {
  76.      char buffer[50];
  77.      U16 i;
  78.      /* 确保是udp_soc的回调 */
  79.      if (socket != udp_soc)
  80.      {
  81.          return (0);
  82.      }
  83.      /* 发消息的远程IP,打印IP和端口号 */
  84.      sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);
  85.      printf_debug("%s  port:%d\\r\\n", buffer, remport);
  86.      /* 接收到的数据长度,单位字节 */
  87.      printf_debug("Data length = %d\\r\\n", len);
  88.      /* 打印接收到的消息 */
  89.      for(i = 0; i < len; i++)
  90.      {
  91.          printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);
  92.      }
  93.      return (0);
  94. }
  95. /*
  96. *********************************************************************************************************
  97. *    函 数 名: TCPnetTest
  98. *    功能说明: TCPnet应用
  99. *    形    参: 无
  100. *    返 回 值: 无
  101. *********************************************************************************************************
  102. */
  103. void TCPnetTest(void)
  104. {
  105.      int32_t iCount;
  106.      uint8_t *sendbuf;
  107.      uint8_t res;
  108.      OS_RESULT xResult;
  109.      const uint16_t usMaxBlockTime = 2; /* 延迟周期 */
  110.    
  111.      /* 获取一个UDP Socket  */
  112.      udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);
  113.      if (udp_soc != 0)
  114.      {
  115.          /* 打开UDP端口号 */
  116.          udp_open (udp_soc, LocalPort_NUM);
  117.      }
  118.      while (1)
  119.      {
  120.           /* RL-TCPnet处理函数 */
  121.          main_TcpNet();
  122.         
  123.           /* 检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC */
  124.          DCHP_ARP_Check();
  125.           /* 按键消息的处理 */
  126.          if(os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)
  127.          {
  128.               xResult = os_evt_get ();
  129.               switch (xResult)
  130.               {
  131.                    /* 接收到K1键按下消息,给远程UDP设备发送8字节数据 */
  132.                    case KEY1_BIT0:
  133.                        /* 用于设置发送次数 */
  134.                        iCount = 1;
  135.                        do
  136.                        {
  137.                             main_TcpNet();
  138.                             /* 申请8字节的空间 */
  139.                             sendbuf = udp_get_buf (8);
  140.                      
  141.                             if(sendbuf != NULL)
  142.                             {
  143.                                  /* 初始化8个字节变量 */
  144.                                  sendbuf[0] = '1';
  145.                                  sendbuf[1] = '2';
  146.                                  sendbuf[2] = '3';
  147.                                  sendbuf[3] = '4';
  148.                                  sendbuf[4] = '5';
  149.                                  sendbuf[5] = '6';
  150.                                  sendbuf[6] = '7';
  151.                                  sendbuf[7] = '8';
  152.                                 
  153.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 8);
  154.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  155.                                  if(res == __TRUE )
  156.                                  {
  157.                                      iCount--;
  158.                                  }
  159.                             }
  160.                            
  161.                             /*
  162.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  163.                                保证远程设备可以接受到数据
  164.                             */
  165.                             os_dly_wait(10);  
  166.                        }while(iCount > 0);
  167.                        break;
  168.                    /* 接收到K2键按下消息,给远程UDP设备发送10240字节数据 */
  169.                    case KEY2_BIT1:   
  170.                         /* 用于设置发送次数,每次1024字节 */                  
  171.                        iCount = 10;
  172.                        do
  173.                        {
  174.                             main_TcpNet();
  175.                            
  176.                             /* 申请1024字节的空间 */
  177.                             sendbuf = udp_get_buf (1024);
  178.                      
  179.                             if(sendbuf != NULL)
  180.                             {
  181.                                  /* 将申请到的1024字节全部清零 */
  182.                                  memset(sendbuf, 0, 1024);
  183.                                 
  184.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  185.                                  sendbuf[0] = 'a';
  186.                                  sendbuf[1] = 'b';
  187.                                  sendbuf[2] = 'c';
  188.                                  sendbuf[3] = 'd';
  189.                                  sendbuf[4] = 'e';
  190.                                  sendbuf[5] = 'f';
  191.                                  sendbuf[6] = 'g';
  192.                                  sendbuf[7] = 'h';
  193.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);  
  194.                                 
  195.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  196.                                  if(res == __TRUE )
  197.                                  {
  198.                                      iCount--;
  199.                                  }
  200.                             }
  201.                            
  202.                             /*
  203.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  204.                                保证远程设备可以接受到数据
  205.                             */                        
  206.                             os_dly_wait(10);
  207.                            
  208.                        }while(iCount > 0);
  209.                        break;
  210.                      
  211.                    /* 接收到K3键按下消息,给远程UDP设备发送2MB数据 */
  212.                    case KEY3_BIT2:
  213.                         /* 用于设置发送次数,每次发送1024字节 */                  
  214.                        iCount = 2048;
  215.                        do
  216.                        {
  217.                             main_TcpNet();
  218.                             /* 申请1024字节的空间 */
  219.                             sendbuf = udp_get_buf (1024);
  220.                      
  221.                             if(sendbuf != NULL)
  222.                             {
  223.                                  /* 将申请到的1024字节全部清零 */
  224.                                  memset(sendbuf, 0, 1024);
  225.                                 
  226.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  227.                                  sendbuf[0] = 'a';
  228.                                  sendbuf[1] = 'b';
  229.                                  sendbuf[2] = 'c';
  230.                                  sendbuf[3] = 'd';
  231.                                  sendbuf[4] = 'e';
  232.                                  sendbuf[5] = 'f';
  233.                                  sendbuf[6] = 'g';
  234.                                  sendbuf[7] = 'h';
  235.                                  res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);  
  236.                            
  237.                             /* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */
  238.                                  if(res == __TRUE )
  239.                                  {
  240.                                     
  241.                                      iCount--;
  242.                                  }
  243.                             }
  244.                             /*
  245.                                由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,
  246.                                保证远程设备可以接受到数据
  247.                             */
  248.                             os_dly_wait(15);
  249.                            
  250.                        }while(iCount > 0);
  251.                        break;
  252.                   
  253.                     /* 其他的键值不处理 */
  254.                    default:                    
  255.                        break;
  256.               }
  257.          }
  258.      }
  259. }
复制代码


努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-11-7 16:23:37 | 显示全部楼层
17.10.2 STM32F429开发板实验


配套例子:
    V6-1021_RL-TCPnet实验_UDP通信(RTX)
实验目的:
    1.     学习RL-TCPnet的UDP通信创建和数据收发。
实验内容:
    1.      强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
    2.      测试此例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要跟实际情况重新配置,如果不会配置,看本例程对应的教程即可):
        #define IP1            192
        #define IP2            168
        #define IP3            1
        #define IP4            2               
        #define PORT_NUM       1001
    3.      创建了一个UDP通信,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,本地端口被设置为1024
    4.      对于UDP通信,UDP Socket是不区分客户端和服务器端的,板子和电脑端的网络助手都开启UDP后,可以直接互发数据。
    5.      由于UDP不需要建立连接就可以收发数据,而且也没有重复、应答、流控制等保证数据可靠发送的机制,程序在创建了UDP Socket后就检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。这样就保证板子已经获得了IP地址并且要访问的远程设备也存在。此时用户就可以做UDP通信了。正常情况下,板子上电后,程序会打印出对应的成功消息。
        (1)printf_debug("通过IP地址可以解析出MAC\\r\\n");  
        (2)printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");   
    6.      按键K1按下,发送8字节的数据给电脑端UDP端口。
    7.      按键K2按下,发送10240字节的数据给电脑端UDP端口。
    8.      按键K3按下,发送2MB字节的数据给电脑端UDP端口。
实验操作:
    详见本章节17.8小节。
配置向导文件设置(Net_Config.c):
    详见本章节17.4小节。
调试文件设置(Net_Debug.c):
    详见本章节17.5小节。     
RTX配置:
    RTX配置向导详情如下:
17.28.png

Task Configuration
(1)Number of concurrent running tasks
允许创建6个任务,实际创建了如下5个任务:
    AppTaskUserIF任务   :按键消息处理。
    AppTaskLED任务     :LED闪烁。
    AppTaskMsgPro任务 :按键检测。
    AppTaskTCPMain任务:RL-TCPnet测试任务。
    AppTaskStart任务  :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。
(2)Number of tasks with user-provided stack
    创建的5个任务都是采用自定义堆栈方式。
(3)Run in privileged mode
    设置任务运行在非特权级模式。
RTX任务调试信息:
17.29.png

程序设计:
u  任务栈大小分配:
    static uint64_t AppTaskUserIFStk[1024/8];   /* 任务栈 */
    static uint64_t AppTaskLEDStk[1024/8];      /* 任务栈 */
    static uint64_t AppTaskMsgProStk[1024/8];  /* 任务栈 */
    static uint64_t AppTaskTCPMainStk[2048/8]; /* 任务栈 */
    static uint64_t AppTaskStartStk[1024/8];     /* 任务栈 */
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。
u  系统栈大小分配:
17.30.png

u  RTX初始化:
  1. [size=3][b]/*[/b][/size]
  2. [size=3][b]*********************************************************************************************************[/b][/size]
  3. [size=3][b]* 函 数 名: main[/b][/size]
  4. [size=3][b]* 功能说明: 标准c程序入口。[/b][/size]
  5. [size=3][b]* 形 参: 无[/b][/size]
  6. [size=3][b]* 返 回 值: 无[/b][/size]
  7. [size=3][b]*********************************************************************************************************[/b][/size]
  8. [size=3][b]*/[/b][/size]
  9. [size=3][b]int main (void)[/b][/size]
  10. [size=3][b]{[/b][/size]
  11. [size=3][b]/* 初始化外设 */[/b][/size]
  12. [size=3][b]bsp_Init();[/b][/size]
  13. [size=3][b]/* 创建启动任务 */[/b][/size]
  14. [size=3][b]os_sys_init_user (AppTaskStart, /* 任务函数 */[/b][/size]
  15. [size=3][b]5, /* 任务优先级 */[/b][/size]
  16. [size=3][b]&AppTaskStartStk, /* 任务栈 */[/b][/size]
  17. [size=3][b]sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */[/b][/size]
  18. [size=3][b]while(1);[/b][/size]
  19. [size=3][b]}[/b][/size]
复制代码

u  硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
  1. [size=3]/*[/size]
  2. [size=3]*********************************************************************************************************[/size]
  3. [size=3]* 函 数 名: bsp_Init[/size]
  4. [size=3]* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次[/size]
  5. [size=3]* 形 参:无[/size]
  6. [size=3]* 返 回 值: 无[/size]
  7. [size=3]*********************************************************************************************************[/size]
  8. [size=3]*/[/size]
  9. [size=3]void bsp_Init(void)[/size]
  10. [size=3]{[/size]
  11. [size=3]/*[/size]
  12. [size=3]由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。[/size]
  13. [size=3]启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。[/size]
  14. [size=3]系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件[/size]
  15. [size=3]*/[/size]
  16. [size=3]/* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/[/size]
  17. [size=3]NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);[/size]
  18. [size=3]SystemCoreClockUpdate(); /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */[/size]
  19. [size=3]bsp_InitDWT(); /* 初始化DWT */[/size]
  20. [size=3]bsp_InitUart(); /* 初始化串口 */[/size]
  21. [size=3]bsp_InitKey(); /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */[/size]
  22. [size=3]bsp_InitExtIO(); /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */[/size]
  23. [size=3]bsp_InitLed(); /* 初始LED指示灯端口 */[/size]
  24. [size=3]}[/size]
复制代码

u  RTX任务创建:
  1. [size=3][b]/*[/b][/size]
  2. [size=3][b]*********************************************************************************************************[/b][/size]
  3. [size=3][b]* 函 数 名: AppTaskCreate[/b][/size]
  4. [size=3][b]* 功能说明: 创建应用任务[/b][/size]
  5. [size=3][b]* 形 参: 无[/b][/size]
  6. [size=3][b]* 返 回 值: 无[/b][/size]
  7. [size=3][b]*********************************************************************************************************[/b][/size]
  8. [size=3][b]*/[/b][/size]
  9. [size=3][b]static void AppTaskCreate (void)[/b][/size]
  10. [size=3][b]{[/b][/size]
  11. [size=3][b]HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF, /* 任务函数 */[/b][/size]
  12. [size=3][b]1, /* 任务优先级 */[/b][/size]
  13. [size=3][b]&AppTaskUserIFStk, /* 任务栈 */[/b][/size]
  14. [size=3][b]sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */[/b][/size]
  15. [size=3][b]HandleTaskLED = os_tsk_create_user(AppTaskLED, /* 任务函数 */[/b][/size]
  16. [size=3][b]2, /* 任务优先级 */[/b][/size]
  17. [size=3][b]&AppTaskLEDStk, /* 任务栈 */[/b][/size]
  18. [size=3][b]sizeof(AppTaskLEDStk)); /* 任务栈大小,单位字节数 */[/b][/size]
  19. [size=3][b]HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro, /* 任务函数 */[/b][/size]
  20. [size=3][b]3, /* 任务优先级 */[/b][/size]
  21. [size=3][b]&AppTaskMsgProStk, /* 任务栈 */[/b][/size]
  22. [size=3][b]sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */[/b][/size]
  23. [size=3][b]HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain, /* 任务函数 */[/b][/size]
  24. [size=3][b]4, /* 任务优先级 */[/b][/size]
  25. [size=3][b]&AppTaskTCPMainStk, /* 任务栈 */[/b][/size]
  26. [size=3][b]sizeof(AppTaskTCPMainStk)); /* 任务栈大小,单位字节数 */[/b][/size]
  27. [size=3][b]}[/b][/size]
复制代码

u  五个RTX任务的实现:
  1. [size=3][b]/*[/b][/size]
  2. [size=3][b]*********************************************************************************************************[/b][/size]
  3. [size=3][b]* 函 数 名: AppTaskUserIF[/b][/size]
  4. [size=3][b]* 功能说明: 按键消息处理[/b][/size]
  5. [size=3][b]* 形 参: 无[/b][/size]
  6. [size=3][b]* 返 回 值: 无[/b][/size]
  7. [size=3][b]* 优 先 级: 1 (数值越小优先级越低,这个跟uCOS相反)[/b][/size]
  8. [size=3][b]*********************************************************************************************************[/b][/size]
  9. [size=3][b]*/[/b][/size]
  10. [size=3][b]__task void AppTaskUserIF(void)[/b][/size]
  11. [size=3][b]{[/b][/size]
  12. [size=3][b]uint8_t ucKeyCode;[/b][/size]
  13. [size=3][b]while(1)[/b][/size]
  14. [size=3][b]{[/b][/size]
  15. [size=3][b]ucKeyCode = bsp_GetKey();[/b][/size]
  16. [size=3][b]if (ucKeyCode != KEY_NONE)[/b][/size]
  17. [size=3][b]{[/b][/size]
  18. [size=3][b]switch (ucKeyCode)[/b][/size]
  19. [size=3][b]{[/b][/size]
  20. [size=3][b]/* K1键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit0 */[/b][/size]
  21. [size=3][b]case KEY_DOWN_K1:[/b][/size]
  22. [size=3][b]printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\\r\\n");[/b][/size]
  23. [size=3][b]os_evt_set (KEY1_BIT0, HandleTaskTCPMain);[/b][/size]
  24. [size=3][b]break;[/b][/size]
  25. [size=3][b]/* K2键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit1 */[/b][/size]
  26. [size=3][b]case KEY_DOWN_K2:[/b][/size]
  27. [size=3][b]printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\\r\\n");[/b][/size]
  28. [size=3][b]os_evt_set (KEY2_BIT1, HandleTaskTCPMain);[/b][/size]
  29. [size=3][b]break;[/b][/size]
  30. [size=3][b]/* K3键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit2 */[/b][/size]
  31. [size=3][b]case KEY_DOWN_K3:[/b][/size]
  32. [size=3][b]printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\\r\\n");[/b][/size]
  33. [size=3][b]os_evt_set (KEY3_BIT2, HandleTaskTCPMain);[/b][/size]
  34. [size=3][b]break;[/b][/size]
  35. [size=3][b]/* 其他的键值不处理 */[/b][/size]
  36. [size=3][b]default:[/b][/size]
  37. [size=3][b]break;[/b][/size]
  38. [size=3][b]}[/b][/size]
  39. [size=3][b]}[/b][/size]
  40. [size=3][b]os_dly_wait(20);[/b][/size]
  41. [size=3][b]}[/b][/size]
  42. [size=3][b]}[/b][/size]
  43. [size=3][b]/*[/b][/size]
  44. [size=3][b]*********************************************************************************************************[/b][/size]
  45. [size=3][b]* 函 数 名: AppTaskLED[/b][/size]
  46. [size=3][b]* 功能说明: LED闪烁。[/b][/size]
  47. [size=3][b]* 形 参: 无[/b][/size]
  48. [size=3][b]* 返 回 值: 无[/b][/size]
  49. [size=3][b]* 优 先 级: 2[/b][/size]
  50. [size=3][b]*********************************************************************************************************[/b][/size]
  51. [size=3][b]*/[/b][/size]
  52. [size=3][b]__task void AppTaskLED(void)[/b][/size]
  53. [size=3][b]{[/b][/size]
  54. [size=3][b]const uint16_t usFrequency = 500; /* 延迟周期 */[/b][/size]
  55. [size=3][b]/* 设置延迟周期 */[/b][/size]
  56. [size=3][b]os_itv_set(usFrequency);[/b][/size]
  57. [size=3][b]while(1)[/b][/size]
  58. [size=3][b]{[/b][/size]
  59. [size=3][b]bsp_LedToggle(2);[/b][/size]
  60. [size=3][b]/* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/[/b][/size]
  61. [size=3][b]os_itv_wait();[/b][/size]
  62. [size=3][b]}[/b][/size]
  63. [size=3][b]}[/b][/size]
  64. [size=3][b]/*[/b][/size]
  65. [size=3][b]*********************************************************************************************************[/b][/size]
  66. [size=3][b]* 函 数 名: AppTaskMsgPro[/b][/size]
  67. [size=3][b]* 功能说明: 按键检测[/b][/size]
  68. [size=3][b]* 形 参: 无[/b][/size]
  69. [size=3][b]* 返 回 值: 无[/b][/size]
  70. [size=3][b]* 优 先 级: 3[/b][/size]
  71. [size=3][b]*********************************************************************************************************[/b][/size]
  72. [size=3][b]*/[/b][/size]
  73. [size=3][b]__task void AppTaskMsgPro(void)[/b][/size]
  74. [size=3][b]{[/b][/size]
  75. [size=3][b]while(1)[/b][/size]
  76. [size=3][b]{[/b][/size]
  77. [size=3][b]bsp_KeyScan();[/b][/size]
  78. [size=3][b]os_dly_wait(10);[/b][/size]
  79. [size=3][b]}[/b][/size]
  80. [size=3][b]}[/b][/size]
  81. [size=3][b]/*[/b][/size]
  82. [size=3][b]*********************************************************************************************************[/b][/size]
  83. [size=3][b]* 函 数 名: AppTaskTCPMain[/b][/size]
  84. [size=3][b]* 功能说明: RL-TCPnet测试任务[/b][/size]
  85. [size=3][b]* 形 参: 无[/b][/size]
  86. [size=3][b]* 返 回 值: 无[/b][/size]
  87. [size=3][b]* 优 先 级: 4[/b][/size]
  88. [size=3][b]*********************************************************************************************************[/b][/size]
  89. [size=3][b]*/[/b][/size]
  90. [size=3][b]__task void AppTaskTCPMain(void)[/b][/size]
  91. [size=3][b]{[/b][/size]
  92. [size=3][b]while (1)[/b][/size]
  93. [size=3][b]{[/b][/size]
  94. [size=3][b]TCPnetTest();[/b][/size]
  95. [size=3][b]}[/b][/size]
  96. [size=3][b]}[/b][/size]
  97. [size=3][b]/*[/b][/size]
  98. [size=3][b]*********************************************************************************************************[/b][/size]
  99. [size=3][b]* 函 数 名: AppTaskStart[/b][/size]
  100. [size=3][b]* 功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。[/b][/size]
  101. [size=3][b]* 形 参: 无[/b][/size]
  102. [size=3][b]* 返 回 值: 无[/b][/size]
  103. [size=3][b]* 优 先 级: 5[/b][/size]
  104. [size=3][b]*********************************************************************************************************[/b][/size]
  105. [size=3][b]*/[/b][/size]
  106. [size=3][b]__task void AppTaskStart(void)[/b][/size]
  107. [size=3][b]{[/b][/size]
  108. [size=3][b]/* 初始化RL-TCPnet */[/b][/size]
  109. [size=3][b]init_TcpNet ();[/b][/size]
  110. [size=3][b]/* 创建任务 */[/b][/size]
  111. [size=3][b]AppTaskCreate();[/b][/size]
  112. [size=3][b]os_itv_set (100);[/b][/size]
  113. [size=3][b]while(1)[/b][/size]
  114. [size=3][b]{[/b][/size]
  115. [size=3][b]os_itv_wait ();[/b][/size]
  116. [size=3][b]/* RL-TCPnet时间基准更新函数 */[/b][/size]
  117. [size=3][b]timer_tick ();[/b][/size]
  118. [size=3][b]}[/b][/size]
  119. [size=3][b]}[/b][/size]
复制代码

u  RL-TCPnet功能测试
    这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,这里是创建了一个UDP Socket。
  1. [size=3]#include "includes.h"[/size]
  2. [size=3]/*[/size]
  3. [size=3]*********************************************************************************************************[/size]
  4. [size=3]* 用于本文件的调试[/size]
  5. [size=3]*********************************************************************************************************[/size]
  6. [size=3]*/[/size]
  7. [size=3]#if 1[/size]
  8. [size=3]#define printf_debug printf[/size]
  9. [size=3]#else[/size]
  10. [size=3]#define printf_debug(...)[/size]
  11. [size=3]#endif[/size]
  12. [size=3]/*[/size]
  13. [size=3]*********************************************************************************************************[/size]
  14. [size=3]* 宏定义,远程服务器的IP和端口[/size]
  15. [size=3]*********************************************************************************************************[/size]
  16. [size=3]*/[/size]
  17. [size=3]/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */[/size]
  18. [size=3]#define IP1 192[/size]
  19. [size=3]#define IP2 168[/size]
  20. [size=3]#define IP3 1[/size]
  21. [size=3]#define IP4 2[/size]
  22. [size=3]#define PORT_NUM 1001[/size]
  23. [size=3]/* 这个是本地端口 */[/size]
  24. [size=3]#define LocalPort_NUM 1024[/size]
  25. [size=3]/*[/size]
  26. [size=3]*********************************************************************************************************[/size]
  27. [size=3]* 变量[/size]
  28. [size=3]*********************************************************************************************************[/size]
  29. [size=3]*/[/size]
  30. [size=3]extern LOCALM localm[];[/size]
  31. [size=3]uint8_t udp_soc;[/size]
  32. [size=3]uint8_t DHCP_Status = __FALSE;[/size]
  33. [size=3]uint8_t CacheARP_Status = __FALSE;[/size]
  34. [size=3]uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};[/size]
  35. [size=3]/*[/size]
  36. [size=3]*********************************************************************************************************[/size]
  37. [size=3]* 函 数 名: DCHP_ARP_Check[/size]
  38. [size=3]* 功能说明: 检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC。[/size]
  39. [size=3]* 形 参: 无[/size]
  40. [size=3]* 返 回 值: 无[/size]
  41. [size=3]*********************************************************************************************************[/size]
  42. [size=3]*/[/size]
  43. [size=3]void DCHP_ARP_Check(void)[/size]
  44. [size=3]{[/size]
  45. [size=3]if(DHCP_Status == __FALSE)[/size]
  46. [size=3]{[/size]
  47. [size=3]if(mem_test(localm[NETIF_ETH].IpAdr, 0, 4) == __FALSE)[/size]
  48. [size=3]{[/size]
  49. [size=3]DHCP_Status = __TRUE;[/size]
  50. [size=3]printf_debug("DHCP已经初始化成功,注意未使能DHCP也是返回成功的\\r\\n");[/size]
  51. [size=3]}[/size]
  52. [size=3]}[/size]
  53. [size=3]if(CacheARP_Status == __FALSE)[/size]
  54. [size=3]{[/size]
  55. [size=3]if(arp_cache_ip (Rem_IP, ARP_FIXED_IP) == __FALSE)[/size]
  56. [size=3]{[/size]
  57. [size=3]CacheARP_Status = __TRUE;[/size]
  58. [size=3]printf_debug("通过IP地址可以解析出MAC\\r\\n");[/size]
  59. [size=3]}[/size]
  60. [size=3]}[/size]
  61. [size=3]}[/size]
  62. [size=3]/*[/size]
  63. [size=3]*********************************************************************************************************[/size]
  64. [size=3]* 函 数 名: tcp_callback[/size]
  65. [size=3]* 功能说明: TCP Socket的回调函数[/size]
  66. [size=3]* 形 参: socket UDP Socket类型[/size]
  67. [size=3]* remip 远程设备的IP地址[/size]
  68. [size=3]* remport 远程设备的端口号[/size]
  69. [size=3]* buf 远程设备发来的数据地址[/size]
  70. [size=3]* len 远程设备发来的数据长度,单位字节[/size]
  71. [size=3]* 返 回 值: 默认返回0即可,一般用不上[/size]
  72. [size=3]*********************************************************************************************************[/size]
  73. [size=3]*/[/size]
  74. [size=3]U16 udp_callback(U8 socket, U8 *remip, U16 remport, U8 *buf, U16 len)[/size]
  75. [size=3]{[/size]
  76. [size=3]char buffer[50];[/size]
  77. [size=3]U16 i;[/size]
  78. [size=3]/* 确保是udp_soc的回调 */[/size]
  79. [size=3]if (socket != udp_soc)[/size]
  80. [size=3]{[/size]
  81. [size=3]return (0);[/size]
  82. [size=3]}[/size]
  83. [size=3]/* 发消息的远程IP,打印IP和端口号 */[/size]
  84. [size=3]sprintf(buffer, "远程连接IP: %d.%d.%d.%d", remip[0], remip[1], remip[2], remip[3]);[/size]
  85. [size=3]printf_debug("%s port:%d\\r\\n", buffer, remport);[/size]
  86. [size=3]/* 接收到的数据长度,单位字节 */[/size]
  87. [size=3]printf_debug("Data length = %d\\r\\n", len);[/size]
  88. [size=3]/* 打印接收到的消息 */[/size]
  89. [size=3]for(i = 0; i < len; i++)[/size]
  90. [size=3]{[/size]
  91. [size=3]printf_debug("buf[%d] = %d\\r\\n", i, buf[i]);[/size]
  92. [size=3]}[/size]
  93. [size=3]return (0);[/size]
  94. [size=3]}[/size]
  95. [size=3]/*[/size]
  96. [size=3]*********************************************************************************************************[/size]
  97. [size=3]* 函 数 名: TCPnetTest[/size]
  98. [size=3]* 功能说明: TCPnet应用[/size]
  99. [size=3]* 形 参: 无[/size]
  100. [size=3]* 返 回 值: 无[/size]
  101. [size=3]*********************************************************************************************************[/size]
  102. [size=3]*/[/size]
  103. [size=3]void TCPnetTest(void)[/size]
  104. [size=3]{[/size]
  105. [size=3]int32_t iCount;[/size]
  106. [size=3]uint8_t *sendbuf;[/size]
  107. [size=3]uint8_t res;[/size]
  108. [size=3]OS_RESULT xResult;[/size]
  109. [size=3]const uint16_t usMaxBlockTime = 2; /* 延迟周期 */[/size]
  110. [size=3]/* 获取一个UDP Socket */[/size]
  111. [size=3]udp_soc = udp_get_socket (0, UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udp_callback);[/size]
  112. [size=3]if (udp_soc != 0)[/size]
  113. [size=3]{[/size]
  114. [size=3]/* 打开UDP端口号 */[/size]
  115. [size=3]udp_open (udp_soc, LocalPort_NUM);[/size]
  116. [size=3]}[/size]
  117. [size=3]while (1)[/size]
  118. [size=3]{[/size]
  119. [size=3]/* RL-TCPnet处理函数 */[/size]
  120. [size=3]main_TcpNet();[/size]
  121. [size=3]/* 检测DHCP是否获取了IP地址以及UDP通信要访问的远程IP地址是否可以解析出对应的MAC */[/size]
  122. [size=3]DCHP_ARP_Check();[/size]
  123. [size=3]/* 按键消息的处理 */[/size]
  124. [size=3]if(os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)[/size]
  125. [size=3]{[/size]
  126. [size=3]xResult = os_evt_get ();[/size]
  127. [size=3]switch (xResult)[/size]
  128. [size=3]{[/size]
  129. [size=3]/* 接收到K1键按下消息,给远程UDP设备发送8字节数据 */[/size]
  130. [size=3]case KEY1_BIT0:[/size]
  131. [size=3]/* 用于设置发送次数 */[/size]
  132. [size=3]iCount = 1;[/size]
  133. [size=3]do[/size]
  134. [size=3]{[/size]
  135. [size=3]main_TcpNet();[/size]
  136. [size=3]/* 申请8字节的空间 */[/size]
  137. [size=3]sendbuf = udp_get_buf (8);[/size]
  138. [size=3]if(sendbuf != NULL)[/size]
  139. [size=3]{[/size]
  140. [size=3]/* 初始化8个字节变量 */[/size]
  141. [size=3]sendbuf[0] = '1';[/size]
  142. [size=3]sendbuf[1] = '2';[/size]
  143. [size=3]sendbuf[2] = '3';[/size]
  144. [size=3]sendbuf[3] = '4';[/size]
  145. [size=3]sendbuf[4] = '5';[/size]
  146. [size=3]sendbuf[5] = '6';[/size]
  147. [size=3]sendbuf[6] = '7';[/size]
  148. [size=3]sendbuf[7] = '8';[/size]
  149. [size=3]res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 8);[/size]
  150. [size=3]/* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */[/size]
  151. [size=3]if(res == __TRUE )[/size]
  152. [size=3]{[/size]
  153. [size=3]iCount--;[/size]
  154. [size=3]}[/size]
  155. [size=3]}[/size]
  156. [size=3]/*[/size]
  157. [size=3]由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,[/size]
  158. [size=3]保证远程设备可以接受到数据[/size]
  159. [size=3]*/[/size]
  160. [size=3]os_dly_wait(10);[/size]
  161. [size=3]}while(iCount > 0);[/size]
  162. [size=3]break;[/size]
  163. [size=3]/* 接收到K2键按下消息,给远程UDP设备发送10240字节数据 */[/size]
  164. [size=3]case KEY2_BIT1:[/size]
  165. [size=3]/* 用于设置发送次数,每次1024字节 */[/size]
  166. [size=3]iCount = 10;[/size]
  167. [size=3]do[/size]
  168. [size=3]{[/size]
  169. [size=3]main_TcpNet();[/size]
  170. [size=3]/* 申请1024字节的空间 */[/size]
  171. [size=3]sendbuf = udp_get_buf (1024);[/size]
  172. [size=3]if(sendbuf != NULL)[/size]
  173. [size=3]{[/size]
  174. [size=3]/* 将申请到的1024字节全部清零 */[/size]
  175. [size=3]memset(sendbuf, 0, 1024);[/size]
  176. [size=3]/* 这里仅初始化了每次所发送数据包的前8个字节 */[/size]
  177. [size=3]sendbuf[0] = 'a';[/size]
  178. [size=3]sendbuf[1] = 'b';[/size]
  179. [size=3]sendbuf[2] = 'c';[/size]
  180. [size=3]sendbuf[3] = 'd';[/size]
  181. [size=3]sendbuf[4] = 'e';[/size]
  182. [size=3]sendbuf[5] = 'f';[/size]
  183. [size=3]sendbuf[6] = 'g';[/size]
  184. [size=3]sendbuf[7] = 'h';[/size]
  185. [size=3]res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);[/size]
  186. [size=3]/* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */[/size]
  187. [size=3]if(res == __TRUE )[/size]
  188. [size=3]{[/size]
  189. [size=3]iCount--;[/size]
  190. [size=3]}[/size]
  191. [size=3]}[/size]
  192. [size=3]/*[/size]
  193. [size=3]由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,[/size]
  194. [size=3]保证远程设备可以接受到数据[/size]
  195. [size=3]*/[/size]
  196. [size=3]os_dly_wait(10);[/size]
  197. [size=3]}while(iCount > 0);[/size]
  198. [size=3]break;[/size]
  199. [size=3]/* 接收到K3键按下消息,给远程UDP设备发送2MB数据 */[/size]
  200. [size=3]case KEY3_BIT2:[/size]
  201. [size=3]/* 用于设置发送次数,每次发送1024字节 */[/size]
  202. [size=3]iCount = 2048;[/size]
  203. [size=3]do[/size]
  204. [size=3]{[/size]
  205. [size=3]main_TcpNet();[/size]
  206. [size=3]/* 申请1024字节的空间 */[/size]
  207. [size=3]sendbuf = udp_get_buf (1024);[/size]
  208. [size=3]if(sendbuf != NULL)[/size]
  209. [size=3]{[/size]
  210. [size=3]/* 将申请到的1024字节全部清零 */[/size]
  211. [size=3]memset(sendbuf, 0, 1024);[/size]
  212. [size=3]/* 这里仅初始化了每次所发送数据包的前8个字节 */[/size]
  213. [size=3]sendbuf[0] = 'a';[/size]
  214. [size=3]sendbuf[1] = 'b';[/size]
  215. [size=3]sendbuf[2] = 'c';[/size]
  216. [size=3]sendbuf[3] = 'd';[/size]
  217. [size=3]sendbuf[4] = 'e';[/size]
  218. [size=3]sendbuf[5] = 'f';[/size]
  219. [size=3]sendbuf[6] = 'g';[/size]
  220. [size=3]sendbuf[7] = 'h';[/size]
  221. [size=3]res = udp_send (udp_soc, Rem_IP, PORT_NUM, sendbuf, 1024);[/size]
  222. [size=3]/* 保证发送成功了发送次数才可以减一,这里发送成功并不保证远程设备接受成功 */[/size]
  223. [size=3]if(res == __TRUE )[/size]
  224. [size=3]{[/size]
  225. [size=3]iCount--;[/size]
  226. [size=3]}[/size]
  227. [size=3]}[/size]
  228. [size=3]/*[/size]
  229. [size=3]由于UDP没有重发,应答,流控制等机制,这里简单的做个延迟,[/size]
  230. [size=3]保证远程设备可以接受到数据[/size]
  231. [size=3]*/[/size]
  232. [size=3]os_dly_wait(15);[/size]
  233. [size=3]}while(iCount > 0);[/size]
  234. [size=3]break;[/size]
  235. [size=3]/* 其他的键值不处理 */[/size]
  236. [size=3]default:[/size]
  237. [size=3]break;[/size]
  238. [size=3]}[/size]
  239. [size=3]}[/size]
  240. [size=3]}[/size]
  241. [size=3]}[/size]
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-29 20:37 , Processed in 0.243241 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表