硬汉嵌入式论坛

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

[RL-TCPnet教程] 【RL-TCPnet网络教程】第10章 RL-TCPnet网络协议栈移植(FreeRTOS)

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2017-10-24 15:29:57 | 显示全部楼层 |阅读模式
第10章    RL-TCPnet网络协议栈移植(FreeRTOS)



        本章教程为大家讲解RL-TCPnet网络协议栈的FreeRTOS操作系统移植方式,学习了第6章讲解的底层驱动接口函数之后,移植就比较容易了,主要是添加库文件、配置文件和驱动文件即可。另外,RL-TCPnet移植到FreeRTOS要重新配置RL-TCPnet的接口函数,以此来支持RL-TCPnet多任务运行。使用RTX无需重新配置,因为默认情况下就是采用RTX的API函数配置的。
        本章教程含STM32F407开发板和STM32F429开发板的移植。
         10.1 移植前准备工作说明
        10.2 STM32F407移植RL-TCPnet协议栈
        10.3 STM32F429移植RL-TCPnet协议栈
        10.4      总结



10.1  移植前准备工作说明



1、学习本章节前,务必要优先学习第6章的底层驱动讲解。
2、RL-TCPnet只有库,没有源码。库分为两个版本,一个用于调试的版本TCPD_CM3.lib和一个正式版本TCP_CM3.lib,当前的例子统一使用调试版本。另外注意,虽然是CM3版本的,但可同时用于CM3和CM4内核的MCU,因为官方没有专门的CM4内核库。
3、测试时,请将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
        而且使能了NetBIOS局域网域名,用户只需在电脑端ping armfly,就可以获得板子的IP地址。
4、如果要使用固定IP进行测试,请看附件C
5、网口使用的是DM9161/9162(挨着9帧串口座的网口),而不是DM9000
6、找一个简单的工程,最好是跑马灯之类的,越简单越好,我们就在这个简单的工程上面移植即可。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-24 15:41:46 | 显示全部楼层
10.2 STM32F407移植RL-TCPnet协议栈



10.2.1 RL-TCPnet网络协议栈移植


        首先准备好一个简单的FreeRTOS工程模板,工程模板的制作就不做讲解了,这里的重点是教大家移植RL-TCPnet协议栈。准备好的工程模板如下图所示(大家也可以制作其它任意的工程模板,不限制):
10.1.png

        准备好工程模板后,就可以开始移植了。首先要做的就是将所有需要的文件放到工程模板里面。下面分4步跟大家进行说明,当然,不限制必须使用下面的方法添加源码到工程,只要将需要的文件添加到工程模板即可。
第1步:将我们FreeRTOS模板中制作好的RL-ARM文件夹复制粘贴到大家准备好的工程模板中。
10.2.png

        RL-ARM文件夹中有如下七个文件夹:
10.3.png

        Config文件夹用于存放RTX及其中间件的配置文件。
        Driver文件夹用于存放中间件的驱动文件,也就是底层移植文件。
        RL-CAN文件夹用于存放CAN总线的源码文件。
        RL-FlashFS文件夹用于存放文件系统RL-FlashFS的库文件。
        RL-RTX文件夹用于存放RTX的源码文件。
        RL-TCPnet文件夹用于存放网络协议栈RL-TCPnet的库文件。
        RL-USB文件夹用于存放USB协议栈RL-USB的库文件。
        也许有用户会问:我们不是仅仅需要移植RL-TCPnet的相关文件就行了吗,为什么把RTX及其所有中间件都添加进来了?这样做的目的是为了以后升级的方便,如果需要添加USB、文件系统、CAN等组件,直接添加到工程即可。
        这些文件全部来自MDK4.74的安装目录,库文件位于路径:C:\\Keil_v474\\ARM\\RV31下,而驱动和配置文件位于路径:C:\\Keil_v474\\ARM\\RL下。
第2步:添加RL-TCPnet的库文件、配置文件和驱动文件到工程,添加完毕后的效果如下:
10.4.png

        Net_lib.c,Net_Config.c和NET_Debug.c在RL-ARM文件夹的Config文件里面。
        TCPD_CM3.lib在RL-ARM文件夹的RL-TCPnet文件里面。
        ETH_STM32F4xx.c和ETH_STM32F4xx.h在RL-ARM文件夹的Driver文件里面。
第3步:添加相应的头文件路径,在原来工程模板的基础上新添加的几个路径:
10.5.png

第4步:也是最后一步,添加预定义宏,点击MDK的option -> c/c++选项,添加上__RTX(注意,字母RTX前面有两个下划线的),添加这个宏定义才可以使能RL-TCPnet的多任务支持。
10.6.png

        至此,RL-TCPnet的移植工作就完成了,剩下就是系统配置和应用了。

10.2.2 RL-TCPnet配置说明(Net_Config.c)


        RL-TCPnet的配置工作是通过配置文件Net_Config.c实现。在MDK工程中打开文件Net_Config.c,可以看到下图所示的工程配置向导:
10.7.png

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

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

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

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

u  UDP Sockets
UDP Sockets配置,打上对勾就使能了此项功能
(1)Number of UDP Sockets
        用于配置可创建的UDP Sockets数量。
        范围1 – 20。
10.12.png

u  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,单位秒。
        重试时间。学习了第6章讲解的底层驱动接口函数之后,移植就比较容易了。
(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接收窗口大小。

10.2.3 RL-TCPnet调试说明(Net_Debug.c)


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

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

        Off:表示关闭此选项的调试功能。
        Errorsonly:表示仅在此选项出错时,将其错误打印出来。
        Fulldebug:表示此选项的全功能调试。
        关于调试功能的使用会在第11章详细为大家讲解,移植阶段将其全部关闭即可。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-24 15:43:42 | 显示全部楼层
10.2.4 RL-TCPnet的多任务驱动接口函数


        要让RL-TCPnet支持多任务,就需要修改Net_lib.c文件。默认情况下,Net_lib.c文件是支持RTX操作系统的,现在要将其修改为支持FreeRTOS,需要修改的几个地方如下:
u  添加FreeRTOS的头文件。
  1. #if (__RTX)
  2.   #include "FreeRTOS.h"
  3.   #include "task.h"
  4.   #include "queue.h"
  5.   #include "semphr.h"
  6. #endif
复制代码
u  定义信号量和互斥信号量。
  1. #if (BSD_ENABLE)
  2. static BSD_INFO bsd_scb[BSD_NUMSOCKS + BSD_SRVSOCKS];
  3. #ifdef __RTX
  4.    SemaphoreHandle_t  bsd_mutex = NULL;
  5.    SemaphoreHandle_t  bsd_sem = NULL;
  6.   #define BSD_INRTX  __TRUE
  7. #else
  8.   #define BSD_INRTX  __FALSE
  9. #endif
  10. BSD_CFG bsd_config = {
  11.    bsd_scb,
  12.    BSD_NUMSOCKS + BSD_SRVSOCKS,
  13.    BSD_INRTX,
  14.    BSD_RCVTOUT * TICK_RATE
  15. };
  16. #endif
复制代码
u  创建互斥信号量和信号量。
  1. /*--------------------------- init_system -----------------------------------*/
  2. void init_system (void) {
  3.   /* Initialize configured interfaces and applications. */
  4.    
  5. #if (ETH_ENABLE)
  6.   eth_init_link ();
  7. #endif
  8. #if (PPP_ENABLE)
  9.   ppp_init_link ();
  10. #endif
  11. #if (SLIP_ENABLE)
  12.   slip_init_link ();
  13. #endif
  14.   ip_init ();
  15.   icmp_init ();
  16. #if (ETH_ENABLE && IGMP_ENABLE)
  17.   igmp_init ();
  18. #endif
  19. #if (UDP_ENABLE)
  20.   udp_init ();
  21. #endif
  22. #if (TCP_ENABLE)
  23.   tcp_init ();
  24. #endif
  25. #if (BSD_ENABLE)
  26.   bsd_init ();
  27. #if (BSD_GETHOSTEN)
  28.   bsd_init_host ();
  29. #endif
  30. #endif
  31. #if (HTTP_ENABLE)
  32.   http_init ();
  33. #endif
  34. #if (TNET_ENABLE)
  35.   tnet_init ();
  36. #endif
  37. #if (TFTP_ENABLE)
  38.   tftp_init ();
  39. #endif
  40. #if (TFTPC_ENABLE)
  41.   tftpc_init ();
  42. #endif
  43. #if (FTP_ENABLE)
  44.   ftp_init ();
  45. #endif
  46. #if (FTPC_ENABLE)
  47.   ftpc_init ();
  48. #endif
  49. #if (ETH_ENABLE && NBNS_ENABLE)
  50.   nbns_init ();
  51. #endif
  52. #if (ETH_ENABLE && DHCP_ENABLE)
  53.   dhcp_init ();
  54. #elif (ETH_ENABLE)
  55.   arp_notify ();
  56. #endif
  57. #if (DNS_ENABLE)
  58.   dns_init ();
  59. #endif
  60. #if (SMTP_ENABLE)
  61.   smtp_init ();
  62. #endif
  63. #if (SNMP_ENABLE)
  64.   snmp_init ();
  65. #endif
  66. #if (SNTP_ENABLE)
  67.   sntp_init ();
  68. #endif
  69. #if (BSD_ENABLE && __RTX)
  70. {
  71.      bsd_mutex = xSemaphoreCreateMutex();
  72.      if(bsd_mutex == NULL)
  73.     {
  74.         /* ERR */
  75.     }   
  76.    
  77.      bsd_sem = xSemaphoreCreateBinary();
  78.      if(bsd_sem == NULL)
  79.     {
  80.         /* ERR */
  81.     }
  82. }
  83. #endif
  84. }
复制代码
u  系统运行时,使用互斥信号量。
  1. /*--------------------------- run_system ------------------------------------*/
  2. void run_system (void) {
  3.   /* Run configured interfaces and applications. */
  4. #if (BSD_ENABLE && __RTX)
  5.      xSemaphoreTake(bsd_mutex, portMAX_DELAY);
  6. #endif
  7. #if (ETH_ENABLE)
  8.   eth_run_link ();
  9. #endif
  10. #if (PPP_ENABLE)
  11.   ppp_run_link ();
  12. #endif
  13. #if (SLIP_ENABLE)
  14.   slip_run_link ();
  15. #endif
  16.   ip_run_local ();
  17.   icmp_run_engine ();
  18. #if (ETH_ENABLE && IGMP_ENABLE)
  19.   igmp_run_host ();
  20. #endif
  21. #if (TCP_ENABLE)
  22.   tcp_poll_sockets ();
  23. #endif
  24. #if (BSD_ENABLE)
  25.   bsd_poll_sockets ();
  26. #endif
  27. #if (HTTP_ENABLE)
  28.   http_run_server ();
  29. #endif
  30. #if (TNET_ENABLE)
  31.   tnet_run_server ();
  32. #endif
  33. #if (TFTP_ENABLE)
  34.   tftp_run_server ();
  35. #endif
  36. #if (TFTPC_ENABLE)
  37.   tftpc_run_client ();
  38. #endif
  39. #if (FTP_ENABLE)
  40.   ftp_run_server ();
  41. #endif
  42. #if (FTPC_ENABLE)
  43.   ftpc_run_client ();
  44. #endif
  45. #if (ETH_ENABLE && DHCP_ENABLE)
  46.   dhcp_run_client ();
  47. #endif
  48. #if (DNS_ENABLE)
  49.   dns_run_client ();
  50. #endif
  51. #if (SMTP_ENABLE)
  52.   smtp_run_client ();
  53. #endif
  54. #if (SNMP_ENABLE)
  55.   snmp_run_agent ();
  56. #endif
  57. #if (SNTP_ENABLE)
  58.   sntp_run_client ();
  59. #endif
  60. #if (BSD_ENABLE && __RTX)
  61.   xSemaphoreGive(bsd_mutex);
  62. #endif
  63. }
复制代码
u  使能BSD Socket的话,Socket挂起和恢复的实现。
  1. /*--------------------------- bsd_suspend/resume ----------------------------*/
  2. #if (BSD_ENABLE && __RTX)
  3. __used void bsd_suspend (U8 *tsk_id) {
  4.   /* Suspend a socket owner task. */
  5.   xSemaphoreGive(bsd_mutex);
  6.   xSemaphoreTake(bsd_sem, portMAX_DELAY);
  7.   xSemaphoreTake(bsd_mutex, portMAX_DELAY);   
  8. }
  9. __used void bsd_resume (U8 tsk_id) {
  10.   /* Resume a task waiting for a socket event. */
  11.   xSemaphoreGive(bsd_sem);
  12. }
  13. #endif
复制代码
u  使能BSD Socket的话,锁机制的实现。
  1. /*--------------------------- bsd_lock/unlock -------------------------------*/
  2. #if (BSD_ENABLE && __RTX)
  3. __used void bsd_lock (void) {
  4.   /* Acquire mutex - Lock TCPnet functions. */
  5.   xSemaphoreTake(bsd_mutex, portMAX_DELAY);   
  6. }
  7. __used void bsd_unlock (void) {
  8.   /* Release mutex - Unlock TCPnet functions. */
  9.   xSemaphoreGive(bsd_mutex);
  10. }
  11. #endif
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-24 15:45:49 | 显示全部楼层
10.2.5 RL-TCPnet应用实例


        为了验证移植的RL-TCPnet是否可以使用,需要添加测试代码。下面是编写的测试代码,配套的测试例子完整版是:V5-1003_RL-TCPnet实验_工程移植模板(FreeRTOS)。
10.15.png

u  FreeRTOS操作系统创建的任务
        经过上面的移植和配置之后,在 main.c 文件中添加如下代码,代码中创建了5个用户任务:
vTaskTaskUserIF 任务: 按键消息处理。
vTaskLED       任务: LED闪烁。
vTaskMsgPro   任务: 消息处理,这里用作按键检测。
vTaskTCPnet   任务: RL-TCPnet测试任务。
vTaskStart     任务: 启动任务,实现RL-TCPnet的时间基准更新。
具体代码如下:
  1. #include "includes.h"
  2. /*
  3. **********************************************************************************************************
  4.                                                    函数声明
  5. **********************************************************************************************************
  6. */
  7. static void vTaskTaskUserIF(void *pvParameters);
  8. static void vTaskLED(void *pvParameters);
  9. static void vTaskMsgPro(void *pvParameters);
  10. static void vTaskTCPnet(void *pvParameters);
  11. static void vTaskStart(void *pvParameters);
  12. static void AppTaskCreate (void);
  13. static void AppObjCreate (void);
  14. static void App_Printf(char *format, ...);
  15. /*
  16. **********************************************************************************************************
  17.                                                    变量声明
  18. **********************************************************************************************************
  19. */
  20. static TaskHandle_t xHandleTaskUserIF = NULL;
  21. static TaskHandle_t xHandleTaskLED = NULL;
  22. static TaskHandle_t xHandleTaskMsgPro = NULL;
  23. static TaskHandle_t xHandleTaskTCPnet = NULL;
  24. static TaskHandle_t xHandleTaskStart = NULL;
  25. static SemaphoreHandle_t  xMutex = NULL;
  26. EventGroupHandle_t xCreatedEventGroup = NULL;
  27. /*
  28. *********************************************************************************************************
  29. *    函 数 名: main
  30. *    功能说明: 标准c程序入口。
  31. *    形    参:无
  32. *    返 回 值: 无
  33. *********************************************************************************************************
  34. */
  35. int main(void)
  36. {
  37.      /*
  38.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  39.        这样做的好处是:
  40.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  41.        2. 保证系统正常启动,不受别的中断影响。
  42.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  43.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  44.        和cpsie i是等效的。
  45.      */
  46.      __set_PRIMASK(1);
  47.    
  48.      /* 硬件初始化 */
  49.      bsp_Init();
  50.    
  51.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  52.            目中不要使用,因为这个功能比较影响系统实时性。
  53.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  54.      */
  55.      vSetupSysInfoTest();
  56.    
  57.      /* 创建任务 */
  58.      AppTaskCreate();
  59.      /* 创建任务通信机制 */
  60.      AppObjCreate();
  61.    
  62.     /* 启动调度,开始执行任务 */
  63.     vTaskStartScheduler();
  64.      /*
  65.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  66.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  67.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  68.      */
  69.      while(1);
  70. }
  71. /*
  72. *********************************************************************************************************
  73. *    函 数 名: vTaskTaskUserIF
  74. *    功能说明: 接口消息处理。
  75. *    形    参: pvParameters 是在创建该任务时传递的形参
  76. *    返 回 值: 无
  77. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  78. *********************************************************************************************************
  79. */
  80. static void vTaskTaskUserIF(void *pvParameters)
  81. {
  82.      uint8_t ucKeyCode;
  83.      uint8_t pcWriteBuffer[500];
  84.    
  85.     while(1)
  86.     {
  87.          ucKeyCode = bsp_GetKey();
  88.         
  89.          if (ucKeyCode != KEY_NONE)
  90.          {
  91.               switch (ucKeyCode)
  92.               {
  93.                    /* K1键按下,直接发送事件标志给任务vTaskTCPnet,设置bit0 */
  94.                    case KEY_DOWN_K1:
  95.                        App_Printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\\r\\n");
  96.                        xEventGroupSetBits(xCreatedEventGroup, KEY1_BIT0);         
  97.                        break;  
  98.                    /* K2键按下,直接发送事件标志给任务vTaskTCPnet,设置bit1 */
  99.                    case KEY_DOWN_K2:
  100.                        App_Printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\\r\\n");
  101.                        xEventGroupSetBits(xCreatedEventGroup, KEY2_BIT1);
  102.                        break;
  103.                   
  104.                    /* K3键按下,直接发送事件标志给任务vTaskTCPnet,设置bit2 */
  105.                    case KEY_DOWN_K3:
  106.                        App_Printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\\r\\n");
  107.                        xEventGroupSetBits(xCreatedEventGroup, KEY3_BIT2);
  108.                        break;
  109.                   
  110.                    /* 摇杆的OK键按下,打印任务执行情况 */
  111.                    case JOY_DOWN_OK:         
  112.                        App_Printf("=================================================\\r\\n");
  113.                        App_Printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  114.                        vTaskList((char *)&pcWriteBuffer);
  115.                        App_Printf("%s\\r\\n", pcWriteBuffer);
  116.                   
  117.                        App_Printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  118.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  119.                        App_Printf("%s\\r\\n", pcWriteBuffer);
  120.                        App_Printf("当前动态内存剩余大小 = %d字节\\r\\n", xPortGetFreeHeapSize());
  121.                        break;
  122.                   
  123.                    /* 其他的键值不处理 */
  124.                    default:                    
  125.                        break;
  126.               }
  127.          }
  128.         
  129.          vTaskDelay(20);
  130.      }
  131. }
  132. /*
  133. *********************************************************************************************************
  134. *    函 数 名: vTaskLED
  135. *    功能说明: LED闪烁
  136. *    形    参: pvParameters 是在创建该任务时传递的形参
  137. *    返 回 值: 无
  138. *   优 先 级: 2
  139. *********************************************************************************************************
  140. */
  141. static void vTaskLED(void *pvParameters)
  142. {
  143.      TickType_t xLastWakeTime;
  144.      const TickType_t xFrequency = 500;
  145.      /* 获取当前的系统时间 */
  146.     xLastWakeTime = xTaskGetTickCount();
  147.    
  148.     while(1)
  149.     {
  150.          bsp_LedToggle(2);
  151.         
  152.           /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  153.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  154.     }
  155. }
  156. /*
  157. *********************************************************************************************************
  158. *    函 数 名: vTaskMsgPro
  159. *    功能说明: 消息处理,这里用作按键检测
  160. *    形    参: pvParameters 是在创建该任务时传递的形参
  161. *    返 回 值: 无
  162. *   优 先 级: 3
  163. *********************************************************************************************************
  164. */
  165. static void vTaskMsgPro(void *pvParameters)
  166. {
  167.     while(1)
  168.     {
  169.           /* 按键扫描 */
  170.          bsp_KeyScan();
  171.          vTaskDelay(10);
  172.     }
  173. }
  174. /*
  175. *********************************************************************************************************
  176. *    函 数 名: vTaskTCPnet
  177. *    功能说明: RL-TCPnet测试任务
  178. *    形    参: pvParameters 是在创建该任务时传递的形参
  179. *    返 回 值: 无
  180. *   优 先 级: 4
  181. *********************************************************************************************************
  182. */
  183. static void vTaskTCPnet(void *pvParameters)
  184. {
  185.     while(1)
  186.     {
  187.          TCPnetTest();
  188.     }
  189. }
  190. /*
  191. *********************************************************************************************************
  192. *    函 数 名: vTaskStart
  193. *    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新
  194. *    形    参: pvParameters 是在创建该任务时传递的形参
  195. *    返 回 值: 无
  196. *   优 先 级: 5
  197. *********************************************************************************************************
  198. */
  199. static void vTaskStart(void *pvParameters)
  200. {
  201.      TickType_t xLastWakeTime;
  202.      const TickType_t xFrequency = 100;
  203.    
  204.      /* 初始化RL-TCPnet */
  205.      init_TcpNet ();
  206.    
  207.      /* 获取当前的系统时间 */
  208.     xLastWakeTime = xTaskGetTickCount();
  209.    
  210.     while(1)
  211.     {   
  212.           /* RL-TCPnet时间基准更新函数 */
  213.          timer_tick ();
  214.         
  215.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  216.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  217.     }
  218. }
  219.                   
  220. /*
  221. *********************************************************************************************************
  222. *    函 数 名: AppTaskCreate
  223. *    功能说明: 创建应用任务
  224. *    形    参:无
  225. *    返 回 值: 无
  226. *********************************************************************************************************
  227. */
  228. static void AppTaskCreate (void)
  229. {
  230.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  231.                  "vTaskUserIF",      /* 任务名    */
  232.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  233.                  NULL,              /* 任务参数  */
  234.                  1,                  /* 任务优先级*/
  235.                  &xHandleTaskUserIF );  /* 任务句柄  */
  236.    
  237.    
  238.      xTaskCreate( vTaskLED,           /* 任务函数  */
  239.                  "vTaskLED",         /* 任务名    */
  240.                  512,                /* stack大小,单位word,也就是4字节 */
  241.                  NULL,               /* 任务参数  */
  242.                  2,                   /* 任务优先级*/
  243.                  &xHandleTaskLED ); /* 任务句柄  */
  244.    
  245.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  246.                  "vTaskMsgPro",           /* 任务名    */
  247.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  248.                  NULL,                    /* 任务参数  */
  249.                  3,                       /* 任务优先级*/
  250.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  251.     xTaskCreate( vTaskTCPnet,             /* 任务函数  */
  252.                  "vTaskTCPnet",           /* 任务名    */
  253.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  254.                  NULL,                    /* 任务参数  */
  255.                  4,                       /* 任务优先级*/
  256.                  &xHandleTaskTCPnet );  /* 任务句柄  */
  257.    
  258.    
  259.      xTaskCreate( vTaskStart,             /* 任务函数  */
  260.                  "vTaskStart",            /* 任务名    */
  261.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  262.                  NULL,                    /* 任务参数  */
  263.                  5,                       /* 任务优先级*/
  264.                  &xHandleTaskStart );   /* 任务句柄  */
  265. }
  266. /*
  267. *********************************************************************************************************
  268. *    函 数 名: AppObjCreate
  269. *    功能说明: 创建任务通信机制
  270. *    形    参: 无
  271. *    返 回 值: 无
  272. *********************************************************************************************************
  273. */
  274. static void AppObjCreate (void)
  275. {
  276.      /* 创建互斥信号量 */
  277.     xMutex = xSemaphoreCreateMutex();
  278.    
  279.      if(xMutex == NULL)
  280.     {
  281.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  282.     }
  283.    
  284.      /* 创建事件标志组 */
  285.      xCreatedEventGroup = xEventGroupCreate();
  286.    
  287.      if(xCreatedEventGroup == NULL)
  288.     {
  289.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  290.     }
  291. }
  292. /*
  293. *********************************************************************************************************
  294. *    函 数 名: App_Printf
  295. *    功能说明: 线程安全的printf方式                     
  296. *    形    参: 同printf的参数。
  297. *             在C中,当无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
  298. *    返 回 值: 无
  299. *********************************************************************************************************
  300. */
  301. static void  App_Printf(char *format, ...)
  302. {
  303.     char  buf_str[512 + 1];
  304.     va_list   v_args;
  305.     va_start(v_args, format);
  306.    (void)vsnprintf((char       *)&buf_str[0],
  307.                    (size_t      ) sizeof(buf_str),
  308.                    (char const *) format,
  309.                                   v_args);
  310.     va_end(v_args);
  311.      /* 互斥信号量 */
  312.      xSemaphoreTake(xMutex, portMAX_DELAY);
  313.     printf("%s", buf_str);
  314.      xSemaphoreGive(xMutex);
  315. }
