关键词:
文章目录
一、关于uc/OS系统
1.操作系统与裸机的区别
裸机运行的程序代码,一般由一个main函数中的while死循环和各种中断服务程序组成,平
时CPU执行while循环中的代码,出现其他事件时,跳转到中断服务程序进行处理,没有多任
务、线程的概念。而引人操作系统后,程序运行时可以把一个应用流程分割为多个任务,每个任务完成一部分工作,并且每个任务都可以写成死循环。操作系统根据任务的优先级,通过调度器使CPU分时执行各个任务,保证每个任务都能够得到运行。若调度方法优良,则可使各任务看起来是并行执行的,减少了CPU的空闲时间,提高了CPU的利用率。
2.uc/OS运行流程
在运行uC/OS系统的设备上,当程序执行时,首先会初始化系统任务管理所需要的各种链表等数据结构,接着根据应用程序需求创建任务,最后由调度器管理各个任务,而中断可由操作系统使能和除能,若使能中断则可以在其他任务运行时跳转到中断服务程序。
uc/OS工作流程图
1)初始化所有全局变量、数据结构,创建最低优先级空闲任务OSTaskIde (如果使用了统计任务,也在此创建),创建6个空数据链表:
- 空任务控制块链表
- 空事件控制块链表
- 空队列控制块链表
- 空标志组链表
- 空内存控制块链表
- 空闲定时器控制块链表
2)至少创建一个任务。一般创建一个最高优先级别
TaskStart任务(建议), 任务调度后,在这个任务中再创建其他任务,初始化硬件并开中断。
3)进入多任务管理阶段,将就绪表中最高优先级任务的
栈指针加载到SP中,并强制中断返回。
4) μC/OS 的任务调度工作。任务调度是内核的主要服务,是区分裸机和多任务系统的最大特点,好的调度策略能更好地发挥系统的效率。调度工作主要包括:查找就绪表中最高优先级任务和实现任务切换。而任务切换又分为两种,分别为任务级的调度器OSSched和中断级的调度器OSIntExt。
5)运行用户任务,某些用户任务会因为主动让出CPU、延时、请求临界资源或优先级不够高而挂起,由调度器调度运行其他任务。
6) μC/OS的任务调度是靠周期时钟中断来实现的,每个时钟节拍到来就会产生一次定时中断,中断后启动调度器,运行就绪表中优先级最高的任务(非抢占型内核中断后继续运行被中断任务)。即过一段时间就检测是否有重要任务需要运行,若是就转而运行更重要的任务,从而确保实时性(裸机程序就无法这样做了)。在CM3芯片平台上,这个周期时钟中断由SysTick提供。
二、详细移植过程
将uc/OS-III移植到stm32上,构建3个任务:其中两个task分别以1s和3s周期对LED等进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。
1.STM32Cubex创建工程
新建project
,选择要用的芯片,这里选择STM32F103C8
,选中星号开启项目。
配置调试接口为Serial Wire
。
配置时钟源,选择Crystal/Ceramic Resonator
。
将PC13、PA3设置为GPIO_OUTPUT
模式,控制LED灯的亮灭,作为判断是否移值成功的依据。
设置串口USART1
为异步模式Asynchronous
。
设置工程相关信息,并点击GENERATE CODE
。
2.为工程添加源码
在工程相同目录下新建下图所示文件夹,提前把下的源码整理进去,后面操作起来就会方便很多。
用Keil打开生成的工程,为项目添加如下图所示文件夹。
点击ADD FILES
,选择ALL FILES
,将提前整理好的源码添加进项目中的CPU
文件夹。
点击LIB
,将下图所示文件添加进项目中。
点击PORT
,将下图所示文件夹加入项目中。
点击Source,将下图所示文件加入项目中。
点击 CONFIG
,将下图所示文件添加进项目中。
点击BSP
,将下图所示文件添加进项目中。
3.添加头文件路径
点击魔法棒,选择C/C++
,点进Include Paths
。
将下图所示的头文件路径添加进项目中。
4.添加代码
1)bsp.h和bsp.c
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;
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)main.c
/* 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 (voidstm32开发--ucosii移植
...人叫uC/OS)由美国人JeanLabrosse在1992年编写的一个实时操作系统。1998年更新到uCOS-II,并于2000年得到美国航天管理局(FAA)认证,可以用 查看详情
ucos2_stm32f1移植详细过程
...置和OS裁剪相关的配置,接着上一篇文章来讲述关于UCOS的移植,该文主要针对uC/OS-IIPorts下面os_cpu_a.asm、os_cpu_c.c和os_cpu.h文件底层端口代码来讲述。请下载“UCOS2_STM32F1_3个简单任务”作为参考工程。 笔者将“UCOS移植详细过程... 查看详情
智能家居系统-软件设计
1智能家居远程控制系统的软件实现 1.1基于uC/OS-II的中央控制器的软件设计1.1.1uC/OS-II系统移植本设计使用uC/OS-II操作系统,uC/OS-II是一个源码公开、可移植、可固化、可剪裁和抢占式的实时多任务操作系统,uC/OS-II的大部分源... 查看详情
stm32开发--ucosii移植
...人叫uC/OS)由美国人JeanLabrosse在1992年编写的一个实时操作系统。1998年更新到uCOS-II,并于2000年得到美国航天管理局(FAA)认证,可以用于飞行器中。官方网站:www.micrium.com2、uCOS-II的特点(1)可裁剪uCOS-II源代码中有一个专门裁剪系统... 查看详情
stm32开发--ucosii移植
...人叫uC/OS)由美国人JeanLabrosse在1992年编写的一个实时操作系统。1998年更新到uCOS-II,并于2000年得到美国航天管理局(FAA)认证,可以用于飞行器中。官方网站:www.micrium.com2、uCOS-II的特点(1)可裁剪uCOS-II源代码中有一个专门裁剪系统... 查看详情
stm32开发--ucosii移植
...人叫uC/OS)由美国人JeanLabrosse在1992年编写的一个实时操作系统。1998年更新到uCOS-II,并于2000年得到美国航天管理局(FAA)认证,可以用于飞行器中。官方网站:www.micrium.com2、uCOS-II的特点(1)可裁剪uCOS-II源代码中有一个专门裁剪系统... 查看详情
stm32开发--ucosii移植
...人叫uC/OS)由美国人JeanLabrosse在1992年编写的一个实时操作系统。1998年更新到uCOS-II,并于2000年得到美国航天管理局(FAA)认证,可以用于飞行器中。官方网站:www.micrium.com2、uCOS-II的特点(1)可裁剪uCOS-II源代码中有一个专门裁剪系统... 查看详情
ucos-ii简介及移植ucos-ii到stm32f103平台详细步骤
...uC/OS)由美国人 JeanLabrosse在1992年编写的一个实时操作系统 3、uCOS特点:①、可剪裁:有一个专门用来裁剪系统的配置文件,通过各种宏的来打开或者关闭那些不需要用到系统服务②、可移植: uCOS-II基本全部是用C语... 查看详情
lvgl移植stm32f1基于stm32cubemx配置硬件spi驱动1.8寸tftst7735s跑lvgl图形demo(代码片段)
【LVGL移植】STM32F1基于STM32CubeMX配置硬件SPI驱动1.8寸TFTST7735S屏幕跑LVGL图形demo🎬运行LVGL按键组件demo✨基于STM32CubeMX配置工程是因为方便移植,只要是STM32芯片,拿到我的这个工程源码就可以根据自己的stm32芯片,自... 查看详情
ina219驱动,基于stm32(stm8移植可用)
之前要做一个电量计,采用INA219电流检测芯片,参考了网上大神的代码后发现在STM32上可用,移植到STM8后不可用,后来找到了官方的示例demo,综合网上大神代码调试后成功驱动,测电流、电压、功率,精确度很高。现在分享出... 查看详情
基于stm32f4移植w5500官方驱动库iolibrary_driver(转)
源:基于STM32F4移植W5500官方驱动库ioLibrary_Driver参考:基于STM32+W5500的Ethernet和Internet移植 UpgradeW5500ThroughputonNucleoSTM32F401REUsingSPI DMA 查看详情
基于stm32移植ucgui图形界面框架(3.9.0源码版本)(代码片段)
...f1b;后续的版本都是提供lib库文件,不再提供源码了。基于STM32的STemwin移植教程可以看这里: https://blog.csdn.net/xiaolong1126 查看详情
uc/os-iii概要
本章主要对uC/OS-III实时操作系统做一些概要介绍,使读者对uC/OS-III有个整体的浅认识,为后面的章节的详细讲解做一个铺垫。下图是uC/OS-III系统从底层到上层的文件结构。①配置文件,通过定义这些文件里宏的值可以轻易地裁剪... 查看详情
stm32mp157系统移植|移植st官方5.10内核到小熊派开发板(代码片段)
...骤:(1)选择一个硬件设计接近的板子,基于此板相关文件开始移植(2)拷贝单板配置文件(arch/arm/configs)(3)拷贝设备树文件(arch/arm/b 查看详情
stm32mp157系统移植|移植st官方5.10内核到小熊派开发板(代码片段)
...骤:(1)选择一个硬件设计接近的板子,基于此板相关文件开始移植(2)拷贝单板配置文件(arch/arm/configs)(3)拷贝设备树文件(arch/arm/b 查看详情
移植freertos到stm32
...本文详细介绍如何移植FreeRTOS到STM32单片机上。移植操作系统是嵌入式开发的入门基础,单片机和嵌入式在物理上其实是一摸一样的,区别就是软件上嵌入式跑了操作系统而单片机没有。文本选用市场上最常见的STM32和 查看详情
移植freertos到stm32
...本文详细介绍如何移植FreeRTOS到STM32单片机上。移植操作系统是嵌入式开发的入门基础,单片机和嵌入式在物理上其实是一摸一样的,区别就是软件上嵌入式跑了操作系统而单片机没有。文本选用市场上最常见的STM32和 查看详情
stm32f1基于stm32cubemx配置移植dmp库通过串口打印mpu6050数据(代码片段)
STM32F1基于STM32CubeMX配置移植dmp库通过串口打印MPU6050数据📜串口打印mpu6050数据信息✨本文将分享基于STM32CubeIDE和KeilMDK两个版本的工程。(当然你也可以根据STM32CubeMX自行重新配置自己所需版本的工程)✨本文不做代码... 查看详情