简明易懂的stm32f103c8t6移植ucosiii教程及完整工程分享

文章目录

  • 前言
  • 一、ucosiii是什么?
  • 二、移植步骤
  • 1.准备工作
  • 2.修改芯片信息
  • 3.解决delay不准的问题
  • 4. 删除lcd文件以及修改led.h中GPIO
  • 三. 现象验证

  • 前言

       最近在做一个机器人项目,需要使用到stm32f103c8t6核心板。考虑程序中的多任务特性,因此决定使用ucosiii用于多任务管理。ucosiii移植可能对于一些嵌入式老鸟来说,可能是信手拈来,但是对于很多新手特别是刚入门的小白来说还是有一定的难度的。尤其是全网的移植教程过于杂乱良莠不齐,甚至有些博主将移植好的工程设置为付费下载。这里决定分享下我的移植过程,并在最后附上工程模板以供学习和参考。
       如果你同样是嵌入式方向的学习者,记得点击下关注哦,后期会分享更多的干货教程与实战项目以供大家学习。


    一、ucosiii是什么?

       uCosIII,全称为Micro-Controller Operating Systems III,是一种轻量级实时操作系统(RTOS),被广泛应用于嵌入式系统中。它由美国Micrium公司开发,提供了一整套完整的多任务处理(Multi-Tasking)功能和软件源码,使得嵌入式系统开发能够更加快捷和高效。uCosIII具有高度的可定制性和可伸缩性,允许使用者按需组装需要的软件库,提供了良好的可移植性,可以在多种处理器和微控制器上运行。
       我们这里是基于正点原子官方ZET6的例程进行移植的,后续实现串口打印与点灯进行验证工程的正确性。

    二、移植步骤

    1.准备工作

       这里需要事先准备正点原子的工程:
       LED文件夹是移植成功的工程文件,UCOSIII源码是官方下载的源码,倘若想移植到别的系列可以直接移植UCOSIII源码,例6-1是原子哥的原工程。

    链接:https://pan.baidu.com/s/18i3NMoFQqX9tNGFYCVjQ4w?pwd=1207
    提取码:1207

    2.修改芯片信息

       打开下载好的正点原子例程,点击魔术棒,选择芯片类型将其更改成STM32F103C8,然后确定。

    3.解决delay不准的问题

       此时由于STM32F03ZET6和STM32F103C8T6的外部时钟频率不一样,因此delay.c里的delay_ms函数定义会出现不准的情况,这里的解决办法是在主函数的delay函数初始化之前添加 SystemInit();函数。
       这里解释下原理以及SystemInit()函数的功能:
       在STM32系列的微控制器中,SystemInit()函数是在启动时钟和系统时钟之前由硬件启动代码调用的函数之一。它是用于初始化芯片系统时钟和系统时钟源的函数,并通过配置STM32内部时钟发生器(Clock Generator)来为微控制器系统提供正确的系统时钟。SystemInit() 函数还会配置其他必要的系统时钟参数,如AHB总线时钟、APB总线时钟、外设时钟和定时器时钟等。
       在具体实现上,SystemInit()函数会通过读取存储在FLASH内的一组初始值来配置STM32的时钟,这些初始值通常是由厂商提供的初始化文件、库或工具生成的。SystemInit()函数还可以在初始化前或初始化后通过内部或者外部调用进行修改或补充。因此,如果你使用了其他的时钟源或者外设,你可以在SystemInit()函数中添加需要的配置来使STM32适应新的硬件设置。
       总之,SystemInit()函数是STM32系列微控制器启动时钟和系统时钟的实际配置入口,它确保了芯片系统时钟被设置为正确的频率,使得STM32在后续的软件开发和外部设备连接中能够正常运行。

    4. 删除lcd文件以及修改led.h中GPIO

       由于这里我们没有用到lcd,因此我们将他删除,并在main.c中删除lcd相关内容。我们使用到的stm32f103c8t6核心板的led是PC13,因此我们修改led.h与led.c文件。

    led.h
    #ifndef __LED_H
    #define __LED_H	 
    #include "sys.h"
    
    #define LED0 PCout(13)// PC13
    	
    
    void LED_Init(void);//初始化
    
    		 				    
    #endif
    
    led.c
    #include "led.h"
    void LED_Init(void)
    {
     GPIO_InitTypeDef  GPIO_InitStructure;	
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	 //使能PB,PE端口时钟
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 //LED0-->PB.5 端口配置
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
     GPIO_Init(GPIOC, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5
     GPIO_SetBits(GPIOC,GPIO_Pin_13);						 //PB.5 输出高
    }
    

       最后修改main函数,让三个任务:start_task创建task1和task2,创建完就删除自己。task1任务是led一秒闪烁与翻转一次,task2是空任务,task1task2都会通过串口一printf输出提示信息。

    main.c如下:

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    #include "includes.h"
    
    //任务优先级
    #define START_TASK_PRIO		3
    //任务堆栈大小	
    #define START_STK_SIZE 		128
    //任务控制块
    OS_TCB StartTaskTCB;
    //任务堆栈	
    CPU_STK START_TASK_STK[START_STK_SIZE];
    //任务函数
    void start_task(void *p_arg);
    
    ///任务二亮灯,并打印出信息
    //任务优先级
    #define TASK1_TASK_PRIO		4
    //任务堆栈大小	
    #define TASK1_STK_SIZE 		128
    //任务控制块
    OS_TCB Task1_TaskTCB;
    //任务堆栈	
    CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
    void task1_task(void *p_arg);
    
    
    ///任务三,打印任务三执行信息
    //任务优先级
    #define TASK2_TASK_PRIO		5
    //任务堆栈大小	
    #define TASK2_STK_SIZE 		128
    //任务控制块
    OS_TCB Task2_TaskTCB;
    //任务堆栈	
    CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE];
    //任务函数
    void task2_task(void *p_arg);
    
    
    
    //主函数
    int main(void)
    {
    	OS_ERR err;
    	CPU_SR_ALLOC();
    	SystemInit();//由于本工程是由stm32f103zet6移植所来,因此这里因加入本函数确保delay函数正常使用
    	delay_init();  //时钟初始化
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组配置
    	uart_init(115200);   //串口初始化
    	LED_Init();         //LED初始化	
    
    
    	
    	OSInit(&err);		    //初始化UCOSIII
    	OS_CRITICAL_ENTER();	//进入临界区			 
    	//创建开始任务
    	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任务控制块
    				 (CPU_CHAR	* )"start task", 		//任务名字
                     (OS_TASK_PTR )start_task, 			//任务函数
                     (void		* )0,					//传递给任务函数的参数
                     (OS_PRIO	  )START_TASK_PRIO,     //任务优先级
                     (CPU_STK   * )&START_TASK_STK[0],	//任务堆栈基地址
                     (CPU_STK_SIZE)START_STK_SIZE/10,	//任务堆栈深度限位
                     (CPU_STK_SIZE)START_STK_SIZE,		//任务堆栈大小
                     (OS_MSG_QTY  )0,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                     (OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,
                     (void   	* )0,					//用户补充的存储区
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                     (OS_ERR 	* )&err);				//存放该函数错误时的返回值
    	OS_CRITICAL_EXIT();	//退出临界区	 
    	OSStart(&err);      //开启UCOSIII
    }
    
    
    //开始任务任务函数
    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  //当使用时间片轮转的时候
    	 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
    	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
    #endif	
    	
    	OS_CRITICAL_ENTER();	//进入临界区
    	//创建TASK1任务
    	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,		
    				 (CPU_CHAR	* )"Task1 task", 		
                     (OS_TASK_PTR )task1_task, 			
                     (void		* )0,					
                     (OS_PRIO	  )TASK1_TASK_PRIO,     
                     (CPU_STK   * )&TASK1_TASK_STK[0],	
                     (CPU_STK_SIZE)TASK1_STK_SIZE/10,	
                     (CPU_STK_SIZE)TASK1_STK_SIZE,		
                     (OS_MSG_QTY  )0,					
                     (OS_TICK	  )0,					
                     (void   	* )0,					
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                     (OS_ERR 	* )&err);				
    				 
    	//创建TASK2任务
    	OSTaskCreate((OS_TCB 	* )&Task2_TaskTCB,		
    				 (CPU_CHAR	* )"task2 task", 		
                     (OS_TASK_PTR )task2_task, 			
                     (void		* )0,					
                     (OS_PRIO	  )TASK2_TASK_PRIO,     	
                     (CPU_STK   * )&TASK2_TASK_STK[0],	
                     (CPU_STK_SIZE)TASK2_STK_SIZE/10,	
                     (CPU_STK_SIZE)TASK2_STK_SIZE,		
                     (OS_MSG_QTY  )0,					
                     (OS_TICK	  )0,					
                     (void   	* )0,				
                     (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, 
                     (OS_ERR 	* )&err);			 
    	OS_CRITICAL_EXIT();	//退出临界区
    	OSTaskDel((OS_TCB*)0,&err);	//删除start_task任务自身
    }
    
    
    //task1任务函数
    void task1_task(void *p_arg)
    {
    	u8 task1_num=0;
    	OS_ERR err;
    	CPU_SR_ALLOC();
    	p_arg = p_arg;
    	
    	OS_CRITICAL_ENTER();
    	
    	OS_CRITICAL_EXIT();
    	while(1)
    	{
    		task1_num++;	//任务执1行次数加1 注意task1_num1加到255的时候会清零!!
    		LED0= ~LED0;
    		printf("任务1已经执行:%d次\r\n",task1_num);
    //		if(task1_num==5) 
    //		{
    //			OSTaskDel((OS_TCB*)&Task2_TaskTCB,&err);	//任务1执行5此后删除掉任务2
    //			printf("任务1删除了任务2!\r\n");
    //		}
    
    		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
    		
    	}
    }
    
    //task2任务函数
    void task2_task(void *p_arg)
    {
    	u8 task2_num=0;
    	OS_ERR err;
    	CPU_SR_ALLOC();
    	p_arg = p_arg;
    	
    	OS_CRITICAL_ENTER();//进入临界区		
    
    	OS_CRITICAL_EXIT();//退出临界区域
    	while(1)
    	{
    		task2_num++;	//任务2执行次数加1 注意task1_num2加到255的时候会清零!!
    
    		printf("任务2已经执行:%d次\r\n",task2_num);
    
    		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
    	}
    }
    

    三. 现象验证

       使用usb转ttl用串口下载程序至单片机内,打开串口助手、并观察板载LED闪烁情况:

    两秒的视频可以看到灯闪烁两次

    STM32F103C8T6移植UCOSIII验证视频

    制作不易,点个关注吧。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 简明易懂的stm32f103c8t6移植ucosiii教程及完整工程分享

    发表评论