当前位置:首页 > 嵌入式 > 嵌入式大杂烩
[导读]之前分享了很多关于freeRTOS的知识,那么我们怎么在实战中去写代码呢?本篇文章重在对基于freeRTOS的架构代码的解析。



关注、星标公众号 ,直达精彩内容

ID:技术让梦想更伟大

作者:李肖遥


之前分享了很多关于freeRTOS的知识,那么我们怎么在实战中去写代码呢?本篇文章重在对基于freeRTOS的架构代码的解析。整个功能如下图:


为什么要用freeRTOS

在实际项目中,如果程序等待一个超时事件,传统的无RTOS情况下,就只能在原地等待而不能执行其它任务,如果使用RTOS,则可以很方便的将当前任务阻塞在该事件下,然后自动去执行别的任务,这样可以高效的利用CPU了。

一般使用情况

我们在开发的时候,我总是在main函数看到以下的代码,这让我感觉不是很爽

int main()
{
  xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
  xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
  xTaskCreate( vTask3, "Task 3", 1000, NULL, 2, NULL );

  vTaskStartScheduler();

  while(1);
}

然后在每个task中,一般代码会这样写

void vTask1( void *pvParameters )
{
  volatile unsigned long ul;
  for( ;; )
  {
    xQueueSend( USART1_MSGQ, "task 1 !\n",portMAX_DELAY);
    for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ );
  }
}

而任务之间的通信也是比较繁琐,总体来说,代码不易维护,增减一个任务的话要改的东西太多了。为此我特意设计一个框架,可以很方便的增减任务,同时任务之间通过事件队列来通信。

demo

任务创建函数的封装

我们首先定义两个任务,把所有任务信息封装在taskRecord里,并且申明如下:

#define TASK_NUM 2
//所有任务的信息
static TaskRecord taskRecord[TASK_NUM];

那么TaskRecord怎么安排呢,我们把所有的任务信息都放在结构体里。其中包括任务ID,任务任务函数taskFucn,任务名字,栈的大小stackDep,还有优先级prio,任务句柄taskHandle,任务队列queue

typedef struct
{
 int16_t Id;
 TaskFunction_t taskFucn;
 const char *  name;
 configSTACK_DEPTH_TYPE stackDep;
 void *  parameters;
 UBaseType_t prio;
 TaskHandle_t taskHandle;
 QueueHandle_t  queue;
} TaskRecord;

把任务中的一些参数封装起来,放在结构体TaskInitPara,其中包括了任务函数taskFucn,任务名字,栈的大小stackDep,还有优先级prio

typedef struct
{
  TaskFunction_t taskFucn;
  const char *  name;
  const configSTACK_DEPTH_TYPE stackDep;
  UBaseType_t prio;
} TaskInitPara;

我们做好了这些之后,就需要把结构体中的参数放到创建任务函数中,那么这个函数createTasks代码如下:

void createTasks(TaskRecord* taskRecord, const TaskInitPara* taskIniPara, int num){
 int i;
 for(i=0;i  taskRecord[i].Id = i;
  taskRecord[i].taskFucn = taskIniPara[i].taskFucn;
  taskRecord[i].name = taskIniPara[i].name;
  taskRecord[i].stackDep = taskIniPara[i].stackDep;
  taskRecord[i].parameters = &taskRecord[i];
  taskRecord[i].prio = taskIniPara[i].prio;
  
  xTaskCreate( taskRecord[i].taskFucn,
    taskRecord[i].name,
    taskRecord[i].stackDep,
    taskRecord[i].parameters,
    taskRecord[i].prio,
    &taskRecord[i].taskHandle );

  taskRecord[i].queue = xQueueCreate( 100, sizeof( Event ) );
 }
}

其中num为任务数量,先把任务信息放到初始化的taskRecord中,再把其中的信息创建任务。那么任务创建函数就做好了。

main函数

接着,在我们的main函数中,就不需要那么繁琐的一个一个的创建任务了,按照这个封装的main函数如下:

int main( void )
{
 createTasks(taskRecord,taskInitPara,TASK_NUM);
 /* Start the tasks and timer running. */
 vTaskStartScheduler();
}

任务间通信

首先我们想想,两个任务之间通信需要知道什么,task1想往task2的发送一些数据,那么需要知道task2的ID吧,需要把数据打包吧,task2需要知道是谁发的,那么task1本身的ID也需要知道吧。

按照这几个明确的东西,我们首先把任务事件ID枚举如下

typedef enum {
 eventID_1,
 eventID_2,
 eventID_3
}Event_ID;

然后把事件ID,发送者ID,以及要传输的结构或者数据打包封装在结构体Event中,代码如下:

typedef struct{
 Event_ID ID;
 int16_t src; //发送者ID
 void* pData; //传结构、数据
}Event;

接着,我们需要构造一个事件,把这些信息都放在这个事件中,代码如下:

void makeEvent(Event* pEvent,int16_t myId,Event_ID evtId, const void* pData){
 pEvent->ID = evtId;
 pEvent->src = myId;
 pEvent->pData = (void*) pData;
}

现在我们假设task2要往task1发送一系列数据,那么task任务中,我们需要做的事如下,获取task1中队列,看是否为空。

 QueueHandle_t task1Queue;
 int16_t myId = pMyTaskRecord->Id;
 task1Queue = getTaskQueue(getTaskId("task1"));

构造事件

 Event event;
 int* ptemp; //这里自定义一些数据
 makeEvent(&event,myId,eventID_1,(void*)ptemp);

然后把事件发送出去:

xQueueSendToBack( task1Queue, &event, 0);

