freertos使用demo

简介

本程序是将freertos移植在Linux中运行,目的是打造一种在LINUX 运行app的架构。

具体展示

#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"
#include "event_groups.h"
#include "semphr.h"

//事件组句柄
EventGroupHandle_t xEventGroup;
//二进制信号量句柄 
SemaphoreHandle_t xBinarySemaphore;
//互斥量句柄 
SemaphoreHandle_t xMutex;
//任务通知句柄
TaskHandle_t notifyTask;

/*"事件组中事件位的定义。 */
#define mainFIRST_TASK_BIT  ( 1UL << 0UL ) /* 事件位0,由任务设置。 */
#define mainSECOND_TASK_BIT ( 1UL << 1UL ) /* 事件位1,由任务设置。 */
#define mainISR_BIT         ( 1UL << 2UL ) /* 事件位2,由ISR设置。 */

static void vTask1( void *pvParameters );
static void vTask2( void *pvParameters );
static void vTask3( void *pvParameters );
static void vTask4( void *pvParameters );
static void vTask5( void *pvParameters );
static void vTask6( void *pvParameters );
static void vTask7( void *pvParameters );
static void vTask8( void *pvParameters );
static void vTask9( void *pvParameters );
int main()
{
	static xQueueHandle xMessageQueue;
	//创建消息队列用于任务间通讯
	xMessageQueue = xQueueCreate( 10, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) );

    //创建队列收发通讯任务
	xTaskCreate( vTask1, "vTask1", configMINIMAL_STACK_SIZE, ( void * ) &xMessageQueue, tskIDLE_PRIORITY, NULL );
	xTaskCreate( vTask2, "vTask2", configMINIMAL_STACK_SIZE, ( void * ) &xMessageQueue, tskIDLE_PRIORITY, NULL );

	/* 在使用事件组之前,必须先创建事件组。 */
    xEventGroup = xEventGroupCreate();

    //创建事件组任务
	xTaskCreate( vTask3, "vtask3", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
	xTaskCreate( vTask4, "vtask4", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
	
	//创建信号量任务
	xTaskCreate( vTask5, "vtask5", configMINIMAL_STACK_SIZE, NULL, 3, NULL);
	xTaskCreate( vTask6, "vtask6", configMINIMAL_STACK_SIZE, NULL, 4, NULL);

	//创建互斥锁任务
	xTaskCreate( vTask7, "vtask7", configMINIMAL_STACK_SIZE, NULL, 5, NULL);

	//创建任务通知任务
	xTaskCreate( vTask8, "vtask8", configMINIMAL_STACK_SIZE, NULL, 7, NULL);
	xTaskCreate( vTask9, "vtask9", configMINIMAL_STACK_SIZE, NULL, 6, &notifyTask);

    //创建动态软件定时器
	TimerHandle_t SoftTmr = NULL; /*  软件定时器句柄 */
	void SoftTmr_Callback(void* parameter)
	{
		//定时处理函数
		printf("hello world\n");
	}
	SoftTmr = xTimerCreate((const char*)"AutoReloadTimer",
	                       (TickType_t)1000,   /* 定时器周期 1000(tick) */
	                       (UBaseType_t)pdTRUE,/*  周期模式 */
	                       (void*)1,           /* 为每个计时器分配一个索引的唯一 ID */
	                       (TimerCallbackFunction_t)SoftTmr_Callback);
	if (SoftTmr != NULL)
	{
	   xTimerStart(SoftTmr, 0); // 开启周期定时器
	}

 	//创建动态的二值信号量(动态二值信号量可以删除回收,静态二值信号量xSemaphoreCreateCountingStatic不可以)
	xBinarySemaphore = xSemaphoreCreateBinary();
    if (NULL == xBinarySemaphore)
	{
		printf("create binary semaphore failed");
        return -1;
    }
    
	//创建互斥锁
    xMutex = xSemaphoreCreateMutex( );

	//启动调度器
	vTaskStartScheduler();
	
	printf("start system failed\n");
	return 1;
}
static void vTask1( void *pvParameters )
{
	unsigned short usValue = 0, usLoop;
	xQueueHandle *pxQueue;
	const unsigned short usNumToProduce = 3;
	short sError = pdFALSE;
	pxQueue = ( xQueueHandle * ) pvParameters;

	for( ;; )
	{		

		for( usLoop = 0; usLoop < usNumToProduce; ++usLoop )
		{
			/* Send an incrementing number on the queue without blocking. */
			printf("Task1 will send: %d\r\n", usValue);

			//将数据发送到队列的后端, 在中断使用则用xQueueSendToBackFromISR()
			if( xQueueSendToBack( *pxQueue, ( void * ) &usValue, ( portTickType ) 0 ) != pdPASS )
			{
				sError = pdTRUE;
			}
			else
			{
				++usValue;
			}
		}
		vTaskDelay( 2000 );

	}

}

static void vTask2( void *pvParameters )
{
	unsigned short usData = 0;
	xQueueHandle *pxQueue;
	
	pxQueue = ( xQueueHandle * ) pvParameters;
	for( ;; )
	{		
		while( uxQueueMessagesWaiting( *pxQueue ) )
		{
			//从队列中接收(读取)一个元素。收到的元素将从队列中删除。在中断则用xQueueReceiveFromISR()
			if( xQueueReceive( *pxQueue, &usData, ( portTickType ) 0 ) == pdPASS )
			{
				printf("Task2 received:%d\r\n", usData);
			}

		}

		vTaskDelay( 5000 );

	}

}

static void vTask3( void *pvParameters )
{

	for( ;; )
	{

		/*在开始下一个循环之前稍作延迟。 */
 		vTaskDelay( 2000 );
 		
 		/*输出一条消息,表示任务即将设置事件位0,然后设置事件位0。*/
 		printf( "Bit setting task -\t ready to set bit 0.\r\n" );
 		xEventGroupSetBits( xEventGroup, mainFIRST_TASK_BIT );
    
 		/*在设置其他位之前稍作延迟。 */
 		vTaskDelay( 2000 );
 		
 		/*输出一个消息,说事件位1即将被任务设置,然后设置事件位1。*/
 		printf( "Bit setting task -\t ready to set bit 1.\r\n" );
 		xEventGroupSetBits( xEventGroup, mainSECOND_TASK_BIT );

	}

}



static void vTask4( void *pvParameters )
{
	EventBits_t xEventGroupValue;
	const EventBits_t xBitsToWaitFor = ( mainFIRST_TASK_BIT  | 
 				     mainSECOND_TASK_BIT | 
 				     mainISR_BIT );

	for( ;; )
	{
      	/* 阻塞以等待事件位在事件组中被设置。*/
 		xEventGroupValue = xEventGroupWaitBits( /* 要读取的事件组。 */
 							xEventGroup,
 							/* 位测试。 */
 							xBitsToWaitFor, 
 							/*如果满足解封条件,则在退出时清除位。*/
 							pdTRUE,    
 							/*不要等待所有的位。对于第二次执行,该参数被设置为pdTRUE。 */
 							pdFALSE,   
 							/* 不要超时。 */
 							portMAX_DELAY );
 		/*为设置的每个位打印一条消息。 */
 		if( ( xEventGroupValue & mainFIRST_TASK_BIT ) != 0 )
 		{
 			printf( "Bit reading task -\t Event bit 0 was set\r\n" );
 		}

 		if( ( xEventGroupValue & mainSECOND_TASK_BIT ) != 0 )
 		{
 			printf( "Bit reading task -\t Event bit 1 was set\r\n" );
 		}

	}

}

static void vTask5( void *pvParameters )
{
   	BaseType_t xReturn = pdTRUE;
	int iCount = 0;
	
	for( ;; )
	{
		printf("start post sem");
		if(iCount >= 3) //发送三次二值信号量后,删除二值信号量
		{
			iCount=0;

			//删除二值信号量
			vSemaphoreDelete(xBinarySemaphore);
			xBinarySemaphore = NULL;
			printf(" but delete binary semaphore\n");
		}

		if( xBinarySemaphore != NULL )
		{		
			xReturn = xSemaphoreGive(xBinarySemaphore);
			if (pdTRUE == xReturn)
			{
				printf("post sem succeed");
			}
			else
			{
				printf("post sem error");
			}
			iCount++;
		}	
		printf("=========icount=%d\n", iCount);

		vTaskDelay(2500);
	}
}



static void vTask6( void *pvParameters )
{
    BaseType_t xReturn = pdTRUE;

	for( ;; )
	{
        printf("wait sem");
        xReturn = xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);
        if (pdTRUE == xReturn)
		{
            printf("get sem succeed\r\n");
        }
		else
		{
            printf("get sem error\r\n");
        }

	}

}

