嵌入式实时操作系统(RTOS)下,通过STM32F103C8T6移植UC/OS实现LED周期点亮及串口数据周期发送_基于stm32f103c8t6freertos移植实时操作系统-程序员宅基地

技术标签: stm32  arm  单片机  

嵌入式操作系统下,STM32F103C8T6基于HAL库移植uC/OS实现周期点灯与数据发送得操作过程



前言

  • 我们知道,在嵌入式的微处理过程中,我们会选择使用具备高性能的运算能力的同时又便于与各种外设连接扩展的系统,进而可以简化硬件设计、维持小型化以及降低系统成本
  • 为此,便衍生出了今天要介绍的嵌入式实时操作系统—uC/OS,它作为一个源代码公开的操作系统,在具体应用中稳定可靠,并且 支持uIP TCP/IP协议栈、ucGUI等,可扩展性强,功能强大;自1992年以来,因其源代码的完全公开和优越性能,已为众多的爱好者和开发人员所了解并得到了广泛应用
  • 今天,笔者将介绍在嵌入式实时操作系统(RTOS)下,通过STM32F103C8T6移植uc/OS来实现LED周期点亮及串口数据周期发送的实验

一、实时操作系统(RTOS)—UC/OS系统简介

1. 系统概述

  • uC/OS是一个可以 基于ROM运行的、可裁减的、抢占式、实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,适合很多商业操作系统的实时操作系统(RTOS)
  • uC/OS可简单的视为一个多任务调度器,在这个任务调度器之上完善并添加了和多任务操作系统相关的系统服务,如信号量、邮箱等;其主要特点有公开源代码,代码结构清晰、明了,注释详尽,组织有条理,可移植性好,可裁剪,可固化。 内核属于抢占式,最多可以管理60个任务;从1992年开始,由于高度可靠性、鲁棒性和安全性,uC/OS已经广泛使用在从照相机到航空电子产品的各种应用中
  • μC/OS实时多任务操作系统被广泛应用于微处理器、微控制器和数字信号处理器;μC/OS最早出自于1992 年美国嵌入式系统专家Jean J.Labrosse 在《嵌入式系统编程》杂志的5 月和6 月刊上刊登的文章连载,并把μC/OS的源码发布在该杂志的BBS 上

2. 组成部分

  • μC/OS可以大致分成核心、任务处理、时间处理、任务同步与通信,CPU的移植等5个部分
    1. 核心部分(OSCore.c):是操作系统的处理核心,包括操作系统初始化、操作系统运行、中断进出的前导、时钟节拍、任务调度、事件处理等多部分;能够维持系统基本工作的部分都在这里
    2. 任务处理部分(OSTask.c):任务处理部分中的内容都是与任务的操作密切相关的;包括任务的建立、删除、挂起、恢复等等
    3. 时钟部分(OSTime.c):μC/OS-II中的最小时钟单位是timetick(时钟节拍);任务延时等操作是在这里完成的
    4. 任务同步和通信部分:为事件处理部分,包括信号量、邮箱、消息队列、事件标志等部分;主要用于任务间的互相联系和对临界资源的访问
    5. CPU的移植部分:这部分内容由于牵涉到SP等系统指针,所以通常用汇编语言编写;主要包括中断级任务切换的底层实现、任务级任务切换的底层实现、时钟节拍的产生和处理、中断的相关处理部分等内容

3. 应用情况

  • 高优先级的任务因为需要某种临界资源:主动请求挂起,让出处理器,此时将调度就绪状态的低优先级任务获得执行,这种调度也称为任务级的上下文切换
  • 高优先级的任务因为时钟节拍到来:在时钟中断的处理程序中,内核发现高优先级任务获得了执行条件(如休眠的时钟到时),则在中断态直接切换到高优先级任务执行;这种调度也称为中断级的上下文切换

这两种调度方式在uC/OS的执行过程中非常普遍, 一般来说前者发生在系统服务中,后者发生在时钟中断的服务程序中。

