《基於Cortex-M4的ucOS-III的應用》課程設計 結題報告
《基於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的應用》課程設計 結題報告