STM32F407ZGT6-UCOSIII笔记2:UCOSIII任务创建实验-Printf 函数卡住 UCOSIII 系统问题解决

news2024/12/19 13:46:11

今日简单编写熟悉一下UCOSIII系统的任务创建代码,理解一下OS系统:

并发现以及解决了 Printf 函数卡住 UCOSIII 系统问题解决

文章提供测试代码讲解、完整工程下载、测试效果图

目录

文件结构解释:

任务函数文件:

目前各个文件任务:

#include "main.h"

#include "ComTask.h"

#include "MessageTask.h"

#include "CalculateTask.h"

测试效果图:

完整工程下载:

遇到的问题:

Printf函数卡住UCOSIII系统:

总结了以下尝试:

解决方案:

解决方案效果截图:


文件结构解释:

我的文件结构可能与其余作者的不太一样,我不喜欢在main.c文件中放置太多东西,因此需要先解释一下工程里的一些函数,以及它们的位置在哪:

任务函数文件:

工程包含一个TASK组,里面含有各个任务的实际函数体:

#include "ComTask.h"包含串口相关任务操作

#include "MessageTask.h" 包含信号灯状态等 对外释放信号安排的 相关操作

#include "CalculateTask.h" 包含数据计算任务

目前各个文件任务:

#include "main.h"

创建开始任务初始化三个基本任务:

代码如下:

#include "main.h"

int main(void)
{
	OS_ERR err;
	CPU_SR_ALLOC();
	
  Init_ALL();
	
	OSInit(&err);		//初始化UCOSIII
	OS_CRITICAL_ENTER();//进入临界区
	//创建开始任务
	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任务控制块
				 (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,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void   	* )0,					//用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                 (OS_ERR 	* )&err);				//存放该函数错误时的返回值
	OS_CRITICAL_EXIT();	//退出临界区	 
	OSStart(&err);  //开启UCOSIII

	while(1);
								 
}

//开始任务函数
void start_task(void *p_arg)
{
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;

	CPU_Init();
#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();	//进入临界区
	//创建ComTask任务
	OSTaskCreate((OS_TCB 	* )&COMTASKTaskTCB,		
				 (CPU_CHAR	* )"com task", 		
                 (OS_TASK_PTR )comTask, 			
                 (void		* )0,					
                 (OS_PRIO	  )COMTASK_TASK_PRIO,     
                 (CPU_STK   * )&COMTASK_TASK_STK[0],	
                 (CPU_STK_SIZE)COMTASK_STK_SIZE/10,	
                 (CPU_STK_SIZE)COMTASK_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);				
				 
	//创建MessageTask任务
	OSTaskCreate((OS_TCB 	* )&MessageTaskTaskTCB,		
				 (CPU_CHAR	* )"Message task", 		
                 (OS_TASK_PTR )MessageTask, 			
                 (void		* )0,					
                 (OS_PRIO	  )MessageTask_TASK_PRIO,     	
                 (CPU_STK   * )&MessageTask_TASK_STK[0],	
                 (CPU_STK_SIZE)MessageTask_STK_SIZE/10,	
                 (CPU_STK_SIZE)MessageTask_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);
				 
	//创建CalculateTask任务
	OSTaskCreate((OS_TCB 	* )&CalculateTaskTaskTCB,		
				 (CPU_CHAR	* )"Calculate task", 		
                 (OS_TASK_PTR )CalculateTask,	
                 (void		* )0,					
                 (OS_PRIO	  )CalculateTask_TASK_PRIO,     	
                 (CPU_STK   * )&CalculateTask_TASK_STK[0],	
                 (CPU_STK_SIZE)CalculateTask_STK_SIZE/10,	
                 (CPU_STK_SIZE)CalculateTask_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();	//进入临界区
}

宏定义依赖如下:

#ifndef __main_H
#define __main_H

#include "headfile.h"


//任务优先级
#define START_TASK_PRIO		3
//任务堆栈大小	
#define START_STK_SIZE 		512
//任务控制块
OS_TCB StartTaskTCB;
//任务堆栈	
CPU_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *p_arg);