4. 任务调度与优化

1、任务调度:

  • uC/OS采用的是可剥夺型实时多任务内核, 在任何时候都运行就绪了的最高优先级的任务;uC/os的任务调度是完全基于任务优先级的抢占式调度,也就是最高优先级的任务一旦处于就绪状态,则立即抢占正在运行的低优先级任务的处理器资源

2、 任务优化:

  • μC/OS在任务级和中断级的任务切换, 使用一种优化的实用堆栈格式和切换形式,以提高资源的利用率。
  • 在嵌入式操作系统领域,由Jean J. Labrosse开发的μC/OS,由于开放源代码和强大而稳定的功能,不管是对于初学者,还是有经验的工程师,μC/OS开放源代码的方式使其不但知其然,还知其所以然,并且在这种条件下,完全可以按照设计要求进行合理的裁减、扩充、配置和移植;自1992第1版问世以来,已有成千上万的开发者把它成功地应用于各种系统,安全性和稳定性已经得到认证,现已经通过美国FAA认证

6. 任务、时间及内存管理

1、任务管理:

  • uC/OS中最多可以支持64个任务,分别对应优先级0~63,其中0为最高优先级,63为最低级;系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个
  • uC/OS提供了任务管理的各种函数调用,包括创建任务,删除任务,改变任务的优先级,任务挂起和恢复等
  • 系统初始化时会自动产生两个任务:一个是空闲任务,它的优先级最低,该任务仅给一个整型变量做累加运算;另一个是统计任务,它的优先级为次低,该任务负责统计当前cpu的利用率

2、时间管理:

  • uC/OS的时间管理是通过定时中断来实现的,该定时中断一般为10毫秒或100毫秒发生一次,时间频率取决于用户对硬件系统的定时器编程来实现;中断发生的时间间隔是固定不变的,该中断也成为一个时钟节拍;uC/OS要求用户在定时中断的服务程序中,调用系统提供的与时钟节拍相关的系统函数,例如中断级的任务切换函数,系统时间函数

2、内存管理:

  • 在ANSI C中是使用malloc和free两个函数来动态分配和释放内存;
  • 但在嵌入式实时系统中,多次这样的操作会导致内存碎片,且由于内存管理算法的原因,malloc和free的执行时间也是不确定;uC/OS把连续的大块内存按分区管理;每个分区中包含整数个大小相同的内存块,但不同分区之间的内存块大小可以不同;用户需要动态分配内存时,系统选择一个适当的分区,按块来分配内存释放内存时将该块放回它以前所属的分区,这样能有效解决碎片问题,同时执行时间也是固定的

7. 任务间的通信与同步

  • 对一个多任务的操作系统来说,任务间的通信和同步是必不可少的;uC/OS中提供了4种同步对象,分别是信号量,邮箱,消息队列和事件;所有这些同步对象都有创建,等待,发送,查询的接口用于实现进程间的通信和同步

8. 可移植性

  • 随着信息化技术的发展和数字化产品的普及,以计算机技术、芯片技术和软件技术为核心的嵌入式系统再度成为当前研究和应用的热点;对功能、可靠性、成本、体积和功耗严格要求的嵌入式系统一般由 嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户的应用程序等四个部分组成,其中嵌入式微处理器和嵌入式操作系统分别是其硬件和软件的核心
  • 市场上主流的嵌入式实时操作系统有Vxworks、pSos、WinCE、Linux等,基于实时性、成本以及开发难度方面的考虑,我们选择uC/OS——开放源代码的嵌入式实时操作系统

9. 正常运行平台要求

  • a)处理器的C编译器能产生可重入代码
    b)用C语言就可以打开和关闭中断
    c)处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)
    d)处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈
    e)处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令