复制代码
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();     /* 初始化按键变量 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21. }
复制代码
u  RL-TCPnet功能测试
        这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,主要功能是创建了一个TCP Server。
  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. *                                      用于本文件的调试
  15. *********************************************************************************************************
  16. */
  17. #define PORT_NUM       1001    /* TCP服务器监听端口号 */
  18. /*
  19. *********************************************************************************************************
  20. *                                         变量
  21. *********************************************************************************************************
  22. */
  23. uint8_t socket_tcp;
  24. /*
  25. *********************************************************************************************************
  26. *    函 数 名: tcp_callback
  27. *    功能说明: TCP Socket的回调函数
  28. *    形    参: soc  TCP Socket类型
  29. *             evt  事件类型
  30. *             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址
  31. *             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号
  32. *    返 回 值:
  33. *********************************************************************************************************
  34. */
  35. U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)
  36. {
  37.      char buf[50];
  38.      uint16_t i;
  39.    
  40.      /* 确保是socket_tcp的回调 */
  41.      if (soc != socket_tcp)
  42.      {
  43.          return (0);
  44.      }
  45.      switch (evt)
  46.      {
  47.          /*
  48.               远程客户端连接消息
  49.              1、数组ptr存储远程设备的IP地址,par中存储端口号。
  50.              2、返回数值1允许连接,返回数值0禁止连接。
  51.          */
  52.          case TCP_EVT_CONREQ:
  53.               sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);
  54.               printf_debug("IP:%s  port:%d\\r\\n", buf, par);
  55.               return (1);
  56.         
  57.          /* 连接终止 */
  58.          case TCP_EVT_ABORT:
  59.               break;
  60.         
  61.          /* Socket远程连接已经建立 */
  62.          case TCP_EVT_CONNECT:
  63.               printf_debug("Socket is connected to remote peer\\r\\n");
  64.               break;
  65.         
  66.          /* 连接断开 */
  67.          case TCP_EVT_CLOSE:
  68.               printf_debug("Connection has been closed\\r\\n");
  69.               break;
  70.         
  71.          /* 发送的数据收到远程设备应答 */
  72.          case TCP_EVT_ACK:
  73.               break;
  74.         
  75.          /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */
  76.          case TCP_EVT_DATA:
  77.               printf_debug("Data length = %d\\r\\n", par);
  78.               for(i = 0; i < par; i++)
  79.               {
  80.                    printf_debug("ptr[%d] = %d\\r\\n", i, ptr[i]);
  81.               }
  82.               break;
  83.      }
  84.    
  85.      return (0);
  86. }
  87. /*
  88. *********************************************************************************************************
  89. *    函 数 名: TCP_StatusCheck
  90. *    功能说明: 检测TCP的连接状态,主要用于网线插拔的判断
  91. *    形    参: 无
  92. *    返 回 值: __TRUE  连接
  93. *             __FALSE 断开
  94. *********************************************************************************************************
  95. */
  96. uint8_t TCP_StatusCheck(void)
  97. {
  98.      uint8_t res;
  99.    
  100.      switch (tcp_get_state(socket_tcp))
  101.      {
  102.          case TCP_STATE_FREE:
  103.          case TCP_STATE_CLOSED:
  104.               res = tcp_listen (socket_tcp, PORT_NUM);
  105.               printf_debug("tcp listen res = %d\\r\\n", res);
  106.               break;
  107.         
  108.          case TCP_STATE_LISTEN:
  109.               break;
  110.         
  111.          case TCP_STATE_CONNECT:
  112.               return (__TRUE);
  113.             
  114.          default:
  115.               break;
  116.      }
  117.    
  118.      return (__FALSE);
  119. }
  120. /*
  121. *********************************************************************************************************
  122. *    函 数 名: TCPnetTest
  123. *    功能说明: TCPnet应用
  124. *    形    参: 无
  125. *    返 回 值: 无
  126. *********************************************************************************************************
  127. */
  128. void TCPnetTest(void)
  129. {
  130.      int32_t iCount;
  131.      uint8_t *sendbuf;
  132.      uint8_t tcp_status;
  133.      uint16_t maxlen;
  134.      uint8_t res;
  135.      const TickType_t xTicksToWait = 2; /* 延迟2ms */
  136.      EventBits_t uxBits;
  137.    
  138.      /*
  139.         创建TCP Socket并创建监听,客户端连接服务器后,10秒内无数据通信将断开连接。
  140.         但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。
  141.      */
  142.     socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);
  143.      if(socket_tcp != 0)
  144.      {
  145.          res = tcp_listen (socket_tcp, PORT_NUM);
  146.          printf_debug("tcp listen res = %d\\r\\n", res);
  147.      }
  148.    
  149.      while (1)
  150.      {
  151.          /* RL-TCPnet处理函数 */
  152.          main_TcpNet();
  153.         
  154.          /* 用于网线插拔的处理 */
  155.          tcp_status = TCP_StatusCheck();
  156.          /* 等待所有任务发来事件标志 */
  157.          uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件标志组句柄 */
  158.                                       0xFFFF,        /* 等待0xFFFF某一位被设置 */
  159.                                       pdTRUE,/* 退出前0xFFFF位被清除,这里是任意0xFFFF位被设置就“退出”*/
  160.                                       pdFALSE,       /* 设置为pdTRUE表示等待0xFFFF任意位被设置*/
  161.                                        xTicksToWait);/* 等待延迟时间 */
  162.          if((uxBits != 0)&&(tcp_status == __TRUE))
  163.          {
  164.               switch (uxBits)
  165.               {
  166.                    /* 接收到K1键按下,给远程TCP客户端发送8字节数据 */
  167.                    case KEY1_BIT0:              
  168.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  169.                        iCount = 8;
  170.                        do
  171.                        {
  172.                             main_TcpNet();
  173.                             if (tcp_check_send (socket_tcp) == __TRUE)
  174.                             {
  175.                                  maxlen = tcp_max_dsize (socket_tcp);
  176.                                  iCount -= maxlen;
  177.                                 
  178.                                  if(iCount < 0)
  179.                                  {
  180.                                      /* 这么计算没问题的 */
  181.                                      maxlen = iCount + maxlen;
  182.                                 }
  183.                                 
  184.                                  sendbuf = tcp_get_buf(maxlen);
  185.                                  sendbuf[0] = '1';
  186.                                  sendbuf[1] = '2';
  187.                                  sendbuf[2] = '3';
  188.                                  sendbuf[3] = '4';
  189.                                  sendbuf[4] = '5';
  190.                                  sendbuf[5] = '6';
  191.                                  sendbuf[6] = '7';
  192.                                  sendbuf[7] = '8';
  193.                                 
  194.                                  /* 测试发现只能使用获取的内存 */
  195.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  196.                             }
  197.                            
  198.                        }while(iCount > 0);
  199.                        break;
  200.                    /* 接收到K2键按下,给远程TCP客户端发送1024字节的数据 */
  201.                    case KEY2_BIT1:      
  202.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  203.                        iCount = 1024;
  204.                        do
  205.                        {
  206.                             main_TcpNet();
  207.                             if (tcp_check_send (socket_tcp) == __TRUE)
  208.                             {
  209.                                  maxlen = tcp_max_dsize (socket_tcp);
  210.                                  iCount -= maxlen;
  211.                                 
  212.                                  if(iCount < 0)
  213.                                  {
  214.                                      /* 这么计算没问题的 */
  215.                                      maxlen = iCount + maxlen;
  216.                                  }
  217.                                 
  218.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  219.                                  sendbuf = tcp_get_buf(maxlen);
  220.                                  sendbuf[0] = 'a';
  221.                                  sendbuf[1] = 'b';
  222.                                  sendbuf[2] = 'c';
  223.                                  sendbuf[3] = 'd';
  224.                                  sendbuf[4] = 'e';
  225.                                  sendbuf[5] = 'f';
  226.                                  sendbuf[6] = 'g';
  227.                                  sendbuf[7] = 'h';
  228.                                 
  229.                                  /* 测试发现只能使用获取的内存 */
  230.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  231.                             }
  232.                            
  233.                        }while(iCount > 0);                     
  234.                        break;
  235.                      
  236.                    /* 接收到K3键按下,给远程TCP客户端发送5MB数据 */
  237.                    case KEY3_BIT2:              
  238.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  239.                        iCount = 5*1024*1024;
  240.                        do
  241.                        {
  242.                             main_TcpNet();
  243.                             if (tcp_check_send (socket_tcp) == __TRUE)
  244.                             {
  245.                                  maxlen = tcp_max_dsize (socket_tcp);
  246.                                  iCount -= maxlen;
  247.                                 
  248.                                  if(iCount < 0)
  249.                                  {
  250.                                       /* 这么计算没问题的 */
  251.                                      maxlen = iCount + maxlen;
  252.                                  }
  253.                                 
  254.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  255.                                  sendbuf = tcp_get_buf(maxlen);
  256.                                  sendbuf[0] = 'a';
  257.                                  sendbuf[1] = 'b';
  258.                                  sendbuf[2] = 'c';
  259.                                  sendbuf[3] = 'd';
  260.                                  sendbuf[4] = 'e';
  261.                                  sendbuf[5] = 'f';
  262.                                  sendbuf[6] = 'g';
  263.                                  sendbuf[7] = 'h';
  264.                                 
  265.                                  /* 测试发现只能使用获取的内存 */
  266.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  267.                             }
  268.                            
  269.                        }while(iCount > 0);
  270.                        break;
  271.                   
  272.                     /* 其他的键值不处理 */
  273.                    default:                    
  274.                        break;
  275.               }
  276.          }
  277.      }
  278. }
