文章目录
一.题目要求
学习嵌入式实时操作系统(RTOS),以uc/OS为例,将其移植到stm32F103上,构建至少3个任务(task):其中两个task分别以1s和3s周期对LED等进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。记录详细的移植过程。
二.UC/OS介绍
1.UC/OS简介
-UCOS是Micrium公司出品的RTOS类实时操作系统, UCOS目前有两个版本:UCOSII和UCOSIII。
- UCOSIII是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制,提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。
- UCOSIII是用C和汇编来写的,其中绝大部分都是用C语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的,
UCOSIII结构简洁,可读性很强!非常适合初次接触嵌入式实时操作系统学生、嵌入式系统开发人员和爱好者学习。
2.组成
μC/OS可以大致分成核心、任务处理、时间处理、任务同步与通信,CPU的移植等5个部分。
核心部分(OSCore.c):是操作系统的处理核心,包括操作系统初始化、操作系统运行、中断进出的前导、时钟节拍、任务调度、事件处理等多部分;能够维持系统基本工作的部分都在这里
任务处理部分(OSTask.c):任务处理部分中的内容都是与任务的操作密切相关的;包括任务的建立、删除、挂起、恢复等等
时钟部分(OSTime.c):μC/OS-II中的最小时钟单位是timetick(时钟节拍);任务延时等操作是在这里完成的
任务同步和通信部分:为事件处理部分,包括信号量、邮箱、消息队列、事件标志等部分;主要用于任务间的互相联系和对临界资源的访问
CPU的移植部分:这部分内容由于牵涉到SP等系统指针,所以通常用汇编语言编写;主要包括中断级任务切换的底层实现、任务级任务切换的底层实现、时钟节拍的产生和处理、中断的相关处理部分等内容
3.uc/OS-II中的数据类型
typedef unsigned char BOOLEAN; typedef unsigned char INT8U;/无符号8位/
typedef signed char INT8S;/带符号8位/ typedef unsigned int INT16U;/无符号16位/
typedef signed int INT16S;/带符号16位/ typedef unsigned long INT32U;/无符号32位数/
typedef signed long INT32S;/带符号32位数/
typedef float FP32;/* 单精度浮点数*/
typedef double FP64;/* 双精度浮点数*/
typedef unsigned int
OS_STK;/堆栈入口宽度/
typedef unsigned int OS_CPU_SR;/寄存器宽度/
- 注意:以上这些类型定义在ARM/OS_CPU.H文件中
4.嵌入式操作系统的基本概念
嵌入式操作系统的特点:
(1)必要性 ---- 嵌入式系统软硬件愈加庞大复杂。
(2)微型化、可裁减 ---- 软、硬件小而精,够用即可。
(3)实时性 ---- 抢占式管理策略,满足时间正确性。
(4)可靠性 ---- 无人值守、自动化设备的使用要求。
(5)易移植 ---- 便于应用到多种的硬件平台。
(6)微内核 ---- 完成OS主要功能的代码很小(附加功能需另挂)。
嵌入式操作系统的基本功能:
多任务管理 ---- 丰富的多任务管理函数供目标系统设计者容易完成多任务应用设计。
内存管理 ---- 动态内存管理充分利用硬件资源。
外设管理 ---- 例如I2C、UART、Timer、SPI等设备的驱动。
实时操作系统VS分时操作系统:
实时性 ---- 即及时性,能够在要求的时间内相应并处理完系统事件。
实时系统的正确性 ---- 既要逻辑正确,还要时间正确。
RTOS ---- 多采用抢占式管理策略,满足时间正确性。
分时OS ---- 多采用时间片轮换调度策略。
5.嵌入式系统的层次结构
6.任务
任务(线程)是简单的程序。单CPU 中,在任何时刻只能是一个任务被执行。
任务看起来像C 函数。在大多数嵌入式系统中,任务通常是无限循环的。任务不能像C 函数那样,它是不能return 的。
在UCOSIII中任务就是程序实体,UCOSIII能够管理和调度这些小任务(程序)。UCOSIII中的任务由三部分组成:任务堆栈
、任务控制块
和任务函数
。
6.1任务堆栈
上下文切换的时候用来保存任务的工作环境,就是STM32的内部寄存器值。
任务堆栈是任务的重要部分,堆栈是在RAM中按照“先进先出(FIFO)”的原则组织的一块连续的存储空间。为了满足任务切换和响应中断时保存CPU寄存器中的内容及任务调用其它函数时的需要,每个任务都应该有自己的堆栈。
任务如何才能切换回上一个任务并且还能接着从上次被中断的地方开始运行?
恢复现场即可,现场就是CPU的内部各个寄存器。因此在创建一个新任务时,必须把系统启动这个任务时所需的CPU各个寄存器初始值事先存放在任务堆栈中。这样当任务获得CPU使用权时,就把任务堆栈的内容复制到CPU的各个寄存器,从而可以任务顺利地启动并运行。
把任务初始数据存放到任务堆栈的工作就叫做任务堆栈的初始化,UCOSIII提供了完成堆栈初始化的函数:OSTaskStkInit()。
当然,用户一般不会直接操作堆栈初始化函数,任务堆栈初始化函数由任务创建函数OSTaskCreate()调用。不同的CPU对于的寄存器和对堆栈的操作方式不同,因此在移植UCOSIII的时候需要用户根据各自所选的CPU来编写任务堆栈初始化函数。
#define START_STK_SIZE 512 //堆栈大小
CPU_STK START_TASK_STK[START_STK_SIZE]; //定义一个数组来作为任务堆栈
任务堆栈初始化
6.2任务控制块
任务控制块是用来记录与任务相关的信息的数据结构,每个任务都要有自己的任务控制块。我们使用OSTaskCreate()函数来创建任务的时候就会给任务分配一个任务控制块。任务控制块由用户自行创建。
USOCIII提供了用于任务控制块初始化的函数:OS_TaskInitTCB()。但是,用户不需要自行初始化任务控制块。因为和任务堆栈初始化函数一样,函数OSTaskCreate()在创建任务的时候会对任务的任务控制块进行初始化。
OS_TCB StartTaskTCB; //创建一个任务控制块
6.3任务函数
由用户编写的任务处理代码,是实实在在干活的,任务函数通常是一个无限循环,也可以是一个只执行一次的任务。任务的参数是一个void类型的,可以可以传递不同类型的数据甚至是函数。
任务函数其实就是一个C语言的函数,但是在使用UCOIII的情况下这个函数不能有用户自行调用,任务函数何时执行执行,何时停止完全有操作系统来控制。
COSIII支持时间片轮转调度,因此在一个优先级下会有多个任务,那么我们就要对这些任务做一个管理,这里使用OSRdyList[]数组管理这些任务。
同一优先级下如果有多个任务的话最先运行的永远是HeadPtr所指向的任务
6.4任务管理
uC/OS中最多可以支持64个任务,分别对应优先级0~63,其中0为最高优先级,63为最低级;系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个
uC/OS提供了任务管理的各种函数调用,包括创建任务,删除任务,改变任务的优先级,任务挂起和恢复等
系统初始化时会自动产生两个任务:一个是空闲任务,它的优先级最低,该任务仅给一个整型变量做累加运算;另一个是统计任务,它的优先级为次低,该任务负责统计当前cpu的利用率
7.正常运行平台要求
a)处理器的C编译器能产生可重入代码
b)用C语言就可以打开和关闭中断
c)处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)
d)处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈
e)处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令
三.工程创建(HAL库)
1.我们首先配置时钟源RCC
2.然后我们配置SYS
3.配置USART1(因为这里我们要用到串口,所以还得初始化USART1)
4.在本次实验中笔者决定使用PC13和PA3作为LED灯的输出管脚,故而将PC13和PA3设为输出。
5.最后我们创建工程即可
四.项目移植
大家可以去官网下载,也可以直接用笔者的百度网盘
链接:百度网盘
提取码:6666
1.准备工作
将uCOS相关文件复制到HAL工程的MDK-ARM文件夹中,之后我们就可以开始移植了。
- 注意:等会的过程可能有点麻烦,但大家要有耐心避免等会产生不必要的麻烦,出现一堆bug。
2.项目添加
首先我们要在工程中创建放置我们需要文件的文件夹,点击Manage Project Items
,新建我们需要的文件夹。
之后就是我们添加文件的过程了(记得打开文件夹的时候选择all files)
大家在寻找文件时可以根据笔者截图显示的路径进行寻找
在
cpu
中添加如下文件
在
LIB
中添加如下文件
在
PORT
中添加如下文件
在
source
中添加如下文件
在
CONFIG
中添加如下文件
在
BSP
中添加如下文件
添加完成后可以看到如图所示的效果
- 注意:在添加完成后一定要点击OK,否则就会前功尽弃重头再来!!!
3.项目导入
添加完成后我们需将改革添加的文件路径导入我们的项目。点击“魔法棒”,选择C/C++,添加路径。读者们按着笔者的图显示的路径进行添加即可,添加完毕后依然记得按ok哟。
五.代码的修改与添加
1.添加bsp.c和bsp.h代码
我们需要向这两个文件夹添加下列代码。
bsp.h:
// bsp.h
#ifndef __BSP_H__
#define __BSP_H__
#include "stm32f1xx_hal.h"
void BSP_Init(void);
#endif
bsp.c:
// bsp.c
#include "includes.h"
#define DWT_CR *(CPU_REG32 *)0xE0001000
#define DWT_CYCCNT *(CPU_REG32 *)0xE0001004
#define DEM_CR *(CPU_REG32 *)0xE000EDFC
#define DBGMCU_CR *(CPU_REG32 *)0xE0042004
#define DEM_CR_TRCENA (1 << 24)
#define DWT_CR_CYCCNTENA (1 << 0)
CPU_INT32U BSP_CPU_ClkFreq (void)
{
return HAL_RCC_GetHCLKFreq();
}
void BSP_Tick_Init(void)
{
CPU_INT32U cpu_clk_freq;
CPU_INT32U cnts;
cpu_clk_freq = BSP_CPU_ClkFreq();
#if(OS_VERSION>=3000u)
cnts = cpu_clk_freq/(CPU_INT32U)OSCfg_TickRate_Hz;
#else
cnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;
#endif
OS_CPU_SysTickInit(cnts);
}
void BSP_Init(void)
{
BSP_Tick_Init();
MX_GPIO_Init();
}
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void CPU_TS_TmrInit (void)
{
CPU_INT32U cpu_clk_freq_hz;
DEM_CR |= (CPU_INT32U)DEM_CR_TRCENA; /* Enable Cortex-M3's DWT CYCCNT reg. */
DWT_CYCCNT = (CPU_INT32U)0u;
DWT_CR |= (CPU_INT32U)DWT_CR_CYCCNTENA;
cpu_clk_freq_hz = BSP_CPU_ClkFreq();
CPU_TS_TmrFreqSet(cpu_clk_freq_hz);
}
#endif
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
CPU_TS_TMR CPU_TS_TmrRd (void)
{
return ((CPU_TS_TMR)DWT_CYCCNT);
}
#endif
#if (CPU_CFG_TS_32_EN == DEF_ENABLED)
CPU_INT64U CPU_TS32_to_uSec (CPU_TS32 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
#if (CPU_CFG_TS_64_EN == DEF_ENABLED)
CPU_INT64U CPU_TS64_to_uSec (CPU_TS64 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
2.修改部分文件相关代码
(1)打开我们的启动文件
在该文件中的第75、76
行和第174、175、178、179
行中将PendSV_Handler和SysTick_Handler改为OS_CPU_PendSVHandler和OS_CPU_SysTickHandler,如图所示:
(2)再从项目中的CONFIG
文件夹中找到app_cfg.h
文件
打开该文件找到该文件的第42
行,将 #define APP_CFG_SERIAL_EN DEF_ENABLED 改为 #define APP_CFG_SERIAL_EN DEF_DISABLED
第85
行,将#define APP_TRACE BSP_Ser_Printf 改为== #define APP_TRACE(void)==
(3)在CONFIG文件夹下找到includes.h
文件
在#include<bsp.h>下添加,#include "gpio.h"和#include “app_cfg.h”
找到第88
行,将 #include <stm32f10x_lib.h> 改为 #include “stm32f1xx_hal.h”
(4)在CONFIG文件夹下找到lib_cfg.h
文件
找到第120
行,将27修改为5
(此处为宏定义设置堆空间的大小,STM32F103C8T6的RAM只有20K,默认的是27K,所以需要我们改小一点,不然会报错的。)
(5)由于我们需要本次实验要求用到串口输出,所以需要我们重定向一下printf函数,找到usart.c
文件,并在其中添加如下代码:
/* USER CODE BEGIN 1 */
int fputc(int ch,FILE *f){
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
return ch;
}
/* USER CODE END 1 */
- 注意:这里由于我们定义的fputc函数中参数f用到了“FILE”类型,但我们没有定义,所以我们需要打开
usart.h
文件(),在开头加上 ,这样就不会报错了
typedef struct __FILE FILE;
- tips:如果不想一点点地去找文件,可以直接找到其调用处,选择open document就可以打开了,如图所示
3.管脚配置
在gpio.c
文件中修改代码
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13|PA3 */
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
4.main函数的撰写
修改main.c
文件
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "usart.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <includes.h>
#include "stm32f1xx_hal.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* 任务优先级 */
#define START_TASK_PRIO 3
#define LED0_TASK_PRIO 4
#define MSG_TASK_PRIO 5
#define LED1_TASK_PRIO 6
/* 任务堆栈大小 */
#define START_STK_SIZE 96
#define LED0_STK_SIZE 64
#define MSG_STK_SIZE 64
#define LED1_STK_SIZE 64
/* 任务栈 */
CPU_STK START_TASK_STK[START_STK_SIZE];
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
CPU_STK MSG_TASK_STK[MSG_STK_SIZE];
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
/* 任务控制块 */
OS_TCB StartTaskTCB;
OS_TCB Led0TaskTCB;
OS_TCB MsgTaskTCB;
OS_TCB Led1TaskTCB;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* 任务函数定义 */
void start_task(void *p_arg);
static void AppTaskCreate(void);
static void AppObjCreate(void);
static void led_pc13(void *p_arg);
static void send_msg(void *p_arg);
static void led_pa3(void *p_arg);
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
OS_ERR err;
OSInit(&err);
HAL_Init();
SystemClock_Config();
//MX_GPIO_Init(); 这个在BSP的初始化里也会初始化
MX_USART1_UART_Init();
/* 创建任务 */
OSTaskCreate((OS_TCB *)&StartTaskTCB, /* Create the start task */
(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,
(OS_TICK ) 0,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
/* 启动多任务系统,控制权交给uC/OS-III */
OSStart(&err); /* Start multitasking (i.e. give control to uC/OS-III). */
}
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
/* YangJie add 2021.05.20*/
BSP_Init(); /* Initialize BSP functions */
//CPU_Init();
//Mem_Init(); /* Initialize Memory Management Module */
#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(); //进入临界区
/* 创建LED0任务 */
OSTaskCreate((OS_TCB * )&Led0TaskTCB,
(CPU_CHAR * )"led_pc13",
(OS_TASK_PTR )led_pc13,
(void * )0,
(OS_PRIO )LED0_TASK_PRIO,
(CPU_STK * )&LED0_TASK_STK[0],
(CPU_STK_SIZE)LED0_STK_SIZE/10,
(CPU_STK_SIZE)LED0_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);
/* 创建LED1任务 */
OSTaskCreate((OS_TCB * )&Led1TaskTCB,
(CPU_CHAR * )"led_pa3",
(OS_TASK_PTR )led_pa3,
(void * )0,
(OS_PRIO )LED1_TASK_PRIO,
(CPU_STK * )&LED1_TASK_STK[0],
(CPU_STK_SIZE)LED1_STK_SIZE/10,
(CPU_STK_SIZE)LED1_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);
/* 创建MSG任务 */
OSTaskCreate((OS_TCB * )&MsgTaskTCB,
(CPU_CHAR * )"send_msg",
(OS_TASK_PTR )send_msg,
(void * )0,
(OS_PRIO )MSG_TASK_PRIO,
(CPU_STK * )&MSG_TASK_STK[0],
(CPU_STK_SIZE)MSG_STK_SIZE/10,
(CPU_STK_SIZE)MSG_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_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起开始任务
OS_CRITICAL_EXIT(); //进入临界区
}
/**
* 函数功能: 启动任务函数体。
* 输入参数: p_arg 是在创建该任务时传递的形参
* 返 回 值: 无
* 说 明:无
*/
static void led_pc13 (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init(); /* Initialize BSP functions */
CPU_Init();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 1, 0,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 1, 0,OS_OPT_TIME_HMSM_STRICT,&err);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
static void led_pa3 (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init(); /* Initialize BSP functions */
CPU_Init();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);
OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
static void send_msg (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init(); /* Initialize BSP functions */
CPU_Init();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
printf("hello uc/OS \r\n");
OSTimeDlyHMSM(0, 0, 2, 0,OS_OPT_TIME_HMSM_STRICT,&err);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
/**
* 函数功能: 创建应用任务
* 输入参数: p_arg 是在创建该任务时传递的形参
* 返 回 值: 无
* 说 明:无
*/
static void AppTaskCreate (void)
{
}
/**
* 函数功能: uCOSIII内核对象创建
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void AppObjCreate (void)
{
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
5.参数配置
点击“仙女棒”,接着点击Target
选项卡按照如下图配置:
5.结果演示
编译没有错误后,进行烧录即可。
具体演示结果如视频所示:
ucos
六.总结
大家可以到这里获取完整工程代码
工程代码
通过本次的学习初次体验了嵌入式RTOS编程,掌握了嵌入式实时操作系统UC/OS的基本原理,并通过STM32进行了移植完成了一个三任务的LED点灯和串口数据周期的实验。
本实验主要是ucos的移植过程要仔细有耐心,各位读者也一定跟着步骤一步一步走,否则可能会在编译时出现大量的错误,笔者在移植过程中也出现了一些问题,例如文件路径设置问题,中英文输入法切换问题等等,故而大家一定要谨慎,遇到问题可以上网查询,询问同学,相信问题都会解决的。
笔者也在老师的提示下发现采用此种方式,定时非常地方便直接在代码中编写不用再计算延时等等,也可以达到多任务的要求。但是可以明显感觉到代码烧录时间变长,大家也可以关注编译完成后的文件容量,发现其比以前更大了。
笔者通过此次实验感觉受益匪浅,以后如果需要处理器完成的任务比较多,为了便于管理就可以引入轻巧的实时操作系统。
期待大家能够积极留言,指出存在的问题与不足,谢谢~
参考
1.uc/OS-II操作系统:uc/OS基本概念及认识(从零到负,打通你的任督二脉)
2.uC/OS基本知识
3.STM32F103C8T6基于HAL库移植uC/OS-III及逻辑分析仪波形观测
4.STM32F103C8T6移植uCOS基于HAL库