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

今日简单编写熟悉一下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 系统运行正常了:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CUDA从入门到精通(三)——CUDA编程示例

CUDA 编程简介 CUDA(Compute Unified Device Architecture)是由 NVIDIA 提供的一种并行计算平台和编程模型。它允许开发者利用 NVIDIA GPU 的并行计算能力,编写可以在 GPU 上高效运行的代码,从而加速计算密集型任务。 CUDA 通过…

【十进制整数转换为其他进制数——短除形式的贪心算法】

之前写过一篇用贪心算法计算十进制转换二进制的方法,详见:用贪心算法计算十进制数转二进制数(整数部分)_短除法求二进制-CSDN博客 经过一段时间的研究,本人又发现两个规律: 1、不仅仅十进制整数转二进制可…

舵机SG90详解

舵机,也叫伺服电机,在嵌入式开发中,舵机作为一种常见的运动控制组件,具有广泛的应用。其中,SG90 舵机以其高效、稳定的性能特点,成为了许多工程师和爱好者的首选,无论是航模、云台、机器人、智能…

如何为IntelliJ IDEA配置JVM参数

在使用IntelliJ IDEA进行Java开发时,合理配置JVM参数对于优化项目性能和资源管理至关重要。IntelliJ IDEA提供了两种方便的方式来设置JVM参数,以确保你的应用程序能够在最佳状态下运行。本文将详细介绍这两种方法:通过工具栏编辑配置和通过服…

跌倒数据集,5345张图片, 使用yolo,coco json,voc xml格式进行标注,平均识别率99.5%以上

跌倒数据集,5345张图片, 使用yolo,coco json,voc xml格式进行标注,平均识别率99.5%以上 ,可用于某些场景下识别人是否跌倒或摔倒并进行告警。 数据集分割 训练组99% 5313图片 有效集0&am…

nods.js之nrm安装及使用

nods.js之nrm安装及使用 一、简介二、安装 nrm与使用三、报错解决 一、简介 nrm 是 Node.js 的一个工具,用于管理和切换 npm 源(Registry)。它使得在不同的 npm 镜像源之间切换变得非常容易,尤其对于那些经常因为网络问题或速度原…

selenium自动化测试基础知识

目录 一、概念知识 (一)三大核心组件 (二)Selenium 自动化测试的工作原理 (三)Selenium 支持的操作 (四)Selenium 自动化测试的优点 (五)Selenium 自动化测试的缺点 (六)Selenium 自动化测试的应用场景 总结 二、实操例子 使用前提--安装步骤 注意事项 (一)浏览器的…

Cisco Packet Tarcer配置计网实验笔记

文章目录 概要整体架构流程网络设备互连基础拓扑图拓扑说明配置步骤 RIP/OSPF混合路由拓扑图拓扑说明配置步骤 BGP协议拓扑图拓扑说明配置步骤 ACL访问控制拓扑图拓扑说明配置步骤 HSRP冗余网关拓扑图拓扑说明配置步骤 小结 概要 一些环境配置笔记 整体架构流程 网络设备互连…

RNN LSTM Seq2Seq Attention

非端到端: data -》 cleaning -》 feature Engining (70%-80%工作 设计特征)-》 分类器 -》预测 端到端 End-to-End: data -》 cleaning -》Deep learning(表示学习,从数据中学习特征) -》…

【AI日记】24.12.17 kaggle 比赛 2-6 | 把做饭看成一种游戏 | 咖喱牛肉

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加:kaggle 比赛 Regression with an Insurance Dataset时间:9 小时睡得好很重要 读书 书名:富兰克林自传时间:0.5 小时阅读原因:100 美元纸…

电脑为什么会提示“msvcr120.dll缺失”?“找不到msvcr120.dll文件”要怎么解决?

电脑故障排查指南:揭秘“msvcr120.dll缺失”的真相与解决方案 在软件开发与日常维护的广阔天地里,遇到系统报错或文件缺失的情况可谓家常便饭。今天,我将带领大家深入探讨一个常见的系统提示——“msvcr120.dll缺失”,并揭秘其背…

Kotlin复习

一、Kotlin类型 1.整数 2.浮点 显示转换: 所有数字类型都支持转换为其他类型,但是转换前会检测长度。 toByte(): Byte toShort(): Short toInt(): Int toLong(): Long toFloat(): Float toDouble(): Double 不同进制的数字表示方法(为了提高…

【BUG】记一次context canceled的报错

文章目录 案例分析gorm源码解读gin context 生命周期context什么时候cancel的什么时候context会被动cancel掉呢? 野生协程如何处理 案例分析 报错信息 {"L":"ERROR","T":"2024-12-17T11:11:33.0050800","file"…

召回系统介绍

一、以Lucene为例介绍召回系统 1、倒排检索 Lucene的倒排索引由 Term Index -> TermDictionary -> Posting List 三层组成,倒排检索实际上就是通过分词Term查询到倒排拉链,然后对所有拉链进行合并。 Term-> Posting List,可以直接…

Ubuntu22.04系统下MVS运行海康威视工业相机

之前的开发环境是Ubuntu16.04,最近因项目需求换到了Ubuntu22.04系统,安装了ROS2-humble,重新记录下开发过程。 Ubuntu16.04系统可参考: VMware虚拟机中Ubuntu16.04系统下通过MVS运行海康威视工业相机 Linux环境中对海康威视工业相…

慧知开源充电桩平台 - OCPP充电桩协议越南充电平台:多语种支持、多元支付、本地化策略

越南充电新体验:多语种支持,便捷支付! 助力充电桩运营本土化落地,为越南市场提供定制化解决方案 随着全球电动汽车市场的迅猛发展,越南作为东南亚新兴的汽车市场,对电动汽车充电基础设施的需求也在急剧增…

基于Clinical BERT的医疗知识图谱自动化构建方法,双层对比框架

基于Clinical BERT的医疗知识图谱自动化构建方法,双层对比框架 论文大纲理解1. 确认目标2. 目标-手段分析3. 实现步骤4. 金手指分析 全流程核心模式核心模式提取压缩后的系统描述核心创新点 数据分析第一步:数据收集第二步:规律挖掘第三步&am…

华为ensp--BGP路径选择-Preferred Value

学习新思想,争做新青年。今天学习的是BGP路径选择-Preferred Value 实验目的 理解BGP路由信息首选值(Preferred Value)的作用 掌握修改Preferred Value属性的方法 掌握通过修改Preferred Value属性来实现流量分担的方法 实验拓扑 实验要求…

如何在OpenCV中运行自定义OCR模型

我们首先介绍如何获取自定义OCR模型,然后介绍如何转换自己的OCR模型以便能够被opencv_dnn模块正确运行,最后我们将提供一些预先训练的模型。 训练你自己的 OCR 模型 此存储库是训练您自己的 OCR 模型的良好起点。在存储库中,MJSynthSynthTe…

“从零到一:揭秘操作系统的奇妙世界”【操作系统的发展】

1.手工操作阶段 此时没有OS,用户采用人工操作方式进行。 方式:程序员在纸带机上打孔---计算机读取---结果输出到纸袋机上---程序员取走结果 缺点:耗时长,难度大、用户独占全机、人机速度矛盾导致资源利用率低 2.单批道处理系统 引…