/*
*互斥锁保护的资源区域代码内容会被当前任务独占地使用,也被称为临界资源,临界锁;
它能够很好的保护这段代码执行,但注意不要处理耗时的事情,以免影响实时性。

*关于死锁:情景一就是当任务A需要任务B才能解锁,任务B需要任务A才能解锁,就会造成死锁;
另外一种情景就是任务A获取了互斥锁,但它休眠了,锁无法得到释放。

*解决死锁的方式就是使用递归锁。
*/
static void vTask7( void *pvParameters )
{
	int i = 0;

	for( ;; )
	{
		//获取互斥锁以上锁
		xSemaphoreTake(xMutex, portMAX_DELAY);

		for(i=0; i<20; i++)
		{
			printf("###i=%d\n", i);
		}
		//释放互斥锁以解锁
		xSemaphoreGive(xMutex);
		
		vTaskDelay(3000);
	}

}

/*
* 优点:效率高,且相比信号量、事件组、消息队列更加省内存;
* 任务通知无法把信息通知给多个任务,如需这样需使用事件组来实现。
*/
static void vTask8( void *pvParameters )
{

	for( ;; )
	{
	#if 0
		//传任意值
		if(xTaskNotify(notifyTask, 88, eSetValueWithoutOverwrite) == pdPASS)
		{
			printf("####put data notify task9 successed\n");
		}
		else
		{
			printf("put data notify task9 faield\n");
		}
	#else
		//传信号
		if(xTaskNotifyGive(notifyTask) == pdPASS)
		{
			printf("$$$ send notify signal success\n");
		}
		else
		{
			printf("$$$ send notify signal failed\n");
		}
	#endif
		vTaskDelay(2000);
	}

}



