使用PY003基于外部中断+定时器的方式实现NEC红外解码

news2024/9/22 6:53:36

写在前边

最近项目用到一款遥控器是38K红外载波,NEC协议的,找了很多帖子有看到用外部中断下降沿判断(但可惜判定数据的方式是while在外部中断里面死等的),有看到用100us定时器定时刷来判断,感觉都不太适合用在我这个工程里,最后没办法自己写了一个,使用没问题,不确定有没有bug。暂且记录着。

配置定时器: 10ms定时中断一次

void TIM3_Init(void)
{
    /* TIM3 配置*/
    Tim3Handle.Instance = TIM3;                                           /* 选择TIM3 */
    Tim3Handle.Init.Period            = 10000 - 1;                        /* 自动重装载值 */
    Tim3Handle.Init.Prescaler         = 24 - 1;                           /* 预分频为1000-1 */
    Tim3Handle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;           /* 时钟不分频 */
    Tim3Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;               /* 向上计数 */
    Tim3Handle.Init.RepetitionCounter = 1 - 1;                            /* 不重复计数 */
    Tim3Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;   /* 自动重装载寄存器没有缓冲 */
    if (HAL_TIM_Base_Init(&Tim3Handle) != HAL_OK)                         /* TIM3初始化 */
    {
        APP_ErrorHandler();
    }

    if (HAL_TIM_Base_Start_IT(&Tim3Handle) != HAL_OK)                     /* TIM3使能启动,并使能中断 */
    {
        APP_ErrorHandler();
    }
}

配置GPIO:GPIO_InitStruct.Mode这里将IO的外部中断配置成上升沿+下降沿中断

void GPIO_Init(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();                           /* GPIOA时钟使能 */

	//PA5 红外解码输入
	GPIO_InitStruct.Pin   = GPIO_PIN_5 ;                    /* PIN脚 */
	GPIO_InitStruct.Mode  = GPIO_MODE_IT_RISING_FALLING ;	/* 工作模式 */
	GPIO_InitStruct.Pull  = GPIO_PULLUP ;	                /* 拉电阻 */
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH ;     /* IO时钟速度 */
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);    

}

主函数中开启定时器和外部中断:(因为是PA5 所以选外部中断5 外部中断4-15都是一个入口地址)

	TIM3_Init();
    HAL_TIM_Base_Stop_IT( &Tim3Handle );    //先关闭TIM3,等收到一个下降沿之后再打开
    TIM3->CNT = 0;                          //清零定时器,为下一次接受红外做准备
    
    /* 使能NVIC GPIO外部中断 */
    HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
    HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);

中断服务函数:
定时器的:

/**
  * @brief This function handles TIM3 Interrupt .
  */
void TIM3_IRQHandler(void)
{
    __HAL_TIM_CLEAR_IT(&Tim3Handle, TIM_IT_UPDATE);         //最长10ms溢出中断,如果是超过10ms的溢出中断,说明此前为空闲,清零所有变量

    HAL_TIM_Base_Stop( &Tim3Handle );						//如果进入了定时器3的中断服务函数,说明是10ms溢出了,直接停止TIM3定时器
    TIM3->CNT = 0;
    IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数
    IR.IR_code = 0;
    IR.IR_event = 0;

    IR.IR_code_get = 0;
    IR.low_8bit = 0;
    IR.hig_8bit = 0;
    IR.rece_ok = 0;
}

外部中断5的:

/**
  * @brief This function handles EXIT Interrupt .
  */
void EXTI4_15_IRQHandler(void)
{
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_5);

    IR.timer_count = TIM3->CNT;     //每次进外部中断,先保存当前的计数值 24M主频,24分频,CNT加1的时间为1us
    TIM3->CNT = 0;                  //清零寄存器
    if( GPIOA->IDR & 0x00000020 )   //检查PA5电平 上升沿    IR.timer_count的值为上一个下降沿到这次的上升沿,中间的间隔时间(即低电平时间)
    {
        if(IR.IR_event == 0 && IR.f_head == 0)          //既没有开始接受01数据,也没有开始接受头码,那么进入接收头码判断
        {   
            if( IR.timer_count > 8900 && IR.timer_count < 9100)     //头码为9ms的低电平,允许误差100us
            {
                IR.f_head = 1;                                      //如果符合,开始判定头码
            }
            else if(IR.timer_count > 9100)                          //如果头码已经大于9.1ms 认为是错误
            {
                IR.f_head = 0;
                IR.IR_decode_bit = 0;                               //电平计数超过了码元长度 清除位数计数
                IR.IR_code = 0;
                IR.IR_event = 0;

                IR.IR_code_get = 0;
                IR.low_8bit = 0;
                IR.hig_8bit = 0;
                IR.rece_ok = 0;
            }
        }
        else if(IR.IR_event == 1)                                   //如果头码判定正确,开始接受01数据
        {
            if(IR.timer_count > 500 && IR.timer_count < 650)        //所有的01数据,开头都是0.56ms的低电平
            {
                IR.get_bit = 1;                                     //如果符合,那么开始根据后面的高电平时间来判定此次为0还是1
            }
            else                                                    //否则清除所有数据
            {
                IR.f_head = 0;
                IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数
                IR.IR_code = 0;
                IR.IR_event = 0;
                
                IR.IR_code_get = 0;
                IR.low_8bit = 0;
                IR.hig_8bit = 0;
                IR.rece_ok = 0;
            }
        }
    }
    else                                                            //上一个上升沿到这次的下降沿,之间的时间间隔(即高电平时间)
    {
        if(IR.f_head==1)                                            //如果9ms的低电平成立了,开始判断是否有4.5ms的高电平时间
        {
            if( IR.timer_count > 4400 && IR.timer_count < 4600)
            {
                IR.IR_event = 1;                                    //4.5ms成立,认为此次的头码成立,开始接收01数据
            }
            else
            {
                IR.IR_decode_bit = 0;                               //电平计数超过了码元长度 清除位数计数
                IR.IR_code = 0;
                IR.IR_event = 0;

                IR.IR_code_get = 0;
                IR.low_8bit = 0;
                IR.hig_8bit = 0;
                IR.rece_ok = 0;
            }
            IR.f_head = 0;                                          //清除接受头码标志
        }

        if(IR.IR_event == 1)                                        //接收数据内容成立
        {
            if(IR.get_bit == 1)                                     //如果0.56ms的低电平已经成立,那么开始判断这次的高电平时间
            {
                if(IR.timer_count > 500 && IR.timer_count < 650)    //0.56ms低+0.56ms高,说明传输的数据为 0 
                {
					IR.IR_code <<= 1;
                    IR.IR_decode_bit ++;
                }
                else if(IR.timer_count > 1500 && IR.timer_count < 1800)     //0.56ms低+1.68s高,说明传输的数据为 1
                {
					IR.IR_code <<= 1;   
					IR.IR_code ++;                                  //数据位置1
                    IR.IR_decode_bit ++;
                }
                else                                                //如果0.56ms的低电平后接着的是不符合要求的高电平时间,那么清零所有数据
                {
                    IR.f_head = 0;
                    IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数
                    IR.IR_event = 0;
                    IR.IR_code = 0;

                    IR.IR_code_get = 0;
                    IR.low_8bit = 0;
                    IR.hig_8bit = 0;
                    IR.rece_ok = 0;
                }

                if( IR.IR_decode_bit == 32 )                        //接收了32位数据之后,开始判断内容
                {
                    IR.IR_code_get = IR.IR_code;
                    
                    IR.low_8bit = IR.IR_code_get & 0x000000FF;      //对于本项目而言之后低16位数据有用,其中15-8为数据,7-0为数据反码
                    IR.hig_8bit = (IR.IR_code_get & 0x0000FF00)>>8;
                    IR.low_8bit ^= 0xFF;                            //拿反码异或1 得到取反数据

                    if( IR.low_8bit == IR.hig_8bit )                //判定数据跟数据反码是否相符,如果相符则为有用数据,否则舍弃所有数据
                    {
                        IR.rece_ok = 1;
                    } 
                    else
                    {
                        IR.rece_ok = 0;
                        IR.low_8bit = 0;
                        IR.hig_8bit = 0;
                        IR.IR_code_get = 0;
                    }

                    IR.IR_decode_bit = 0;
                    IR.IR_code = 0;
                    IR.f_head = 0;
                    IR.IR_event = 0;
                }

                IR.get_bit = 0;                                     //每次处理完一位数据之后,清楚标志位,待下次0.56ms低电平成立之后再次判断
            }
        }
    }

    HAL_TIM_Base_Start_IT( &Tim3Handle );                           //每次进入外部中断都要启动计数器
}

IR结构体声明在main.h文件中:

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "py32f0xx_hal.h"

