1. 程式人生 > >在Linux下實現FreeRTOS的簡單模擬器

在Linux下實現FreeRTOS的簡單模擬器

FreeRTOS

基礎知識不贅述,請參考朱工的專欄, 本文主要描述怎麼在Linux的環境下跑一個FreeRTOS的模擬器

官方示例

FreeRTOS的官方提供了一個在Linux下的Simulator的示例,但是用的Kernel的版本非常老,是V6的版本,FreeRTOS現在已經進化到V10了,作為一個標準碼農,不用最新版本簡直不舒服斯基 >_<。
先把官方示例下載下來,在官方示例中,有一個Debug和Release的目錄,在這兩個目錄下使用make all命令就可以直接編出來可執行檔案在Linux下直接執行,當然,使用Eclipse直接開啟對應的工程來編譯也是可以的。

更新Kernel

先建立一個資料夾Simulator_Linux,其下有三個目錄

FreeRTOS_Kernel
inc
src

FreeRTOS_Kernel中儲存核心程式碼,inc和src儲存APP的程式碼,當然,可以按照自己的愛好自行調整目錄結構。

再去FreeRTOS官網下載最新的Kernel程式碼,解壓後進入FreeRTOS\Source目錄。
按照官方的示例,將最新的程式碼拷貝到FreeRTOS_Kernel目錄中。
include目錄中的標頭檔案不管三七二十一全拷貝過來即可(我懶,不想一個個去梳理>_<)。
.c檔案只需要拷貝croutine.c, list.c, queue.c以及tasks.c即可。(croutine其實也可以不用拷貝,但是要做一些配置)
portable資料夾不從FreeRTOS\Source拷貝,而從simulator的示例中拷貝(\Posix_GCC_Simulator\FreeRTOS_Posix\FreeRTOS_Kernel)

退回到上層目錄,在將官方示例的simulator的根目錄下的FreeRTOSConfig.h拷貝到inc目錄下。
在src目錄下建立main.c檔案,在其中定義一個空的main函式即可。

此時,我們就擁有了一個完整的Kernel的程式碼,當然這個時候還是沒法編譯的,一來缺少makefile,二來portable的檔案與最新的Kernel其實並不完全匹配。當然,APP的程式碼也就是main函式的程式碼也還是空的。

Makefile

參考官方示例的makefile,在根目錄下建立Makefile檔案,同樣在子目錄下也包含兩個subdir.mk用來編譯需要的對應的.o,具體不再贅述:
Makefile:

RM := rm -rf

PROJ_ROOT  :=.
BUILD_TMP  :=$(PROJ_ROOT)/tmp
TARGET_INC := -I$(PROJ_ROOT)/inc \
              -I$(PROJ_ROOT)/FreeRTOS_Kernel/include \
              -I$(PROJ_ROOT)/FreeRTOS_Kernel/portable/GCC/Posix 

-include subdir.mk
-include FreeRTOS_Kernel/subdir.mk

ifneq ($(MAKECMDGOALS),clean)
ifneq ($(strip $(C_DEPS)),)
-include $(C_DEPS)
endif
endif

all:simulator_linux.bin

simulator_linux.bin: $(OBJS)
    @echo 'Building target: [email protected]'
    gcc -pthread -lrt -o"simulator_linux.bin" $(OBJS) $(LIBS)
    @echo 'Finished building target: [email protected]'
    @echo ' '

clean:
    -$(RM) $(OBJS)$(C_DEPS)$(EXECUTABLES) simulator_linux.bin
    -@echo ' '

.PHONY: all clean dependents
.SECONDARY:

subdir.mk:

C_SRCS += \
$(PROJ_ROOT)/src/main.c 

OBJS += \
$(BUILD_TMP)/main.o 

C_DEPS += \
$(BUILD_TMP)/main.d 

# Each subdirectory must supply rules for building sources it contributes
$(BUILD_TMP)/%.o: $(PROJ_ROOT)/src/%.c
    @echo 'Building file: $<'
    gcc -DUSE_STDIO=1 -D__GCC_POSIX__=1 $(TARGET_INC) -O2 -Wall -c -fmessage-length=0 -pthread -lrt -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"[email protected]" "$<"
    @echo 'Finished building: $<'
@echo ' '

FreeRTOS_Kernel/subdir.mk

C_SRCS += \
$(PROJ_ROOT)/FreeRTOS_Kernel/croutine.c \
$(PROJ_ROOT)/FreeRTOS_Kernel/list.c \
$(PROJ_ROOT)/FreeRTOS_Kernel/queue.c \
$(PROJ_ROOT)/FreeRTOS_Kernel/tasks.c \
$(PROJ_ROOT)/FreeRTOS_Kernel/portable/GCC/Posix/port.c \
$(PROJ_ROOT)/FreeRTOS_Kernel/portable/MemMang/heap_3.c 

OBJS += \
$(BUILD_TMP)/croutine.o \
$(BUILD_TMP)/list.o \
$(BUILD_TMP)/queue.o \
$(BUILD_TMP)/tasks.o \
$(BUILD_TMP)/port.o \
$(BUILD_TMP)/heap_3.o 

C_DEPS += \
$(BUILD_TMP)/croutine.d \
$(BUILD_TMP)/list.d \
$(BUILD_TMP)/queue.d \
$(BUILD_TMP)/tasks.d \
$(BUILD_TMP)/port.d \
$(BUILD_TMP)/heap_3.d 

$(BUILD_TMP)/%.o: $(PROJ_ROOT)/FreeRTOS_Kernel/%.c
    @echo 'Building file: $<'
    gcc -DUSE_STDIO=1 -D__GCC_POSIX__=1 $(TARGET_INC) -O2 -Wall -c -fmessage-length=0 -pthread -lrt -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"[email protected]" "$<"
    @echo 'Finished building: $<'
    @echo ' '

$(BUILD_TMP)/%.o: $(PROJ_ROOT)/FreeRTOS_Kernel/portable/GCC/Posix/%.c
    @echo 'Building file: $<'
    gcc -DUSE_STDIO=1 -D__GCC_POSIX__=1 $(TARGET_INC) -O2 -Wall -c -fmessage-length=0 -pthread -lrt -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"[email protected]" "$<"
    @echo 'Finished building: $<'
    @echo ' '

$(BUILD_TMP)/%.o: $(PROJ_ROOT)/FreeRTOS_Kernel/portable/MemMang/%.c
    @echo 'Building file: $<'
    gcc -DUSE_STDIO=1 -D__GCC_POSIX__=1 $(TARGET_INC) -O2 -Wall -c -fmessage-length=0 -pthread -lrt -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"[email protected]" "$<"
    @echo 'Finished building: $<'
@echo ' '

OK,編譯體系已經搞好。此時在根目錄下直接敲 make all 就應該可以進行編譯啦。

配置更新

此時直接make all會發現有一大堆錯誤,這是因為FreeRTOS的版本更新後一些結構體的名字發生了變化,在FreeRTOS.h中有一個相容性的巨集可以控制一部分的相容性,但是因為版本跨度比較大, 我們依然需要在portmacro.h中做適當的適配:

/*-----------------------------------------------------------*/
typedef portSTACK_TYPE StackType_t;
typedef portBASE_TYPE BaseType_t;
typedef unsigned long UBaseType_t;

#define portTICK_PERIOD_MS                                ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#if( configUSE_16_BIT_TICKS == 1 )
        typedef unsigned portSHORT TickType_t;
        #define portMAX_DELAY ( TickType_t ) 0xffff
#else
        typedef unsigned portLONG TickType_t;
        #define portMAX_DELAY ( TickType_t ) 0xffffffff
#endif

/*-----------------------------------------------------------*/
/*
#if( configUSE_16_BIT_TICKS == 1 )
    typedef unsigned portSHORT portTickType;
    #define portMAX_DELAY ( portTickType ) 0xffff
#else
    typedef unsigned portLONG portTickType;
    #define portMAX_DELAY ( portTickType ) 0xffffffff
#endif
*/
/*-----------------------------------------------------------*/

編譯執行

此時再make clean後重新make all,編譯即可通過。但是實際上main.c裡並沒有執行任何程式碼,所以感受不到FreeRTOS的實際效果,我們在main.c中新增一些程式碼,來建立兩個任務並通過訊息佇列來傳遞一些資料:

#include <stdio.h>
#include <stdlib.h>
#include "main.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

static void vTask1( void *pvParameters );
static void vTask2( void *pvParameters );

int main()
{
    static xQueueHandle xTestQueue;
    xTestQueue = xQueueCreate( 10, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) );
    xTaskCreate( vTask1, "vTask1", configMINIMAL_STACK_SIZE, ( void * ) &xTestQueue, tskIDLE_PRIORITY, NULL );
    xTaskCreate( vTask2, "vTask2", configMINIMAL_STACK_SIZE, ( void * ) &xTestQueue, tskIDLE_PRIORITY, NULL );

    vTaskStartScheduler();
    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);
            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 ) )
        {
            if( xQueueReceive( *pxQueue, &usData, ( portTickType ) 0 ) == pdPASS )
            {
                printf("Task2 received:%d\r\n", usData);
            }
        }
        vTaskDelay( 5000 );
    }
}

/********************************************************/
/* This is a stub function for FreeRTOS_Kernel */
void vMainQueueSendPassed( void )
{
    return;
}

/* This is a stub function for FreeRTOS_Kernel */
void vApplicationIdleHook( void )
{
    return;
}

再次重新編譯,執行編譯後在根目錄下生成的simulator_linux.bin,即可看到兩個Task之間的互動過程:
Result

總結

FreeRTOS的核心極其小巧,只需要幾個簡單的檔案就可以進行編譯執行,當在不同的硬體上進行移植的時候,只需要修改portable目錄裡的檔案即可完成對硬體的適配,實際上官方也提供了大量的已經完成移植的裝置的portable檔案,我們只需要簡單的拷貝過來即可。^_^

btw: 本文完整的程式碼見我的github. :)

相關推薦

Linux實現FreeRTOS簡單模擬器

FreeRTOS 基礎知識不贅述,請參考朱工的專欄, 本文主要描述怎麼在Linux的環境下跑一個FreeRTOS的模擬器 官方示例 FreeRTOS的官方提供了一個在Linux下的Simulator的示例,但是用的Kernel的版本非常老,是V6的版本

Linux實現 OpenSSL 簡單加密與解密字串

場景 shell指令碼中存在明文密碼 客戶要求禁止使用明文密碼,密碼做加密處理. 方案 在網上了解到了Linux OpenSSL加密解密工具 可以指定各種加密演算法為字元,檔案做加密處理. 加密的案例比較多,解密的寥寥無幾. 有興趣的可以去查下中文教程 案例中使用加密演算法 : AES 例項

linux的一個簡單執行緒安全記憶體池實現

