迅维网
标题:
【NUCLEO-F412ZG试用体验】跑个RTOS
[打印本页]
作者:
バ幸福De右岸
时间:
2016-12-19 13:59
标题:
【NUCLEO-F412ZG试用体验】跑个RTOS
又是一周过去了,这周使用F412跑个RTOS,性能绝对杠杠的,不过这次改成了标准库,整个软件工程的结构也进行了调整。1MB的flash可以做很多事情。只不过有一点遗憾,就是这个flash的SECTOR太大,用来模拟eeprom有点浪费,也浪费内存。从最开始玩了一下uCOS,对实时操作系统也有一个简单的入门,不过当时虽然听说有FreeRTOS这么一个轻便的实时内核,但是由于编程能力有限,再加上思维上的错误,导致刚接触就退缩了。后来出于好奇,再次开始捣鼓这个实时内核,不过这次有点不同,主要是思维上的转变,开始不再深入内核源码,而是直接使接口函数,把它当个工具来用,后来熟悉了,遇到问题了再稍微深入源码看一下。前期还是要多看一些资料。
FreeRTOS的移植要比uCOS简单,重点就在FreeRTOSConfig.h这个文件中进行配置,对内核进行裁剪。
既然有了内核,那么就应该好好利用内核提供的接口函数。内核固然很好,但是过分依赖于内核,会导我们的代码过分依赖于内核,降低了可移植性。所以需要尽量做一些
封装
,先对信号量做一些封装,这也是学了一下lwip协议栈。
先定义一下信号量类型。
typedef struct{
int count;//初始值为0,表示当前已被占用信号量的个数。
void *sem;//信号量。
}os_sem_t;
[color=rgb(51, 102, 153) !important]
复制代码
下面是信号量的两个操作函数。
int os_sem_get(os_sem_t*sem,int count);//获取信号量,count是创建信号量时的信号量总数,这个必须确定,这个地方做的不太好。
int os_sem_del(os_sem_t*sem);//用完之后需要释放信号量
[color=rgb(51, 102, 153) !important]
复制代码
接下来是这两个函数的具体实现,os_sem_get在获取信号量时,如果不存在,则会创建,这也是其参数count的作用。os_sem_del在释放信号量时,如果信号量已全部释放,则会自动释放信号量暂用的内存空间。
int os_sem_get(os_sem_t*sem,int count)
{
BaseType_t xResult;
if(sem->sem==NULL)
{
sem->count=0;
sem->sem = pvPortMalloc(sizeof(SemaphoreHandle_t));//首次使用,需要创建
if(sem->sem==NULL)
return -1;
sem->sem = xSemaphoreCreateCoun
ti
ng(count, count);
}
if(sem->sem==NULL)
return -2;
xResult = xSemaphoreTake(sem->sem, portMAX_DELAY);
if(xResult!=pdTRUE)
return -3;
sem->count++;
return 0;
}
<blockquote>int os_sem_del(os_sem_t*sem)
[color=rgb(51, 102, 153) !important]
复制代码
接下来就使用这两个函数把printf函数封装成线程安全的print函数。关键就是调用printf之前先获取信号量,获取成功后再执行printf,执行完后要释放信号量,不然下次就无法再次执行printf。这里再插一句,由于printf这里是重映射到串口,所以,应该建立一个二值信号量,保证串口的发送函数顺序不会出错。所以这里再次对这个信号量进行封装,如下,注意应该首先建立一个com_tx_sem信号量。
#define com_get_sem() os_sem_get(&com_tx_sem, 1)
#define com_del_sem() os_sem_del(&com_tx_sem)
[color=rgb(51, 102, 153) !important]
复制代码
下面对printf进行封装。printf是一个参数个数可变的函数,暂时也就是仿照网上的方法,使用,printf内部的具体实现没有深入了解。封装之后就可以使用了。
int os_print(const char *fmt, ...)
{
int res = -1;
com_get_sem();
va_list args; //定义va_list类型变量,用来存储单个参数
va_start(args, fmt); //使用args指向可变参数的第一个参数
res = printf(fmt, args); //直接传给printf
va_end(args); //结束可变参数的获取
com_del_sem();
return res;
}
[color=rgb(51, 102, 153) !important]
复制代码
虽然互斥信号量可以解决优先级反转的问题,但是这需要暂时提升低优先级任务的优先级,在学习FreeRTOS的时候了解到,可以使用守护任务,对于共享资源,使用一个任务进行操作,其他任务通过这个任务间接的使用共享资源,这样就不会有优先级反转的问题了,这样的结构确实挺好。
这里就是使用一个任务来打印字符,另外建立两个任务,向这个任务发送数据,通过这个任务把数据打印到串口终端上。任务之前的数据传送是通过os_printstr函数向队列中发送数据。再专门建立一个任务来处理队列中的数据。先贴个效果图。
登录/注册后看高清大图
接下来看一下处理数据的具体实现,又是贴代码,见谅了。
int os_printstr(const char *str)
{
portBASE_TYPE xStatus;
int ret=-1;
if(pxPrintQueue==NULL)
{
pxPrintQueue = xQueueCreate(5, sizeof(int));
}
xStatus=xQueueSendToBack(pxPrintQueue, &str, portMAX_DELAY);
if(xStatus!=pdPASS)
{
ret = os_print("Could not send to printstr queue.\r\n");
}
return ret;
}
void vTaskPrint(void *pvParameter)
{
portBASE_TYPE xStatus;
char *str = NULL;
os_print("print task startup!\r\n");
while(1)
{
if(pxPrintQueue!=NULL)
{
xStatus = xQueueReceive(pxPrintQueue, &str, portMAX_DELAY);
if(xStatus==pdPASS)
os_print(str);
else
os_print("Could not receive from printstr queue.\r\n");
}
vTaskDelay(10);
}
}
[color=rgb(51, 102, 153) !important]
复制代码
接下来是向队列中发送数据的任务。这个就很简单了,使用xTaskCreate建立两个任务,仅仅传入参数不一样。
void vTaskPrintDemo(void *pvParameter)
{
while(1)
{
os_printstr((const char *)pvParameter);
vTaskDelay(1000);
}
}
[color=rgb(51, 102, 153) !important]
复制代码
欢迎光临 迅维网 (https://www.chinafix.com/)
Powered by Discuz! X3.4