10. 主体移植过程

  • 设置与处理器及编译器相关的代码,不同的编译器会使用不同的字节长度来表示同一数据类型,所以要定义一系列数据类型以确保移植的正确性。下面是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;/寄存器宽度/

  • uC/OS需要先关中断再访问临界区的代码,并且在访问完后重新允许中断,uC/OS II定义了两个宏来禁止和允许中断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL():
    #define OS_ENTER_CRITICAL()(cpu_sr=OSCPUSaveSR()) /Disable interrupts/
    #define OS_EXIT_CRITICAL()(OSCPURestoreSR(cpu_sr)) /Enable interrupts/
    EXPORT OSCPUSaveSR  OSCPUSaveSR  mrs r1,cpsr  mov r0,r1  orr r1,r1,#0xc0  msr cpsr_cxsf,r1  mov pc,lr
    EXPORT OSCPURestoreSR  OSCPURestoreSR  msr cpsr_cxsf,r0  mov pc,lr

  • 用C语言实现与处理器任务相关的函数
    OSTaskStkInit()  OSTaskCreateHook()  OSTaskDelHook()  OSTaskSwHook()  OSTaskStatHook()  OSTimeTickHook()

  • 处理器相关部分汇编实现:
    a、OSStartHighRdy() :该函数在OSStart()多任务启动之后,负责从最高优先级任务的TCB控制块中获得该任务的堆栈指针sp,通过sp依次将CPU现场恢复,此时系统就将控制权交给用户创建的该任务的进程,直到该任务被阻塞或者被其他更高优先级的任务抢占了CPU;该函数仅仅在多任务启动时被执行一次,用来启动第一个,也就是最高优先级的任务执行
    b、OSCtxSw() :该函数是任务级的上下文切换函数,在任务因为被阻塞而主动请求与CPU调度时执行,主要工作是先将当前任务的CPU现场保存到该任务堆栈中,然后获得最高优先级任务的堆栈指针,从该堆栈中恢复此任务的CPU现场,使之继续执行,从而完成一次任务切换
    c、OSIntExit() :该函数是中断级的任务切换函数,在时钟中断ISR中发现有高优先级任务在等待时,需要在中断退出后不返回被中断的任务,而是直接调度就绪的高优先级任务执行;其目的在于能够尽快让高优先级的任务得到响应,保证系统的实时性能
    d、OSTickISR() :该函数是时钟中断处理函数,主要任务是负责处理时钟中断,调用系统实现的OSTimeTick函数,如果有等待 时钟信号的高优先级任务,则需要在中断级别上调度其执行;另外两个相关函数是OSIntEnter()和OSIntExit(),都需要在ISR中执行

二、工程项目创建

1. CubeMX工程创建

  • 打开stm32cubeMX,选择芯片stm32f103c8
    在这里插入图片描述
    在这里插入图片描述
  • 配置RCC
    在这里插入图片描述
  • 配置SYS
    在这里插入图片描述
  • 配置串口USART1
    在这里插入图片描述
  • 设置PA3PC13作为两个LED灯的端口:
    在这里插入图片描述
  • 设置好路径,导出生成目标工程文件

2. UC/OS开源码获取

  • 可直接下载此网盘链接内容
    链接:https://pan.baidu.com/s/10RqsDRecbmVteWmDv2oUNQ
    提取码:1234

在这里插入图片描述

3. “移植”前期准备工作

  • uC-BSP文件夹新建bsp.c和bsp.h文件:
    在这里插入图片描述
  • 将app.c 、 app_cfg.h 、 cpu_cfg.h 、 includes.h 、 lib_cfg.h 、 os_app_hooks.c 、os_app_hook.h、os_cfg.h、os_cfg_app.h复制到文件夹文件夹uC-CONFIG:
    在这里插入图片描述
  • 将uCOS的5个相关文件复制到cubeMX工程的MDK-ARM文件夹下:
    在这里插入图片描述
    在这里插入图片描述

4. 开始“移植”

