STM32F103C8T6单片机开发:简单说说单片机的外部GPIO中断(标准库)

目录

前言

如何使用STM32F1系列的标准库完成外部中断的抽象

初始化我们的GPIO为输入的一个模式

初识GPIO复用,开启GPIO的复用功能时钟

GPIO_EXTILineConfig和EXTI_Init配置外部中断参数

插入一个小知识——如何正确的配置结构体?

初始化中断:NVIC注册

处理中断回调函数

本篇的实验代码


前言

在之前,我们就详细的讨论过了最简单的GPIO设备的驱动和使用,但是如你所见,这些调用都是同步,即我们程序主动的去设置和读取GPIO的状态。这很正常——我们只有在想要主动出击的时候,比如说点亮LED电平,查询按钮的状态的时候,我们主动的申请和读取结果。这是很正常的事情。

但是仍然有一些场景,我们实际上并不是这样的,甚至完全与此相反。举个例子,一些事情我们更希望是外部来告知我们发生了。举个例子,一个经典的场景就是——我们希望在按键嗯下的时候,我们来做一部分事情,比如说后面我们切换LCD界面啊等等的事情。按照我们之前的方法,那就是:

while(1)
{if(isKeyPressed()){do_things();}else{do_other_things();}...
}

这个的问题看起来不大,但是实际上你仔细思考一下,问题很大。在我们上一个小节中,我们做一个改动,您可以尝试一下:

while(1)
{if(isKeyPressed()){reverse_gpio_status(&led0);}else{system_clock_delay_ms(1000);    // delay for 1 seconds}...
}

你会发现一个致命的问题,整个系统罢工了,为什么,我们仔细看看这个代码——我们在一瞬间检查完Key没有按下之后,整个系统直接挂起来1秒钟,如果我们之后写更加庞大的项目的时候,问题还能更加尖锐(比如说使用ESP8266等待网络请求,那可就是一个等待就是好几秒),这下你的客户如果发了疯的按按钮就会发现根本不起作用的时候,你的好日子也就到头了。那咋办?

仔细想下,我们的逻辑是不是有点问题——分明是我们想要“按下按钮的时候,我们才会翻转LED的电平。”,整个程序的逻辑是——询问一下按钮按下了嘛?有则处理按钮按下的逻辑,没有处理其他程序。这不对的。

问题就在于,我们需要把逻辑颠倒过来!一个事件发生了,我们需要如何处理这个事情。而不是反复的询问这个事情有没有发生。因此,我们就需要一个机制,这个机制,就是外部中断,外部中断按照一种通知的方式,通知我们的单片机一个抽象的事件发生了。

一个光传感器察觉到了一个光亮度的变化的时候,光传感器发送了一个电平脉冲。与其一直轮循电平的变化,不如直接让我们的GPIO处于一个中断的前端,让接受到电平变化的引脚申请我们的单片机触发一个中断,极其短暂的打断我们的程序执行,跳转道一个新的处理流处理我们的电平变化的事件。比如说,光暗了,我们需要开灯,这个时候,我们处理光传感器发过来的“光暗下去了”的信号,处理的程序就是打开一个LED灯。你看,简单吧!

因此,按照这种方式,我们就可以摆脱我们的轮循式的询问,转向语义更好的“中断事件通知了”。

关于中断的本质和STM32的中断,我们放到旋转编码器和光传感器的之后作为一个硬核原理篇章,仔细的介绍其根本原理,这里,我们仍然只是保留到会用即可的水平。

下面,我们就来聊一聊,标准库是如何使用外部中断来完成我们的事情的。

如何使用STM32F1系列的标准库完成外部中断的抽象

标准库已经给我们提供好了脚手架了,只需要我们按照步骤进行配置即可。简单的说,就是做这些事情

  • 初始化我们的GPIO为输入的一个模式

  • 初始化GPIO外部中断的资源寄存器

  • 使能我们的外部中断

这需要我们一步步来看。

初始化我们的GPIO为输入的一个模式

为什么是输入模式呢?你想想,我们接受外部的中断是不是需要从外界获取信息?是的,这就是我们的GPIO中断模式本质上需要以输入模式进行配置的原因,如果你还看笔者的HAL库教程,你就会知道,HAL库直接封装好了体系,将输入模式中的一部分特化出来了外部中断,合并了我们标准库的配置,标准库比较原始,因此我们需要做的事情就是将这些步骤正确的组合起来。

关于输入模式的配置,这里我们不再重复谈论了,实在乏味。我们马上进入下一个阶段,配置外部中断的GPIO引脚资源。

初识GPIO复用,开启GPIO的复用功能时钟

关于复用的底层原理,一并合并到我们的GPIO中断中讨论,这里,你只需要知道的是:GPIO现在具备了片上外设和外部设备的沟通能力,在这里,就是我们的中断控制器现在可以监控我们的外部设备的电平变化了,我们这一步就是做这个事情。

开启GPIO的AF时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

开启这个时钟,我们的片上外设复用就变得可用,下一步,就是提交我们要监察的外部中断是如何的。

GPIO_EXTILineConfig和EXTI_Init配置外部中断参数

使用的函数是GPIO_EXTILineConfig来注册EXTI中断控制子系统,连接GPIO和外部中断线,进而再EXTI_Init函数来初始化。

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

EXTI_InitTypeDef里,提供的就是我们写的参数了

typedef struct
{uint32_t EXTI_Line;               /*!< Specifies the EXTI lines to be enabled or disabled.This parameter can be any combination of @ref EXTI_Lines */EXTIMode_TypeDef EXTI_Mode;       /*!< Specifies the mode for the EXTI lines.This parameter can be a value of @ref EXTIMode_TypeDef */
​// 笔者注释:之前的版本中这里的注释类型写错了EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.This parameter can be a value of @ref EXTITrigger_TypeDef */
​FunctionalState EXTI_LineCmd;     /*!< Specifies the new state of the selected EXTI lines.This parameter can be set either to ENABLE or DISABLE */ 
}EXTI_InitTypeDef;
插入一个小知识——如何正确的配置结构体?

我发现,大部分嵌入式的人,不太会查看手册/查看注释来进行学习。你看,我们的标准库实际上已经把事情说的很明白了,我们这里实际上需要填写的参数

第一个蚕食,人家的说法是任意组合的EXTI_Line,我们一看,嗯很,没有具体说明,一个最快速的办法就是看注释,EXTI_LINE的一个组合,这也就说明了,直接全局搜索一个EXTI_LINE,这里的EXTI_Mode对应的就是对应的GPIO_Pin所在的EXTI线,举个例子,GPIO_Pin_0对应的就是EXTI_Line0,GPIO_Pin_1对应的就是EXTI_Line1,类推!

#define EXTI_Line0       ((uint32_t)0x00001)  /*!< External interrupt line 0 */
#define EXTI_Line1       ((uint32_t)0x00002)  /*!< External interrupt line 1 */
#define EXTI_Line2       ((uint32_t)0x00004)  /*!< External interrupt line 2 */
#define EXTI_Line3       ((uint32_t)0x00008)  /*!< External interrupt line 3 */
#define EXTI_Line4       ((uint32_t)0x00010)  /*!< External interrupt line 4 */
#define EXTI_Line5       ((uint32_t)0x00020)  /*!< External interrupt line 5 */
#define EXTI_Line6       ((uint32_t)0x00040)  /*!< External interrupt line 6 */
#define EXTI_Line7       ((uint32_t)0x00080)  /*!< External interrupt line 7 */
#define EXTI_Line8       ((uint32_t)0x00100)  /*!< External interrupt line 8 */
#define EXTI_Line9       ((uint32_t)0x00200)  /*!< External interrupt line 9 */
#define EXTI_Line10      ((uint32_t)0x00400)  /*!< External interrupt line 10 */
#define EXTI_Line11      ((uint32_t)0x00800)  /*!< External interrupt line 11 */
#define EXTI_Line12      ((uint32_t)0x01000)  /*!< External interrupt line 12 */
#define EXTI_Line13      ((uint32_t)0x02000)  /*!< External interrupt line 13 */
#define EXTI_Line14      ((uint32_t)0x04000)  /*!< External interrupt line 14 */
#define EXTI_Line15      ((uint32_t)0x08000)  /*!< External interrupt line 15 */

EXTI_Mode是一个enum枚举类型

typedef enum
{EXTI_Mode_Interrupt = 0x00,EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;

这里有两个参数,我们理解和学习的是中断,很显然是EXTI_Mode_Interrupt,但是这里笔者还是要简单的说明一下这两个参数有什么区别。

特性EXTI_Mode_Interrupt (中断模式)EXTI_Mode_Event (事件模式)
是否触发中断✔️ (调用我们处理的回调函数)❌ (仅硬件事件)
是否唤醒 CPU✔️
CPU 参与需要 CPU 处理中断无需 CPU 干预
典型应用紧急任务(如按键、故障检测)硬件触发(如 DMA、ADC)

EXTI_Mode_Event不是我们这里可以管的,因此,等到我们谈论到了DMA,ADC等概念的时候,我们会回来学习事件模式的。

EXTITrigger_TypeDef说明我们如何触发这个中断。

typedef enum
{EXTI_Trigger_Rising = 0x08,EXTI_Trigger_Falling = 0x0C,  EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;

嗯,上升?下降?上升下降?这些是什么东西啊?别急,我们来仔细思考一下。一个很自然的疑问,举个例子:我们如何认为外部的GPIO中断道来了呢?你可能会说这是我关心的嘛?你需要关心!,这需要你仔细阅读器件的手册,举一个例子,笔者的光传感器,查看手册后,可以做出这样的一个时序图。

我们看到,这个器件的特性是阈值触发类型的传感器,也就是扫描过一次阈值,就会触发一次信号。可以看到,我们触发一次,我们的光传感器就会快速向外发射一个低电平,因此,我们只需要检测低电平,就能捕捉到我们的光强变化事件了!

仔细看看下面的这个图,思考一下上面的三个枚举,你认为,这三个枚举,想要监控的是哪些电平变化呢?答案如下!

现在我们很清楚了,对于我们的器件,如果你的手册上说:光强变化给一个下降触发的时候,你就需要配置成Falling了。

剩下的Cmd实际上就是Enable,没啥好说的。这就是使能的意思。

很好,我们申请GPIO中断的第一个子步骤,是注册GPIO的中断Pin线,使用的函数是

/*** @brief  Selects the GPIO pin used as EXTI Line.* @param  GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines.*   This parameter can be GPIO_PortSourceGPIOx where x can be (A..G).* @param  GPIO_PinSource: specifies the EXTI line to be configured.*   This parameter can be GPIO_PinSourcex where x can be (0..15).* @retval None*/void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)

比如说,我们想要给GPIOB注册中断,办法就是提供:GPIO_PortSourceGPIOB和GPIO_PinSource0。这个时候,使用这个函数的后,我们的GPIO_Pin就能根我们的外部中断线连接起来,下一步只需要把中断使能起来,就可以使用外部中断来监控事件了。

初始化中断:NVIC注册

NVIC是啥呢?NVIC的全称是Nested vectored interrupt controller,即嵌套向量中断控制器。关于通用的中断,我们需要做的理解就是跟上面说的一致——是事件(中断也可以来自内部,但对于最核心的CPU而言,你认为他们都是外部好像毛病也不大,总而言之,就是一些事件需要触发我们的处理器暂停手下的活来处理通知的中断事件,这里的嵌套你可以认为中断是可以嵌套的)

如果感觉中断不好理解,笔者在手搓操作系统的博客中有打过这样的比喻:

现实生活中,我们可能往往会被任何啥东西打断,比如说,此时此刻我正在写一个阐述中断的博客,我的一个好朋友突然给我发QQ,向我抱怨该死的爬虫实在是太慢了,需要优化一下并发的结构。这个时候,我就跑去回应他,刚准备打几个字,我的母上大人着急的找我问点事情,我又抛下对这个哥们的回复,冲过去回复我母上大人的问题,之后再回复我兄弟的抱怨,最后回来,继续抓耳挠腮的写下这段话。

从0开始的操作系统手搓教程11:为底层添砖加瓦:中断_intr指令-CSDN博客

Charliechen114514/CCOperateSystem: A Tutorial Trying teach you how to make an os in modern tools with gcc, nasm and bochs!

理解好中断之后,我们继续说明我们的配置。STM32对我们的中断管理非常的细致,将中断分组,其描述优先级的位实际上非常了两个比较大的部分。如下所示

  • 抢占优先级(Preemption Priority):决定中断是否可以打断正在执行的其他中断

  • 子优先级(Sub-Priority):当多个挂起中断具有相同抢占优先级时,决定它们的执行顺序

换而言之,回复我母上大人的消息的抢占优先级是1,回复我兄弟的抱怨抢占优先级是2的时候,我显然会先处理母上大人的事情,这个时候,尽管我正在回复我的兄弟,我也会让他靠边站!,这就是抢占优先级的效果。

那子优先级呢?答案是当我们没有发生中断的时候,或者说发生的是同级优先级的中断的时候(可以认为是特殊的“没有中断发生”),我们决定先处理谁。举个例子,我跟好朋友A和好朋友B中,我更喜欢跟A相处的时候,我优先回复A的消息,完事了之后回复B的消息。因此,在存在多个中断配置的时候,我们需要牢记好,谁会霸道的优先发生,这样,在处理中断的时候,就会理清楚基本的思路。

来,举个例子,我们的Systicks更新中断总是放到优先级的最后,这个时候,如果你在高优先级的中断中,调用了跟Systicks相关的系统中断,你知道会发生什么的。那就是你希望Systicks更新,好衡量给定的毫秒数,但是你又在高优先级中断中,没办法执行Systicks更新的回调函数,导致Systicks又不更新,这个事情在HAL中就会非常明显,HAL库就是一个强依赖软件更新的时钟基础系统,因此,这个时候调用基于这个时钟的延时,都会导致矛盾:希望Systicks中断正常工作触发软件定时器更新,但是你处于高优先级中断中无法打断此时此刻的中断去更新软件定时器。导致系统直接死机。

配置上,对于STM32F103C8T6而言,就是这样了。

分组抢占优先级位数子优先级位数抢占优先级范围子优先级范围
Group00位4位00-15
Group11位3位0-10-7
Group22位2位0-30-3
Group33位1位0-70-1
Group44位0位0-150

这个中断,就对应了如下的结构体的NVIC_IRQChannelPreemptionPriority参数和NVIC_IRQChannelSubPriority参数,我们依次对应了抢占优先级和子优先级:

typedef struct
{uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.This parameter can be a value of @ref IRQn_Type (For the complete STM32 Devices IRQ Channels list, pleaserefer to stm32f10x.h file) */
​uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channelspecified in NVIC_IRQChannel. This parameter can be a valuebetween 0 and 15 as described in the table @ref NVIC_Priority_Table */
​uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specifiedin NVIC_IRQChannel. This parameter can be a valuebetween 0 and 15 as described in the table @ref NVIC_Priority_Table */
​FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannelwill be enabled or disabled. This parameter can be set either to ENABLE or DISABLE */   
} NVIC_InitTypeDef;

剩下的NVIC_IRQChannelCmd我们直接填写enable即可。那NVIC_IRQChannel说明什么呢?答案是——标识使能哪一个中断。中断也是需要使能的,牢记一句话——我们的一切电子设备几乎都是依靠时钟才能进行节拍有序的工作,没有基准时钟,所有的器件没有了节拍,自然也就黯淡失色

随后,我们把这个结构体提交给NVIC_Init函数即可。

处理中断回调函数

剩下的发生的事情,那就是交给单片机了,他经过一系列复杂的机制,终于到达了ST工程师们预设的中断,对于PB0(举个例子),这个IRQ_Handler是谁呢?EXTI0_IRQHandler。是随便起的名字嘛?不是。请看汇编——

g_pfnVectors:
​.word _estack.word Reset_Handler.......word EXTI0_IRQHandler.word EXTI1_IRQHandler.word EXTI2_IRQHandler.word EXTI3_IRQHandler.word EXTI4_IRQHandler.......word EXTI9_5_IRQHandler.......word EXTI15_10_IRQHandler......

在这一串代码里,我们很快找到了几个我们看起来很熟悉的东西,对的,就是这些,你会发现这些就是著名的IRQHandler,也就是中断处理回调函数,对于应用层次,你只需要理解的是发生了这个中断,就会触发回调(回调回调,回来调用)函数,来执行你的处理逻辑。

PB0对应的外部中断线是EXTI0线,也就是EXTI0_IRQn,触发的函数回调就是EXTI0_IRQHandler。因此,我们只需要在EXTI0_IRQHandler写上我们的处理逻辑即可。

需要注意的是,中断处理结束后,需要清理标置位,这个东西相当于我们的中断是否发生的标志,只有标志为——处理结束,没有发生了之后,我们才会理回新的中断!

本篇的实验代码

出于篇幅原因,这里不再粘贴代码,地址在这里:

BetterATK/103c8t6/standard/3_GPIO_EXIT at STM32F1 · Charliechen114514/BetterATKhttps://github.com/Charliechen114514/BetterATK/tree/STM32F1/103c8t6/standard/3_GPIO_EXIT

这里需要注意的是——我们引入了一个新的器件就是OLED器件,关于OLED器件的编程属于额外的内容,你需要做的就是将SCL接到PB8,SDA接到PB9,笔者写的CCGraphic框架会自动的在你的OLED屏上展示内容。具体的原理需要等待你掌握IIC协议和SPI协议之后,我们才能开始真正的修改内部的代码。

现象如下

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

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

相关文章

【自然语言处理】深度学习中文本分类实现

文本分类是NLP中最基础也是应用最广泛的任务之一&#xff0c;从无用的邮件过滤到情感分析&#xff0c;从新闻分类到智能客服&#xff0c;都离不开高效准确的文本分类技术。本文将带您全面了解文本分类的技术演进&#xff0c;从传统机器学习到深度学习&#xff0c;手把手实现一套…

Java Lambda与方法引用:函数式编程的颠覆性实践

在Java 8引入Lambda表达式和方法引用后&#xff0c;函数式编程范式彻底改变了Java开发者的编码习惯。本文将通过实战案例和深度性能分析&#xff0c;揭示如何在新项目中优雅运用这些特性&#xff0c;同时提供传统代码与函数式代码的对比优化方案。 文章目录 一、Lambda表达式&a…

剑指offer经典题目(三)

目录 动态规划入门 二进制运算 链表相关 动态规划入门 题目1&#xff1a;一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法&#xff08;先后次序不同算 不同的结果&#xff09;。OJ地址 简单图示如下。 题目分析&#…

【每日随笔】丛林法则 ( 弱肉强食 | 适者生存 | 资源有限稀缺 | 没有道德约束 | 自发性与无序性 | 丛林法则映射 - 资源分配 与 社会分层 )

文章目录 一、丛林法则1、弱肉强食2、适者生存3、资源有限稀缺4、没有道德约束5、自发性与无序性6、丛林法则映射 - 资源分配 与 社会分层 一、丛林法则 丛林法则 是 在 资源有限 的环境中 , 竞争 是生存的基础 , 弱肉强食 , 适者生存 , 且过程 不受道德约束 ; 丛林法则 在 自…

【含文档+PPT+源码】基于小程序的智能停车管理系统设计与开发

项目视频介绍&#xff1a; 毕业作品基于小程序的智能停车管理系统设计与开发 课程简介&#xff1a; 本课程演示的是一款基于小程序的智能停车管理系统设计与开发&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;…

Navicat连接远程PostGreSQL失败

问题描述 使用本地Navicat连接Windows远程服务器上部署的PostGreSQL数据库时,出现以下错误: 解决方案 出现以上报错信息,是因为PostGreSQL数据库服务尚未设置允许客户端建立远程连接。可做如下配置, 1. 找到PostGreSQL数据库安装目录下的data子文件夹,重点关注:postgres…

【Linux】jumpserver开源堡垒机部署

JumpServer 安装部署指南 本文档详细记录了 JumpServer 安装部署的过程、核心脚本功能说明以及后续管理使用提示&#xff0c;方便运维人员快速查阅和二次安装。 1. 前提条件 操作系统要求&#xff1a; 仅支持 Linux 系统&#xff0c;不支持 Darwin&#xff08;macOS&#xff0…

餐饮厨房开源监控安全系统的智能革命

面对日益严格的合规要求和消费者对卫生的信任危机&#xff0c;传统人工监督已力不从心&#xff1a;卫生死角难发现、违规操作难追溯、安全隐患防不胜防。如何让后厨更透明、更安全、更可信&#xff1f;餐饮厨房视频安全系统横空出世&#xff01;这套系统融合实时监控与AI技术&a…

HashMap为什么扩容为原来2倍呢?

1、减少哈希碰撞 核心原因&#xff1a;HashMap的所有设计都依赖于数组长度为2的幂次方这一前提。 索引计算使用 &#xff08;n-1)&hash &#xff0c;其中 n 是数组长度当 n 是 2 的幂次方时&#xff0c;n-1 的二进制形式是全 1&#xff08;例如&#xff0c;15——>111…

debian系统中文输入法失效解决

在 Debian 9.6 上无法切换中文输入法的问题通常与输入法框架&#xff08;如 Fcitx 或 IBus&#xff09;的配置或依赖缺失有关。以下是详细的解决步骤&#xff1a; 1. 安装中文语言包 确保系统已安装中文语言支持&#xff1a; sudo apt update sudo apt install locales sudo…

3DGS之光栅化

光栅化&#xff08;Rasterization&#xff09;是计算机图形学中将连续的几何图形&#xff08;如三角形、直线等&#xff09;转换为离散像素的过程&#xff0c;最终在屏幕上形成图像。 一、光栅化的核心比喻 像画家在画布上作画 假设你是一个画家&#xff0c;要把一个3D立方体画…

学习51单片机Day02---实验:点亮一个LED灯

目录 1.先看原理图 2.思考一下&#xff08;sbit的使用&#xff09;&#xff1a; 3.给0是要让这个LED亮&#xff08;LED端口设置为低电平&#xff09; 4.完成的代码 1.先看原理图 比如我们要让LED3亮起来&#xff0c;对应的是P2^2。 2.思考一下&#xff08;sbit的使用&…

Redis与Lua原子操作深度解析及案例分析

一、Redis原子操作概述 Redis作为高性能的键值存储系统&#xff0c;其原子性操作是保证数据一致性的核心机制。在Redis中&#xff0c;原子性指的是一个操作要么完全执行&#xff0c;要么完全不执行&#xff0c;不会出现部分执行的情况。 Redis原子性的实现原理 单线程模型&a…

深入理解 GLOG_minloglevel 与 GLOG_v:原理与使用示例

文章目录 深入理解 GLOG_minloglevel 与 GLOG_v&#xff1a;原理与使用示例1. GLOG_minloglevel&#xff1a;最低日志等级控制2. GLOG_v&#xff1a;控制 VLOG() 的详细输出等级3. GLOG_minloglevel 与 GLOG_v 的优先级关系4. 使用示例4.1 基础示例&#xff1a;不同日志等级4.2…

Cline Memory Bank 结构化文档持久化 AI 上下文详解

&#x1f3ae; 什么是 Cline Memory Bank&#xff1f; Memory Bank 是一个结构化文档系统&#xff0c;允许 Cline 在会话之间保持上下文。它能让 Cline 从无状态的助手转变为持久记忆的开发伙伴&#xff0c;随着时间推移有效地“记住”项目细节。 &#x1f5e1;️ 关键优势 上…

【JavaScript】面向对象与设计模式

个人主页&#xff1a;Guiat 归属专栏&#xff1a;HTML CSS JavaScript 文章目录 1. JavaScript 中的面向对象编程1.1 对象基础1.2 构造函数1.3 原型和原型链1.4 ES6 类1.5 继承1.6 封装 2. 创建型设计模式2.1 工厂模式2.2 单例模式2.3 建造者模式2.4 原型模式 3. 结构型设计模式…

网络安全防护技术

边界安全防护——防火墙 控制&#xff1a;在网络连接点上建立一个安全控制点&#xff0c;对进出数据进行限制隔离&#xff1a;将需要保护的网络与不可信任网络进行隔离&#xff0c;隐藏信息并进行安全防护记录&#xff1a;对进出数据进行检查&#xff0c;记录相关信息 防火墙…

Spring MVC 视图解析器(JSP、Thymeleaf、Freemarker、 JSON/HTML、Bean)详解

Spring MVC 视图解析器详解 1. 视图解析器概述 视图解析器&#xff08;ViewResolver&#xff09;是 Spring MVC 的核心组件&#xff0c;负责将控制器返回的视图名称&#xff08;如 success&#xff09;转换为具体的 View 对象&#xff08;如 Thymeleaf 模板或 JSP 文件&#x…

# 爬虫技术的实现

手把手教你网络爬虫&#xff1a;从入门到实践 一、网络爬虫简介 网络爬虫&#xff08;Web Crawler&#xff09;是一种自动化获取互联网数据的程序&#xff0c;广泛应用于搜索引擎、数据分析、市场调研等领域。通过模拟浏览器行为&#xff0c;爬虫可以高效地从网页中提取结构化…

【HarmonyOS 5】鸿蒙中@State的原理详解

一、State在鸿蒙中是做什么的&#xff1f; State 是 HarmonyOS ArkTS 框架中用于管理组件状态的核心装饰器&#xff0c;其核心作用是实现数据驱动 UI 的响应式编程模式。通过将变量标记为 State&#xff0c;开发者可以确保当状态值发生变化时&#xff0c;依赖该状态的 UI 组件…