//任务优先级
#define COMTASK_TASK_PRIO		6
//任务堆栈大小	
#define COMTASK_STK_SIZE 		128
//任务控制块
OS_TCB COMTASKTaskTCB;
//任务堆栈	
CPU_STK COMTASK_TASK_STK[COMTASK_STK_SIZE];
//任务函数定义在#include "ComTask.h"


//任务优先级
#define MessageTask_TASK_PRIO		7
//任务堆栈大小	
#define MessageTask_STK_SIZE 		128
//任务控制块
OS_TCB MessageTaskTaskTCB;
//任务堆栈	
CPU_STK MessageTask_TASK_STK[MessageTask_STK_SIZE];
//任务函数定义在#include "MessageTask.h"


//任务优先级
#define CalculateTask_TASK_PRIO		8
//任务堆栈大小
#define CalculateTask_STK_SIZE		128
//任务控制块
OS_TCB	CalculateTaskTaskTCB;
//任务堆栈
__align(8) CPU_STK	CalculateTask_TASK_STK[CalculateTask_STK_SIZE];
//任务函数定义在#include "CalculateTask.h"


#endif

#include "ComTask.h"

串口打印自己运行次数,然后等待800ms:

代码如下:

#include "ComTask.h"


//ComTask 打印自己运行次数 然后等待800ms
void comTask(void * p_arg)
{
	OS_ERR err;
	int i=0;
	
	p_arg = p_arg;
	while (DEF_TRUE)
	{
		i++;
		UsartPrintf(USART1, "comTask Print%d\r\n",i);		
		//printf("comTask Print%d\r\n",i);
		//UsartPrintf(USART1, "%d\r\n",i);
		
		OSTimeDlyHMSM(0,0,0,800,OS_OPT_TIME_HMSM_STRICT,&err); //延时800ms
		
	}
}

注意其中的打印函数有修改,修改后代码如下:

添加了临界段代码保护


//选择串口发送数据--自定义Printf
void UsartPrintf (USART_TypeDef *USARTx, char *fmt,...)
{
  // 等待TC标志位被设置,但加入延时以避免长时间阻塞
  //int start_tick; // 获取当前系统时钟节拍
	//OS_ERR  *p_err;
	
	unsigned char UsartPrintfBuf[296];                                  //最大长度296
	va_list ap;
	unsigned char *pStr = UsartPrintfBuf;
	
	CPU_SR_ALLOC();
	OS_CRITICAL_ENTER();//进入临界区
	
	va_start(ap, fmt);
	vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);	//格式化
	va_end(ap);

	while(*pStr != 0)
	{
		
		USART_SendData(USARTx, *pStr++);
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET)
		{
//			start_tick++;
//			if(start_tick>5) break;
		}
	}
		OS_CRITICAL_EXIT();	//退出临界区	 
}

#include "MessageTask.h"

每205ms切换一次灯显示效果,然后打印任务运行次数(每205*4ms)

代码如下:

#include "MessageTask.h"

/*
	打印任务MessageTask运行次数
	分别按顺序 200ms间隔延迟 单独点亮 红绿蓝 LED 最后全亮 
	同时打印当前灯颜色
*/
void MessageTask (void * p_arg)
{
	OS_ERR err;
	int i=0;
	
	p_arg = p_arg;
	while (DEF_TRUE)
	{
		i++;
		//UsartPrintf(USART1, "MessageTask Print%d\r\n",i);	
		UsartPrintf(USART1, "M %d\r\n",i);	
		
		//单独亮红灯
	  LED_RED_L;LED_GREEN_H;LED_Blue_H;
		OSTimeDlyHMSM(0,0,0,205,OS_OPT_TIME_HMSM_STRICT,&err); //延时205ms
		//单独亮绿灯		
		LED_GREEN_L;LED_RED_H;LED_Blue_H;	
		OSTimeDlyHMSM(0,0,0,205,OS_OPT_TIME_HMSM_STRICT,&err); //延时205ms
		//单独亮蓝灯
		LED_Blue_L;LED_RED_H;LED_GREEN_H;		
		OSTimeDlyHMSM(0,0,0,205,OS_OPT_TIME_HMSM_STRICT,&err); //延时205ms
		//全部点亮 
		LED_RED_L;LED_GREEN_L;LED_Blue_L;
		OSTimeDlyHMSM(0,0,0,205,OS_OPT_TIME_HMSM_STRICT,&err); //延时205ms		
	}
	
}