static void vTask9( void *pvParameters )
{
	uint32_t receData = 0;
	BaseType_t xResult;
	int notifySignalNum = 0;
	
	for( ;; )
	{
	#if 0
		//收任意值
		xResult = xTaskNotifyWait( //阻塞等待,后面不执行的方式
			0, //接收方进入函数时数据清0
			0, //接收方退出函数时数据清0
			&receData, //保存的数据
			portMAX_DELAY //一直等待数据
		);

		if(xResult == pdPASS)
		{
			printf("####receive notify data: %d success\n", receData);
		}
	#else
		//收信号
		notifySignalNum = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
		while (notifySignalNum--)
		{
			printf("$$$ i am receive task8 signal\n");
		}

	#endif
	}
}

/*堆栈溢出检测钩子函数,(ps:因为是PC端运行freertos,所以不支持该检测,但我还是放这里,在硬件上运行时,为了方便对异常进行调试,该函数还是得加上)
* 一般将 configCHECK_FOR_STACK_OVERFLOW 设置为 2 相比设置1能够准确的找到堆栈溢出;
*/
# if 0
/* in run time check stack overflow */
void vApplicationStackOverflowHook( TaskHandle_t pxTask,
                                    char * pcTaskName )
{
    ( void ) pxTask;

    /* Run time stack overflow checking is performed if
     * configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2.  This hook
     * function is called if a stack overflow is detected.  This function is
     * provided as an example only as stack overflow checking does not function
     * when running the FreeRTOS POSIX port. */
	 while(1)
     {
         printf("task %s is stack overflow. \r\n", pcTaskName);
         vTaskDelay(500);
     }
}

//检测栈剩余的方法
/*

static xTaskHandle pv_handle = NULL;

if(xTaskCreate(test, "test", 512, NULL,1,&pv_handle) != pdPASS)
{
        printf("[%s] test error\n",__func__);
}
    
void test(void *param)
{
    //方式一:使用NULL,默认获取当前任务栈大小
    printf("stack:%d\n",(int)uxTaskGetStackHighWaterMark(NULL));
    for(;;)
    {
        //方式二:使用任务句柄,获取指定任务栈大小(ps:一般单独一个任务检测栈使用情况)
        printf("current stack:%d\n",(int)uxTaskGetStackHighWaterMark(pv_handle));
        vTaskDelay(500);
    }
}

*/

#endif

PS:这里顺便补充个知识点
回调函数和钩子函数的区别
    根本上,他们都是为了捕获消息而生的,但是钩子函数在捕获消息的第一时间就会执行,而回调函数是在整个捕获过程结束时,最后一个被执行的
    回调函数其实就是调用者把回调函数的函数指针传递给调用函数,当调用函数执行完毕时,通过函数指针来调用回调函数。

关于信号量使用场景选择补充

背景
Freertos是一个多进程操作系统。多进程的一个重要控制,就是进程同步。所以信号量和互斥量,可也只能用于进程间的同步,且不能传递更多的数据。

二值信号量:一般用于进程同步,或者ISR和TASK的同步。
计数信号量:一般用于共享资源的缓冲区统计。
互斥量:一般用于共享资源的独立访问。

1.信号量
信号量在RTOS中,会引起优先级反转问题。
低优先级任务获取信号量后,如果高优先级任务也需要这个信号量,则需要被阻塞,最终,实际上高优先级任务被降低到更低的级别了。

2.互斥量
当任务获取一个互斥量时,它的优先级会被OS暂时提高到和所有等待该互斥量的任务中的最高优先级。从而保证了,任务在互斥访问时,不会被抢占
当任务归还互斥量时,优先级会被OS恢复到之前的级别。这被称为优先级继承。
优先级继承不能解决优先级反转,但是能够将危害降到最低。

异常场景常见问题汇总

printf的不当使用

不适当地使用printf()是一个常见的错误来源,而且,由于没有意识到这一点,应用程序开发人员通常会进一步调用printf()来帮助调试,这样做会使问题更加严重。
许多交叉编译器供应商将提供一个适合在小型嵌入式系统中使用的 printf() 实现。即使是这样,该实现也可能不是线程安全的,可能不适合在中断服务例程中使用,而且根据输出的方向,需要花费相对较长的时间来执行
如果没有专门为小型嵌入式系统设计的printf()实现,而使用通用的 printf() 实现,则必须特别小心,如:
仅仅包括对printf()或 sprintf() 的调用就会大量增加应用程序的可执行文件的大小
printf() 和 sprintf() 可能会调用 malloc() ,如果使用的是heap_3以外的内存分配方案,这可能是无效的。更多信息请参见内存分配方案示例。
printf() 和 sprintf() 可能需要一个比原来大很多倍的栈。

调试运行的Demo代码
https://download.csdn.net/download/qq_32348883/88065207

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路过的小熊~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值