STM32 FreeRTOS高级应用,动态创建通道/流

本文介绍了一种在FreeRTOS环境下实现数据通道的方法,通过创建动态任务处理不同I/O口间的数据流转。利用队列传输数据,并通过回调函数处理各种异常情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      今天介绍个有意思的点子,在FreeRtos中实现通道/流概念。什么意思呢?比如一个系统有3个数据I/O口,运行时可能每个口都有数据流入,每个口可能需要其它口的数据(比如转发)。那这个时候内,定死的队列传输不是那么合适,需要动态的创建任务。所以今天借鉴通道/流的方式,进行本次实践。

指导思想:一个任务模板,需要传递任务参数。模板实现了从队列1获取成员加入队列2中。成员中有一项属性指示该任务结束(删除)。

首先,队列成员:

typedef enum
{
	MSG_TYPE_DATA_TRANS,
	MSG_TYPE_DATA_TRANS_END,//通道结束标志
        MSG_TYPE_DATA_UNDEFINE,
}LinkType;
typedef struct {
	LinkType linkType;
        unsigned char pcMessage[MAX_MSG_LENGTH];
}Message_t;

再次,任务参数(也就是通道里队列相关配置)

typedef struct DataChannel{
	DataChannelIndexDef index;//通道配置列表序列号
	char* name;//通道命名
	xQueueHandle *In;//输入队列地址
	xQueueHandle *Out;//输出队列地址
	uint32_t Inwaittime;//输入超时时间
	uint32_t Outwaittime;//输出超时时间
	void (*CpltCallBack)(struct DataChannel*);
	void (*ErrorCallback)(struct DataChannel*);
	void (*InTimeoutCallback)(struct DataChannel*);
	void (*OutTimeoutCallback)(struct DataChannel*);
}DataChannel_t;
typedef enum{
	CHANNEL_xx2xx,
	CHANNEL_MAX,
}DataChannelIndexDef;

And,任务模板

void vDataChannelTask(void const *argument)
{
	DataChannel_t *channel;
	channel = (DataChannel_t*)argument;
	Message_t mMessage;
	
	for( ;; ){
		LOG(LOG_INFO, "vDataChannelTask [%s] start\n", channel->name);
		while(1){
			if(pdPASS == xQueueReceive(*(channel->In), &mMessage, channel->Inwaittime)){
				if(mMessage.linkType == MSG_TYPE_DATA_TRANS)
				{
					
					if(pdFAIL == xQueueSend(*(channel->Out), &mMessage, channel->Outwaittime)){
						xQueueSend(*(channel->Back), &mMessage, osWaitForever);
						DataChannel_OutTimeoutCallback(channel);//输出队列插入超时
						break;
					}
				}
				else if(mMessage.linkType == MSG_TYPE_DATA_TRANS_END){
					if(pdPASS == xQueueSend(*(channel->Out), &mMessage, channel->Outwaittime)){
						DataChannel_CpltCallback(channel);
						break;
					}
					else{
						//if failed push current data to back
						xQueueSend(*(channel->Back), &mMessage, osWaitForever);
						DataChannel_OutTimeoutCallback(channel);//输出队列等待超时
						break;
					}
					
				}
				else{
					DataChannel_ErrorCallback(channel);
					break;
				}
			}
			else{
				DataChannel_InTimeoutCallback(channel);//输入队列等待超时
				break;
			}
		}
		LOG(LOG_INFO, "vDataChannelTask [%s] end\n", channel->name);
		osSemaphoreWait(binSemTaskCountHandle, osWaitForever);
		--channelTaskList[channel->index].count;//通道计数减一
		if(channelTaskList[channel->index].count == 0){//无新的请求,则结束任务,否则继续
			osSemaphoreRelease(binSemTaskCountHandle);
			osThreadTerminate(NULL);
			LOG(LOG_INFO, "vDataChannelTask [%s] end\n", channel->name);			
		}
		else
			osSemaphoreRelease(binSemTaskCountHandle);
		
	}


}
上面的任务模板中,涉及到了一个问题,就是多次请求数据通道的问题。所以引入了一个计数,来保证通道的无缝衔接。
void ChannelTaskCountIncreace(DataChannelIndexDef index)
{
	if(index >= CHANNEL_MAX)
		return;
	osSemaphoreWait(binSemTaskCountHandle, osWaitForever);
	++channelTaskList[index].count;
	osSemaphoreRelease(binSemTaskCountHandle);	
}

void ChannelTaskCountDecreace(DataChannelIndexDef index)
{
	if(index >= CHANNEL_MAX)
		return;
	osSemaphoreWait(binSemTaskCountHandle, osWaitForever);
	--channelTaskList[index].count;
	osSemaphoreRelease(binSemTaskCountHandle);
}