#include "CalculateTask.h"

每100ms打印自己运行次数:

代码如下:

#include "CalculateTask.h"

//CalculateTask 打印自己运行次数 然后等待100ms
void  CalculateTask(void  *p_arg)
{
	OS_ERR err;
	int i=0;
	
	p_arg = p_arg;
	while (DEF_TRUE)
	{
		i++;
		UsartPrintf(USART1, "CalculateTask Print %d\r\n",i);	
		//UsartPrintf(USART1, "C:%d\r\n",i);	
		OSTimeDlyHMSM(0,0,0,100,OS_OPT_TIME_HMSM_STRICT,&err); //延时100ms
	}
}


测试效果图:

从打印情况直观地感受到各个任务运行情况:

ComTask与MessageTask运行周期都是最长的(800ms与820ms)

它们俩次任务之间,最快的CalculateTask运行了 8次

同时灯也有相应的闪变:(这里就不完全截图了)

完整工程下载:

https://download.csdn.net/download/qq_64257614/90139424

遇到的问题:

Printf函数卡住UCOSIII系统:

Printf函数卡住UCOSIII系统,导致每个任务只运行一次就卡死

检查发现是串口发送函数有个有阻塞的等待逻辑在里面:

起初以为是阻塞的等待逻辑的问题,后文发现,其实是临界区代码保护的问题......

 后面我还尝试了printf函数,他也会卡住UCOSIII系统:

总结了以下尝试:

最原始的直接轮询等待方式:(只执行一次)

添加了简单超时计数机制:(OS系统正常运行(从LED行为分析),但打印不正常)

这里为了查看comtask运行是否正常,改了一句最简单的串口打印,发现打印有问题,但发送时间戳正确,是800ms一次的

改进尝试读取时钟节拍方式来设定超时:

打印与系统都没正常运行:(此时ComTask都没启动完整)


//选择串口发送数据--自定义Printf
void UsartPrintf (USART_TypeDef *USARTx, char *fmt,...)
{
  // 等待TC标志位被设置,但加入延时以避免长时间阻塞
  int start_tick; // 获取当前系统时钟节拍
	OS_ERR  *p_err;
	
	unsigned char UsartPrintfBuf[296];                                  //最大长度296
	va_list ap;
	unsigned char *pStr = UsartPrintfBuf;
	
	va_start(ap, fmt);
	vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);	//格式化
	va_end(ap);

	while(*pStr != 0)
	{
		USART_SendData(USARTx, *pStr++);
		start_tick=OSTimeGet(p_err); //获取当前时钟节拍
		do
		{
			//大于10节拍 
			if( OSTimeGet(p_err)-start_tick > 100000) 
			{
				//超时处理
				break;
			}
		}
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
	}
}

解决方案:

添加进入退出临界段代码,防止printf函数被打断查验标志位的部分:


//选择串口发送数据--自定义Printf
void UsartPrintf (USART_TypeDef *USARTx, char *fmt,...)
{
  // 等待TC标志位被设置,但加入延时以避免长时间阻塞
  //int start_tick; // 获取当前系统时钟节拍
	//OS_ERR  *p_err;
	
	unsigned char UsartPrintfBuf[296];                                  //最大长度296
	va_list ap;
	unsigned char *pStr = UsartPrintfBuf;
	
	CPU_SR_ALLOC();
	OS_CRITICAL_ENTER();//进入临界区
	
	va_start(ap, fmt);
	vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);	//格式化
	va_end(ap);

	while(*pStr != 0)
	{
		
		USART_SendData(USARTx, *pStr++);
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET)
		{
//			start_tick++;
//			if(start_tick>5) break;
		}
	}
		OS_CRITICAL_EXIT();	//退出临界区	 
}