/* Private includes ----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/

#define IR_CODE_GET_POWER (uint8_t)(0xE2)
#define IR_CODE_GET_SPEED (uint8_t)(0xA2)
#define IR_CODE_GET_TIMER (uint8_t)(0xA8)
#define IR_CODE_GET_SHAKE (uint8_t)(0x18)



/* Exported variables prototypes ---------------------------------------------*/
extern UART_HandleTypeDef UartHandle;
extern __IO ITStatus UartReady;
extern TIM_HandleTypeDef TimHandle,Tim3Handle,Tim1Handle, Tim17Handle;
extern EXTI_HandleTypeDef exti_handle;
extern GPIO_InitTypeDef GPIO_InitStruct;

typedef struct IR_decode
{
    uint32_t  timer_count; //定时器数据
    uint32_t  IR_code;     //读取到的红外码 
    uint32_t  IR_code_get;
    uint32_t  IR_decode_bit;//解码接收位数
    uint32_t  IR_event;    //红外输出事件
    uint8_t   data_recving_flag;//红外接收中的标志
    uint8_t   f_head;
    uint8_t   get_bit;
    uint8_t   low_8bit;
    uint8_t   hig_8bit;
    uint8_t   rece_ok;

    // int16_t   *send_data;//红外发送码数据的首地址
    // uint8_t   send_code_bits;//红外发送位
    // uint8_t   send_rep_times;//红外码重复发送次数
    // uint8_t   send_times;//切换档位所需要次数
    // uint8_t   send_delay;//多包间隔时间
    // uint8_t   data_send_flag;//红外发送标志 

    uint8_t   printf_datalen;

}IR_struct;

extern IR_struct IR;


/* Exported functions prototypes ---------------------------------------------*/
void APP_ErrorHandler(void);

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

/************************ (C) COPYRIGHT Puya *****END OF FILE******************/

结构体初始化在main.c中:

	IR_struct IR;           //红外部分需要用到的结构体变量声明

    IR.IR_code = 0;
    IR.IR_decode_bit = 0;
    IR.IR_event = 0;
    IR.timer_count = 0;
    IR.f_head = 0;
    IR.get_bit = 0;
    IR.low_8bit = 0;
    IR.hig_8bit = 0;
    IR.rece_ok = 0;

C文件内容是:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "string.h"
#include "setup.h"

/* Private define ------------------------------------------------------------*/


/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef UartHandle;
__IO ITStatus UartReady = RESET;
TIM_HandleTypeDef TimHandle,Tim3Handle,Tim1Handle, Tim17Handle;
TIM_OC_InitTypeDef Tim17OCHandle,Tim16OCHandle;
I2C_HandleTypeDef I2cHandle;
TIM_IC_InitTypeDef sConfig;
TIM_SlaveConfigTypeDef   sSlaveConfig;
EXTI_HandleTypeDef exti_handle;
GPIO_InitTypeDef GPIO_InitStruct;



IR_struct IR;           //红外部分需要用到的结构体变量声明


/* Private user code ---------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/


/**
  * @brief  应用程序入口函数.
  * @retval int
  */
int main(void)
{   
    /* 初始化所有外设,Flash接口,SysTick */
    HAL_Init();
    /* 系统时钟配置 */
    APP_SystemClockConfig();
    GPIO_Init();

    TIM3_Init();
    HAL_TIM_Base_Stop_IT( &Tim3Handle );    //先关闭TIM3,等收到一个下降沿之后再打开
    TIM3->CNT = 0;                          //清零定时器,为下一次接受红外做准备
    
    /* 使能NVIC GPIO外部中断 */
    HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
    HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);


    IR.IR_code = 0;
    IR.IR_decode_bit = 0;
    IR.IR_event = 0;
    IR.timer_count = 0;
    IR.f_head = 0;
    IR.get_bit = 0;
    IR.low_8bit = 0;
    IR.hig_8bit = 0;
    IR.rece_ok = 0;
    
    while (1)
    {
	 	if( IR.rece_ok )
        {
            switch (IR.hig_8bit)
            {
                case IR_CODE_GET_POWER :
                    
                break;
                case IR_CODE_GET_SHAKE :
                    
                break;
                case IR_CODE_GET_SPEED :
                    
                break;
                case IR_CODE_GET_TIMER :
                    
                break;  
                default:
                break;
            }
        
            IR.IR_code_get = 0;
            IR.low_8bit = 0;
            IR.hig_8bit = 0;

            boot_cheak_link_over = 0;

            IR.rece_ok = 0;
        }
    }
}


/**
  * @brief  错误执行函数
  * @param  无
  * @retval 无
  */
void APP_ErrorHandler(void)
{
    /* 无限循环 */
    while (1)
    {
    }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  输出产生断言错误的源文件名及行号
  * @param  file:源文件名指针
  * @param  line:发生断言错误的行号
  * @retval 无
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* 用户可以根据需要添加自己的打印信息,
        例如: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* 无限循环 */
    while (1)
    {
    }
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT Puya *****END OF FILE******************/

贴一下抓到的接收头波形:
在这里插入图片描述
数据内容发完之后如果一直按住不松手,那么发过来的其实就是重复码。

代码内容比较乱,其实核心就是开启一个上升沿+下降沿的中断,判断进入外部中断时是什么电平,如果进入外部中断时是高电平,说明TIM3的CNT存放的就是这次上升沿与上一次的下降沿之间低电平的持续时间;如果进入外部中断时是低电平,说明TIM3的CNT存放的就是这次下降沿与上一次的上升沿之间高电平的持续时间。通过不停进外部中断判断TIM3的CNT数值来判断高低电平的持续时间,完成解码。这种方式不会堵塞MCU,比进入外部中断之后判定电平死等的那种方式更加合理。

更加详细的内容可以看这篇博客,这个大佬写得比较详细,不过他用的是进中断后while的方式来判定时间:
链接: STM32入门开发: NEC红外线协议解码(超低成本无线传输方案)

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

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

相关文章

基于MATLAB实现WSN(无线传感器网络)的LEACH(低能耗自适应集群层次结构)(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 低能耗自适应集群层次结构&#xff08;“LEACH”&#xff09;是一种基于 TDMA 的 MAC 协议&#xff0c;它与无线传感器网络 &a…

[2018.09.25][Sourceinsight]4.0配置

1 字体放大 (1)panel fonts: option,preference,colors&font (2)code fonts: option,file type options 2 修改默认字体 Alt y 3 显示行号 点击菜单栏View->Line Numbers 4 破解 https://blog.csdn.net/biubiuibiu/article/details/78044232 5 全局搜索字…

在Spring Boot微服务使用knife4j发布后端API接口

记录&#xff1a;422 场景&#xff1a;在Spring Boot微服务上&#xff0c;应用knife4j发布后端API接口&#xff0c;辅助开发与调试。 版本&#xff1a;JDK 1.8,Spring Boot 2.6.3,knife4j-3.0.3,springfox-swagger2-3.0.0。 Knife4j: 是一个集Swagger2 和 OpenAPI3为一体的增…

第三十二篇,记一次Windows下Qt使用boost的经历

Win10系统 Qt版本如下所示 Qt中使用的编译器是MinGW&#xff0c;如下图 boost版本是1.82.0 好的&#xff0c;描述一下过程&#xff1a; 按这个教程下载、编译boost&#xff0c;在boost的目录下生成了stage/lib/目录&#xff0c;然后加入到Qt工程里&#xff0c;主要是include目…

win10安装Anaconda,配置Pytorch环境

一、安装Anaconda Anaconda实际上是一个包管理器&#xff0c;可以理解为一个工具。Anaconda自带Python&#xff08;选中版本&#xff09;解释器以及其他一些数据分析与挖掘需要的模块而无需用户手动添加这些常用模块&#xff08;安装模块会出现各种错误&#xff09;。早期学Pyt…

Django个性化推荐系统,以电影为例

背景 随着科学技术发展&#xff0c;电脑已成为人们生活中必不可少的生活办公工具&#xff0c;在这样的背景下&#xff0c;网络技术被应用到各个方面&#xff0c;为了提高办公生活效率&#xff0c;网络信息技术飞速发展。在这样的背景下人类社会进入了全新的信息化的时代。电影…

flask+opencv:实时视频直播推流平台Demo

简介&#xff1a;推流&#xff0c;指的是把采集阶段封包好的内容传输到服务器的过程。其实就是将现场的视频信号传到网络的过程。网上调查、对话访谈、在线培训等内容现场发布到互联网上。利用互联网的直观、快速&#xff0c;表现形式好、内容丰富、交互性强、地域不受限制、受…

华为OD机试真题(Java),猴子爬山(100%通过+复盘思路)

一、题目描述 一天一只顽猴想去从山脚爬到山顶&#xff0c;途中经过一个有个N个台阶的阶梯&#xff0c;但是这猴子有一个习惯&#xff1a; 每一次只能跳1步或跳3步&#xff0c;试问猴子通过这个阶梯有多少种不同的跳跃方式&#xff1f; 二、输入描述 输入只有一个整数N&…

AI语音生成器是下一大安全威胁吗?

ChatGPT一经上市&#xff0c;有关监管人工智能的讨论就开始升温。任何试图遏制这种技术的做法都可能需要国际合作&#xff0c;需要我们在过去几十年来从未见过的合作程度&#xff0c;因此不太可能遏制人工智能。 人工智能是一项功能强大的技术&#xff0c;有望彻底改变我们生活…

AIGC - 生产力新工具 Bito AI

文章目录 Bito AI 是什么Bito AI 能干啥官网免费的吗&#xff1f;如何使用 Bito方式一&#xff1a;方式二&#xff1a;在这里插入图片描述方式三 Bito AI 是什么 Bito AI是继Github Copilot、Cursor、CodeWhisperer等AI智能编程产品之后发了一大AI编程工具 。 Bito是一款建立…

表情迁移 - 2D人像动起来(附带生成web服务提供api接口)

左边原图,右边是渲染后的视频文件 开源地址:https://github.com/AliaksandrSiarohin/first-order-model 官方模型下载地址(需科学上网): google-driveyandex-disk本文docker容器已自带人脸模型 若还需要行为、物品、动画等追踪模型需下载后拷贝至容器内即可使用 API请求…

Android之 Camera相机使用

一 简介 1.1 随着信息时代的发展&#xff0c;相机在我们生活中使用越来越频繁&#xff0c;也成为手机的基本配置之一。相机可以用来拍照&#xff0c;拍视频&#xff0c;人脸识别&#xff0c;视频聊天&#xff0c;扫码支付&#xff0c;监控等常见领域 不管什么场景&#xff0c…

DolphinScheduler 2.0.5详解

文章目录 第一章 DolphinScheduler介绍1.1 关于DolphinScheduler1.2 DolphinScheduler特性1.3 配置建议1.3.1 Linux 操作系统版本要求1.3.2 服务器建议配置1.3.3 生产环境1.3.4 网络要求1.3.5 客户端 Web 浏览器要求 第二章 DolphinScheduler安装部署2.1 安装部署介绍2.2 单机版…

Mybatis笔记分享【狂神说java】

MyBatis 1、简介 1.1什么是MyBatis MyBatis 是一款优秀的持久层框架 它支持自定义 SQL、存储过程以及高级映射 MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作 MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO&#xff08;…

Windows环境下使用Internet Information Service( IIS)管理器上搭建Web资源网站

一、业务需求 在项目的开发过程中,需要将指定文件夹发布称为一个网站,可以通过网页查看该文件夹下的内容和子目录(及其子目录内容);同时也可以将内容上传到该文件夹中,方便他人使用,如下图所示: 二、思路分析 将文件夹发布为网站,可以使用Windows的IIS管理器搭建一个W…

Scala之模式匹配与隐式转换

目录 模式匹配&#xff1a; 基础语法如下&#xff1a; 模式守卫&#xff1a; 类型匹配&#xff1a; 对象匹配 样例类&#xff1a; 偏函数&#xff1a; 偏函数的化简写法&#xff1a; 偏函数的使用&#xff1a; 隐式转换&#xff1a; 官方定义&#xff1a; 个人理解&…

(小甲鱼python)文件永久存储(下)总结 文件处理with语句和上下文管理器、pickle模块详解

一、基础复习 上节课回顾&#xff1a; 1.文件永久存储(上) python文件永久存储(创建打开文件、文件对象的各种方法及含义) 2.文件永久存储(中)总结 路径处理 pathlib–面向对象的文件系统路径 绝对路径vs相对路径&#xff08;路径查询、路径修改、查找功能&#xff09; 二、文…

【JavaScript】new命令精华总结

相关概念 对象是什么? 1.对象是单个实物的抽象 2.对象是一个容器&#xff0c;封装了属性和方法 属性是对象的状态&#xff0c;方法是对象的行为&#xff0c;把对象中的函数一般称为方法 构造函数 专门用来生成实例对象的函数&#xff0c;是对象的模板&#xff0c;第一个字…

vue2数据响应式原理(7) 收集依赖,用get和set叙述出最基础的至高vue哲学

收集依赖在整个数据响应式中算是比较难的 首先 要理解这里所指的依赖 依赖 可能vue项目做多了就会想到 npm i 但其实跟这个是没有什么关系的 我们这里所指的依赖 是用到数据的地方 什么地方用到数据 什么地方就是依赖 简单说 就是依赖这个响应式数据 首先 我们看一下 vue1 和…

【Linux初阶】进程状态 | Linux下常见进程状态讲解 进程循环打印方法 ls>makefile指令 makefile$@^特殊符号的应用

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;进程状态的概念&#xff0c;进程状态在普遍操作系统层面和Linux层面的理解&#xff0c;Linux常见进程状态&…