本文使用到的硬件:STM32F103C8T6最小系统板、一个小灯、USB-TTL转接口、若干杜邦线、逻辑分析仪
本文使用到的软件:Keil MDK、STM32CubeMX、串口调试助手、SALEAE_LOGIC_16(观测逻辑分析波形)
题目要求:学习嵌入式实时操作系统(RTOS),以uc/OS-III为例,将其移植到stm32F103上,构建至少3个任务(task):其中两个task分别以1s和3s周期对LED等进行点亮-熄灭的控制;另外一个task以2s周期通过串口发送“hello uc/OS! 欢迎来到RTOS多任务环境!”。记录详细的移植过程。
目录
一、uc/OS-III介绍
1.1 uc/OS-III是什么
- uc/OS-III是嵌入式实时操作系统的一种,他的结构小巧,是可以完成多任务实时内核,典型的前后台嵌入式系统。
- 前台系统和后台系统包含一个死循环和若干中断服务程序,前台系统是中断级,后台系统是任务级。
1.2 uc/OS-III部分工作介绍
1.2.1 多任务调度
任务
(也叫做线程)是简单的程序。单CPU 中,在任何时刻只能是一个任务被执行。 任务看起来像C
函数。在大多数嵌入式系统中,任务通常是无限循环的。任务不能像C 函数那样,它是不能return
的。- 任务调度
uc/OS-III实际上它就是一个法官,决定当前由哪个任务占用CPU,基于优先级内核的可抢占型
,分为循环轮转调度和抢占型调度,抢占型通过给每一个进程一个优先级,若系统中有优先级比当前任务优先级高的任务,则系统会中断当前任务进行优先级高的进程。 - 上下文切换
上下文切换是指CPU的控制权由运行任务转移到另外一个就绪任务时所发生的事件,当前运行任务
转为就绪(或者挂起、删除)状态,另一个
被选定的就绪任务成为当前任务
。上下文切换包括保存当前任务的运行环境
,恢复将要运行任务的运行环境。 - 中断
当中断响应时,CPU会保存寄存器值并跳转到中断服务程序,当处理完成后,程序会返回中断前的任务或更高优先级的任务。
1.2.2 共享资源
多个任务
可能会同时要求占用资源:内存空间、全局变量、指针、缓冲区、列表、环形缓冲区等。 通过共享资源,任务间通信将会比较简单。
当然,避免任务间的竞争和防止资源被破坏是非常重要的。大部分独占资源的方法都是创建临界段
。
1.2.3 信号量
信号量
是一个“锁定机构
”,代码需要获得“钥匙” 才可以访问共享资源。占用该资源的任务不再使用该资源并释放资源
(钥匙)时,其它任务才能够访问这个资源。只有任务
才允许使用信号量,中断服务程序
不允许。
二、STM32F103C8T6移植uc/OS-III系统过程
2.1 使用CubeMX创建工程
- 创建工程
- 选择芯片
- 配置RCC
- 配置SYS
- 配置串口usart1
- 选择PC13为引脚输出,用于点亮LED灯
-
设置工程
-
生成代码
2.2 移植uC/OS-III源码
- 可以在我分享的链接中下载,文件都配置好了
链接:https://pan.baidu.com/s/1BN7X56LluRmyPHVBlDr9Tg
提取码:1111 - 下载好后,文件夹如下
- 我们要将文件移动到刚刚建立的HAL工程的MDK-ARM文件夹中
2.3 开始移植
-
添加文件
首先创建如下所示文件夹
-
为CPU和LIB添加文件
注意CPU文件夹和LIB文件夹需要如上图找到对应名称文件夹下的所有文件及RealView文件夹下的所有文件 -
PROT和SOURCE文件夹
PORT文件夹所包含的文件也在其下子目录的RealView文件夹中 -
CONFIG和BSP文件也是对应文件名称下的文件
-
注意添加完所有文件后,点OK,Project会发生变化
-
导入文件路径
文件路径如上图所示
三、修改函数
3.1 main函数如下
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* 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 ------------------------------------------------------------*/
/* 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)
{
uint8_t hello[20]="hello uc/os\n";
HAL_UART_Transmit(&huart1,hello,20,1000);
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****/
3.2 修改其他函数
- 在以下位置处将PendSV_Handler和SysTick_Handler改为OS_CPU_PendSVHandler和OS_CPU_SysTickHandler
- 如下位置修改#define
42行改为#define APP_CFG_SERIAL_EN DEF_DISABLED
85行改为#define APP_TRACE(viod)
-
includes.h改变为如图
-
修改宏定义
该处宏定义设置堆空间的大小,STM32F103C8T6的RAM只有20K,所以要改小一点
-
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);
}
3.3 设置参数
四、结果展示
4.1 硬件及上位机结果展示
4.2 Keil仿真
- LED1与LED2的周期与预期相同,一个三秒亮一次,一个一秒亮一次。
- 下面是串口的具体波形
4.3 逻辑分析仪
- 灯以及串口波形如下
channel 0 对应波形是三秒亮一次的灯,channel 2 对应波形是一秒亮一次的灯,channel 1 是上位机串口通讯的波形
- 放大后的上位机波形如下图
- 各字符的ascii码如下
-
h 的ascii码为01101000
-
o的波形分析
我们可以看到在抽样时,对应的数据位为00010110,数据倒一下,刚好是ascii码。
五、总结
uC/OS的植入帮助我们完成多进程任务,通过逻辑分析仪我们可以分析波形,硬件查错等。
参考文章: