1. 程式人生 > >《基於Cortex-M4的ucOS-III的應用》課程設計 結題報告

《基於Cortex-M4的ucOS-III的應用》課程設計 結題報告

設計思路 分享 流程圖 處理 肩膀 wstring ID eva 概念

《基於Cortex-M4的ucOS-III的應用》課程設計 結題報告

  • 小組成員姓名:20155211 解雪瑩 20155217 楊笛 20155227 辜彥霖
  • 指導教師:婁嘉鵬

一、設計方案及可行性分析

  • 題目要求:ucOS-III的移植;設計三個小實驗:單一任務、多任務、並發任務。

1、設計方案

首先運行老師給的範例代碼熟悉開發軟件和開發板的使用;收集資料簡單了解UCOSIII的基本概念,然後進行UCOSIII移植(移植到STM32f407開發板);移植成功後開始進行UCOSIII實例編程(實例老師給了,就只需要進行代碼修改與調試運行)

2、可行性分析

  • uC/OS-III(Micro C OS Three微型的C語言編寫的操作系統第3版)是一個可升級、可固化、基於優先級的實時內核。它管理的任務個數不受限制。它是第三代內核,提供了現代實時內核所期望的所有功能包括資源管理、同步、內部任務交流等。uC/OS-III也提供了很多特性是在其他實時內核中所沒有的,比如能在運行時測量運行性能,直接得發送信號或消息給任務,任務能同時等待多個信號量和消息隊列。

  • OSIII有以下幾個重要特性:
  • 極短的關中斷時間
  • 任務數目不受限制
  • 優先級數量不受限制
  • 內核對象數目不受限制
  • 軟件定時器
  • 同時等待多個內核對象
  • 直接向任務發送信號
  • 直接向任務發送消息
  • 任務寄存器
  • 任務時鐘節拍處理
  • 防止死鎖

  • 所謂移植,是指能讓uC/OS-III在某個微處理器或者為控制器上能夠運行。為了方便移植,uC/OS-III的絕大部分代碼使用C語言寫的。在移植過程中我們重點是需要用C語言和匯編語言編寫一些與處理器有關的代碼。而且uC/OS-III中那些與CPU寄存器打交道的代碼只能用匯編語言編寫(除非C編譯器支持內嵌匯編語言)。得益於uC/OS-III在設計時對可移植性的充分考慮,其在各個平臺上的移植還是比較容易的。值得一提的是Micrium公司已經在各個主流的處理器上做好了移植工作,這些移植好的代碼在官網上是可以直接免費下載的。我們站在巨人的肩膀上從官方移植文件入手。

二、詳細設計思路

1.UCOSIII移植過程

1)移植成功的幾個前提條件:

  • 處理器有可用的ANSI C編譯器,能生成可重入性代碼。
  • 處理器支持中斷,並且可產生定時中斷(通常在10~1000Hz之間)。
  • 可以開關中斷。
  • 處理器支持能夠容納足夠多的數據(數千字節)的硬件堆棧。
  • 處理器有將堆棧指針和其他CPU寄存器讀出並存儲到堆棧或內存中的指令。
  • 處理器有足夠的RAM空間用來存儲UCOSIII的變量、數據結構體和內部任務堆棧。
  • 編譯器應該支持32位數據類型。

移植主要涉及三方面的內容:CPU、操作系統和板級代碼(板級支持包BSP)。我們的移植對象為STM32F407。

2)移植具體過程

  • 選擇一個基礎的用ST官方庫代碼進行編寫的程序作為移植基礎。這裏我們選擇一個簡單的跑馬燈程序作為移植的基礎程序(跑馬燈程序本來是裸機跑的程序,我們要將uC/OS-III移植到該跑馬燈程序中)。
  • 下載UCOSIII源碼。
  • 在待移植的工程目錄(跑馬燈程序)中新建一個uC/OS-III文件夾,然後將我們下載的Micrium官方移植工程中的uC-CPU、uC-LIB、UCOS-III這三個文件復制到工程中。這裏還需在UCOSIII文件中新建兩個文件:UCOS_BSP、UCOS_CONFIG。向UCOS_CONFIG中添加文件,復制Micrium官方移植好的工程中的相關文件到UCOS_CONFIG文件夾下,文件路徑為:Micrium官方移植\Software\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III;復制Micrium官方移植好的工程的相關文件到UCOS_BSP文件下,路徑為:Micrium官方移植\Software\EvalBoards\Micrium\uC-Eval-STM32F107\BSP。
  • 準備好了所需文件後,還要將文件添加到我們的工程中去,在KEIL中先添加分組,如下圖所示:

技術分享圖片

  • 添加完分組還要給分組添加文件,添加情況如圖所示:

技術分享圖片

  • 為了編譯時能找到相關文件,這裏還需要設置包含路徑,設置情況如下圖所示:

技術分享圖片

  • 修改bsp.c和bsp.h文件;修改os_cpu_c.c文件;修改os_cfg_app.h
  • 修改sys.h: 將SYSTEM_SUPPORT_OS置1,以支持UCOS操作系統。

2.說明程序中用到的關鍵數據類型的定義,繪制關鍵程序的流程圖,以及各子模塊間的調用關系圖。

  • 本次實驗中,我們用到FSMC驅動LCD,通過前面電路的介紹,我們知道TFTLCD的RS接在FSMC的A18上面,CS接在FSMC_NE4上,並且是16位數據總線,即我們使用的是FSMC的第四區,我們在lcd.h裏面定義LCD操作結構體為:

    //LCD地址結構體
    typedef struct
    {
    u16 LCD_REG;
    u16 LCD_RAM;
    } LCD_TypeDef;
    //使用NOR/SRAM的 Bank1.sector4,地址位HADDR[27,26]=11 A18作為數據命令區分線 
    //註意16位數據總線時STM32內部地址會右移一位對齊!               
    #define LCD_BASE        ((u32)(0x6C000000 | 0x0007FFFE))
    #define LCD             ((LCD_TypeDef *) LCD_BASE)
  • 其中,LCD_BASE,必須根據外部電路的連接設定,我們使用Bank1.sector4就是從地址0X6C000000開始,而0X0007FFFE,是A18的偏移量。我們將這個地址強制轉換成LCD_TypeDef的結構體地址,就可以得到LCD->LCD_REG的地址為0X6C07FFFE,而LCD->LCD_RAM的地址為0X6C080000。所以有了這個定義,當我們要往LCD寫命令/數據時,可以這樣寫:

    LCD->LCD_REG=CMD;//寫命令
    LCD->LCD_RAM=DATA;//寫數據
    而讀的時候反過來操作就可以了,如下表示:
    CMD =LCD->LCD_REG;//讀LCD寄存器
    DATA =LCD->LCD_RAM;//讀LCD數據
  • 接下來介紹lcd.h裏的另一個重要結構體:

//寫寄存器函數
//regval:寄存器值
void LCD_WR_REG(vu16 regval)
{   
    regval=regval;      //使用-O2優化的時候,必須插入的延時
    LCD->LCD_REG=regval;//寫入要寫的寄存器序號     
}
//寫LCD數據
//data:要寫入的值
void LCD_WR_DATA(vu16 data)
{     
    data=data;          //使用-O2優化的時候,必須插入的延時
    LCD->LCD_RAM=data;       
}
//讀LCD數據
//返回值:讀到的值
u16 LCD_RD_DATA(void)
{
    vu16 ram;           //防止被優化
    ram=LCD->LCD_RAM;   
    return ram;  
}                      
//寫寄存器
//LCD_Reg:寄存器地址
//LCD_RegValue:要寫入的數據
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue)
{   
    LCD->LCD_REG = LCD_Reg;     //寫入要寫的寄存器序號     
    LCD->LCD_RAM = LCD_RegValue;//寫入數據               
}      
//讀寄存器
//LCD_Reg:寄存器地址
//返回值:讀到的數據
u16 LCD_ReadReg(u16 LCD_Reg)
{                                          
    LCD_WR_REG(LCD_Reg);        //寫入要讀的寄存器序號
    delay_us(5);          
    return LCD_RD_DATA();       //返回讀到的值
}   
//開始寫GRAM
void LCD_WriteRAM_Prepare(void)
{
    LCD->LCD_REG=lcddev.wramcmd;      
}    
//LCD寫GRAM
//RGB_Code:顏色值
void LCD_WriteRAM(u16 RGB_Code)
{                               
    LCD->LCD_RAM = RGB_Code;//寫十六位GRAM
}
  • 因為FSMC自動控制了WR/RD/CS等這些信號,通過這些函數,我們就可以對LCD進行各種操作了。

  • 接下來要介紹的函數是坐標設置函數,代碼如下:

    //設置光標位置
    //Xpos:橫坐標
    //Ypos:縱坐標
    void LCD_SetCursor(u16 Xpos, u16 Ypos)
    {    
    if(lcddev.id==0X9341||lcddev.id==0X5310)
    {           
        LCD_WR_REG(lcddev.setxcmd); 
        LCD_WR_DATA(Xpos>>8); 
        LCD_WR_DATA(Xpos&0XFF);  
        LCD_WR_REG(lcddev.setycmd); 
        LCD_WR_DATA(Ypos>>8); 
        LCD_WR_DATA(Ypos&0XFF);
    }else if(lcddev.id==0X6804)
    {
        if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//橫屏時處理
        LCD_WR_REG(lcddev.setxcmd); 
        LCD_WR_DATA(Xpos>>8); 
        LCD_WR_DATA(Xpos&0XFF);  
        LCD_WR_REG(lcddev.setycmd); 
        LCD_WR_DATA(Ypos>>8); 
        LCD_WR_DATA(Ypos&0XFF);
    }else if(lcddev.id==0X5510)
    {
        LCD_WR_REG(lcddev.setxcmd); 
        LCD_WR_DATA(Xpos>>8); 
        LCD_WR_REG(lcddev.setxcmd+1); 
        LCD_WR_DATA(Xpos&0XFF);  
        LCD_WR_REG(lcddev.setycmd); 
        LCD_WR_DATA(Ypos>>8); 
        LCD_WR_REG(lcddev.setycmd+1); 
        LCD_WR_DATA(Ypos&0XFF);     
    }else
    {
        if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//橫屏其實就是調轉x,y坐標
        LCD_WriteReg(lcddev.setxcmd, Xpos);
        LCD_WriteReg(lcddev.setycmd, Ypos);
    }    
    }       
  • 該函數實現將LCD的當前操作點設置到指定坐標(x,y)。因為不同型號的屏不太一樣,所以進行了區別對待。

