|
楼主 |
发表于 2017-10-24 15:45:49
|
显示全部楼层
10.2.5 RL-TCPnet应用实例
为了验证移植的RL-TCPnet是否可以使用,需要添加测试代码。下面是编写的测试代码,配套的测试例子完整版是:V5-1003_RL-TCPnet实验_工程移植模板(FreeRTOS)。
u FreeRTOS操作系统创建的任务
经过上面的移植和配置之后,在 main.c 文件中添加如下代码,代码中创建了5个用户任务:
vTaskTaskUserIF 任务: 按键消息处理。
vTaskLED 任务: LED闪烁。
vTaskMsgPro 任务: 消息处理,这里用作按键检测。
vTaskTCPnet 任务: RL-TCPnet测试任务。
vTaskStart 任务: 启动任务,实现RL-TCPnet的时间基准更新。
具体代码如下:
- #include "includes.h"
-
-
-
- /*
- **********************************************************************************************************
- 函数声明
- **********************************************************************************************************
- */
- static void vTaskTaskUserIF(void *pvParameters);
- static void vTaskLED(void *pvParameters);
- static void vTaskMsgPro(void *pvParameters);
- static void vTaskTCPnet(void *pvParameters);
- static void vTaskStart(void *pvParameters);
- static void AppTaskCreate (void);
- static void AppObjCreate (void);
- static void App_Printf(char *format, ...);
-
-
- /*
- **********************************************************************************************************
- 变量声明
- **********************************************************************************************************
- */
- static TaskHandle_t xHandleTaskUserIF = NULL;
- static TaskHandle_t xHandleTaskLED = NULL;
- static TaskHandle_t xHandleTaskMsgPro = NULL;
- static TaskHandle_t xHandleTaskTCPnet = NULL;
- static TaskHandle_t xHandleTaskStart = NULL;
- static SemaphoreHandle_t xMutex = NULL;
- EventGroupHandle_t xCreatedEventGroup = NULL;
-
-
- /*
- *********************************************************************************************************
- * 函 数 名: main
- * 功能说明: 标准c程序入口。
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- int main(void)
- {
- /*
- 在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
- 这样做的好处是:
- 1. 防止执行的中断服务程序中有FreeRTOS的API函数。
- 2. 保证系统正常启动,不受别的中断影响。
- 3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
- 在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
- 和cpsie i是等效的。
- */
- __set_PRIMASK(1);
-
- /* 硬件初始化 */
- bsp_Init();
-
- /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
- 目中不要使用,因为这个功能比较影响系统实时性。
- 2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
- */
- vSetupSysInfoTest();
-
- /* 创建任务 */
- AppTaskCreate();
-
- /* 创建任务通信机制 */
- AppObjCreate();
-
- /* 启动调度,开始执行任务 */
- vTaskStartScheduler();
-
- /*
- 如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
- heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
- #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
- */
- while(1);
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskTaskUserIF
- * 功能说明: 接口消息处理。
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 1 (数值越小优先级越低,这个跟uCOS相反)
- *********************************************************************************************************
- */
- static void vTaskTaskUserIF(void *pvParameters)
- {
- uint8_t ucKeyCode;
- uint8_t pcWriteBuffer[500];
-
-
- while(1)
- {
- ucKeyCode = bsp_GetKey();
-
- if (ucKeyCode != KEY_NONE)
- {
- switch (ucKeyCode)
- {
- /* K1键按下,直接发送事件标志给任务vTaskTCPnet,设置bit0 */
- case KEY_DOWN_K1:
- App_Printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\\r\\n");
- xEventGroupSetBits(xCreatedEventGroup, KEY1_BIT0);
- break;
-
- /* K2键按下,直接发送事件标志给任务vTaskTCPnet,设置bit1 */
- case KEY_DOWN_K2:
- App_Printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\\r\\n");
- xEventGroupSetBits(xCreatedEventGroup, KEY2_BIT1);
- break;
-
- /* K3键按下,直接发送事件标志给任务vTaskTCPnet,设置bit2 */
- case KEY_DOWN_K3:
- App_Printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\\r\\n");
- xEventGroupSetBits(xCreatedEventGroup, KEY3_BIT2);
- break;
-
- /* 摇杆的OK键按下,打印任务执行情况 */
- case JOY_DOWN_OK:
- App_Printf("=================================================\\r\\n");
- App_Printf("任务名 任务状态 优先级 剩余栈 任务序号\\r\\n");
- vTaskList((char *)&pcWriteBuffer);
- App_Printf("%s\\r\\n", pcWriteBuffer);
-
- App_Printf("\\r\\n任务名 运行计数 使用率\\r\\n");
- vTaskGetRunTimeStats((char *)&pcWriteBuffer);
- App_Printf("%s\\r\\n", pcWriteBuffer);
- App_Printf("当前动态内存剩余大小 = %d字节\\r\\n", xPortGetFreeHeapSize());
- break;
-
- /* 其他的键值不处理 */
- default:
- break;
- }
- }
-
- vTaskDelay(20);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskLED
- * 功能说明: LED闪烁
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 2
- *********************************************************************************************************
- */
- static void vTaskLED(void *pvParameters)
- {
- TickType_t xLastWakeTime;
- const TickType_t xFrequency = 500;
-
- /* 获取当前的系统时间 */
- xLastWakeTime = xTaskGetTickCount();
-
- while(1)
- {
- bsp_LedToggle(2);
-
- /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
- vTaskDelayUntil(&xLastWakeTime, xFrequency);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskMsgPro
- * 功能说明: 消息处理,这里用作按键检测
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 3
- *********************************************************************************************************
- */
- static void vTaskMsgPro(void *pvParameters)
- {
- while(1)
- {
- /* 按键扫描 */
- bsp_KeyScan();
- vTaskDelay(10);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskTCPnet
- * 功能说明: RL-TCPnet测试任务
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 4
- *********************************************************************************************************
- */
- static void vTaskTCPnet(void *pvParameters)
- {
- while(1)
- {
- TCPnetTest();
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskStart
- * 功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 5
- *********************************************************************************************************
- */
- static void vTaskStart(void *pvParameters)
- {
- TickType_t xLastWakeTime;
- const TickType_t xFrequency = 100;
-
- /* 初始化RL-TCPnet */
- init_TcpNet ();
-
- /* 获取当前的系统时间 */
- xLastWakeTime = xTaskGetTickCount();
-
- while(1)
- {
- /* RL-TCPnet时间基准更新函数 */
- timer_tick ();
-
- /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
- vTaskDelayUntil(&xLastWakeTime, xFrequency);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskCreate
- * 功能说明: 创建应用任务
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppTaskCreate (void)
- {
- xTaskCreate( vTaskTaskUserIF, /* 任务函数 */
- "vTaskUserIF", /* 任务名 */
- 512, /* 任务栈大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 1, /* 任务优先级*/
- &xHandleTaskUserIF ); /* 任务句柄 */
-
-
- xTaskCreate( vTaskLED, /* 任务函数 */
- "vTaskLED", /* 任务名 */
- 512, /* stack大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 2, /* 任务优先级*/
- &xHandleTaskLED ); /* 任务句柄 */
-
- xTaskCreate( vTaskMsgPro, /* 任务函数 */
- "vTaskMsgPro", /* 任务名 */
- 512, /* 任务栈大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 3, /* 任务优先级*/
- &xHandleTaskMsgPro ); /* 任务句柄 */
-
- xTaskCreate( vTaskTCPnet, /* 任务函数 */
- "vTaskTCPnet", /* 任务名 */
- 512, /* 任务栈大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 4, /* 任务优先级*/
- &xHandleTaskTCPnet ); /* 任务句柄 */
-
-
- xTaskCreate( vTaskStart, /* 任务函数 */
- "vTaskStart", /* 任务名 */
- 512, /* 任务栈大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 5, /* 任务优先级*/
- &xHandleTaskStart ); /* 任务句柄 */
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppObjCreate
- * 功能说明: 创建任务通信机制
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppObjCreate (void)
- {
- /* 创建互斥信号量 */
- xMutex = xSemaphoreCreateMutex();
-
- if(xMutex == NULL)
- {
- /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
- }
-
- /* 创建事件标志组 */
- xCreatedEventGroup = xEventGroupCreate();
-
- if(xCreatedEventGroup == NULL)
- {
- /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: App_Printf
- * 功能说明: 线程安全的printf方式
- * 形 参: 同printf的参数。
- * 在C中,当无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void App_Printf(char *format, ...)
- {
- char buf_str[512 + 1];
- va_list v_args;
-
-
- va_start(v_args, format);
- (void)vsnprintf((char *)&buf_str[0],
- (size_t ) sizeof(buf_str),
- (char const *) format,
- v_args);
- va_end(v_args);
-
- /* 互斥信号量 */
- xSemaphoreTake(xMutex, portMAX_DELAY);
-
- printf("%s", buf_str);
-
- xSemaphoreGive(xMutex);
- }
复制代码 u 硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_Init
- * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void bsp_Init(void)
- {
- /*
- 由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
- 启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
-
- 系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
- */
- /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
-
- bsp_InitUart(); /* 初始化串口 */
- bsp_InitKey(); /* 初始化按键变量 */
- bsp_InitLed(); /* 初始LED指示灯端口 */
- }
复制代码 u RL-TCPnet功能测试
这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,主要功能是创建了一个TCP Server。
- #include "includes.h"
-
-
-
- /*
- *********************************************************************************************************
- * 用于本文件的调试
- *********************************************************************************************************
- */
- #if 1
- #define printf_debug printf
- #else
- #define printf_debug(...)
- #endif
-
-
- /*
- *********************************************************************************************************
- * 用于本文件的调试
- *********************************************************************************************************
- */
- #define PORT_NUM 1001 /* TCP服务器监听端口号 */
-
-
- /*
- *********************************************************************************************************
- * 变量
- *********************************************************************************************************
- */
- uint8_t socket_tcp;
-
-
- /*
- *********************************************************************************************************
- * 函 数 名: tcp_callback
- * 功能说明: TCP Socket的回调函数
- * 形 参: soc TCP Socket类型
- * evt 事件类型
- * ptr 事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址
- * par 事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号
- * 返 回 值:
- *********************************************************************************************************
- */
- U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)
- {
- char buf[50];
- uint16_t i;
-
- /* 确保是socket_tcp的回调 */
- if (soc != socket_tcp)
- {
- return (0);
- }
-
- switch (evt)
- {
- /*
- 远程客户端连接消息
- 1、数组ptr存储远程设备的IP地址,par中存储端口号。
- 2、返回数值1允许连接,返回数值0禁止连接。
- */
- case TCP_EVT_CONREQ:
- sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);
- printf_debug("IP:%s port:%d\\r\\n", buf, par);
- return (1);
-
- /* 连接终止 */
- case TCP_EVT_ABORT:
- break;
-
- /* Socket远程连接已经建立 */
- case TCP_EVT_CONNECT:
- printf_debug("Socket is connected to remote peer\\r\\n");
- break;
-
- /* 连接断开 */
- case TCP_EVT_CLOSE:
- printf_debug("Connection has been closed\\r\\n");
- break;
-
- /* 发送的数据收到远程设备应答 */
- case TCP_EVT_ACK:
- break;
-
- /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */
- case TCP_EVT_DATA:
- printf_debug("Data length = %d\\r\\n", par);
- for(i = 0; i < par; i++)
- {
- printf_debug("ptr[%d] = %d\\r\\n", i, ptr[i]);
- }
- break;
- }
-
- return (0);
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: TCP_StatusCheck
- * 功能说明: 检测TCP的连接状态,主要用于网线插拔的判断
- * 形 参: 无
- * 返 回 值: __TRUE 连接
- * __FALSE 断开
- *********************************************************************************************************
- */
- uint8_t TCP_StatusCheck(void)
- {
- uint8_t res;
-
- switch (tcp_get_state(socket_tcp))
- {
- case TCP_STATE_FREE:
- case TCP_STATE_CLOSED:
- res = tcp_listen (socket_tcp, PORT_NUM);
- printf_debug("tcp listen res = %d\\r\\n", res);
- break;
-
- case TCP_STATE_LISTEN:
- break;
-
- case TCP_STATE_CONNECT:
- return (__TRUE);
-
- default:
- break;
- }
-
- return (__FALSE);
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: TCPnetTest
- * 功能说明: TCPnet应用
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void TCPnetTest(void)
- {
- int32_t iCount;
- uint8_t *sendbuf;
- uint8_t tcp_status;
- uint16_t maxlen;
- uint8_t res;
- const TickType_t xTicksToWait = 2; /* 延迟2ms */
- EventBits_t uxBits;
-
- /*
- 创建TCP Socket并创建监听,客户端连接服务器后,10秒内无数据通信将断开连接。
- 但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。
- */
- socket_tcp = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);
- if(socket_tcp != 0)
- {
- res = tcp_listen (socket_tcp, PORT_NUM);
- printf_debug("tcp listen res = %d\\r\\n", res);
- }
-
- while (1)
- {
- /* RL-TCPnet处理函数 */
- main_TcpNet();
-
- /* 用于网线插拔的处理 */
- tcp_status = TCP_StatusCheck();
-
- /* 等待所有任务发来事件标志 */
- uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件标志组句柄 */
- 0xFFFF, /* 等待0xFFFF某一位被设置 */
- pdTRUE,/* 退出前0xFFFF位被清除,这里是任意0xFFFF位被设置就“退出”*/
- pdFALSE, /* 设置为pdTRUE表示等待0xFFFF任意位被设置*/
- xTicksToWait);/* 等待延迟时间 */
-
- if((uxBits != 0)&&(tcp_status == __TRUE))
- {
- switch (uxBits)
- {
- /* 接收到K1键按下,给远程TCP客户端发送8字节数据 */
- case KEY1_BIT0:
- printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
- iCount = 8;
- do
- {
- main_TcpNet();
- if (tcp_check_send (socket_tcp) == __TRUE)
- {
- maxlen = tcp_max_dsize (socket_tcp);
- iCount -= maxlen;
-
- if(iCount < 0)
- {
- /* 这么计算没问题的 */
- maxlen = iCount + maxlen;
- }
-
- sendbuf = tcp_get_buf(maxlen);
- sendbuf[0] = '1';
- sendbuf[1] = '2';
- sendbuf[2] = '3';
- sendbuf[3] = '4';
- sendbuf[4] = '5';
- sendbuf[5] = '6';
- sendbuf[6] = '7';
- sendbuf[7] = '8';
-
- /* 测试发现只能使用获取的内存 */
- tcp_send (socket_tcp, sendbuf, maxlen);
- }
-
- }while(iCount > 0);
- break;
-
- /* 接收到K2键按下,给远程TCP客户端发送1024字节的数据 */
- case KEY2_BIT1:
- printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
- iCount = 1024;
- do
- {
- main_TcpNet();
- if (tcp_check_send (socket_tcp) == __TRUE)
- {
- maxlen = tcp_max_dsize (socket_tcp);
- iCount -= maxlen;
-
- if(iCount < 0)
- {
- /* 这么计算没问题的 */
- maxlen = iCount + maxlen;
- }
-
- /* 这里仅初始化了每次所发送数据包的前8个字节 */
- sendbuf = tcp_get_buf(maxlen);
- sendbuf[0] = 'a';
- sendbuf[1] = 'b';
- sendbuf[2] = 'c';
- sendbuf[3] = 'd';
- sendbuf[4] = 'e';
- sendbuf[5] = 'f';
- sendbuf[6] = 'g';
- sendbuf[7] = 'h';
-
- /* 测试发现只能使用获取的内存 */
- tcp_send (socket_tcp, sendbuf, maxlen);
- }
-
- }while(iCount > 0);
- break;
-
- /* 接收到K3键按下,给远程TCP客户端发送5MB数据 */
- case KEY3_BIT2:
- printf_debug("tcp_get_state(socket_tcp) = %d\\r\\n", tcp_get_state(socket_tcp));
- iCount = 5*1024*1024;
- do
- {
- main_TcpNet();
- if (tcp_check_send (socket_tcp) == __TRUE)
- {
- maxlen = tcp_max_dsize (socket_tcp);
- iCount -= maxlen;
-
- if(iCount < 0)
- {
- /* 这么计算没问题的 */
- maxlen = iCount + maxlen;
- }
-
- /* 这里仅初始化了每次所发送数据包的前8个字节 */
- sendbuf = tcp_get_buf(maxlen);
- sendbuf[0] = 'a';
- sendbuf[1] = 'b';
- sendbuf[2] = 'c';
- sendbuf[3] = 'd';
- sendbuf[4] = 'e';
- sendbuf[5] = 'f';
- sendbuf[6] = 'g';
- sendbuf[7] = 'h';
-
- /* 测试发现只能使用获取的内存 */
- tcp_send (socket_tcp, sendbuf, maxlen);
- }
-
- }while(iCount > 0);
- break;
-
- /* 其他的键值不处理 */
- default:
- break;
- }
- }
- }
- }
复制代码 至此,FreeRTOS方式移植的RL-TCPnet就可以运行了。 |
|