uint32_t GetChannelTaskCount(DataChannelIndexDef index)
{
	uint32_t count;
	if(index >= CHANNEL_MAX)
		return 0;
	osSemaphoreWait(binSemTaskCountHandle, osWaitForever);
	count = channelTaskList[index].count;
	osSemaphoreRelease(binSemTaskCountHandle);
	return count;
}

callback,钩子函数

void DataChannel_CpltCallback(DataChannel_t *channel)
{
	LOG(LOG_INFO, "DataChannel [%s] CpltCallback\n", channel->name);
	if(channel->CpltCallBack)
		channel->CpltCallBack(channel);
}

void DataChannel_ErrorCallback(DataChannel_t *channel)
{
	LOG(LOG_INFO, "DataChannel [%s] ErrorCallback\n", channel->name);
	if(channel->ErrorCallback)
		channel->ErrorCallback(channel);
}

void DataChannel_InTimeoutCallback(DataChannel_t *channel)
{
	LOG(LOG_INFO, "DataChannel [%s] InTimeoutCallback\n", channel->name);
	if(channel->InTimeoutCallback)
		channel->InTimeoutCallback(channel);
}

void DataChannel_OutTimeoutCallback(DataChannel_t *channel)
{
	LOG(LOG_INFO, "DataChannel [%s] OutTimeoutCallback\n", channel->name);
	if(channel->OutTimeoutCallback)
		channel->OutTimeoutCallback(channel);
}

通道的配置示例

DataChannel_t dataChanel[] = {
	//xx2xx
	{
		.index = CHANNEL_xx2xx,
		.name = "xx2xx",
		.In = &xQueuexxRecv,
		.Out = &xQueuexxSend,
		.Inwaittime = osWaitForever,
		.Outwaittime = 1000,
		.CpltCallBack = NULL,
		.ErrorCallback = xx2xx_ERROR_TIMEOUT,
		.InTimeoutCallback = xx2xx_ERROR_TIMEOUT,
		.OutTimeoutCallback = xx2xx_ERROR_TIMEOUT,
	},
};
void xx2xx_ERROR_TIMEOUT(DataChannel_t *channel)
{
     //I'm out
}

事件驱动行创建任务

void ScheduleChannel_Process(uint32_t v)
{
	switch(v)
	{
		//usb -> bluetooth
		case CHANNEL_xx2xx:
			if(GetChannelTaskCount(CHANNEL_xx2xx) == 0){//该通道不存在时创建
				osThreadCreate(channelTaskList[CHANNEL_xx2xx].thread, &dataChanel[CHANNEL_xx2xx]);
			}
			ChannelTaskCountIncreace(CHANNEL_xx2xx);
			break;

		default:
			LOG(LOG_ERROR, "Datachannel invalid channel!!!\n");
			break;
	}
}

void vScheduleChannelTask(void const *argument)
{
	osEvent event;

	for(;;)
	{
		event = osMessageGet(ScheduleChannelEvent, osWaitForever );
		
		if( event.status == osEventMessage )
		{
			ScheduleChannel_Process(event.value.v);
		}
	}

}

任务相关的全局定义

//if you add code at vDataChannelTask,please make sure stack size is big enough
osThreadDef(xx2xxTask, 	vDataChannelTask, osPriorityNormal, 0, 156);

typedef struct{
	uint32_t count; //引用计数
	const osThreadDef_t *thread;
}TaskCreateList_t;

TaskCreateList_t channelTaskList[CHANNEL_MAX]={
	{		0,		osThread(xx2xxTask),		},
};

队列,锁初始化

void ScheduleChannelServerStart()
{
	xQueuexxSend = xQueueCreate(1, sizeof(Message_t));
	xQueuexxRecv = xQueueCreate(1, sizeof(Message_t));
	
	//cmsis_os中的定义osMessageQDef
	osMessageQDef(SC_Queue, 10, uint16_t);
	ScheduleChannelEvent = osMessageCreate (osMessageQ(SC_Queue), NULL);
	
	osSemaphoreDef(binSemTaskCount);
	binSemTaskCountHandle = osSemaphoreCreate( osSemaphore(binSemTaskCount), 1);	
	
	osThreadDef(ScheduleChannelTask, vScheduleChannelTask, osPriorityNormal, 0, 128);
	osThreadId ScheduleChannelTaskHandle = osThreadCreate(osThread(ScheduleChannelTask), NULL);
	
}

只需在其它任务中这么做:

osMessagePut(ScheduleChannelEvent, CHANNEL_xx2xx, 0);
即可创建通道,想要结束通道请记得,向输入队列中发送MSG_TYPE_DATA_TRANS_END的message。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值