复制代码
至此,FreeRTOS方式移植的RL-TCPnet就可以运行了。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-24 15:47:38 | 显示全部楼层
10.2.6  RL-TCPnet实验测试和实验现象


        测试前,先将开发板上面的DM9161/9162网口通过网线接到路由器或者交换机上面。
u  RJ45网络变压器插座上绿灯和黄灯现象
        各种网卡、交换机等网络设备都不一样,一般来讲:绿灯分为亮或不亮(代表网络速度),黄灯分为闪烁或不闪烁(代表是否有数据收发)。
        绿灯:长亮代表100M; 不亮代表10M。
        黄灯:长亮代表无数据收发; 闪烁代表有数据收发。
也有些千兆网卡的灯以颜色区分,不亮代表10M / 绿色代表100M / 黄色代表1000M。现在10M的网络基本看不到了,如果一个灯长亮,基本可以说明100M网络或更高,而另一个灯时而闪烁,那代表有数据收发,具体要看你的网络设备了。甚至有些低等网卡如TP-LINK,只有一个灯,亮代表连通,闪烁代表数据收发。
        对于STM32F407开发板上面的RJ45网络变压器插座上面的灯而言,绿灯代表数据收发,长亮的话表示无数据收发,闪烁代表有数据收发。黄灯代表网络速度,长亮代表100M,不亮代表10M
u  底层驱动执行情况
        为了验证RL-TCPnet底层驱动接口函数是否有问题,专门在ETH_STM32F4xx.c文件中配置了串口调试打印函数:
  1. /*
  2. *********************************************************************************************************
  3. *                                      用于本文件的调试
  4. *********************************************************************************************************
  5. */
  6. #if 1
  7.      #define printf_eth printf
  8. #else
  9.      #define printf_eth(...)
  10. #endif
复制代码
如果底层驱动正常执行了,打印的效果如下:
10.16.png