4.1 工程项目添加uCOS文件

  • 打开cubeMX生成的keil文件
    1、点击Manage Project Items,为项目新建如下的文件夹:
    在这里插入图片描述
    2、点击CPU–>Add Files…
  • 在MDK-ARM\uC-CPU路径下选中以下文件,Add添加
    在这里插入图片描述
  • 再打开MDK-ARM\uC-CPU\ARM-Cortex-M3\RealView路径,选中以下文件,Add添加:
    在这里插入图片描述
    3、点击LIB–>Add Files…
  • 在MDK-ARM\uC-LIB路径下选中下图文件,Add添加:
    在这里插入图片描述
  • 再打开MDK-ARM\uC-LIB\Ports\ARM-Cortex-M3\RealView路径,选中下图框文件,Add添加:
    在这里插入图片描述
    4、点击PORT–>Add Files…
  • 打开MDK-ARM\uCOS-III\Ports\ARM-Cortex-M3\Generic\RealView路径,选中以下文件,Add添加:
    在这里插入图片描述
    5、点击SOURCE–>Add Files…
  • MDK-ARM\uCOS-III\Source路径下选中以下全部 .c .h 文件,Add添加:
    在这里插入图片描述
    6、点击CONFIG–>Add Files…
  • MDK-ARM\uC-CONFIG路径下选中以下全部文件,Add添加:
    在这里插入图片描述
    7、点击BSP–>Add Files…
  • LMDK-ARM\uC-BSP路径下选中以下全部文件,Add添加:
    在这里插入图片描述

4.2 导入文件路径

  • 按下图所示步骤操作即可:
    在这里插入图片描述

5. 构建工程任务

5.1 任务要求

  • 构建至少3个任务(task):其中两个task分别以1s和3s周期对LED灯进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”

5.2 代码修订

1、为bsp.c和bsp.h添加代码

  • 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

在这里插入图片描述

  • bsp.h代码:
#ifndef  __BSP_H__
#define  __BSP_H__

#include "stm32f1xx_hal.h"

void BSP_Init(void);

#endif

在这里插入图片描述

2、修改startup_stm32f103xb.s文件代码

  • 在以下位置处:
    PendSV_Handler改为OS_CPU_PendSVHandler
    SysTick_Handler改为OS_CPU_SysTickHandler
    在这里插入图片描述
    在这里插入图片描述

3、修改app_cfg.h文件代码

  • DEF_ENABLED 改为 DEF_DISABLED
    在这里插入图片描述
  • #define APP_TRACE BSP_Ser_Printf 改为 #define APP_TRACE(void)
    在这里插入图片描述

4、修改includes.h文件代码

  • 在#include <bsp.h>下面添加 #include “gpio.h”#include “app_cfg.h”
    将#include <stm32f10x_lib.h> 改为 #include “stm32f1xx_hal.h”
    在这里插入图片描述

5、修改lib_cfg.h文件代码

  • 修改为5u
    在这里插入图片描述

6、修改usart.c文件代码

  • 添加代码完成printf重定向
/* 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 */

在这里插入图片描述

7、修改usart.h文件代码

  • 添加定义代码:
typedef struct __FILE FILE;

在这里插入图片描述

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);
		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!欢迎来到RTOS多任务环境! \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. 编译环境配置

  • 按照如图配置环境:
    在这里插入图片描述

三、硬件效果展示

1. 线路连接

  • 3V3 —> 3V3
  • GND —> GND
  • RXD —> A9
  • TXD —> A10
  • LED灯短脚 —> A3
  • LED灯长脚 —> 3V3
    在这里插入图片描述

2. 工程烧录

  • 使用FlyMcu烧录,烧录时,BOOT0置1,BOOT1置0;
    在这里插入图片描述

3. 视频效果展示

  • 烧录成功后,将BOOT0与BOOT1均置0

  • UC/OS实现LED周期点亮:

    UC/OS实现LED周期点亮

  • UC/OS实现串口数据周期发送:

    UC/OS实现串口数据周期发送


总结

