FreeRTOS消息队列 传递结构体指针和结构体异同

原文链接:https://blog.csdn.net/weixin_44333597/article/details/107523343

FreeRTOS消息队列应用
https://blog.csdn.net/weixin_44333597/article/details/107725480

[VC]SendMessage和PostMessage发送消息(不同进程传递字符串
https://blog.csdn.net/slj_win/article/details/40795159?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.highlightwordscore&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.highlightwordscore

1 消息队列传递结构体指针和结构体异同

2 使用队列传递复合数据类型

接收方接收由多个发送源发送至队列中的消息时,需要判断数据的来源,使用方式:利用队列传递结构体,在结构体中包含数据值和数据来源信息。

typedef struct
{
    int  iValue;  //数据值
    int iMeaning; //数据来源信息
}xData;

3 利用队列传递数据的指针(处理速度更快)

3.1 指针指向的内存空间所有权必须明确

共享内存在其指针发送到队列之前,其内容只允许被发送任务访问;

共享内存指针从队列中被读出之后,其内容亦只允许被接收任务访问。

3.2 指针指向的内存空间必须有效

指针指向的内存空间是动态分配的,只应该有一个任务对其进行内存释放,当这段内存空间被释放后,就不应该有其他任务再次访问这段空间。

传递结构体的指针

//传递结构体的指针
/* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
xQueue2 = xQueueCreate(10, sizeof(struct Msg *));    //单元大小为一个指针变量的大小

//发送消息,实现结构体指针的传递 
MSG_T   *ptMsg;        //MSG_T为结构体声明
ptMsg = &g_tMsg;   /* 初始化结构体指针 */
// 初始化数组 
    ptMsg->ucMessageID = 0;
    ptMsg->ulData[0] = 0;
    ptMsg->usData[0] = 0;
//使用消息队列实现指针变量的传递 
 if(xQueueSend(xQueue2,                  /* 消息队列句柄 */
               (void *) &ptMsg,           // 发送结构体指针变量ptMsg的地址  “&”  取结构体指针的地址,传递指针
               (TickType_t)10) != pdPASS )
     
//接收消息,接收结构体的指针
MSG_T *ptMsg; //定义一个结构体指针
xResult = xQueueReceive(xQueue2,            /* 消息队列句柄 */
                       (void *)&ptMsg,      // 这里获取的是结构体的地址,类似于 char *a="stm";char *b;b=a 指针赋值,a和b指向同一个地址
                       (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
if(xResult == pdPASS)
            {
                /* 成功接收,并通过串口将数据打印出来 */
                printf("接收到消息队列数据ptMsg->ucMessageID = %d\r\n",ptMsg->ucMessageID);
                printf("接收到消息队列数据ptMsg->ulData[0] = %d\r\n", ptMsg->ulData[0]);
                printf("接收到消息队列数据ptMsg->usData[0] = %d\r\n", ptMsg->usData[0]);
            }     

传递结构体本身

//传递结构体本身
//创建一个消息队列
xQueue2 = xQueueCreate(10, sizeof(struct Msg));  //单员大小为结构体的大小  

//发送消息,实现结构体的传递 
MSG_T   ptMsg;       //MSG_T为结构体声明
//初始化数组
    ptMsg.ucMessageID = 0;
    ptMsg.ulData[0] = 0;
    ptMsg.usData[0] = 0;
 //使用消息队列实现指针变量的传递
 if(xQueueSend(xQueue2,                  /* 消息队列句柄 */
               (void *) &ptMsg,           // 发送结构体ptMsg的值,将值拷贝至队列中
               (TickType_t)10) != pdPASS )
     
//接收消息,接收结构体的值
MSG_T ptMsg; //定义一个结构体指针
xResult = xQueueReceive(xQueue2,            
                       (void *)&ptMsg,      // 这里获取的是结构体的值
                       (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */

if(xResult == pdPASS)
            {
                /* 成功接收,并通过串口将数据打印出来 */
                printf("接收到消息队列数据ptMsg.ucMessageID = %d\r\n",ptMsg.ucMessageID);
                printf("接收到消息队列数据ptMsg.ulData[0] = %d\r\n", ptMsg.ulData[0]);
                printf("接收到消息队列数据ptMsg.usData[0] = %d\r\n", ptMsg.usData[0]);
            }     

4 传递结构体指针和结构体本身的异同

  1. 创建消息队列时,单元大小声明不同

  2. 变量初始化不同,一个是定义指针,并所赋结构体实体是全局的,一个是定义结构体

  3. 数据输出不同,前者是用指针成员运算符“->”,后者是用结构体成员运算符

  4. 在发送消息和请求消息时,两者的格式是相同的,但意义不用,前者传递的是结构体指针的地址后者传递的是结构体的值

5 验证

5.1 结构体传值模式

在这里插入图片描述
tcpecho.h

#ifndef LWIP_TCPECHO_H
#define LWIP_TCPECHO_H

/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "queue.h"
#include "cc.h"

typedef struct _MSG_t 
{
	u8_t ucMessageID;
	u8_t data[20];	
}MSG_T;

//消息队列句柄,用户任务间进程通信,收发消息
extern QueueHandle_t Eth_Queue;
extern MSG_T g_tMsg;

#define	QUEUE_LEN	5  //队列的长度,最大可包含多少个消息
#define QUEUE_SIZE	21  //队列中每个消息大小(字节),MSG_T结构体大小

void tcpecho_init(void);

#endif /* LWIP_TCPECHO_H */

tcpecho.c

#include "tcpecho.h"

#include "lwip/opt.h"

#if LWIP_NETCONN

#include "lwip/sys.h"
#include "lwip/api.h"

QueueHandle_t Eth_Queue = NULL;
void *data_pbuf;
u8_t recv_data[1024];
MSG_T g_tMsg; //全局结构体变量


/*-----------------------------------------------------------------------------------*/
static void 
tcpecho_thread(void *arg)
{
  struct netconn *conn, *newconn;
  err_t err;
  LWIP_UNUSED_ARG(arg);

  /* Create a new connection identifier. */
  /* Bind connection to well known port number 7. */
#if LWIP_IPV6
  conn = netconn_new(NETCONN_TCP_IPV6);
  netconn_bind(conn, IP6_ADDR_ANY, LOCAL_PORT);
#else /* LWIP_IPV6 */
  conn = netconn_new(NETCONN_TCP);
  netconn_bind(conn, IP_ADDR_ANY, LOCAL_PORT);
#endif /* LWIP_IPV6 */
  LWIP_ERROR("tcpecho: invalid conn", (conn != NULL), return;);

  PRINTF("本地端口号是%d\n\n",LOCAL_PORT);
  
  /* Tell connection to go into listening mode. */
  netconn_listen(conn);

  BaseType_t xReturn = pdTRUE; //定义一个创建信息返回值,默认为pdTRUE

  MSG_T ptMsg; //MSG_T结构体声明

  while (1) {

    /* Grab new connection. */
    err = netconn_accept(conn, &newconn);
    /*printf("accepted new connection %p\n", newconn);*/
    /* Process the new connection. */
    if (err == ERR_OK) {
      struct netbuf *buf;
      void *data;
      u16_t len;
	  char str1[] = "netconn_write data123";
	  u16_t len1 = sizeof(str1);
      
      while ((err = netconn_recv(newconn, &buf)) == ERR_OK) {
        /*printf("Recved\n");*/
        do {
             //netbuf_data(buf, &data, &len);
			 netbuf_data(buf, &data_pbuf, &len);
			 //发送消息,实现结构体的值传递模式,不是结构体指针传递
			 //初始化结构体内容
			 ptMsg.ucMessageID = *((u8_t*)data_pbuf);
			 ptMsg.data[0] = *((u8_t*)data_pbuf+1);
			 ptMsg.data[1] = *((u8_t*)data_pbuf+2);
			 ptMsg.data[2] = *((u8_t*)data_pbuf+3);
			 ptMsg.data[3] = *((u8_t*)data_pbuf+4);
			 ptMsg.data[4] = *((u8_t*)data_pbuf+5);
			 ptMsg.data[5] = *((u8_t*)data_pbuf+6);
			 xReturn = xQueueSend(Eth_Queue, &ptMsg, 0); //发送结构体ptMsg的值,将值拷贝至消息队列中
			 if(pdPASS == xReturn)
			 	PRINTF("消息发送成功!\n");
			 
             err = netconn_write(newconn, data_pbuf, len, NETCONN_COPY);
		     err = netconn_write(newconn, str1, len1, NETCONN_COPY);
#if 0
            if (err != ERR_OK) {
              PRINTF("tcpecho: netconn_write: error \"%s\"\n", lwip_strerr(err));
            }
#endif
        } while (netbuf_next(buf) >= 0);
        netbuf_delete(buf);
      }
      /*printf("Got EOF, looping\n");*/ 
      /* Close connection and discard connection identifier. */
      netconn_close(newconn);
      netconn_delete(newconn);
    }
  }
}
/*-----------------------------------------------------------------------------------*/
void
tcpecho_init(void)
{
  sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, 512, 4);
}
/*-----------------------------------------------------------------------------------*/

#endif /* LWIP_NETCONN */

main.c

/**********************************************************************
  * @ 函数名  : Test2_Task
  * @ 功能说明: Test2_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Test2_Task(void* parameter)
{	 
	BaseType_t xReturn = pdTRUE; //定义一个创建信息返回值,默认为pdTRUE
	MSG_T ptMsg; //定义一个MSG_T接收结构体

  while (1)
  {
    LED2_TOGGLE;

	xReturn = xQueueReceive(Eth_Queue,      //消息队列的句柄
		                    &ptMsg,       //接收的消息内容
		                    portMAX_DELAY); //等待时间,一直等
	if(pdTRUE == xReturn){
		PRINTF("本次接收到的消息为:\n");
		
		PRINTF("消息内容为:%c %c %c %c %c %c\n",ptMsg.ucMessageID, ptMsg.data[0],ptMsg.data[1],
		                                ptMsg.data[2],ptMsg.data[3],ptMsg.data[4],ptMsg.data[5]);
		}
	else
		PRINTF("数据接收出错,错误代码: 0x%lx\n", xReturn);
	}
  	
}

5.2 传递结构体指针模式

在这里插入图片描述
tcpecho.h

#ifndef LWIP_TCPECHO_H
#define LWIP_TCPECHO_H

/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "queue.h"
#include "cc.h"

typedef struct _MSG_t 
{
	u8_t length;
	u8_t data[30];	
}MSG_T;

//消息队列句柄,用户任务间进程通信,收发消息
extern QueueHandle_t Eth_Queue;
extern MSG_T g_tMsg;

#define	QUEUE_LEN	5  //队列的长度,最大可包含多少个消息
#define QUEUE_SIZE	4  //队列中每个消息大小(字节),MSG_T结构体指针大小

void tcpecho_init(void);

#endif /* LWIP_TCPECHO_H */

tcpecho.c

#include "tcpecho.h"

#include "lwip/opt.h"

#if LWIP_NETCONN

#include "lwip/sys.h"
#include "lwip/api.h"

QueueHandle_t Eth_Queue = NULL;
void *data_pbuf;
u8_t recv_data[1024];
MSG_T g_tMsg; //全局结构体变量


/*-----------------------------------------------------------------------------------*/
static void 
tcpecho_thread(void *arg)
{
  struct netconn *conn, *newconn;
  err_t err;
  LWIP_UNUSED_ARG(arg);

  /* Create a new connection identifier. */
  /* Bind connection to well known port number 7. */
#if LWIP_IPV6
  conn = netconn_new(NETCONN_TCP_IPV6);
  netconn_bind(conn, IP6_ADDR_ANY, LOCAL_PORT);
#else /* LWIP_IPV6 */
  conn = netconn_new(NETCONN_TCP);
  netconn_bind(conn, IP_ADDR_ANY, LOCAL_PORT);
#endif /* LWIP_IPV6 */
  LWIP_ERROR("tcpecho: invalid conn", (conn != NULL), return;);

  PRINTF("本地端口号是%d\n\n",LOCAL_PORT);
  
  /* Tell connection to go into listening mode. */
  netconn_listen(conn);

  BaseType_t xReturn = pdTRUE; //定义一个创建信息返回值,默认为pdTRUE

  MSG_T* ptMsg = &g_tMsg; //MSG_T结构体声明

  while (1) {

    /* Grab new connection. */
    err = netconn_accept(conn, &newconn);
    /*printf("accepted new connection %p\n", newconn);*/
    /* Process the new connection. */
    if (err == ERR_OK) {
      struct netbuf *buf;
      void *data;
      u16_t len;
	  char str1[] = "netconn_write data123";
	  u16_t len1 = sizeof(str1);
      
      while ((err = netconn_recv(newconn, &buf)) == ERR_OK) {
        /*printf("Recved\n");*/
        do {
             //netbuf_data(buf, &data, &len);
			 netbuf_data(buf, &data_pbuf, &len);
			 //发送消息,实现结构体的指针传递模式
			 //初始化结构体内容
			 g_tMsg.length = len;
		     memcpy(g_tMsg.data, data_pbuf, len); //复制缓冲区数据
			 xReturn = xQueueSend(Eth_Queue, &ptMsg, 0); //发送结构体ptMsg的值,将值拷贝至消息队列中
			 if(pdPASS == xReturn)
			 	PRINTF("消息发送成功!\n");
			 
             err = netconn_write(newconn, data_pbuf, len, NETCONN_COPY);
		     err = netconn_write(newconn, str1, len1, NETCONN_COPY);
#if 0
            if (err != ERR_OK) {
              PRINTF("tcpecho: netconn_write: error \"%s\"\n", lwip_strerr(err));
            }
#endif
        } while (netbuf_next(buf) >= 0);
        netbuf_delete(buf);
      }
      /*printf("Got EOF, looping\n");*/ 
      /* Close connection and discard connection identifier. */
      netconn_close(newconn);
      netconn_delete(newconn);
    }
  }
}
/*-----------------------------------------------------------------------------------*/
void
tcpecho_init(void)
{
  sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, 512, 4);
}
/*-----------------------------------------------------------------------------------*/

#endif /* LWIP_NETCONN */

main.c

/**********************************************************************
  * @ 函数名  : Test2_Task
  * @ 功能说明: Test2_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Test2_Task(void* parameter)
{	 
	BaseType_t xReturn = pdTRUE; //定义一个创建信息返回值,默认为pdTRUE
	MSG_T* ptMsg; //定义一个MSG_T接收结构体指针

  while (1)
  {
    LED2_TOGGLE;

	xReturn = xQueueReceive(Eth_Queue,      //消息队列的句柄
		                    &ptMsg,       //接收的消息内容
		                    portMAX_DELAY); //等待时间,一直等
	if(pdTRUE == xReturn){
		PRINTF("本次接收到的消息为:\n");
		
	for (int i=0; i<ptMsg->length; i++)
		{
		PRINTF("%c ", ptMsg->data[i]);
		}
		PRINTF("\n\n");
		}
	else
		PRINTF("数据接收出错,错误代码: 0x%lx\n", xReturn);
	}
  	
}

5.3 memcpy 函数

memcpy函数是C/C++语言中的一个用于内存复制的函数,声明在 string.h 中(C++是 cstring)。其原型是:

void *memcpy(void *destin, void *source, unsigned n);

作用是:以source指向的地址为起点,将连续的n个字节数据,复制到以destin指向的地址为起点的内存中。
函数有三个参数,第一个是目标地址,第二个是源地址,第三个是数据长度。

使用memcpy函数时,需要注意:

数据长度(第三个参数)的单位是字节(1byte = 8bit)。
注意该函数有一个返回值,类型是void*,是一个指向destin的指针。

void *memcpy(void *dst, const void *src, size_t size)
{
    char *psrc;
    char *pdst;

    if (NULL == dst || NULL == src)
    {
        return NULL;
    }

    if ((src < dst) && (char *)src + size > (char *)dst) // 出现地址重叠的情况,自后向前拷贝
    {
        psrc = (char *)src + size - 1;
        pdst = (char *)dst + size - 1;
        while (size--)
        {
            *pdst-- = *psrc--;
        }
    }
    else
    {
        psrc = (char *)src;
        pdst = (char *)dst;
        while (size--)
        {
            *pdst++ = *psrc++;
        }
    }

    return dst;
}

memcpy函数复制的数据长度
使用memcpy函数时,特别要注意数据长度。如果复制的数据类型是char,那么数据长度就等于元素的个数。而如果数据类型是其他(如int, double, 自定义结构体等),就要特别注意数据长度的值。
好的习惯是,无论拷贝何种数据类型,都用 n * sizeof(type_name)的写法。

https://blog.csdn.net/GoodLinGL/article/details/114602721?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163981818916780255230930%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163981818916780255230930&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-114602721.pc_search_em_sort&utm_term=memcpy&spm=1018.2226.3001.4187

5.4 memset函数及其用法

定义变量时一定要进行初始化,尤其是数组和结构体这种占用内存大的数据结构。在使用数组的时候经常因为没有初始化而产生“烫烫烫烫烫烫”这样的野值,俗称“乱码”。

每种类型的变量都有各自的初始化方法,memset() 函数可以说是初始化内存的“万能函数”,通常为新申请的内存进行初始化工作。它是直接操作内存空间,mem即“内存”(memory)的意思。该函数的原型为:

include <string.h>
void *memset(void *s, int c, unsigned long n);

函数的功能是:将指针变量 s 所指向的前 n 字节的内存单元用一个“整数” c 替换,注意 c 是 int 型。s 是 void* 型的指针变量,所以它可以为任何类型的数据进行初始化。

memset() 的作用是在一段内存块中填充某个给定的值。因为它只能填充一个值,所以该函数的初始化为原始初始化,无法将变量初始化为程序中需要的数据。用memset初始化完后,后面程序中再向该内存空间中存放需要的数据。

memset 一般使用“0”初始化内存单元,而且通常是给数组或结构体进行初始化。一般的变量如 char、int、float、double 等类型的变量直接初始化即可,没有必要用 memset。如果用 memset 的话反而显得麻烦。

当然,数组也可以直接进行初始化,但 memset 是对较大的数组或结构体进行清零初始化的最快方法,因为它是直接对内存进行操作的。

这时有人会问:“字符串数组不是最好用’\0’进行初始化吗?那么可以用 memset 给字符串数组进行初始化吗?也就是说参数 c 可以赋值为’\0’吗?”

可以的。虽然参数 c 要求是一个整数,但是整型和字符型是互通的。但是赋值为 ‘\0’ 和 0 是等价的,因为字符 ‘\0’ 在内存中就是 0。所以在 memset 中初始化为 0 也具有结束标志符 ‘\0’ 的作用,所以通常我们就写“0”。

memset 函数的第三个参数 n 的值一般用 sizeof() 获取,这样比较专业。注意,如果是对指针变量所指向的内存单元进行清零初始化,那么一定要先对这个指针变量进行初始化,即一定要先让它指向某个有效的地址。而且用memset给指针变量如p所指向的内存单元进行初始化时,n 千万别写成 sizeof§,这是新手经常会犯的错误。因为 p 是指针变量,不管 p 指向什么类型的变量,sizeof§ 的值都是 4。

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值