u  ping是否正确
        ping命令的主要作用是通过发送数据包并接收应答信息来检测两台设备之间的网络是否连通。ping命令成功说明当前主机与目的主机之间存在连通的路径。如果不成功,需要查看网线是否连通、网卡设置是否正确、IP地址是否可用等。测试方法如下:
        (1)WIN+R组合键打开“运行”窗口,输入cmd。
10.17.png

        (2)弹出的命令窗口中,输入ping armfly,因为在前面的配置中我们使能了NetBIOS局域网域名,并将名字设置为armfly,而且使能了DHCP,通过ping命令还可以获得板子自动获取的IP地址。
10.18.png

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


       收发相同,没有数据丢失,说明ping命令也是成功的。
u  电脑端创建一个TCP Client与板子上面的TCP Server通信
        具体测试方法,查看第13章的13.6小节即可,因为配套例子实现的功能是一样的。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-25 16:14:23 | 显示全部楼层
10.3 STM32F429移植RL-TCPnet协议栈



10.3.1 RL-TCPnet网络协议栈移植


        首先准备好一个简单的FreeRTOS工程模板,工程模板的制作就不做讲解了,这里的重点是教大家移植RL-TCPnet协议栈。准备好的工程模板如下图所示(大家也可以制作其它任意的工程模板,不限制):
10.20.png

        准备好工程模板后,就可以开始移植了。首先要做的就是将所有需要的文件放到工程模板里面。下面分4步跟大家进行说明,当然,不限制必须使用下面的方法添加源码到工程,只要将需要的文件添加到工程模板即可。
第1步:将我们FreeRTOS模板中制作好的RL-ARM文件夹复制粘贴到大家准备好的工程模板中。
10.21.png

        RL-ARM文件夹中有如下七个文件夹:
10.22.png

        Config文件夹用于存放RTX及其中间件的配置文件。
        Driver文件夹用于存放中间件的驱动文件,也就是底层移植文件。
        RL-CAN文件夹用于存放CAN总线的源码文件。
        RL-FlashFS文件夹用于存放文件系统RL-FlashFS的库文件。
        RL-RTX文件夹用于存放RTX的源码文件。
        RL-TCPnet文件夹用于存放网络协议栈RL-TCPnet的库文件。
        RL-USB文件夹用于存放USB协议栈RL-USB的库文件。
        也许有用户会问:我们不是仅仅需要移植RL-TCPnet的相关文件就行了吗,为什么把RTX及其所有中间件都添加进来了?这样做的目的是为了以后升级的方便,如果需要添加USB、文件系统、CAN等组件,直接添加到工程即可。
        这些文件全部来自MDK4.74的安装目录,库文件位于路径:C:\\Keil_v474\\ARM\\RV31下,而驱动和配置文件位于路径:C:\\Keil_v474\\ARM\\RL下。
第2步:添加RL-TCPnet的库文件、配置文件和驱动文件到工程,添加完毕后的效果如下:
10.23.png

        Net_lib.c,Net_Config.c和NET_Debug.c在RL-ARM文件夹的Config文件里面。
         TCPD_CM3.lib在RL-ARM文件夹的RL-TCPnet文件里面。
        ETH_STM32F4xx.c和ETH_STM32F4xx.h在RL-ARM文件夹的Driver文件里面。
第3步:添加相应的头文件路径,在原来工程模板的基础上新添加的几个路径:
10.24.png

第4步:也是最后一步,添加预定义宏,点击MDK的option -> c/c++选项,添加上__RTX(注意,字母RTX前面有两个下划线的),添加这个宏定义才可以使能RL-TCPnet的多任务支持。
10.25.png

        至此,RL-TCPnet的移植工作就完成了,剩下就是系统配置和应用了。

10.3.2 RL-TCPnet配置说明(Net_Config.c)


         RL-TCPnet的配置工作是通过配置文件Net_Config.c实现。在MDK工程中打开文件Net_Config.c,可以看到下图所示的工程配置向导:
10.26.png

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

u  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。
10.28.png

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

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

u  UDP Sockets
UDP Sockets配置,打上对勾就使能了此项功能
(1)Number of UDP Sockets
         用于配置可创建的UDP Sockets数量。
        范围1 – 20。
10.31.png

u  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接收窗口大小。

10.3.3 RL-TCPnet调试说明(Net_Debug.c)


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

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

        Off:表示关闭此选项的调试功能。
        Errorsonly:表示仅在此选项出错时,将其错误打印出来。
        Fulldebug:表示此选项的全功能调试。
        关于调试功能的使用会在第11章详细为大家讲解,移植阶段将其全部关闭即可。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-25 16:16:57 | 显示全部楼层
10.3.4 RL-TCPnet的多任务驱动接口函数


要让RL-TCPnet支持多任务,就需要修改Net_lib.c文件。默认情况下,Net_lib.c文件是支持RTX操作系统的,现在要将其修改为支持FreeRTOS,需要修改的几个地方如下:
u  添加FreeRTOS的头文件。
  1. #if (__RTX)
  2.   #include "FreeRTOS.h"
  3.   #include "task.h"
  4.   #include "queue.h"
  5.   #include "semphr.h"
  6. #endif
复制代码
u  定义信号量和互斥信号量。
  1. #if (BSD_ENABLE)
  2. static BSD_INFO bsd_scb[BSD_NUMSOCKS + BSD_SRVSOCKS];
  3. #ifdef __RTX
  4.    SemaphoreHandle_t  bsd_mutex = NULL;
  5.    SemaphoreHandle_t  bsd_sem = NULL;
  6.   #define BSD_INRTX  __TRUE
  7. #else
  8.   #define BSD_INRTX  __FALSE
  9. #endif
  10. BSD_CFG bsd_config = {
  11.    bsd_scb,
  12.    BSD_NUMSOCKS + BSD_SRVSOCKS,
  13.    BSD_INRTX,
  14.    BSD_RCVTOUT * TICK_RATE
  15. };
  16. #endif
复制代码
u  创建互斥信号量和信号量。
  1. /*--------------------------- init_system -----------------------------------*/
  2. void init_system (void) {
  3.   /* Initialize configured interfaces and applications. */
  4.    
  5. #if (ETH_ENABLE)
  6.   eth_init_link ();
  7. #endif
  8. #if (PPP_ENABLE)
  9.   ppp_init_link ();
  10. #endif
  11. #if (SLIP_ENABLE)
  12.   slip_init_link ();
  13. #endif
  14.   ip_init ();
  15.   icmp_init ();
  16. #if (ETH_ENABLE && IGMP_ENABLE)
  17.   igmp_init ();
  18. #endif
  19. #if (UDP_ENABLE)
  20.   udp_init ();
  21. #endif
  22. #if (TCP_ENABLE)
  23.   tcp_init ();
  24. #endif
  25. #if (BSD_ENABLE)
  26.   bsd_init ();
  27. #if (BSD_GETHOSTEN)
  28.   bsd_init_host ();
  29. #endif
  30. #endif
  31. #if (HTTP_ENABLE)
  32.   http_init ();
  33. #endif
  34. #if (TNET_ENABLE)
  35.   tnet_init ();
  36. #endif
  37. #if (TFTP_ENABLE)
  38.   tftp_init ();
  39. #endif
  40. #if (TFTPC_ENABLE)
  41.   tftpc_init ();
  42. #endif
  43. #if (FTP_ENABLE)
  44.   ftp_init ();
  45. #endif
  46. #if (FTPC_ENABLE)
  47.   ftpc_init ();
  48. #endif
  49. #if (ETH_ENABLE && NBNS_ENABLE)
  50.   nbns_init ();
  51. #endif
  52. #if (ETH_ENABLE && DHCP_ENABLE)
  53.   dhcp_init ();
  54. #elif (ETH_ENABLE)
  55.   arp_notify ();
  56. #endif
  57. #if (DNS_ENABLE)
  58.   dns_init ();
  59. #endif
  60. #if (SMTP_ENABLE)
  61.   smtp_init ();
  62. #endif
  63. #if (SNMP_ENABLE)
  64.   snmp_init ();
  65. #endif
  66. #if (SNTP_ENABLE)
  67.   sntp_init ();
  68. #endif
  69. #if (BSD_ENABLE && __RTX)
  70. {
  71.      bsd_mutex = xSemaphoreCreateMutex();
  72.      if(bsd_mutex == NULL)
  73.     {
  74.         /* ERR */
  75.     }   
  76.    
  77.      bsd_sem = xSemaphoreCreateBinary();
  78.      if(bsd_sem == NULL)
  79.     {
  80.         /* ERR */
  81.     }
  82. }
  83. #endif
  84. }
