今天介绍个有意思的点子,在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。