這裡提供一個簡單執行緒安全記憶體池, 基於linux pthread 如下圖: 具體的資料結構: typedef struct LocMap{ char * point;

Linux實現簡單Echo中繼伺服器

Linux下編寫一個Echo中繼伺服器,echo客戶端通過它獲取Echo伺服器的響應。中繼伺服器能同時作為多個echo伺服器的中繼,並且具有一個簡單的負載均衡演算法。 1. 伺服器與客戶端描述與設計 支援多個伺服器進行Echo服務,伺服器需要設定輸入埠引數,伺服器和客戶

Linux實現簡單的檔案上傳至git

文章屬於自己整理的學習筆記,針對新手~ ================================================================================

Linux實現一個簡單的進度條【轉】

做的 會有 發現 文件 rsquo 實時 時間 改進 常見 轉自:http://blog.csdn.net/yuehailin/article/details/53999288 說起進度條,其實大家常常見到,比如說你在下載視頻或文件的時候,提示你當前下載進度的就是我們今天

linux實現nginx安裝實現端口區分,域名區分

方便 img per 修改配置文件 mpat 直接 exp reg 獨立 nginx是一款高性能的http服務器/反向代理服務器及電子郵件代理服務器. 官方網站: http://nginx.org/ 1、http服務器。Nginx是一個http服務可以獨立提供http服務。

Linux實現免密碼登錄(超詳細)_Linux_腳本之家

.html ini 服務 meta word ssh密鑰 密碼登錄 rda lin Linux ssh密鑰登錄和取消密鑰登錄 2016-05-18? linux技巧 ? 暫無評論 在VPS中利用vi編輯器編輯sshd的配置文件 vi /etc/ssh/sshd_conf

Linux實現多網卡綁定

bond team nmctl 使用bind綁定多個網卡 由於服務器上對於可用性的要求都比較高,對於各項功能都會有有冗余設計,比如,磁盤、電源、網卡、甚至服務器本身等等,今天嘗試做一下網卡綁定實現網卡的冗余。網卡綁定的實現表面上看起來有些像是硬盤實現邏輯卷,都是通過創建一個邏輯設備來實現的。實現網

LinuxMySQL的簡單操作

max name 失效 發現 root用戶 修改 statement times l數據庫 更改mysql數據庫root的密碼 首次進入數據庫是不用密碼的: [root@localhost ~]# /usr/local/mysql/bin/mysql -uroot W

linux實現目錄即文件的完整刪除

truct remove define limits In continue tin mit ret 功能:   1、刪除目錄   2、刪除文件   3、刪除不為空的目錄即下屬文件 #ifndef _DELETE_FILE #define _DELETE_FILE #in

linux實現ssh免密登錄

復制 由於 linu width tro watermark linux roc 密碼登錄 設置ssh無密碼登錄可以提高我們主機的安全性。ssh 無密碼登錄要使用公鑰與私鑰。linux下可以用ssh-keygen生成公鑰/私鑰對,接下來以Centos為例。例圖:實驗主機A無

Linux實現彩色進度條程式

程式碼: #include <stdio.h> #include <unistd.h> #include <string.h> int main() { int i = 0; char bar[101]; const char *la

Linux實現指令碼監測特定程序佔用記憶體情況

記憶體洩露是C/C++程式設計師經常需要面對的問題,除了有效地經常查找出記憶體洩露的位置外,在嵌入式的開發中,還經常需要確定自己寫的程式是否存在記憶體洩露的情況 Linux系統下,我們可以利用以下命令來獲取特定程序的執行情況: cat /proc/$PID/status 其中

Linux實現客戶端兩連跳ping百度,修改dns和nmcil的用法

1.客戶端跳兩次路由器ping百度 rht vmctl reset 重置虛擬機器 真機和虛擬機器開啟火牆策略 用在配置網路單元學的修改兩機閘道器 設定server為雙網絡卡路由端接觸客戶端Desktop閘道器為1.1.1.100 路由器端設定GATEWAY為真機,記得syste

Linux實現進度條程式. 通過makefile進行編譯. 建議自主完成一個彩色的進度條.

Linux下用C語言完成一個彩色進度條 1.建一個Makefile檔案 2.vim Makefile test:test.c

Linux一個最簡單的不依賴第三庫的的C程式(1)

如下程式碼是一段彙編程式碼,雖然標題中使用了C語言這個詞語,但下面確實是一段彙編程式碼,弄清楚了這個程式碼,後續的知識點才會展開。 #PURPOSE: Simple program that exits and returns a # status code back to the Lin

linux的selinux簡單運用

首先通過getenforce命令我們可以檢視selimux狀態 通過vim /etc/sysconfig/selinux 編輯selimux配置檔案,啟動 引數SELINUX=enforcing  強制狀態,不可以操作,也會發出警告    &

Linux實現腳本監測特定進程占用內存情況

date 信息 進程pid -s 虛擬內存 狀況 文件 python 完整 Linux系統下,我們可以利用以下命令來獲取特定進程的運行情況: cat /proc/$PID/status 其中PID是具體的進程號,這個命令打印出/proc/特定進程/status文件的內

Linux實現Mysql定時任務備份資料

建立備份目錄 本例項將建立目錄放置於/mnt目錄下,可根據具體情況放置於其他目錄: cd /mnt mkdir dbback pwd /mnt/dbback 建立shell指令碼 指令碼名稱可根據自己規範進行自定義: vim bcmysql.sh 進入編輯器