复制代码
u  系统运行时,使用互斥信号量。
  1. /*--------------------------- run_system ------------------------------------*/
  2. void run_system (void) {
  3.   /* Run configured interfaces and applications. */
  4. #if (BSD_ENABLE && __RTX)
  5.      xSemaphoreTake(bsd_mutex, portMAX_DELAY);
  6. #endif
  7. #if (ETH_ENABLE)
  8.   eth_run_link ();
  9. #endif
  10. #if (PPP_ENABLE)
  11.   ppp_run_link ();
  12. #endif
  13. #if (SLIP_ENABLE)
  14.   slip_run_link ();
  15. #endif
  16.   ip_run_local ();
  17.   icmp_run_engine ();
  18. #if (ETH_ENABLE && IGMP_ENABLE)
  19.   igmp_run_host ();
  20. #endif
  21. #if (TCP_ENABLE)
  22.   tcp_poll_sockets ();
  23. #endif
  24. #if (BSD_ENABLE)
  25.   bsd_poll_sockets ();
  26. #endif
  27. #if (HTTP_ENABLE)
  28.   http_run_server ();
  29. #endif
  30. #if (TNET_ENABLE)
  31.   tnet_run_server ();
  32. #endif
  33. #if (TFTP_ENABLE)
  34.   tftp_run_server ();
  35. #endif
  36. #if (TFTPC_ENABLE)
  37.   tftpc_run_client ();
  38. #endif
  39. #if (FTP_ENABLE)
  40.   ftp_run_server ();
  41. #endif
  42. #if (FTPC_ENABLE)
  43.   ftpc_run_client ();
  44. #endif
  45. #if (ETH_ENABLE && DHCP_ENABLE)
  46.   dhcp_run_client ();
  47. #endif
  48. #if (DNS_ENABLE)
  49.   dns_run_client ();
  50. #endif
  51. #if (SMTP_ENABLE)
  52.   smtp_run_client ();
  53. #endif
  54. #if (SNMP_ENABLE)
  55.   snmp_run_agent ();
  56. #endif
  57. #if (SNTP_ENABLE)
  58.   sntp_run_client ();
  59. #endif
  60. #if (BSD_ENABLE && __RTX)
  61.   xSemaphoreGive(bsd_mutex);
  62. #endif
  63. }
复制代码
u  使能BSD Socket的话,Socket挂起和恢复的实现。
  1. /*--------------------------- bsd_suspend/resume ----------------------------*/
  2. #if (BSD_ENABLE && __RTX)
  3. __used void bsd_suspend (U8 *tsk_id) {
  4.   /* Suspend a socket owner task. */
  5.   xSemaphoreGive(bsd_mutex);
  6.   xSemaphoreTake(bsd_sem, portMAX_DELAY);
  7.   xSemaphoreTake(bsd_mutex, portMAX_DELAY);   
  8. }
  9. __used void bsd_resume (U8 tsk_id) {
  10.   /* Resume a task waiting for a socket event. */
  11.   xSemaphoreGive(bsd_sem);
  12. }
  13. #endif
复制代码
u  使能BSD Socket的话,锁机制的实现。
  1. /*--------------------------- bsd_lock/unlock -------------------------------*/
  2. #if (BSD_ENABLE && __RTX)
  3. __used void bsd_lock (void) {
  4.   /* Acquire mutex - Lock TCPnet functions. */
  5.   xSemaphoreTake(bsd_mutex, portMAX_DELAY);   
  6. }
  7. __used void bsd_unlock (void) {
  8.   /* Release mutex - Unlock TCPnet functions. */
  9.   xSemaphoreGive(bsd_mutex);
  10. }
  11. #endif
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-25 16:40:27 | 显示全部楼层
10.3.5 RL-TCPnet应用实例


        为了验证移植的RL-TCPnet是否可以使用,需要添加测试代码。下面是编写的测试代码,配套的测试例子完整版是:V6-1003_RL-TCPnet实验_工程移植模板(FreeRTOS)。
10.34.png

u  FreeRTOS操作系统创建的任务
        经过上面的移植和配置之后,在 main.c 文件中添加如下代码,代码中创建了5个用户任务:
vTaskTaskUserIF 任务: 按键消息处理。
vTaskLED       任务: LED闪烁。
vTaskMsgPro   任务: 消息处理,这里用作按键检测。
vTaskTCPnet   任务: RL-TCPnet测试任务。
vTaskStart     任务: 启动任务,实现RL-TCPnet的时间基准更新。
具体代码如下:
  1. #include "includes.h"
  2. /*
  3. **********************************************************************************************************
  4.                                                    函数声明
  5. **********************************************************************************************************
  6. */
  7. static void vTaskTaskUserIF(void *pvParameters);
  8. static void vTaskLED(void *pvParameters);
  9. static void vTaskMsgPro(void *pvParameters);
  10. static void vTaskTCPnet(void *pvParameters);
  11. static void vTaskStart(void *pvParameters);
  12. static void AppTaskCreate (void);
  13. static void AppObjCreate (void);
  14. static void App_Printf(char *format, ...);
  15. /*
  16. **********************************************************************************************************
  17.                                                    变量声明
  18. **********************************************************************************************************
  19. */
  20. static TaskHandle_t xHandleTaskUserIF = NULL;
  21. static TaskHandle_t xHandleTaskLED = NULL;
  22. static TaskHandle_t xHandleTaskMsgPro = NULL;
  23. static TaskHandle_t xHandleTaskTCPnet = NULL;
  24. static TaskHandle_t xHandleTaskStart = NULL;
  25. static SemaphoreHandle_t  xMutex = NULL;
  26. EventGroupHandle_t xCreatedEventGroup = NULL;
  27. /*
  28. *********************************************************************************************************
  29. *    函 数 名: main
  30. *    功能说明: 标准c程序入口。
  31. *    形    参:无
  32. *    返 回 值: 无
  33. *********************************************************************************************************
  34. */
  35. int main(void)
  36. {
  37.      /*
  38.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  39.        这样做的好处是:
  40.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  41.        2. 保证系统正常启动,不受别的中断影响。
  42.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  43.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  44.        和cpsie i是等效的。
  45.      */
  46.      __set_PRIMASK(1);
  47.    
  48.      /* 硬件初始化 */
  49.      bsp_Init();
  50.    
  51.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  52.            目中不要使用,因为这个功能比较影响系统实时性。
  53.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  54.      */
  55.      vSetupSysInfoTest();
  56.    
  57.      /* 创建任务 */
  58.      AppTaskCreate();
  59.      /* 创建任务通信机制 */
  60.      AppObjCreate();
  61.    
  62.     /* 启动调度,开始执行任务 */
  63.     vTaskStartScheduler();
  64.      /*
  65.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  66.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  67.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  68.      */
  69.      while(1);
  70. }
  71. /*
  72. *********************************************************************************************************
  73. *    函 数 名: vTaskTaskUserIF
  74. *    功能说明: 接口消息处理。
  75. *    形    参: pvParameters 是在创建该任务时传递的形参
  76. *    返 回 值: 无
  77. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  78. *********************************************************************************************************
  79. */
  80. static void vTaskTaskUserIF(void *pvParameters)
  81. {
  82.      uint8_t ucKeyCode;
  83.      uint8_t pcWriteBuffer[500];
  84.    
  85.     while(1)
  86.     {
  87.          ucKeyCode = bsp_GetKey();
  88.         
  89.          if (ucKeyCode != KEY_NONE)
  90.          {
  91.               switch (ucKeyCode)
  92.               {
  93.                    /* K1键按下,直接发送事件标志给任务vTaskTCPnet,设置bit0 */
  94.                    case KEY_DOWN_K1:
  95.                        App_Printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\\r\\n");
  96.                        xEventGroupSetBits(xCreatedEventGroup, KEY1_BIT0);         
  97.                        break;  
  98.                    /* K2键按下,直接发送事件标志给任务vTaskTCPnet,设置bit1 */
  99.                    case KEY_DOWN_K2:
  100.                        App_Printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\\r\\n");
  101.                        xEventGroupSetBits(xCreatedEventGroup, KEY2_BIT1);
  102.                        break;
  103.                   
  104.                    /* K3键按下,直接发送事件标志给任务vTaskTCPnet,设置bit2 */
  105.                    case KEY_DOWN_K3:
  106.                        App_Printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\\r\\n");
  107.                        xEventGroupSetBits(xCreatedEventGroup, KEY3_BIT2);
  108.                        break;
  109.                   
  110.                    /* 摇杆的OK键按下,打印任务执行情况 */
  111.                    case JOY_DOWN_OK:         
  112.                        App_Printf("=================================================\\r\\n");
  113.                        App_Printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  114.                        vTaskList((char *)&pcWriteBuffer);
  115.                        App_Printf("%s\\r\\n", pcWriteBuffer);
  116.                   
  117.                        App_Printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  118.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  119.                        App_Printf("%s\\r\\n", pcWriteBuffer);
  120.                        App_Printf("当前动态内存剩余大小 = %d字节\\r\\n", xPortGetFreeHeapSize());
  121.                        break;
  122.                   
  123.                    /* 其他的键值不处理 */
  124.                    default:                    
  125.                        break;
  126.               }
  127.          }
  128.         
  129.          vTaskDelay(20);
  130.      }
  131. }
  132. /*
  133. *********************************************************************************************************
  134. *    函 数 名: vTaskLED
  135. *    功能说明: LED闪烁
  136. *    形    参: pvParameters 是在创建该任务时传递的形参
  137. *    返 回 值: 无
  138. *   优 先 级: 2
  139. *********************************************************************************************************
  140. */
  141. static void vTaskLED(void *pvParameters)
  142. {
  143.      TickType_t xLastWakeTime;
  144.      const TickType_t xFrequency = 500;
  145.      /* 获取当前的系统时间 */
  146.     xLastWakeTime = xTaskGetTickCount();
  147.    
  148.     while(1)
  149.     {
  150.          bsp_LedToggle(2);
  151.         
  152.           /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  153.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  154.     }
  155. }
  156. /*
  157. *********************************************************************************************************
  158. *    函 数 名: vTaskMsgPro
  159. *    功能说明: 消息处理,这里用作按键检测
  160. *    形    参: pvParameters 是在创建该任务时传递的形参
  161. *    返 回 值: 无
  162. *   优 先 级: 3
  163. *********************************************************************************************************
  164. */
  165. static void vTaskMsgPro(void *pvParameters)
  166. {
  167.     while(1)
  168.     {
  169.           /* 按键扫描 */
  170.          bsp_KeyScan();
  171.          vTaskDelay(10);
  172.     }
  173. }
  174. /*
  175. *********************************************************************************************************
  176. *    函 数 名: vTaskTCPnet
  177. *    功能说明: RL-TCPnet测试任务
  178. *    形    参: pvParameters 是在创建该任务时传递的形参
  179. *    返 回 值: 无
  180. *   优 先 级: 4
  181. *********************************************************************************************************
  182. */
  183. static void vTaskTCPnet(void *pvParameters)
  184. {
  185.     while(1)
  186.     {
  187.          TCPnetTest();
  188.     }
  189. }
  190. /*
  191. *********************************************************************************************************
  192. *    函 数 名: vTaskStart
  193. *    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新
  194. *    形    参: pvParameters 是在创建该任务时传递的形参
  195. *    返 回 值: 无
  196. *   优 先 级: 5
  197. *********************************************************************************************************
  198. */
  199. static void vTaskStart(void *pvParameters)
  200. {
  201.      TickType_t xLastWakeTime;
  202.      const TickType_t xFrequency = 100;
  203.    
  204.      /* 初始化RL-TCPnet */
  205.      init_TcpNet ();
  206.    
  207.      /* 获取当前的系统时间 */
  208.     xLastWakeTime = xTaskGetTickCount();
  209.    
  210.     while(1)
  211.     {   
  212.           /* RL-TCPnet时间基准更新函数 */
  213.          timer_tick ();
  214.         
  215.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  216.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  217.     }
  218. }
  219.                   
  220. /*
  221. *********************************************************************************************************
  222. *    函 数 名: AppTaskCreate
  223. *    功能说明: 创建应用任务
  224. *    形    参:无
  225. *    返 回 值: 无
  226. *********************************************************************************************************
  227. */
  228. static void AppTaskCreate (void)
  229. {
  230.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  231.                  "vTaskUserIF",      /* 任务名    */
  232.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  233.                  NULL,              /* 任务参数  */
  234.                  1,                  /* 任务优先级*/
  235.                  &xHandleTaskUserIF );  /* 任务句柄  */
  236.    
  237.    
  238.      xTaskCreate( vTaskLED,           /* 任务函数  */
  239.                  "vTaskLED",         /* 任务名    */
  240.                  512,                /* stack大小,单位word,也就是4字节 */
  241.                  NULL,               /* 任务参数  */
  242.                  2,                   /* 任务优先级*/
  243.                  &xHandleTaskLED ); /* 任务句柄  */
  244.    
  245.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  246.                  "vTaskMsgPro",           /* 任务名    */
  247.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  248.                  NULL,                    /* 任务参数  */
  249.                  3,                       /* 任务优先级*/
  250.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  251.     xTaskCreate( vTaskTCPnet,             /* 任务函数  */
  252.                  "vTaskTCPnet",           /* 任务名    */
  253.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  254.                  NULL,                    /* 任务参数  */
  255.                  4,                       /* 任务优先级*/
  256.                  &xHandleTaskTCPnet );  /* 任务句柄  */
  257.    
  258.    
  259.      xTaskCreate( vTaskStart,             /* 任务函数  */
  260.                  "vTaskStart",            /* 任务名    */
  261.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  262.                  NULL,                    /* 任务参数  */
  263.                  5,                       /* 任务优先级*/
  264.                  &xHandleTaskStart );   /* 任务句柄  */
  265. }
  266. /*
  267. *********************************************************************************************************
  268. *    函 数 名: AppObjCreate
  269. *    功能说明: 创建任务通信机制
  270. *    形    参: 无
  271. *    返 回 值: 无
  272. *********************************************************************************************************
  273. */
  274. static void AppObjCreate (void)
  275. {
  276.      /* 创建互斥信号量 */
  277.     xMutex = xSemaphoreCreateMutex();
  278.    
  279.      if(xMutex == NULL)
  280.     {
  281.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  282.     }
  283.    
  284.      /* 创建事件标志组 */
  285.      xCreatedEventGroup = xEventGroupCreate();
  286.    
  287.      if(xCreatedEventGroup == NULL)
  288.     {
  289.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  290.     }
  291. }
  292. /*
  293. *********************************************************************************************************
  294. *    函 数 名: App_Printf
  295. *    功能说明: 线程安全的printf方式                     
  296. *    形    参: 同printf的参数。
  297. *             在C中,当无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
  298. *    返 回 值: 无
  299. *********************************************************************************************************
  300. */
  301. static void  App_Printf(char *format, ...)
  302. {
  303.     char  buf_str[512 + 1];
  304.     va_list   v_args;
  305.     va_start(v_args, format);
  306.    (void)vsnprintf((char       *)&buf_str[0],
  307.                    (size_t      ) sizeof(buf_str),
  308.                    (char const *) format,
  309.                                   v_args);
  310.     va_end(v_args);
  311.      /* 互斥信号量 */
  312.      xSemaphoreTake(xMutex, portMAX_DELAY);
  313.     printf("%s", buf_str);
  314.      xSemaphoreGive(xMutex);
  315. }