3.實驗現象

1.編譯結果

技術分享圖片

2.下載

技術分享圖片

3.LCD顯示高優先級任務

技術分享圖片

4.LCD顯示中優先級任務

技術分享圖片

5.LCD顯示低優先級任務

技術分享圖片

三、源代碼及註釋

int main(void)
{
    OS_ERR err;
    CPU_SR_ALLOC();
    LCD_Init();                     //初始化LCD
    POINT_COLOR = RED;
    LCD_ShowString(30,10,200,16,16,"Apollo STM32F4/F7");    
    LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 10-5");
    LCD_ShowString(30,50,200,16,16,"Mutex test");
    LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
    LCD_ShowString(30,90,200,16,16,"2018/5/30");

//創建HIGH任務
    OSTaskCreate((OS_TCB    * )&High_TaskTCB,       
         (CPU_CHAR  * )"High task",         
                 (OS_TASK_PTR )high_task,           
                 (void      * )0,                   
                 (OS_PRIO     )HIGH_TASK_PRIO,     
                 (CPU_STK   * )&HIGH_TASK_STK[0],   
                 (CPU_STK_SIZE)HIGH_STK_SIZE/10,    
                 (CPU_STK_SIZE)HIGH_STK_SIZE,       
                 (OS_MSG_QTY  )0,                   
                 (OS_TICK     )0,                   
                 (void      * )0,                   
(OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP,
                 (OS_ERR    * )&err);   
         OS_CRITICAL_EXIT();    //退出臨界區
    OSTaskDel((OS_TCB*)0,&err); //刪除start_task任務自身
}

//開始任務函數
void start_task(void *p_arg)
{
    OS_ERR err;
    CPU_SR_ALLOC();
    p_arg = p_arg;
    CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);    //統計任務                
#endif  
#ifdef CPU_CFG_INT_DIS_MEAS_EN      //如果使能了測量中斷關閉時間
    CPU_IntDisMeasMaxCurReset();    
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN  //當使用時間片輪轉的時候
    //使能時間片輪轉調度功能,設置默認的時間片長度
    OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif  
        
    OS_CRITICAL_ENTER();    //進入臨界區
    //創建一個互斥信號量
    OSMutexCreate((OS_MUTEX*    )&TEST_MUTEX,
                  (CPU_CHAR*    )"TEST_MUTEX",
                  (OS_ERR*      )&err);

    //創建HIGH任務
    OSTaskCreate((OS_TCB    * )&High_TaskTCB,       
         (CPU_CHAR  * )"High task",         
                 (OS_TASK_PTR )high_task,           
                 (void      * )0,                   
                 (OS_PRIO     )HIGH_TASK_PRIO,     
                 (CPU_STK   * )&HIGH_TASK_STK[0],   
                 (CPU_STK_SIZE)HIGH_STK_SIZE/10,    
                 (CPU_STK_SIZE)HIGH_STK_SIZE,       
                 (OS_MSG_QTY  )0,                   
                 (OS_TICK     )0,                   
                 (void      * )0,                   
(OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP,
                 (OS_ERR    * )&err);

//創建MIDDLE任務
//  OSTaskCreate((OS_TCB    * )&Middle_TaskTCB,     
//           (CPU_CHAR  * )"Middle task",       
//               (OS_TASK_PTR )middle_task,             
//               (void      * )0,                   
//               (OS_PRIO     )MIDDLE_TASK_PRIO,     
//               (CPU_STK   * )&MIDDLE_TASK_STK[0], 
//               (CPU_STK_SIZE)MIDDLE_STK_SIZE/10,  
//               (CPU_STK_SIZE)MIDDLE_STK_SIZE,     
//               (OS_MSG_QTY  )0,                   
//               (OS_TICK     )0,                   
//               (void      * )0,                   
//               (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP,
//               (OS_ERR    * )&err);

//創建LOW任務
//  OSTaskCreate((OS_TCB    * )&Low_TaskTCB,        
//        (CPU_CHAR * )"Low task",      
//                (OS_TASK_PTR )low_task,           
//                (void     * )0,                   
//                (OS_PRIO    )LOW_TASK_PRIO,     
//                (CPU_STK   * )&LOW_TASK_STK[0],   
//                (CPU_STK_SIZE)LOW_STK_SIZE/10,    
//                (CPU_STK_SIZE)LOW_STK_SIZE,       
//                (OS_MSG_QTY  )0,                  
//                (OS_TICK    )0,                   
//                (void     * )0,                   
//                (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP,
//                (OS_ERR   * )&err);                    
    OS_CRITICAL_EXIT(); //退出臨界區
    OSTaskDel((OS_TCB*)0,&err); //刪除start_task任務自身
}

//高優先級任務的任務函數
void high_task(void *p_arg)
{
    u8 num;
    OS_ERR err;
    CPU_SR_ALLOC();
    LCD_Init();                     //初始化LCD
    while(1)
    {
        LCD_ShowString(30,110,200,16,16,"2018/5/30");
        POINT_COLOR = BLACK;
            OS_CRITICAL_ENTER();
            LCD_DrawRectangle(5,110,115,314);   //畫一個矩形 
        OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err);  //延時500ms
    }
}
//中等優先級任務的任務函數
void middle_task(void *p_arg)
{   
    u8 num;
    OS_ERR err;
    CPU_SR_ALLOC();
    POINT_COLOR = BLACK;
    OS_CRITICAL_ENTER();
    LCD_DrawRectangle(125,110,234,314); //畫一個矩形 
    LCD_DrawLine(125,130,234,130);      //畫線
    POINT_COLOR = BLUE;
    LCD_ShowString(126,111,110,16,16,"Middle Task");
    OS_CRITICAL_EXIT();
    while(1)
    {
        num++;
        printf("middle task Running!\r\n");
        LCD_Fill(126,131,233,313,lcd_discolor[13-num%14]); //填充區域
        LED0=!LED0;
        OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);  //延時1s
    }
}
//低優先級任務的任務函數
void low_task(void *p_arg)
{   
    static u32 times;
    OS_ERR err;
    while(1)
    {
                LCD_ShowString(30,90,200,16,16,"Low Task");
    OSMutexPend (&TEST_MUTEX,0,OS_OPT_PEND_BLOCKING,0,&err);//請求互斥信號量
    printf("low task Running!\r\n");
    for(times=0;times<20000000;times++)
    {
        OSSched();                              //發起任務調度
    }
    OSMutexPost(&TEST_MUTEX,OS_OPT_POST_NONE,&err);//釋放互斥信號量
    OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);   //延時1s
    }
}
  • 該部分代碼通過切換不同的任務來得到不同的實驗現象。其中,高優先級任務將顯示一個矩形並帶有實驗日期;中優先級將顯示綠色矩形+橫線,帶有“Middle Task”字樣;低優先級將顯示“Low Task”字樣和實驗日期。

五、個人報告

1、小組貢獻排序及依據(每個人的工作量)。

  • 20155227辜彥霖:參與課設題目討論及完成全過程;資料收集;負責主要代碼調試。
  • 20155217楊笛:參與課設題目討論及完成全過程;輔助調試代碼;撰寫實驗指導;撰寫小組結題報告。
  • 20155211解雪瑩:參與課設題目討論及完成全過程;輔助調試代碼。

    2、個人報告(附件)。

《基於Cortex-M4的ucOS-III的應用》課程設計 結題報告