Xilinx软件开发:FreeRTOS快速入门

目录    
第一章. 测试环境和软件版本    
第二章. 创建hello world    
第三章. 创建FreeRTOS2    
第四章. 增加两个任务    
1. 增加任务    
2. 增加计数    
第五章. 发送增加延时    
第六章. 接收增加消息判断    
第七章. 创建两个生产者    
第八章. 注意事项    
1. 分析xQueueReceive源码    
2. 实际验证xQueueSend传递的数据是否线程安全    
3. 增加打印地址    
第九章. 其他问题    
1. 如何设置Producer 任务以1hz的频率执行?    
2. 如何展开队列以容纳20条消息?    
3. 如何为以下消息类型创建一个长度为5的队列?    
4. 如何修改Consumer代码以仅在队列满时读取消息?    
5. 能否从队列读取所有消息而不是一条消息?    
第十章. 优先级说明    
 


 
第一章.测试环境和软件版本

Win10 
Vivado/Vitis 2020.1

第二章.创建hello world


F:\debug\leo_training\201\FreeRTOS1
 


 

Platform命名为
FreeRTOS_Basic_sys

 

 

 

app命名为
FreeRTOS_Basic_app


注意cpu要选r5_0

 

 

 

 

 


 

第三章.创建FreeRTOS2

参考创建hello world的流程
创建成功后的界面如图

 

把support目录的以下三个文件复制到F:\debug\leo_training\201\FreeRTOS2\FreeRTOS2\src
freertos_hello_world.c
ProducerConsumer.h
ProducerConsumer.c

 

 


然后点debug运行,这时候是个空程序,什么都没做

第四章.增加两个任务


1. 增加任务
打开ProducerConsumer.c
修改setupTasks,创建一个队列和两个任务


void setupTasks() {
  xQueueHandle xQueue = xQueueCreate (10, sizeof (uint32_t));

  xTaskCreate (prvProducer, (const char *) "Prod", configMINIMAL_STACK_SIZE, (void *) xQueue, PRODUCER_PRIORITY, NULL);
  xTaskCreate (prvConsumer, (const char *) "Cons", configMINIMAL_STACK_SIZE, (void *) xQueue, CONSUMER_PRIORITY, NULL);
}

xQueueCreate的定义如下

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
    #define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif


参数1 函数指针
参数2 名字
参数3 堆栈大小
参数4 参数指针
参数5 优先级
参数6 NULL

xTaskCreate的定义如下

#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 200)

    BaseType_t xTaskCreate(    TaskFunction_t pxTaskCode,
                            const char * const pcName,        /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )


2. 增加计数


static void prvProducer(void *pvParameters) {
  BaseType_t counter = 0;

  for (;;) {
   counter++;
   xil_printf("Producer %d\r\n", counter);
  }
}

/*-----------------------------------------------------------*/
static void prvConsumer(void *pvParameters) {
  BaseType_t counter = 0;
  for (;;) {
    counter++;
    xil_printf("Consumer %d\r\n", counter);
 }
}

可以看到此时打印得非常快


第五章.发送增加延时


我们在prvProducer中增加延时

static void prvProducer(void *pvParameters) {
  xQueueHandle xQueue = (xQueueHandle) pvParameters;
  BaseType_t counter = 0;
  TickType_t xLastWakeTime = xTaskGetTickCount ();
  TickType_t xFreq = 500 / portTICK_RATE_MS;

  for (;;) {
      vTaskDelayUntil (&xLastWakeTime, xFreq);
      counter++;
      if (uxQueueSpacesAvailable(xQueue) != 0) {
         if (xQueueSendToBack(xQueue, &counter, 0) != pdPASS) {
            xil_printf ("Producer: Error inserting into queue\r\n");
         }
         else
            xil_printf ("Producer Send counter=%d\r\n", counter);
      }
   }
}

vTaskDelayUntil  绝对延时 参数2是频率    

#define portTICK_RATE_MS portTICK_PERIOD_MS
#define portTICK_PERIOD_MS     ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define configTICK_RATE_HZ (100)

uxQueueSpacesAvailable用于查询队列中可用的空闲空间数量,不为0,队列有效
xQueueSendToBack  将数据发送至队尾,返回pdPASS,发送成功

#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )

第六章.接收增加消息判断

static void prvConsumer(void *pvParameters) {
  xQueueHandle xQueue = (xQueueHandle) pvParameters;
  BaseType_t counter = 0;

  for (;;) {
      BaseType_t message;
       counter++;
      if (xQueueReceive (xQueue, &message, portMAX_DELAY) != pdPASS) {
         xil_printf ("Consumer: Error upon reception of message\r\n");
      } else {
         xil_printf ("Consumer: Received %d counter=%d\r\n", message, counter);
      }
  }
}


第七章.创建两个生产者

 

void setupTasks() {
  xQueueHandle xQueue = xQueueCreate (10, sizeof (uint32_t));

  xTaskCreate (prvProducer, (const char *) "Prod", configMINIMAL_STACK_SIZE, (void *) xQueue, PRODUCER_PRIORITY, NULL);
  xTaskCreate (prvProducer2, (const char *) "Prod2", configMINIMAL_STACK_SIZE, (void *) xQueue, PRODUCER_PRIORITY, NULL);

  xTaskCreate (prvConsumer, (const char *) "Cons", configMINIMAL_STACK_SIZE, (void *) xQueue, CONSUMER_PRIORITY, NULL);
}