解决方案效果截图:

发现ucos 系统运行正常了:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2262196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux centos 7 安装 mongodb7

MongoDB 是一个基于文档的 NoSQL 数据库。 MongoDB 是一个文档型数据库,数据以类似 JSON 的文档形式存储。 MongoDB 的设计理念是为了应对大数据量、高性能和灵活性需求。 MongoDB使用集合(Collections)来组织文档(Documents&a…

kafka的处理的一些问题 消费延迟

kafka的处理的一些问题 消费者客户端不但没有背压而且内存充足,但产生的消费延迟越来越大在Kafka的Leader副本宕机时 消费者客户端不但没有背压而且内存充足,但产生的消费延迟越来越大 比如我们这个kakfa集群一共有3个Broker节点 TOp1有5个分区&#xf…

计算机网络技术基础:3.计算机网络的拓扑结构

网络拓扑结构是指用传输媒体互连各种设备的物理布局,即用什么方式把网络中的计算机等设备连接起来。将工作站、服务站等网络设备抽象为点,称为“节点”;将通信线路抽象为线,称为“链路”。由节点和链路构成的抽象结构就是网络拓扑…

Vue3源码笔记阅读1——Ref响应式原理

本专栏主要用于记录自己的阅读源码的过程,希望能够加深自己学习印象,也欢迎读者可以帮忙完善。接下来每一篇都会从定义、运用两个层面来进行解析 定义 运用 例子:模板中访问ref(1) <template><div>{{str}}</div> </template> <script> impo…

STM32F407寄存器点灯

背景描述&#xff1a; 最近用32开发遇到问题不得不看寄存器了&#xff0c;就回顾了一下寄存器手册的查看方式和寄存器的使用方法&#xff1b; 上一次这么细致的记录还是在刚学习STM32的时候&#xff0c;之前觉得看寄存器手册以及配置寄存器是有点难度的事情&#xff0c;现在回头…

2024年12月11日Github流行趋势

项目名称&#xff1a;maigret 项目维护者&#xff1a;soxoj, kustermariocoding, dependabot, fen0s, cyb3rk0tik项目介绍&#xff1a;通过用户名从数千个站点收集个人档案信息的工具。项目star数&#xff1a;12,055项目fork数&#xff1a;870 项目名称&#xff1a;uv 项目维护…

Halcon中histo_2dim(Operator)算子原理及应用详解

在Halcon中&#xff0c;histo_2dim算子是一个用于计算双通道灰度值图像的直方图的工具。以下是对该算子的原理及应用的详细解释&#xff1a; 一、原理 histo_2dim算子的函数原型为&#xff1a;histo_2dim(Regions, ImageCol, ImageRow : Histo2Dim : : )。 输入参数&#xff…

mysql免安装版配置教程

一、将压缩包解压至你想要放置的文件夹中&#xff0c;注意&#xff1a;绝对路径中要避免出现中文 二、在解压目录下新建my.ini文件&#xff0c;已经有的就直接覆盖 my.ini文件内容 [mysqld] # 设置3306端口 port3306 # 设置mysql的安装目录 basedirD:\\tools\\mysql-8.1.0-win…

(六)- DRM驱动开发(qcom)

一&#xff0c;Linux Android Display 1&#xff0c;Linux Android Display Software Subsystem 密 2&#xff0c;Linux Android Display Architecture 密 二&#xff0c;DRM/KMS Adreno DPU 1&#xff0c;硬件框图 密 1.1 Qualcomm Adreno DPU 8-Series Overview 密 …

手眼标定工具操作文档

1.手眼标定原理介绍 术语介绍 手眼标定&#xff1a;为了获取相机与机器人坐标系之间得位姿转换关系&#xff0c;需要对相机和机器人坐标系进行标定&#xff0c;该标定过程成为手眼标定&#xff0c;用于存储这一组转换关系的文件称为手眼标定文件。 ETH&#xff1a;即Eye To …

CTFshow-文件上传(Web151-170)

CTFshow-文件上传(Web151-170) 参考了CTF show 文件上传篇&#xff08;web151-170&#xff0c;看这一篇就够啦&#xff09;-CSDN博客 Web151 要求png&#xff0c;然后上传带有一句话木马的a.png&#xff0c;burp抓包后改后缀为a.php&#xff0c;然后蚁剑连接&#xff0c;找fl…

基于YOLOv8模型监控视频中的车辆检测与识别应用

1.摘要 该项目旨在通过技术手段加强交通纪律&#xff0c;提供一种更为人性化和智能化的交通监控方法。具体而言&#xff0c;通过利用PyQt5、YOLOv8和TensorFlow等技术栈&#xff0c;实现了对车辆的高效检测与识别&#xff0c;主要实现车辆类型识别以及速度监测等功能&#xff0…

CISC RISC

CISC&#xff1a;设计目标是通过复杂的指令来提高代码密度&#xff0c;减少指令数量&#xff0c;适合内存资源较为有限的系统。CISC处理器的硬件复杂度较高&#xff0c;但在某些应用场合&#xff08;如桌面计算机&#xff09;能够提供足够的性能。 RISC&#xff1a;设计目标是…

AI Agent与MEME:技术与文化融合驱动Web3创新

AI Agent如何引领Web3新时代&#xff1f; 随着Web3与区块链技术的迅速发展&#xff0c;AI Agent作为人工智能与区块链的交汇点&#xff0c;正在逐步成为推动去中心化生态的重要力量。同时&#xff0c;MEME文化凭借其强大的社区驱动力和文化渗透力&#xff0c;在链上生态中扮演着…

前端的知识(部分)

11 前端的编写步骤 第一步:在HTML的页面中声明方法 第二步:在<script>中定义一个函数,其中声明一个data来为需要的数据 赋值一个初始值 第三步:编写这个方法实现对应的功能

【鸿睿创智开发板试用】移植OpenCV 4到OpenHarmony 4.1

目录 目录 引言 编译系统镜像 (1) 下载代码后解压SDK (2) 下载docker镜像   (3) 编译OH 编译OpenCV 下载OpenCV源代码 构建编译配置文件 执行编译命令 安装库和头文件 测试 结语 引言 最近有个需求是在基于RK3568的OpenHarmony 4.1系统中使用OpenCV&#xff0c…

二分查找【Lecode_HOT100】

文章目录 1.搜索插入位置No.352.搜索二维矩阵No.743.在排序数组中查找元素的第一个和最后一个位置No.344.搜索旋转排序数组No.335.寻找旋转排序数组中的最小值No.153 1.搜索插入位置No.35 class Solution {public int searchInsert(int[] nums, int target) {int l 0;int r n…

蜂窝结构机械超材料

本研究设计了两种蜂窝结构机械超材料&#xff0c;具有可变的、依赖于拉伸或压缩的正负泊松比&#xff0c;并通过NOKOV度量动作捕捉验证了超材料的形变特性。 研究人员以《Mechanical Metamaterials with Discontinuous and Tension/Compression-Dependent Positive/Negative Po…

JAVA入门:使用IDE开发

JAVA入门:使用IDE开发 什么是IDE IDE(Integrated Development Environment,集成开发环境)是一种软件应用程序,它为程序开发、软件设计、项目管理等提供全面的设施。 简单来说就是简化开发过程,让编程更加方便。 IDEA 业界公认最好用的JAVA IDE 安装IDEA 打开IDEA官…

opencv # Sobel算子、Laplacian算子、Canny边缘检测、findContours、drawContours绘制轮廓、外接矩形

一、Sobel算子 案例图片 cv2.Sobel(src, ddepth, dx, dy, ksize3, scale1, delta0, borderTypeNone) 功能&#xff1a;用于计算图像梯度&#xff08;gradient&#xff09;的函数 参数&#xff1a; src: 输入图像&#xff0c;它应该是灰度图像。 ddepth: 输出图像的所需深度&am…