FreeRTOS-Qemu 實現三任務同步通訊機制以及API資訊

HustWolfzzb發表於2017-10-06


1. 本次作業的考察要點:

作業地址:
github.com/HustWolfzzb…
Git/GCC/GDB/QEMU等工具的使用。
FreeRTOS多工同步和通訊機制的掌握。


2. 程式設計作業:

在github上,Fork例程專案(github.com/cbhust/STM3… 到自己的個人賬號。
clone專案到本地電腦的Ubuntu虛擬機器中(虛擬機器環境在第一次作業中已搭建)。
按照/Projects/Demo1/README.md中的提示編譯Demo1例程並通過Qemu執行例程。
在Demo1的框架基礎上完成自己的本次程式設計作業(具體要求見第3點)。
程式碼完成後提交到自己的github賬號上,確保助教可以正常的clone並編譯執行。
在作業部落格上給出程式碼的github連結、程式碼說明以及執行結果展示。


3. 程式設計作業具體要求:

建立三個任務:Sender_Task,Receiver_Task, Monitor_Task

  • Sender_Task的任務執行週期為2ms,Receiver_Task的任務執行週期為1000ms, Monitor_Task的任務執行週期為10000ms。
  • Sender_Task在每個執行週期向Receiver_Task傳送一個32位無符號整數,第一次傳送1,然後依次傳送2,3,4......,傳送完10000後再從1開始傳送。同時對傳送的資料進行計算累加計算並儲存當前累加結果。
  • Receiver_Task對接收到的資料進行和Sender_Task同樣的累加計算並儲存當前累加結果。
  • Monitor_Task在每個執行週期檢查Sender_Task傳送的每個資料是否都被Receiver_Task正確的接收和處理,請自行設計一種檢查機制並實現。
  • 可利用STM32F429I Discovery開發板的相關硬體(LED/LCD/串列埠)來輸出相關狀態資訊。
  • 使用FreeRTOS的任務間通訊和同步API完成上述功能。

4. 任務說明(會用到的API)

  • 任務建立
    標頭檔案:task.h
    portBASE_TYPE xTaskCreate (
    pdTASK_CODE pvTaskCode, 指向任務的實現函式的指標。效果上僅僅是函式名
    const portCHAR const pcNane, 具有描述性的任務名。FreeRTOS 不會使用它。
    unsigned portSHORT usStackDepth, 指定任務堆疊的大小
    void
    pvParameters, 指標用於作為一個引數傳向建立的任務
    unsigned portBASE_TYPE uxPriority, 任務執行時的優先順序
    xTaskHandle *pvCreatedTask 用於傳遞任務的控制程式碼,可以引用從而對任務進行其他操作。
    )

說明:

  1. 這裡的任務是指一個永遠不會退出的C 函式,通常是一個死迴圈。
  2. pcNane 其只是單純地用於輔助除錯。應用程式可以通過定義常量
    config_MAX_TASK_NAME_LEN 來定義任務名的最大長度——包括’\0’結束符。如果傳入的
    字串長度超過了這個最大值,字串將會自動被截斷
  3. usStackDepth 這個值指定的是棧空間可以儲存多少個字(word),而不是多少個位元組(byte)。棧空間
    大小為usStackDepth*4(bytes)。
  4. uxPriority 優先順序的取值範圍可以從最低優先順序0 到最高優先順序(configMAX_PRIORITIES–1)。
    返回:
  5. pdPASS 表明任務建立成功,準備執行。
  6. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 由於記憶體堆空間不足,FreeRTOS 無法分配
    足夠的空間來儲存任務結構資料和任務棧,因此無法建立任務。
  • 任務延時
    標頭檔案:task.h
    void vTaskDelay (
    portTickType xTicksToDelay 時間數量,呼叫任務應該鎖住的時間片週期
    )

說明:

  1. FreeRTOSConfig.h 中的INCLUDE_vTaskDelay=1,這個函式才能用。
  2. 延時任務為已知時間片,任務被鎖住剩餘的實際時間由時間片速率決定。portTICK_RATE_MS 常量
    以時間片速率來計算實際時間
  3. vTaskDelay()指定一個任務希望的時間段,這個時間之後任務解鎖。
  4. vTaskDelay()不提供一個控制週期性任務頻率的好方法,和其他任務和中斷一樣,在呼叫vTaskDelay()
  • 建立一個新的佇列
    標頭檔案:queue. H
    xQueueHandle xQueueCreate (
    unsigned portBASE_TYPE uxQueueLength, 佇列中包含最大專案數量
    unsigned portBASE_TYPE uxItemSize 佇列中每個專案所需的位元組數
    );

說明:建立一個新的佇列。為新的佇列分配所需的儲存記憶體,並返回一個佇列處理。
注意:專案通過複製而不是引用排隊,因此,所需的位元組數,將複製給每個專案。佇列中每個專案必須分配同樣大小。
返回:如果佇列成功建立,則返回一個新建佇列的處理。如果不能建立佇列,將返回0。

  • 傳遞專案到一個佇列中的後面
    標頭檔案:queue. H
    portBASE_TYPE xQueueSendToBack (
    xQueueHandle xQueue, 將專案傳進的佇列
    const void * pvItemToQueue, 專案的指標【源資料】
    portTickType xTicksToWait 等待的最大時間量
    );

說明:這個與xQueueSend 是一樣的,參照xQueueSend 的用法

  • 從佇列接收一個專案
    標頭檔案:queue. H
    portBASE_TYPE xQueueReceive (
    xQueueHandle xQueue, 傳送專案的佇列控制程式碼
    void *pvBuffer, 指向緩衝區的指標,將接收的專案被複制進去
    portTickType xTicksToWait 任務中斷並等待佇列中可用空間的最大時間
    );

說明:這個專案通過複製接收,因此緩衝器必須提供足夠大的空間。這個函式一定不能在中斷服務程式中使用當佇列空時,肯定複製傳遞不成功,則等待xTicksToWait 個滴答週期後再複製,但如果xTicksToWait 設定為0,呼叫將立即返回。
返回:如果專案成功被佇列接收為pdTRUE ,否則為 pdFALSE。


~~ ~~ ~ ~~~

#                     作業過程(多圖預警)複製程式碼

~ ~ ~~ ~~ ~~ ~~ ~


1. QEMU安裝

本例程使用qemu-system-gnuarmeclipse,該qemu分支對stm32f4有更好的支援,主要面向Eclipse開發環境,本文件給出在Ubuntu 16.04命令列環境下單獨使用的方式。

  • 到網頁 github.com/gnu-mcu-ecl… 下載二進位制檔案 gnuarmeclipse-qemu-debian64-2.8.0-201612271623-dev.tgz到~/work目錄

  • 在工作目錄解壓縮檔案,並把路徑新增到$PATH變數中
    #cd ~/work
    #tar xvf gnuarmeclipse-qemu-debian64-2.8.0-201612271623-dev.tgz
    #chmod -R -w ./qemu
    export PATH=~/work/qemu/2.8.0-201612271623-dev/bin/:$PATH複製程式碼
  • 測試qemu能否正常執行
#qemu-system-gnuarmeclipse --version複製程式碼

如正常則會顯示版本資訊為2.8.0。

2. 編譯本例程

  • 在Demo1目錄下執行make,生成hello_rtos.elf檔案


說明:qemu-system-gnuarmeclipse當前版本不支援STM32F4的浮點,相應的,FreeRTOS使用的portable目錄沒有使用ARM_CM4F而是使用ARM_CM3。

3. QEMU模擬

在Demo1目錄下提供了一個qemu.sh指令碼檔案,內容如下:

#!/bin/bash

qemu-system-gnuarmeclipse --verbose --verbose --board STM32F429I-Discovery --mcu STM32F429ZI -d unimp,guest_errors  --image hello_rtos.elf --semihosting-config enable複製程式碼

在Demo1目錄下執行指令碼檔案:

#./qemu.sh複製程式碼

則qemu開始執行hello_rtos.elf檔案,在彈出的GUI中看到程式執行的效果。

4. GDB除錯

Ubuntu預設安裝中沒有gdb-arm-none-eabi工具,需要先安裝

 #sudo apt-get install gdb-arm-none-eabi複製程式碼

在Demo1目錄下執行qemu_gdb指令碼檔案,該檔案中新增了--gdb tcp::1234 -S

qemu啟動後等待來自gdb的除錯指令,開啟另外一個終端視窗,執行

#arm-none-eabi-gdb複製程式碼

在gdb介面內執行:

(gdb)target remote localhost:1234
(gdb)continue複製程式碼

可以看到qemu開始執行程式,GUI介面上可以看到程式執行效果。


5. Trace(略)

本例程啟動了FreeRTOS的trace功能。

  • 用STM32 ST-LINK Utility把hello_rtos.hex燒寫入STM32F429I Discovery開發板,程式執行時,用STM32 ST-LINK Utility讀取0x20000000到0x20030000範圍內的地址資料,並儲存到一個二進位制檔案trace_data.bin中。
  • percepio.com/tz/freertos… 下載並安裝Tracealyzer for FreeRTOS軟體。
  • 在Tracealyzer for FreeRTOS軟體中開啟trace_data.bin,可以看到詳細的FreeRTOS的任務和事件資訊。

##6.自主程式設計實現三個同步通訊的函式:Sender_Task,Receiver_Task,Monitor_Task;
我採用了Mac程式設計,然後通過git的方式同步到ubuntu,不僅鍛鍊了團隊寫作的神器--“Git”的使用技巧,同時也優化了自己的程式設計環境

說一下我的思路:
首先,採用全域性變數來統領傳送的資料和接收到的資料,通過在main函式外宣告瞭一個傳送資料的和,一個接收資料的和,一個佇列用於傳遞由於週期不對等的傳送和接受的資料。然後Sender_Task用於2ms傳送一個資料,從1-10000迴圈,Receiver_Task 用於1000ms接收一波資料,大概是500個左右,所以佇列的大小我定為510 ,雖然有不小的浪費,但是至少不會資料溢位。最後的Monitor_Task用於判定是否爭取的傳送和接受,所以這個時候就要用到兩個全域性變數,在10000ms的週期內判定一次是否兩個資料和相等,如果傳送的資料之和等於接受的資料之和,那麼就毫無疑問的,傳送沒有問題。而且由於我規定,優先順序上,Monitor>Receiver>Sender ,所以可以保證不會發生資料競爭導致的錯漏。如果正確,那就亮綠燈,錯誤就是紅燈,事實顯示,一直是正確的!!

/**
  ******************************************************************************
  * @file    IO_Toggle/main.c 
  * @author  MCD Application Team
  * @version V1.0.0
  * @date    19-September-2011
  * @brief   Main program body
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>© COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************  
  */ 

/* Includes ------------------------------------------------------------------*/
#include "stm32f429i_discovery.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"

/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "queue.h"

/** @addtogroup STM32F4_Discovery_Peripheral_Examples
  * @{
  */

/** @addtogroup IO_Toggle
  * @{
  */ 

/* Private typedef -----------------------------------------------------------*/
GPIO_InitTypeDef  GPIO_InitStructure;

/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/

/* Private functions ---------------------------------------------------------*/
void Hardware_Init(void);
void Red_LED_On(void);
void Red_LED_Off(void);
void Green_LED_On(void);
void Green_LED_Off(void);
void ToggleLED1_Task(void*);
void ToggleLED2_Task(void*);
/**
  * @brief  Main program
  * @param  None
  * @retval None
  */


//張照博自己寫的--START
int32_t Send_Sum=0;
int32_t Received_Sum=0;
   //建立佇列 
xQueueHandle MyQueue; 
void Sender_Task(void *pvParameters)
{
    int32_t Send_Num = 1; 
    for( ;; )  
    {  
        vTaskDelay( 2 / portTICK_RATE_MS );
        if (Send_Num>10000)
        {
          Send_Num=1;
        }
        /* 向佇列中填充內容 */  
        xQueueSendToBack( MyQueue, ( void* )&Send_Num, 0 );   
        *((int32_t*)pvParameters)+=Send_Sum;
        Send_Num++; 
     }
}

void Receiver_Task(void *pvParameters)
{
  int32_t  Received_Num = 0;  
    for( ;; )  
    {  
        /* 從佇列中獲取內容 */  
        if( xQueueReceive( MyQueue, &Received_Num, 1000 / portTICK_RATE_MS ) == pdTRUE)  
        {  

          *((int32_t*)pvParameters)+=Received_Num;
           Received_Num=*((int32_t*)pvParameters);
        }  
    }  
}

void Monitor_Task(void *pvParameters)
{
  vTaskDelay( 10000 / portTICK_RATE_MS );  
  if(Send_Sum-Received_Sum<5 && Send_Sum-Received_Sum>-5)
    {
      Green_LED_On();
      Red_LED_Off();
      Send_Sum=0;
      Received_Sum=0;
    } 
  else 
    {
      Green_LED_Off();
      Red_LED_On();
      Send_Sum=0;
      Received_Sum=0;
    }
}

//張照博自己寫的--END

int main(void)
{
  /*!< At this stage the microcontroller clock setting is already configured, 
       this is done through SystemInit() function which is called from startup
       file (startup_stm32f4xx.s) before to branch to application main.
       To reconfigure the default setting of SystemInit() function, refer to
        system_stm32f4xx.c file
     */
    Hardware_Init();
    // 初始化硬體平臺  
    //prvSetupHardware();  
    //建立全域性變數
 MyQueue = xQueueCreate( 510 , sizeof( int32_t ) ); 
    // 建立任務  
    xTaskCreate( Sender_Task, ( signed portCHAR * ) "Sender_Task", configMINIMAL_STACK_SIZE,(void*)&Send_Sum, tskIDLE_PRIORITY+3, NULL );  
    xTaskCreate( Receiver_Task, ( signed portCHAR * ) "Receiver_Task", configMINIMAL_STACK_SIZE,(void*)&Received_Sum, tskIDLE_PRIORITY+4, NULL );   
    xTaskCreate( Monitor_Task, ( signed portCHAR * ) "Monitor_Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+5, NULL ); 
  //啟動OS  
    vTaskStartScheduler();  
    return 0; 
 //我的第一次除錯,需要去掉後面的
       /* Init and start tracing*/
         vTraceEnable(TRC_INIT);
         vTraceEnable(TRC_START);

 //       /* Create tasks */
 //       xTaskCreate(
    //       ToggleLED1_Task,                 /* Function pointer */
    //       "Task_LED1",                          /* Task name - for debugging only*/
    //       configMINIMAL_STACK_SIZE,         /* Stack depth in words */
    //       (void*) NULL,                     /* Pointer to tasks arguments (parameter) */
    //       tskIDLE_PRIORITY + 3UL,           /* Task priority*/
    //       NULL                              /* Task handle */
 //       );

 //       xTaskCreate(
    //       ToggleLED2_Task,                 /* Function pointer */
    //       "Task_LED2",                           Task name - for debugging only
    //       configMINIMAL_STACK_SIZE,         /* Stack depth in words */
    //       (void*) NULL,                     /* Pointer to tasks arguments (parameter) */
    //       tskIDLE_PRIORITY + 2UL,           /* Task priority*/
    //       NULL                              /* Task handle */
 //       );

    // /* Start the scheduler. */
    // vTaskStartScheduler();

    // /* If all is well, the scheduler will now be running, and the following line
    // will never be reached.  If the following line does execute, then there was
    // insufficient FreeRTOS heap memory available for the idle and/or timer tasks
    // to be created.  See the memory management section on the FreeRTOS web site
    // for more details. */
    // for( ;; );
//我的第一次除錯,去掉的位置結尾


}


/**
 * Hardware_Init: 
 */
void Hardware_Init(void)
{
        /* GPIOG Periph clock enable */
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);

        /* Configure PG13, PG14 in output pushpull mode */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13| GPIO_Pin_14;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
        GPIO_Init(GPIOG, &GPIO_InitStructure);

}
/**
 * Red_LED_On: 
 */
void Red_LED_On(void)
{
//    GPIO_SetBits(GPIOG, GPIO_Pin_14);
    GPIOG->ODR |= 0x4000;
}

/**
 * Red_LED_Off: 
 */
void Red_LED_Off(void)
{
//    GPIO_ResetBits(GPIOG, GPIO_Pin_14);
  GPIOG->ODR &= 0xBFFF;
}

/**
 * Green_LED_On: 
 */
void Green_LED_On(void)
{
//    GPIO_SetBits(GPIOG, GPIO_Pin_13);
    GPIOG->ODR |= 0x2000;
}

/**
 * Green_LED_Off: 
 */
void Green_LED_Off(void)
{
//    GPIO_ResetBits(GPIOG, GPIO_Pin_13);
    GPIOG->ODR &= 0xDFFF;
}
/**
 * ToggleLED1_Task: Toggle LED1 via RTOS Timer
 */
void ToggleLED1_Task(void *pvParameters)
{
    int led = 0;  

    while (1) 
    {
        if(led == 0)
        {
            Red_LED_On();
            led = 1;
        } 
        else
        {
            Red_LED_Off();
            led = 0;
         }
        /*
        Delay for a period of time. vTaskDelay() places the task into
        the Blocked state until the period has expired.
        The delay period is spacified in 'ticks'. We can convert
        yhis in milisecond with the constant portTICK_RATE_MS.
        */
        vTaskDelay(1000 / portTICK_RATE_MS);
  }
}

/**
 * ToggleLED2_Task: Toggle LED2 via RTOS Timer
 */
void ToggleLED2_Task(void *pvParameters)

{
    int led = 0;  
    while (1) 
    {
        if(led == 0)
        {
            Green_LED_On();
            led = 1;
        } 
        else
        {
            Green_LED_Off();
            led = 0;
         }
        /*
        Delay for a period of time. vTaskDelay() places the task into
        the Blocked state until the period has expired.
        The delay period is spacified in 'ticks'. We can convert
        yhis in milisecond with the constant portTICK_RATE_MS.
        */
        vTaskDelay(2000 / portTICK_RATE_MS);
  }
}

void vApplicationTickHook( void )
{
}
/*-----------------------------------------------------------*/


/*-----------------------------------------------------------*/

void vApplicationMallocFailedHook( void )
{
    /* vApplicationMallocFailedHook() will only be called if
    configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h.  It is a hook
    function that will get called if a call to pvPortMalloc() fails.
    pvPortMalloc() is called internally by the kernel whenever a task, queue,
    timer or semaphore is created.  It is also called by various parts of the
    demo application.  If heap_1.c or heap_2.c are used, then the size of the
    heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
    FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
    to query the size of free heap space that remains (although it does not
    provide information on how the remaining heap might be fragmented). */
    taskDISABLE_INTERRUPTS();
    for( ;; );
}
/*-----------------------------------------------------------*/

void vApplicationIdleHook( void )
{
    /* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
    to 1 in FreeRTOSConfig.h.  It will be called on each iteration of the idle
    task.  It is essential that code added to this hook function never attempts
    to block in any way (for example, call xQueueReceive() with a block time
    specified, or call vTaskDelay()).  If the application makes use of the
    vTaskDelete() API function (as this demo application does) then it is also
    important that vApplicationIdleHook() is permitted to return to its calling
    function, because it is the responsibility of the idle task to clean up
    memory allocated by the kernel to any task that has since been deleted. */
}
/*-----------------------------------------------------------*/

void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
    ( void ) 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. */
    taskDISABLE_INTERRUPTS();
    for( ;; );
}
/*-----------------------------------------------------------*/
#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

/**
  * @}
  */ 

/**
  * @}
  */ 

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/複製程式碼

相關文章