复制代码
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();     /* 初始化按键变量 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21. }
复制代码
u  RL-TCPnet功能测试
这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,主要功能是创建了一个TCP Server。
  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. *                                      用于本文件的调试
  15. *********************************************************************************************************
  16. */
  17. #define PORT_NUM       1001    /* TCP服务器监听端口号 */
  18. /*
  19. *********************************************************************************************************
  20. *                                         变量
  21. *********************************************************************************************************
  22. */
  23. uint8_t socket_tcp;
  24. /*
  25. *********************************************************************************************************
  26. *    函 数 名: tcp_callback
  27. *    功能说明: TCP Socket的回调函数
  28. *    形    参: soc  TCP Socket类型
  29. *             evt  事件类型
  30. *             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址
  31. *             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号
  32. *    返 回 值:
  33. *********************************************************************************************************
  34. */
  35. U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)
  36. {
  37.      char buf[50];
  38.      uint16_t i;
  39.    
  40.      /* 确保是socket_tcp的回调 */
  41.      if (soc != socket_tcp)
  42.      {
  43.          return (0);
  44.      }
  45.      switch (evt)
  46.      {
  47.          /*
  48.               远程客户端连接消息
  49.              1、数组ptr存储远程设备的IP地址,par中存储端口号。
  50.              2、返回数值1允许连接,返回数值0禁止连接。
  51.          */
  52.          case TCP_EVT_CONREQ:
  53.               sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);
  54.               printf_debug("IP:%s  port:%d\\r\\n", buf, par);
  55.               return (1);
  56.         
  57.          /* 连接终止 */
  58.          case TCP_EVT_ABORT:
  59.               break;
  60.         
  61.          /* Socket远程连接已经建立 */
  62.          case TCP_EVT_CONNECT:
  63.               printf_debug("Socket is connected to remote peer\\r\\n");
  64.               break;
  65.         
  66.          /* 连接断开 */
  67.          case TCP_EVT_CLOSE:
  68.               printf_debug("Connection has been closed\\r\\n");
  69.               break;
  70.         
  71.          /* 发送的数据收到远程设备应答 */
  72.          case TCP_EVT_ACK:
  73.               break;
  74.         
  75.          /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */
  76.          case TCP_EVT_DATA:
  77.               printf_debug("Data length = %d\\r\\n", par);
  78.               for(i = 0; i < par; i++)
  79.               {
  80.                    printf_debug("ptr[%d] = %d\\r\\n", i, ptr[i]);
  81.               }
  82.               break;
  83.      }
  84.    
  85.      return (0);
  86. }
  87. /*
  88. *********************************************************************************************************
  89. *    函 数 名: TCP_StatusCheck
  90. *    功能说明: 检测TCP的连接状态,主要用于网线插拔的判断
  91. *    形    参: 无
  92. *    返 回 值: __TRUE  连接
  93. *             __FALSE 断开
  94. *********************************************************************************************************
  95. */
  96. uint8_t TCP_StatusCheck(void)
  97. {
  98.      uint8_t res;
  99.    
  100.      switch (tcp_get_state(socket_tcp))
  101.      {
  102.          case TCP_STATE_FREE:
  103.          case TCP_STATE_CLOSED:
  104.               res = tcp_listen (socket_tcp, PORT_NUM);
  105.               printf_debug("tcp listen res = %d\\r\\n", res);
  106.               break;
  107.         
  108.          case TCP_STATE_LISTEN:
  109.               break;
  110.         
  111.          case TCP_STATE_CONNECT:
  112.               return (__TRUE);
  113.             
  114.          default:
  115.               break;
  116.      }
  117.    
  118.      return (__FALSE);
  119. }
  120. /*
  121. *********************************************************************************************************
  122. *    函 数 名: TCPnetTest
  123. *    功能说明: TCPnet应用
  124. *    形    参: 无
  125. *    返 回 值: 无
  126. *********************************************************************************************************
  127. */
  128. void TCPnetTest(void)
  129. {
  130.      int32_t iCount;
  131.      uint8_t *sendbuf;
  132.      uint8_t tcp_status;
  133.      uint16_t maxlen;
  134.      uint8_t res;
  135.      const TickType_t xTicksToWait = 2; /* 延迟2ms */
  136.      EventBits_t uxBits;
  137.    
  138.      /*
  139.         创建TCP Socket并创建监听,客户端连接服务器后,10秒内无数据通信将断开连接。
  140.         但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。
  141.      */
  142.     socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);
  143.      if(socket_tcp != 0)
  144.      {
  145.          res = tcp_listen (socket_tcp, PORT_NUM);
  146.          printf_debug("tcp listen res = %d\\r\\n", res);
  147.      }
  148.    
  149.      while (1)
  150.      {
  151.          /* RL-TCPnet处理函数 */
  152.          main_TcpNet();
  153.         
  154.          /* 用于网线插拔的处理 */
  155.          tcp_status = TCP_StatusCheck();
  156.          /* 等待所有任务发来事件标志 */
  157.          uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件标志组句柄 */
  158.                                       0xFFFF,        /* 等待0xFFFF某一位被设置 */
  159.                                       pdTRUE,/* 退出前0xFFFF位被清除,这里是任意0xFFFF位被设置就“退出”*/
  160.                                       pdFALSE,       /* 设置为pdTRUE表示等待0xFFFF任意位被设置*/
  161.                                        xTicksToWait);/* 等待延迟时间 */
  162.          if((uxBits != 0)&&(tcp_status == __TRUE))
  163.          {
  164.               switch (uxBits)
  165.               {
  166.                    /* 接收到K1键按下,给远程TCP客户端发送8字节数据 */
  167.                    case KEY1_BIT0:              
  168.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  169.                        iCount = 8;
  170.                        do
  171.                        {
  172.                             main_TcpNet();
  173.                             if (tcp_check_send (socket_tcp) == __TRUE)
  174.                             {
  175.                                  maxlen = tcp_max_dsize (socket_tcp);
  176.                                  iCount -= maxlen;
  177.                                 
  178.                                  if(iCount < 0)
  179.                                  {
  180.                                      /* 这么计算没问题的 */
  181.                                      maxlen = iCount + maxlen;
  182.                                  }
  183.                                 
  184.                                  sendbuf = tcp_get_buf(maxlen);
  185.                                  sendbuf[0] = '1';
  186.                                  sendbuf[1] = '2';
  187.                                 sendbuf[2] = '3';
  188.                                  sendbuf[3] = '4';
  189.                                  sendbuf[4] = '5';
  190.                                  sendbuf[5] = '6';
  191.                                  sendbuf[6] = '7';
  192.                                  sendbuf[7] = '8';
  193.                                 
  194.                                  /* 测试发现只能使用获取的内存 */
  195.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  196.                             }
  197.                            
  198.                        }while(iCount > 0);
  199.                        break;
  200.                    /* 接收到K2键按下,给远程TCP客户端发送1024字节的数据 */
  201.                    case KEY2_BIT1:      
  202.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  203.                        iCount = 1024;
  204.                        do
  205.                        {
  206.                             main_TcpNet();
  207.                             if (tcp_check_send (socket_tcp) == __TRUE)
  208.                             {
  209.                                  maxlen = tcp_max_dsize (socket_tcp);
  210.                                  iCount -= maxlen;
  211.                                 
  212.                                  if(iCount < 0)
  213.                                  {
  214.                                      /* 这么计算没问题的 */
  215.                                      maxlen = iCount + maxlen;
  216.                                  }
  217.                                 
  218.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  219.                                  sendbuf = tcp_get_buf(maxlen);
  220.                                  sendbuf[0] = 'a';
  221.                                  sendbuf[1] = 'b';
  222.                                  sendbuf[2] = 'c';
  223.                                  sendbuf[3] = 'd';
  224.                                  sendbuf[4] = 'e';
  225.                                  sendbuf[5] = 'f';
  226.                                  sendbuf[6] = 'g';
  227.                                  sendbuf[7] = 'h';
  228.                                 
  229.                                  /* 测试发现只能使用获取的内存 */
  230.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  231.                             }
  232.                            
  233.                        }while(iCount > 0);                     
  234.                        break;
  235.                      
  236.                    /* 接收到K3键按下,给远程TCP客户端发送5MB数据 */
  237.                    case KEY3_BIT2:              
  238.                        printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
  239.                        iCount = 5*1024*1024;
  240.                        do
  241.                        {
  242.                             main_TcpNet();
  243.                             if (tcp_check_send (socket_tcp) == __TRUE)
  244.                             {
  245.                                  maxlen = tcp_max_dsize (socket_tcp);
  246.                                  iCount -= maxlen;
  247.                                 
  248.                                  if(iCount < 0)
  249.                                  {
  250.                                       /* 这么计算没问题的 */
  251.                                      maxlen = iCount + maxlen;
  252.                                  }
  253.                                 
  254.                                  /* 这里仅初始化了每次所发送数据包的前8个字节 */
  255.                                  sendbuf = tcp_get_buf(maxlen);
  256.                                  sendbuf[0] = 'a';
  257.                                  sendbuf[1] = 'b';
  258.                                  sendbuf[2] = 'c';
  259.                                  sendbuf[3] = 'd';
  260.                                  sendbuf[4] = 'e';
  261.                                  sendbuf[5] = 'f';
  262.                                  sendbuf[6] = 'g';
  263.                                  sendbuf[7] = 'h';
  264.                                 
  265.                                  /* 测试发现只能使用获取的内存 */
  266.                                  tcp_send (socket_tcp, sendbuf, maxlen);
  267.                             }
  268.                            
  269.                        }while(iCount > 0);
  270.                        break;
  271.                   
  272.                     /* 其他的键值不处理 */
  273.                    default:                    
  274.                        break;
  275.               }
  276.          }
  277.      }
  278. }
