• 《基于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官方移植SoftwareEvalBoardsMicriumuC-Eval-STM32F107uCOS-III;复制Micrium官方移植好的工程的相关文件到UCOS_BSP文件下,路径为:Micrium官方移植SoftwareEvalBoardsMicriumuC-Eval-STM32F107BSP。
    • 准备好了所需文件后,还要将文件添加到我们的工程中去,在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!
    ");
    		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!
    ");
    	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、个人报告(附件)。

  • 相关阅读:
    精益产品探索
    vue 之 pdf预览
    arcgis js 之 渔网工具(调用地图服务)
    arcgis js之卷帘工具
    arcgis js之调用wms服务
    vue-cli3 本地数据模拟后台接口
    cmd设置电脑自动关机
    Arcgis js之web墨卡托(3857)转经纬度坐标(4326)
    arcgis js之地图分屏同步
    arcgis之gp服务发布
  • 原文地址:https://www.cnblogs.com/guyanlin/p/9126428.html
Copyright © 2020-2023  润新知