一、UCOS系统简介
1.版本介绍
UCOS是Micrium公司出品的RTOS类实时操作系统, UCOS目前有两个版本:UCOSII和UCOSIII。
◉UCOSIII是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。
◉UCOSIII提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。
◉UCOSIII是用C和汇编来写的,其中绝大部分都是用C语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的, UCOSIII结构简洁,可读性很强!最主要的是非常适合初次接触嵌入式实时操作系统学生、嵌入式系统开发人员和爱好者学习。
2.UCOSIII的任务简介
在UCOSIII中任务就是程序实体,UCOSIII能够管理和调度这些小任务(程序)。UCOSIII中的任务由三部分组成:任务堆栈、任务控制块和任务函数。
✦任务堆栈:上下文切换的时候用来保存任务的工作环境,就是STM32的内部寄存器值;
✦任务控制块:任务控制块用来记录任务的各个属性;
✦任务函数:由用户编写的任务处理代码,是实实在在干活的。一般写法如下:
void XXX_task(void *p_arg) {
while(1)
{
… //任务处理过程
}
}
◉可以看出用任务函数通常是一个无限循环,当然了,也可以是一个只执行一次的任务。任务的参数是一个void类型的,这么做的目的是可以可以传递不同类型的数据甚至是函数。
◉可以看出任务函数其实就是一个C语言的函数,但是在使用UCOIII的情况下这个函数不能有用户自行调用,任务函数何时执行执行,何时停止完全有操作系统来控制。
3.UCOSIII的系统任务
UCOSIII默认有5个系统任务
:
◉空闲任务:
UCOSIII创建的第一个任务,UCOSIII必须创建的任务,此任务有UCOSIII自动创建,不需要用户手动创建;
◉时钟节拍任务:
此任务也是必须创建的任务;
◉统计任务:
可选任务,用来统计CPU使用率和各个任务的堆栈使用量。此任务是可选任务,由宏OS_CFG_STAT_TASK_EN控制是否使用此任务;
◉定时任务:
用来向用户提供定时服务,也是可选任务,由宏OS_CFG_TMR_EN控制是否使用此任务;
◉中断服务管理任务:
可选任务,由宏OS_CFG_ISR_POST_DEFERRED_EN控制是否使用此任务。
前两个系统任务时必须创建的任务,而后三者不是。控制后三者任务的宏都是在文件OS_CFG.h中。
4.UCOSIII的任务状态
从用户的角度看,UCOSIII的任务一共有5种状态
:
◉休眠态:
任务已经在CPU的flash中了,但是还不受UCOSIII管理;
◉就绪态:
系统为任务分配了任务控制块,并且任务已经在就绪表中登记,这时这个任务就具有了运行的条件,此时任务的状态就是就绪态;
◉运行态:
任务获得CPU的使用权,正在运行;
◉等待态:
正在运行的任务需要等待一段时间,或者等待某个事件,这个任务就进入了等待态,此时系统就会把CPU使用权转交给别的任务;
◉中断服务态:
当发送中断,当前正在运行的任务会被挂起,CPU转而去执行中断服务函数,此时任务的任务状态叫做中断服务态。
这5种状态之间相互转化的关系如下图:
5.UCOSIII的任务详解
(1)任务堆栈
任务堆栈是任务的重要部分,堆栈是在RAM中按照“先进先出(FIFO)”的原则组织的一块连续的存储空间。为了满足任务切换和响应中断时保存CPU寄存器中的内容及任务调用其它函数时的需要,每个任务都应该有自己的堆栈。
◉任务堆栈创建
代码如下:
#define START_STK_SIZE 512 //堆栈大小 CPU_STK START_TASK_STK[START_STK_SIZE]; //定义一个数组来作为任务堆栈
◉任务堆栈的大小
CPU_STK为CPU_INT32U类型,也就是unsigned int类型,为4字节的,那么任务堆栈START_TASK_STK的大小就为:512 X 4=2048字节。
◉任务堆栈初始化
任务切换回上一个任务并且还能接着从上次被中断的地方开始运行的办法是恢复现场,现场就是CPU的内部各个寄存器。因此在创建一个新任务时,必须把系统启动这个任务时所需的CPU各个寄存器初始值事先存放在任务堆栈中。这样当任务获得CPU使用权时,就把任务堆栈的内容复制到CPU的各个寄存器,从而可以任务顺利地启动并运行。
把任务初始数据存放到任务堆栈的工作就叫做任务堆栈的初始化,UCOSIII提供了完成堆栈初始化的函数:OSTaskStkInit()
。
CPU_STK *OSTaskStkInit (OS_TASK_PTR p_task,
void *p_arg,
CPU_STK *p_stk_base,
CPU_STK *p_stk_limit,
CPU_STK_SIZE stk_size,
OS_OPT opt) {
… //函数内容
return (p_stk); }
当然,用户一般不会直接操作堆栈初始化函数,任务堆栈初始化函数由任务创建函数OSTaskCreate()调用。不同的CPU对于的寄存器和对堆栈的操作方式不同,因此在移植UCOSIII的时候需要用户根据各自所选的CPU来编写任务堆栈初始化函数。
void OSTaskCreate (OS_TCB *p_tcb, //任务控制块
CPU_CHAR *p_name, //任务名字
OS_TASK_PTR p_task, //任务函数
void *p_arg, //传递给任务函数的参数
OS_PRIO prio, //任务优先级
CPU_STK *p_stk_base, //------任务堆栈基地址
CPU_STK_SIZE stk_limit, //------任务堆栈深度限位
CPU_STK_SIZE stk_size, //------任务堆栈大小
OS_MSG_QTY q_size,
OS_TICK time_quanta,
void *p_ext, //用户补充的存储区
OS_OPT opt,
OS_ERR *p_err) //存放该函数错误时的返回值 {
… //函数内容 }
✦函数OSTaskCreate()中的参数p_stk_base(任务堆栈基地址)的确定方式:
根据堆栈的增长方式,堆栈有两种增长方式
:
向上增长:
堆栈的增长方向从低地址向高地址
增长;
向下增长:
堆栈的增长方向从高地址向低地址
增长。
函数OSTaskCreate()中的参数p_stk_base是任务堆栈基地址,那么如果CPU的堆栈是向上增长的话那么基地址就&START_TASK_STK[0];如果CPU堆栈是向下增长的话基地址就是&START_TASK_STK[START_STK_SIZE-1]。STM32的堆栈是向下增长的。
(2)任务控制块
任务控制块是用来记录与任务相关的信息的数据结构,每个任务都要有自己的任务控制块。我们使用OSTaskCreate()函数来创建任务的时候就会给任务分配一个任务控制块。任务控制块由用户自行创建,代码如下:
OS_TCB StartTaskTCB; //创建一个任务控制块
OS_TCB为一个结构体,描述了任务控制块,任务控制块中的成员变量用户不能直接访问,更不可能改变他们。
OS_TCB为一个结构体,其中有些成员采用了条件编译的方式来确定。
struct os_tcb {
CPU_STK StkPtr; / 指向当前任务堆栈的栈顶 */
void ExtPtr; / 指向用户可定义的数据区 */
CPU_STK StkLimitPtr; / 可指向任务堆栈中的某个位置 */
OS_TCB NextPtr; / Pointer to next TCB in the TCB list */
OS_TCB PrevPtr; / Pointer to previous TCB in the TCB list */
OS_TCB *TickNextPtr;
OS_TCB *TickPrevPtr;
OS_TICK_SPOKE TickSpokePtr; / Pointer to tick spoke if task is in the tick list */
CPU_CHAR NamePtr; / Pointer to task name */
CPU_STK StkBasePtr; / Pointer to base address of stack /
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS TLS_Tbl[OS_CFG_TLS_TBL_SIZE];
#endif
OS_TASK_PTR TaskEntryAddr; / Pointer to task entry point address /
void TaskEntryArg; / Argument passed to task when it was created /
OS_PEND_DATA PendDataTblPtr; / Pointer to list containing objects pended on /
OS_STATE PendOn; / Indicates what task is pending on /
OS_STATUS PendStatus; / Pend status /
OS_STATE TaskState; / See OS_TASK_STATE_xxx /
OS_PRIO Prio; / Task priority (0 == highest) /
CPU_STK_SIZE StkSize; / Size of task stack (in number of stack elements) /
OS_OPT Opt; / Task options as passed by OSTaskCreate()/
OS_OBJ_QTY PendDataTblEntries; / Size of array of objects to pend on /
CPU_TS TS; / Timestamp /
OS_SEM_CTR SemCtr; / Task specific semaphore counter /
/ DELAY / TIMEOUT /
OS_TICK TickCtrPrev; / Previous time when task was ready /
OS_TICK TickCtrMatch; / Absolute time when task is going to be ready /
OS_TICK TickRemain; / Number of ticks remaining for a match /
/ … run-time by OS_StatTask() */
OS_TICK TimeQuanta;
OS_TICK TimeQuantaCtr;
#if OS_MSG_EN > 0u
void MsgPtr; / Message received /
OS_MSG_SIZE MsgSize;
#endif
#if OS_CFG_TASK_Q_EN > 0u
OS_MSG_Q MsgQ; / Message queue associated with task /
#if OS_CFG_TASK_PROFILE_EN > 0u
CPU_TS MsgQPendTime; / Time it took for signal to be received /
CPU_TS MsgQPendTimeMax; / Max amount of time it took for signal to be received /
#endif
#endif
#if OS_CFG_TASK_REG_TBL_SIZE > 0u
OS_REG RegTbl[OS_CFG_TASK_REG_TBL_SIZE]; / Task specific registers /
#endif
#if OS_CFG_FLAG_EN > 0u
OS_FLAGS FlagsPend; / Event flag(s) to wait on /
OS_FLAGS FlagsRdy; / Event flags that made task ready to run /
OS_OPT FlagsOpt; / Options (See OS_OPT_FLAG_xxx) /
#endif
#if OS_CFG_TASK_SUSPEND_EN > 0u
OS_NESTING_CTR SuspendCtr; / Nesting counter for OSTaskSuspend() /
#endif
#if OS_CFG_TASK_PROFILE_EN > 0u
OS_CPU_USAGE CPUUsage; / CPU Usage of task (0.00-100.00%) /
OS_CPU_USAGE CPUUsageMax; / CPU Usage of task (0.00-100.00%) - Peak /
OS_CTX_SW_CTR CtxSwCtr; / Number of time the task was switched in /
CPU_TS CyclesDelta; / value of OS_TS_GET() - .CyclesStart /
CPU_TS CyclesStart; / Snapshot of cycle counter at start of task resumption /
OS_CYCLES CyclesTotal; / Total number of # of cycles the task has been running /
OS_CYCLES CyclesTotalPrev; / Snapshot of previous # of cycles /
CPU_TS SemPendTime; / Time it took for signal to be received /
CPU_TS SemPendTimeMax; / Max amount of time it took for signal to be received /
#endif
#if OS_CFG_STAT_TASK_STK_CHK_EN > 0u
CPU_STK_SIZE StkUsed; / Number of stack elements used from the stack /
CPU_STK_SIZE StkFree; / Number of stack elements free on the stack /
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
CPU_TS IntDisTimeMax; / Maximum interrupt disable time /
#endif
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
CPU_TS SchedLockTimeMax; / Maximum scheduler lock time */
#endif
#if OS_CFG_DBG_EN > 0u
OS_TCB *DbgPrevPtr;
OS_TCB *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif };
◉任务控制块初始化
USOCIII提供了用于任务控制块初始化的函数
:OS_TaskInitTCB()
。但是,用户不需要自行初始化任务控制块。因为和任务堆栈初始化函数一样,函数OSTaskCreate()在创建任务的时候会对任务的任务控制块进行初始化。
二、STM32CubeMX工程创建
1.创建新项目
打开STM32CubeMX主界面,点击“ACCESS TO MCU SELECTOR”,创建新项目
2.选择芯片
在“Commerical Part Number”里面选择自己需要的芯片,点击信息栏中的具体芯片信息选中,再点击“Start Project”
3.配置RCC
先点击System Core将其展开,再点击RCC,将HSE设置为Crystal/Ceramic Resonator
4.配置SYS
点击SYS,将Debug设置为Serial Wire
5.设置GPIO
点击GPIO,然后点击管脚PA3和PC13,将其设置为GPIO_Output
6.设置串口USART1
先点击Connectivity将其展开,再点击USART1,将Mode设置为Asynchronous,然后点击Parameter Settings,设置波特率为115200,1位停止位,无校验位
7.设置时钟树
点击“Clock Configuration”,勾选PLLCLK和HSE,并将晶振频率设置为最大值72MHz
8.设置工程
(1)点击Project Manager→Project,配置好自己的项目名和项目存放路径,将IDE设置为MDK-ARM
(2)点击Code Generate界面,选择生成初始化文件.c/.h,之后再点击GENERATE CODE即可成功创建项目
三、获取uCOS-III源码
(1)进入 Micrium 公司官网下载中心:http://micrium.com/downloadcenter/
选择ST系列,点击 View all STMicroelectronics,点击 STMicroelectronics STM32F103
之后按照提示注册下载即可。
(2)直接下载如下网盘链接内容
链接:https://pan.baidu.com/s/10RqsDRecbmVteWmDv2oUNQ
提取码:1234
四、移植前的文件准备及移植过程
1.找到下载好的文件夹
打开下载好的文件夹(我是网盘链接直接下载),如下图:
2.复制文件到MDK-ARM文件夹下
将uCOS的5个相关文件复制到cubeMX工程的MDK-ARM文件夹下
3.打开cubeMX生成的keil文件,将uCOS文件添加到项目中
依次点击Manage Project Items→New(Insert),将下图6个文件添加到项目中
4.分别给新增的文件夹添加文件
(1)点击CPU→Add Files…,按如下路径选中以下文件(文件类型选择“All Files”),然后点击Add
◉MDK-ARM\uC-CPU
路径下选中下图红框文件,Add添加
◉MDK-ARM\uC-CPU\ARM-Cortex-M3\RealView
路径下选中下图红框文件,Add添加
(2)点击LIB→Add Files…,按如下路径选中以下文件(文件类型选择“All Files”),然后点击Add
◉MDK-ARM\uC-LIB
路径下选中下图红框文件,Add添加
◉MDK-ARM\uC-LIB\Ports\ARM-Cortex-M3\RealView
路径下选中下图红框文件,Add添加
(3)点击PORT→Add Files…,按如下路径选中以下文件(文件类型选择“All Files”),然后点击Add
◉MDK-ARM\uCOS-III\Ports\ARM-Cortex-M3\Generic\RealView
路径下选中下图红框文件,Add添加
(4)点击SOURCE→Add Files…,按如下路径选中以下文件(文件类型选择“All Files”),然后点击Add
◉MDK-ARM\uCOS-III\Source
路径下选中目录下全部 .c .h 文件,Add添加
(5)点击CONFIG→Add Files…,按如下路径选中以下文件(文件类型选择“All Files”),然后点击Add
◉MDK-ARM\uC-CONFIG
路径下选中目录下全部文件,Add添加
(6)点击BSP→Add Files…,按如下路径选中以下文件(文件类型选择“All Files”),然后点击Add
◉MDK-ARM\uC-BSP
路径下选中以下全部文件,Add添加
上述步骤全部完成后点击OK(一定记得点击OK,不然就前功尽弃!!!)
此时项目结构会发生变化,如下图:
5.导入文件路径
依次点击魔法棒→C/C++→…→New(Insert)→需要添加的路径→OK
五、构建多任务环境
1.代码添加
(1)为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)为bsp.h
添加如下代码:
// bsp.h
#ifndef __BSP_H__
#define __BSP_H__
#include "stm32f1xx_hal.h"
void BSP_Init(void);
#endif
2.修改部分代码
(1)打开startup_stm32f103xb.s
文件
◉在以下位置处将PendSV_Handler
改为OS_CPU_PendSVHandler
,
SysTick_Handler
改为OS_CPU_SysTickHandler
(2)打开app_cfg.h
文件
◉DEF_ENABLED
改为 DEF_DISABLED
◉
#define APP_TRACE BSP_Ser_Printf
改为 #define APP_TRACE(void)
(3)打开includes.h
文件
在#include <bsp.h>
下面添加 #include “gpio.h”
,#include “app_cfg.h”
将#include <stm32f10x_lib.h>
改为 #include “stm32f1xx_hal.h”
(4)打开lib_cfg.h
文件
修改为5u*1024u(该处宏定义设置堆空间的大小,STM32F103C8T6的RAM只有20K,所以要改小一点)
(5)打开usart.c
文件,添加如下代码完成printf重定向
/* USER CODE BEGIN 1 */
typedef struct __FILE FILE;
int fputc(int ch,FILE *f){
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
return ch;
}
/* USER CODE END 1 */
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.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);//设置固定接在 PC13 GPIO端口以1s为周期闪烁
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);//延时函数,括号内的4个参数分别表示:小时,分钟,秒,毫秒
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
OSTimeDlyHMSM(0, 0, 3, 0,OS_OPT_TIME_HMSM_STRICT,&err);//设置外接LED以3s为周期闪烁
/* 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!欢迎来到RTOS多任务环境! \r\n");
OSTimeDlyHMSM(0, 0, 2, 0,OS_OPT_TIME_HMSM_STRICT,&err);//设置串口以2s为周期发送数据
/* 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.程序编译及生成hex文件
点击Rebuild编译程序,若没有错误即可成功创建hex文件
6.电路连接
USB转TTL | STM32F103C8T6 |
---|---|
GND | GND |
3v3 | 3v3 |
TXD | A10 |
RXD | A9 |
LED负极接STM32F103C8T6的A3管脚,正极接3.3v管脚。
STM32F103C8T6的B6管脚接自身的C13管脚。
7.程序烧录
置BOOT0为0,BOOT1为1
(1)打开mcuisp,选择串口为COM5,并选择生成的hex文件
(2)点击读器件信息,若显示一切正常则进行下一步
(3)点击开始编程,若显示一切正常则说明烧录成功
8.串口调试
打开野火串口调试助手,将波特率设置为115200,数据位设置为8,停止位设置为1,校验位设置为None,然后点击打开串口
9.运行结果
由上面的运行结果可以观察到,实现了以1s和3s周期对LED灯进行点亮-熄灭的控制,同时以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。说明实验成功!
六、总结
通过本次实验,我对嵌入式实时操作系统(RTOS)有了一定的了解,特别是对uc/OS,熟悉了其移植过程,也学会了构建多任务环境。当然,如果我们需要处理器完成的任务比较单一,可以不使用操作系统;如果需要处理器完成的任务比较多,为了便于管理可以引入轻巧的实时操作系统。在实验过程中,要特别仔细,本实验步骤比较繁琐,需要添加的东西也比较多,稍不注意就会漏掉某个步骤,然后就会导致程序编译不成功,从而达不到预期实验效果。总的来说,此实验并不是很复杂,只要多参考学长学姐地博客,细心一点,一步一步地做就能成功。
七、参考链接
https://blog.csdn.net/qq_45659777/article/details/121570886?spm=1001.2014.3001.5506
https://blog.csdn.net/qq_46467126/article/details/121441622?spm=1001.2014.3001.5506