通过本次学习,掌握了嵌入式实时操作系统UC/OS的基本原理,以及通过STM32F103C8T6移植UC/OS来实现LED周期点亮及串口数据周期发送的实验过程;
本次实验主要是在已有的开源代码上进行适当的修改以及参考学长学姐的博客为辅助来完成的,代码文件的移动、添加和修改过程比较繁琐,一定要有耐心,不然就会一步错步步错;
同时,本次实验的时间控制是直接在代码里规定的,较比于之前的时钟控制方式简单了不少,但同时换取的代价就是文件容量变大了,这一变化在代码烧录时,就可以明显的观察到!
同时也期待大家能够积极留言,指出我存在的问题,谢谢!

参考文献:
https://blog.csdn.net/qq_48641886/article/details/121696731?spm=1001.2014.3001.5502
https://blog.csdn.net/qq_46467126/article/details/121441622?spm=1001.2014.3001.5502

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_53112972/article/details/127640270

智能推荐

linux里面ping www.baidu.com ping不通的问题_linux桥接ping不通baidu-程序员宅基地

文章浏览阅读3.2w次,点赞16次,收藏90次。对于这个问题我也是从网上找了很久,终于解决了这个问题。首先遇到这个问题,应该确认虚拟机能不能正常的上网,就需要ping 网关,如果能ping通说明能正常上网,不过首先要用命令route -n来查看自己的网关,如下图:第一行就是默认网关。现在用命令ping 192.168.1.1来看一下结果:然后可以看一下电脑上面百度的ip是多少可以在linux里面ping 这个IP,结果如下:..._linux桥接ping不通baidu

android 横幅弹出权限,有关 android studio notification 横幅弹出的功能没有反应-程序员宅基地

文章浏览阅读512次。小妹在这里已经卡了2-3天了,研究了很多人的文章,除了低版本api 17有成功外,其他的不是channel null 就是没反应 (channel null已解决)拜托各位大大,帮小妹一下,以下是我的程式跟 gradle, 我在这里卡好久又没有人可问(哭)![image](/img/bVcL0Qo)public class MainActivity extends AppCompatActivit..._android 权限申请弹窗 横屏

CNN中padding参数分类_cnn “相同填充”(same padding)-程序员宅基地

文章浏览阅读1.4k次,点赞4次,收藏6次。valid padding(有效填充):完全不使用填充。half/same padding(半填充/相同填充):保证输入和输出的feature map尺寸相同。full padding(全填充):在卷积操作过程中,每个像素在每个方向上被访问的次数相同。arbitrary padding(任意填充):人为设定填充。..._cnn “相同填充”(same padding)

Maven的基础知识,java技术栈-程序员宅基地

文章浏览阅读790次,点赞29次,收藏28次。手绘了下图所示的kafka知识大纲流程图(xmind文件不能上传,导出图片展现),但都可提供源文件给每位爱学习的朋友一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长![外链图片转存中…(img-Qpoc4gOu-1712656009273)][外链图片转存中…(img-bSWbNeGN-1712656009274)]

getFullYear()和getYear()有什么区别_getyear和getfullyear-程序员宅基地

文章浏览阅读469次。Date对象取得年份有getYear和getFullYear两种方法经 测试var d=new Date;alert(d.getYear())在IE中返回 2009,在Firefox中会返回109。经查询手册,getYear在Firefox下返回的是距1900年1月1日的年份,这是一个过时而不被推荐的方法。而alert(d.getFullYear())在IE和FF中都会返回2009。因此,无论何时都应使用getFullYear来替代getYear方法。例如:2016年用 getFullYea_getyear和getfullyear

Unix传奇 (上篇)_unix传奇pdf-程序员宅基地

文章浏览阅读182次。Unix传奇(上篇) 陈皓 了解过去,我们才能知其然,更知所以然。总结过去,我们才会知道我们明天该如何去规划,该如何去走。在时间的滚轮中,许许多的东西就像流星一样一闪而逝,而有些东西却能经受着时间的考验散发着经久的魅力,让人津津乐道,流传至今。要知道明天怎么去选择,怎么去做,不是盲目地跟从今天各种各样琳琅满目前沿技术,而应该是去 —— 认认真真地了解和回顾历史。 Unix是目前还在存活的操作系_unix传奇pdf