复制代码
至此,FreeRTOS方式移植的RL-TCPnet就可以运行了。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-25 16:42:34 | 显示全部楼层
10.3.6 RL-TCPnet实验测试和实验现象


       测试前,先将开发板上面的DM9161/9162网口通过网线接到路由器或者交换机上面。
RJ45网络变压器插座上绿灯和黄灯现象
        各种网卡、交换机等网络设备都不一样,一般来讲:绿灯分为亮或不亮(代表网络速度),黄灯分为闪烁或不闪烁(代表是否有数据收发)。
        绿灯:长亮代表100M; 不亮代表10M。
        黄灯:长亮代表无数据收发; 闪烁代表有数据收发。
也有些千兆网卡的灯以颜色区分,不亮代表10M / 绿色代表100M / 黄色代表1000M。现在10M的网络基本看不到了,如果一个灯长亮,基本可以说明100M网络或更高,而另一个灯时而闪烁,那代表有数据收发,具体要看你的网络设备了。甚至有些低等网卡如TP-LINK,只有一个灯,亮代表连通,闪烁代表数据收发。
        对于STM32F429开发板上面的RJ45网络变压器插座上面的灯而言,绿灯代表数据收发,长亮的话表示无数据收发,闪烁代表有数据收发。黄灯代表网络速度,长亮代表100M,不亮代表10M
底层驱动执行情况
        为了验证RL-TCPnet底层驱动接口函数是否有问题,专门在ETH_STM32F4xx.c文件中配置了串口调试打印函数:
  1. /*
  2. *********************************************************************************************************
  3. *                                      用于本文件的调试
  4. *********************************************************************************************************
  5. */
  6. #if 1
  7.      #define printf_eth printf
  8. #else
  9.      #define printf_eth(...)
  10. #endif
复制代码
如果底层驱动正常执行了,打印的效果如下:
10.35.png

ping是否正确
        ping命令的主要作用是通过发送数据包并接收应答信息来检测两台设备之间的网络是否连通。ping命令成功说明当前主机与目的主机之间存在连通的路径。如果不成功,需要查看网线是否连通、网卡设置是否正确、IP地址是否可用等。测试方法如下:
(1)WIN+R组合键打开“运行”窗口,输入cmd。
10.36.png

(2)弹出的命令窗口中,输入ping armfly,因为在前面的配置中我们使能了NetBIOS局域网域名,并将名字设置为armfly,而且使能了DHCP,通过ping命令还可以获得板子自动获取的IP地址。
10.37.png

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


        收发相当,没有数据丢失,说明ping命令也是成功的。
电脑端创建一个TCP Client与板子上面的TCP Server通信
        具体测试方法,查看第13章的13.6小节即可,因为配套例子实现的功能是一样的。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2017-10-25 16:42:56 | 显示全部楼层
10.4 总结

        本章节为大家讲解了RL-TCPnet网络协议栈的FreeRTOS操作系统移植方法,移植相对比较简单。另一个重要内容是Net_Config.c配置向导文件的说明,这个比较重要,初学者要好好熟悉下。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-4-30 00:14 , Processed in 0.220035 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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