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


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

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


2. 編程做業:

在github上,Fork例程項目(github.com/cbhust/STM3… 到本身的我的帳號。
clone項目到本地電腦的Ubuntu虛擬機中(虛擬機環境在第一次做業中已搭建)。
按照/Projects/Demo1/README.md中的提示編譯Demo1例程並經過Qemu運行例程。
在Demo1的框架基礎上完成本身的本次編程做業(具體要求見第3點)。
代碼完成後提交到本身的github帳號上,確保助教能夠正常的clone並編譯運行。
在做業博客上給出代碼的github連接、代碼說明以及運行結果展現。github


3. 編程做業具體要求:

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

  • 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 時間數量,調用任務應該鎖住的時間片週期
    )

說明:ubuntu

  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。bash

  • 傳遞項目到一個隊列中的後面
    頭文件:queue. H
    portBASE_TYPE xQueueSendToBack (
    xQueueHandle xQueue, 將項目傳進的隊列
    const void * pvItemToQueue, 項目的指針【源數據】
    portTickType xTicksToWait 等待的最大時間量
    );

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

  • 從隊列接收一個項目
    頭文件:queue. H
    portBASE_TYPE xQueueReceive (
    xQueueHandle xQueue, 發送項目的隊列句柄
    void *pvBuffer, 指向緩衝區的指針,將接收的項目被複制進去
    portTickType xTicksToWait 任務中斷並等待隊列中可用空間的最大時間
    );

說明:這個項目經過複製接收,所以緩衝器必須提供足夠大的空間。這個函數必定不能在中斷服務程序中使用當隊列空時,確定複製傳遞不成功,則等待xTicksToWait 個滴答週期後再複製,但若是xTicksToWait 設置爲0,調用將當即返回。
返回:若是項目成功被隊列接收爲pdTRUE ,不然爲 pdFALSE。框架


~~ ~~ ~ ~~~eclipse

# 做業過程(多圖預警)複製代碼

~ ~ ~~ ~~ ~~ ~~ ~electron


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****/複製代碼

相關文章
相關標籤/搜索