随便推点

ACwing 哈希算法入门:_ac算法 哈希-程序员宅基地

文章浏览阅读308次。哈希算法:将字符串映射为数字形式,十分巧妙,一般运用为进制数,进制据前人经验,一般为131,1331时重复率很低,由于字符串的数字和会很大,所以一般为了方便,一般定义为unsigned long long,爆掉时,即为对 2^64 取模,可以对于任意子序列的值进行映射为数字进而进行判断入门题目链接:AC代码:#include<bits/stdc++.h>using na..._ac算法 哈希

VS配置Qt和MySQL_在vs中 如何装qt5sqlmysql模块-程序员宅基地

文章浏览阅读952次,点赞13次,收藏27次。由于觉得Qt的编辑界面比较丑,所以想用vs2022的编辑器写Qt加MySQL的项目。_在vs中 如何装qt5sqlmysql模块

【渝粤题库】广东开放大学 互联网营销 形成性考核_画中画广告之所以能有较高的点击率,主要由于它具有以下特点-程序员宅基地

文章浏览阅读1k次。选择题题目:下面的哪个调研内容属于经济环境调研?()题目:()的目的就是加强与客户的沟通,它是是网络媒体也是网络营销的最重要特性。题目:4Ps策略中4P是指产品、价格、顾客和促销。题目:网络市场调研是目前最为先进的市场调研手段,没有任何的缺点或不足之处。题目:市场定位的基本参数有题目:市场需求调研可以掌握()等信息。题目:在开展企业网站建设时应做好以下哪几个工作。()题目:对企业网站首页的优化中,一定要注意下面哪几个方面的优化。()题目:()的主要作用是增进顾客关系,提供顾客服务,提升企业_画中画广告之所以能有较高的点击率,主要由于它具有以下特点

爬虫学习(1):urlopen库使用_urlopen the read operation timed out-程序员宅基地

文章浏览阅读1k次,点赞2次,收藏5次。以爬取CSDN为例子:第一步:导入请求库第二步:打开请求网址第三步:打印源码import urllib.requestresponse=urllib.request.urlopen("https://www.csdn.net/?spm=1011.2124.3001.5359")print(response.read().decode('utf-8'))结果大概就是这个样子:好的,继续,看看打印的是什么类型的:import urllib.requestresponse=urllib.r_urlopen the read operation timed out

分享读取各大主流邮箱通讯录(联系人)、MSN好友列表的的功能【升级版(3.0)】-程序员宅基地

文章浏览阅读304次。修正sina.com/sina.cn邮箱获取不到联系人,并精简修改了其他邮箱代码,以下就是升级版版本的介绍:完整版本,整合了包括读取邮箱通讯录、MSN好友列表的的功能,目前读取邮箱通讯录支持如下邮箱:gmail(Y)、hotmail(Y)、 live(Y)、tom(Y)、yahoo(Y)(有点慢)、 sina(Y)、163(Y)、126(Y)、yeah(Y)、sohu(Y) 读取后可以发送邮件(完..._通讯录 应用读取 邮件 的相关

云计算及虚拟化教程_云计算与虚拟化技术 教改-程序员宅基地

文章浏览阅读213次。云计算及虚拟化教程学习云计算、虚拟化和计算机网络的基本概念。此视频教程共2.0小时,中英双语字幕,画质清晰无水印,源码附件全课程英文名:Cloud Computing and Virtualization An Introduction百度网盘地址:https://pan.baidu.com/s/1lrak60XOGEqMOI6lXYf6TQ?pwd=ns0j课程介绍:https://www.aihorizon.cn/72云计算:概念、定义、云类型和服务部署模型。虚拟化的概念使用 Type-2 Hyperv_云计算与虚拟化技术 教改