static void prvProducer2(void *pvParameters) {
  xQueueHandle xQueue = (xQueueHandle) pvParameters;
  BaseType_t counter = 0;
  TickType_t xLastWakeTime = xTaskGetTickCount ();
  TickType_t xFreq = 500 / portTICK_RATE_MS;

  for (;;) {
      vTaskDelayUntil (&xLastWakeTime, xFreq);
      counter++;
      if (uxQueueSpacesAvailable(xQueue) != 0) {
         if (xQueueSendToBack(xQueue, &counter, 0) != pdPASS) {
            xil_printf ("Producer2: Error inserting into queue\r\n");
         }
         else
            xil_printf ("Producer2 Send counter=%d\r\n", counter);
      }
   }
}


从运行结果可以看到,每次两个Producer各发送了一个消息,Consumer接收到两个消息

 


第八章.注意事项


1. 分析xQueueReceive源码


在queue.c中可以看到数据是复制了一份,所以通过queue传递数据是线程安全的
prvCopyDataFromQueue( pxQueue, pvBuffer )

注意这些操作都是由临界区保护起来的
taskENTER_CRITICAL();
taskEXIT_CRITICAL();

2. 实际验证xQueueSend传递的数据是否线程安全

xQueueSend( xQueue,            /* The queue being written to. */
        HWstring, /* The address of the data being sent. */
        0UL );            /* The block time. */

xQueueReceive(xQueue,                /* The queue being read. */
        Recdstring,    /* Data is read into this address. */
        portMAX_DELAY );    /* Wait without a timeout for data. */

是不是只传了指针,指向的是否是同一地址

3. 增加打印地址


static void prvTxTask( void *pvParameters )
{
const TickType_t x1second = pdMS_TO_TICKS( DELAY_1_SECOND );
xil_printf( "HWstring address: 0x%.8X\r\n", (int)&HWstring );
    for( ;; )
    {
        /* Delay for 1 second. */
        vTaskDelay( x1second );

        /* Send the next value on the queue.  The queue should always be
        empty at this point so a block time of 0 is used. */
        xQueueSend( xQueue,            /* The queue being written to. */
                    HWstring, /* The address of the data being sent. */
                    0UL );            /* The block time. */
    }
}

static void prvRxTask( void *pvParameters )
{
char Recdstring[15] = "";

    for( ;; )
    {
        /* Block to wait for data arriving on the queue. */
        xQueueReceive(xQueue,                /* The queue being read. */
                Recdstring,    /* Data is read into this address. */
                portMAX_DELAY );    /* Wait without a timeout for data. */

        /* Print the received data. */
        xil_printf( "Rx task received string from Tx task: %s 0x%.8X\r\n", Recdstring, (int)&Recdstring );
        RxtaskCntr++;
    }
}


第九章.其他问题


1. 如何设置Producer 任务以1hz的频率执行?


TickType_t xFreq = 1000 / portTICK_RATE_MS; 


 
2. 如何创建队列以容纳20条消息?


xQueueHandle = xQueueCreate(20, sizeof(uint32_t));


3. 如何为以下消息类型创建一个长度为5的队列?

 
struct message {
    int value;
    long lValue;
    float fValue;
};
 
xQueueHandle xQueue = xQueueCreate(5, sizeof(struct message));
队列只需要知道消息个数,而不关心数据类型 

4. 如何修改Consumer代码以仅在队列满时读取消息?

if(uxQueueSpaceAvailable(xQueue) == 0){} 

5. 能否从队列读取所有消息而不是一条消息?

是的。使用类似于
while(uxQueueMessagesWaiting(xQueue)){
//todo
}

第十章.优先级说明

freertos、ucosiii允许优先级相同,ucos2不允许任务的优先级相同
FreeRTOS任务优先:优先级数值越小,任务优先级越低。
ucos、linux任务优先: 优先级数值越小,任务优先级越高

FreeRTOS 中任务的最高优先级是通过 FreeRTOSConfig.h 文件中的 configMAX_PRIORITIES 进行配置的,用户实际可以使用的优先级范围是 0 到 configMAX_PRIORITIES – 1。比如我们配置此宏定义为 5,那么用户可以使用的优先级号是 0,1,2,3,4,不包含 5

用户配置任务的优先级数值越小,那么此任务的优先级越低,空闲任务的优先级是 0。

建议用户配置宏定义 configMAX_PRIORITIES 的最大值不要超过 32,即用户任务可以使用的优先级范围是0到31,设置太大在优先级判断的算法中会浪费时钟周期

如果用户在 FreeRTOSConfig.h 文件中配置宏定义 configUSE_TIME_SLICING 为 1,或者没有配置此宏定义,时间片调度都是使能的。 另外,只要芯片资源允许,可以配置任意多个同优先级任务。没有定义 configUSE_TIME_SLICING,也能使用时间片调度是因为此宏定义默认已经在FreeRTOS.h 文件中使能。

最优先的是中断,其次是用户界面的任务优先,比如按键输入、显示刷新
采集数据,根据用户按键,切换不同的显示内容。
这时候按键判断io电平,用sleep延时消抖,再判断,相同,则发送按键按下消息。
显示任务一般有两个逻辑,没有按键的时候轮显,每隔几秒切换一次画面,有按键则进入按键状态

任务永远不应该从初始函数返回。如果要结束一个任务,销毁任务的正确方法是调用vTaskDelete(NULL);
 
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值