对于task1来说,看队列中是否为空,如果有任务事件来,从队列中获取事件

 TaskRecord* pMyTaskRecord = (TaskRecord*)pPara;
 QueueHandle_t* evntQueue=pMyTaskRecord->queue;

当队列中确实有事件时,接收事件

BaseType_t status = xQueueReceive( *evntQueue, &event, portMAX_DELAY );
if( status == pdPASS )
{
  task1HandleEvent(event);
}
else
{
  printf"Task1 could not receive from the queue.\r\n" );}

然后我们在task1HandleEvent处理接收到的事件,代码如下:

void task1HandleEvent(Event event){
 xil_printf( "Task1 is processing event...\r\n" );
 int* p;
 switch(event.ID){
 case eventID_1:
  p= (int*) event.pData;
  xil_printf("ID=%d From: %d data=%d\r\n",event.ID, event.src,p[7]);
  free(event.pData);
  break;
 case eventID_2:
  break;
 default:
  break;
 }
}

上面代码表示根据事件ID来判断接收的是哪个事件,再把事件ID,数据等等打印出来。

外部中断通信

如果不是任务间的通信,而是有外部中断触发,需要与某个任务进行信息交互,怎么办?例如有一个以太网任务,当外部网络需要发送一个数据包到这个网络任务的时候,那么就需要进行外部通信了。同样我们这样做,在以太网接收函数中,构造事件

 Event event;
 int* ptemp; //这里自定义一些数据
 makeEvent(&event,myId,IntrID_1,(void*)ptemp);//可以再自定一些事件ID如IntrID_1

然后再发送到这个事件到这个任务中,如下

测试

如上,我们构造一个事件,发送一些数据如下

 Event event;
 int* ptemp = malloc(sizeof(int)*10);
 memset(ptemp,0x77,sizeof(int)*10);
 makeEvent(&event,myId,eventID_1,(void*)ptemp);

我们看到结果如下

task1接到来自任务ID为0,事件1的数据。这里每个任务的等待时间也是可以设置的,设置方法如下:

/* 设置最大等待时间500ms */
const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); 
BaseType_t status = xQueueReceive( *evntQueue, &event, xMaxBlockTime );

如果等待时间为portMAX_DELAY或者0的话,说明某个任务一直处于激活状态,比如task2,当等待时间为portMAX_DELAY时候,则测试结果如下:

所以每个任务设置的时间,优先级,栈大小都是很重要的,具体的就需要在项目中调试了。

最后总结

本篇是属于代码实战篇,对于freeRTOS的具体讲解需要大家自己去领会,我这里是写了一个架构,帮助大家在项目中去更好的搭好架子,当我们有很多任务的时候,任务间又有很多交互通信的时候,就更需要理解这种架构了。



猜你喜欢

C语言、嵌入式位操作精华技巧大汇总

认识认识#pragma、#error指令

C语言、嵌入式中几个非常实用的宏技巧

    
            

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

特朗普集团近日取消了其新推出的T1智能手机“将在美国制造”的宣传标语,此举源于外界对这款手机能否以当前定价在美国本土生产的质疑。

关键字: 特朗普 苹果 AI

美国总统特朗普在公开场合表示,他已要求苹果公司CEO蒂姆·库克停止在印度建厂,矛头直指该公司生产多元化的计划。

关键字: 特朗普 苹果 AI

4月10日消息,据媒体报道,美国总统特朗普宣布,美国对部分贸易伙伴暂停90天执行新关税政策,同时对中国的关税提高到125%,该消息公布后苹果股价飙升了15%。这次反弹使苹果市值增加了4000多亿美元,目前苹果市值接近3万...

关键字: 特朗普 AI 人工智能 特斯拉

3月25日消息,据报道,当地时间3月20日,美国总统特朗普在社交媒体平台“真实社交”上发文写道:“那些被抓到破坏特斯拉的人,将有很大可能被判入狱长达20年,这包括资助(破坏特斯拉汽车)者,我们正在寻找你。”

关键字: 特朗普 AI 人工智能 特斯拉

1月22日消息,刚刚,新任美国总统特朗普放出重磅消息,将全力支持美国AI发展。

关键字: 特朗普 AI 人工智能

特朗普先生有两件事一定会载入史册,一个是筑墙,一个是挖坑。在美墨边境筑墙的口号确保边境安全,降低因非法移民引起的犯罪率过高问题;在中美科技产业之间挖坑的口号也是安全,美国企业不得使用对美国国家安全构成威胁的电信设备,总统...

关键字: 特朗普 孤立主义 科技产业

据路透社1月17日消息显示,知情人士透露,特朗普已通知英特尔、铠侠在内的几家华为供应商,将要撤销其对华为的出货的部分许可证,同时将拒绝其他数十个向华为供货的申请。据透露,共有4家公司的8份许可被撤销。另外,相关公司收到撤...

关键字: 华为 芯片 特朗普

曾在2018年时被美国总统特朗普称作“世界第八奇迹”的富士康集团在美国威斯康星州投资建设的LCD显示屏工厂项目,如今却因为富士康将项目大幅缩水并拒绝签订新的合同而陷入了僵局。这也导致富士康无法从当地政府那里获得约40亿美...

关键字: 特朗普 富士康

今年5月,因自己发布的推文被贴上“无确凿依据”标签而与推特发生激烈争执后,美国总统特朗普签署了一项行政令,下令要求重审《通信规范法》第230条。

关键字: 谷歌 facebook 特朗普

众所周知,寄往白宫的所有邮件在到达白宫之前都会在他地进行分类和筛选。9月19日,根据美国相关执法官员的通报,本周早些时候,执法人员截获了一个寄给特朗普总统的包裹,该包裹内包含蓖麻毒蛋白。

关键字: 美国 白宫 特朗普
关闭