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而言,就是这样了。
| 分组 | 抢占优先级位数 | 子优先级位数 | 抢占优先级范围 | 子优先级范围 |
|---|---|---|---|---|
| Group0 | 0位 | 4位 | 0 | 0-15 |
| Group1 | 1位 | 3位 | 0-1 | 0-7 |
| Group2 | 2位 | 2位 | 0-3 | 0-3 |
| Group3 | 3位 | 1位 | 0-7 | 0-1 |
| Group4 | 4位 | 0位 | 0-15 | 0 |
这个中断,就对应了如下的结构体的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/BetterATK
https://github.com/Charliechen114514/BetterATK/tree/STM32F1/103c8t6/standard/3_GPIO_EXIT
这里需要注意的是——我们引入了一个新的器件就是OLED器件,关于OLED器件的编程属于额外的内容,你需要做的就是将SCL接到PB8,SDA接到PB9,笔者写的CCGraphic框架会自动的在你的OLED屏上展示内容。具体的原理需要等待你掌握IIC协议和SPI协议之后,我们才能开始真正的修改内部的代码。
现象如下


相关文章:
STM32F103C8T6单片机开发:简单说说单片机的外部GPIO中断(标准库)
目录 前言 如何使用STM32F1系列的标准库完成外部中断的抽象 初始化我们的GPIO为输入的一个模式 初识GPIO复用,开启GPIO的复用功能时钟 GPIO_EXTILineConfig和EXTI_Init配置外部中断参数 插入一个小知识——如何正确的配置结构体? 初始化中断&#…...
Oracle序列介绍
文章目录 Oracle序列介绍1. Oracle序列演进2. Oracle序列使用3. Oracle身份列(自增列)4. Oracle序列常见使用与问题 Oracle序列介绍 1. Oracle序列演进 Oracle序列(Sequence)是数据库生成唯一数值序列的对象,主要用于…...
docker的安装使用0废话版本自学软硬件工程师778天
见字如面, 这里是AIGC创意人_竹相左边 上一篇 因为 自己开发客户系统,为了解决一键启动 前端后端,涉及到了docker-compose 在新的电脑上安装docker 有各种问题这里再次记录下,既是笔记也是分享。 我先用自己的话说一遍࿰…...
探秘 Svelte+Vite+TS+Melt - UI 框架搭建,开启高效开发
框架太“重”了:通常一个小型项目只由少数几个简单页面构成,如果使用 Vue 或者 React 这些框架来研发的话,有点“大材小用”了。构建的产物中包含了不少框架运行时代码(虚拟 DOM、响应式、状态管理等),这些代码对于小型项目而言是…...
3D数据共享标准——GLB文件格式揭秘
GLB 文件格式:跨平台 3D 数据共享的标准 简介 在这个数据爆炸的时代,3D 数据因其直观、逼真的特点而得到广泛应用。然而,不同 3D 软件和平台之间的兼容性一直是一个难题。 为了解决这一问题,GLB 文件格式应运而生。作为一种标准…...
微信小程序事件绑定基本语法
微信小程序使用 bind 或 catch 前缀绑定事件,语法如下: <组件 bind事件名"处理函数" catch事件名"处理函数"></组件> bind:事件绑定,允许事件冒泡(向父组件传递)。 catc…...
页面编辑器CodeMirror初始化不显示行号或文本内容
延迟刷新 本来想延迟100毫秒的,但是会出现样式向左偏移的情况,于是试了试500毫秒,发现就没有问题了,可能是样式什么是需要一个加载过程吧。 useEffect(() > {editorRef.current?.setValue(value || );setTimeout(() > {edi…...
vscode 连不上 Ubuntu 18 server 的解决方案
下载 vscode 历史版本 18.5(windows请装在 系统盘 C 盘) 打开 vdcode,将 自动更新 设置为 None (很关键,否则容易前功尽弃) 重命名(删除) 服务器上的 .vscode-server 文件夹 重新…...
云原生运维在 2025 年的发展蓝图
随着云计算技术的不断发展和普及,云原生已经成为了现代应用开发和运维的主流趋势。云原生运维是指在云原生环境下,对应用进行部署、监控、管理和优化的过程。在 2025 年,云原生运维将迎来更加广阔的发展前景,同时也将面临着一系列…...
Redis进阶--哨兵
目录 一、引言 二、介绍 三、哨兵的核心功能 四、使用docker进行单个主机redis主从复制哨兵操作 五、哨兵重新选取主节点的流程 1.主观下线 2.客观下线 3.主节点挂了 4.挑选新主节点 六、总结 一、引言 如果主从复制中,主节点挂了,从节点会迷茫…...
Franka 机器人x Dexterity Gen引领遥操作精细任务新时代
教授机器人工具灵活操作难题 在教授机器人灵活使用工具方面,目前主要有两种策略:一是人类遥控(用于模仿学习),二是模拟到现实的强化学习。然而,这两种方法均存在明显的局限性。 1、人类遥控(用…...
UE5 RPC调用示例详解
文章目录 前言一、示例场景二、代码实现三、关键点解析3.1 RPC类型选择3.2 可靠性设置3.3 权限控制3.4 输入处理 四、测试与验证总结 前言 在UE5中,RPC(远程过程调用)是实现多人游戏逻辑同步的核心机制。以下通过一个玩家跳跃的示例…...
专题|MATLAB-R语言Logistic逻辑回归增长模型在互联网金融共生及移动通信客户流失分析实例合集
全文链接:https://tecdat.cn/?p41343 作为数据科学家,我们始终关注如何通过模型创新揭示复杂系统的动态规律。本专题合集聚焦两大核心应用场景,通过 Logistic 增长模型与逻辑回归技术,为互联网金融共生演化与移动通信客户流失预警…...
从零构建一个全栈AI应用:Next.js + FastAPI + OpenAI API
💡 为什么写这篇文章? 很多开发者希望构建一个能“聊天、问答、调用AI能力”的完整应用,但在前端、后端、模型接口之间打通时,常常踩坑。 今天这篇文章将手把手教你如何从零构建一个 AI 全栈应用: 前端用 Next.js 构…...
idea里面不能运行 node 命令 cmd 里面可以运行咋回事啊
idea里面不能运行 node 命令 cmd 里面可以运行咋回事啊 在 IntelliJ IDEA(或其他 JetBrains 系列 IDE)中无法运行某些命令,但在系统的命令提示符(CMD)中可以正常运行,这种情况通常是由于以下原因之一导致的…...
Dify 生成提示词的 Prompt
Dify 生成提示词的 Prompt **第1次提示词****第2次提示词****第3次提示词**总结 Dify 生成提示词是,会和LLM进行3次交互,下面是和LLM进行交互是的Prompt。 以下是每次提示词的概要、目标总结以及原始Prompt: 第1次提示词 概要: …...
ocr python库
ocr python库 上手Git、Gitee和Github!watt toolkit...
【学生管理系统升级版】
学生管理系统升级版 需求分析:注册功能:登录功能:验证码规则:忘记密码: 实操:系统主页面注册功能登录功能忘记密码效果演示 需求 为学生管理系统书写一个登陆、注册、忘记密码的功能。 只有用户登录成功之后&…...
HTTP:二.URI及相关术语
HTTP相关技术和术语 WEB开发语言 **http:**Hyper Text Transfer Protocol 应用层协议,默认端口: 80/tcp WEB前端开发语言: htmlcssjavascripthtml Hyper Text Markup Language 超文本标记语言,编程语言,主要负责实现页面的结构 范例:html 语言 <html> <h…...
Web3 的云基础设施正在成型,Polkadot 2.0 用三项技术改写“上链成本”
在Web3基础设施内卷加剧的今天,“如何以更低成本、更大灵活性部署一条高性能应用链”正成为开发者们最关心的问题。而刚刚走出“技术慢热”误区的Polkadot,正在用一套名为 Polkadot 2.0 的架构升级方案,重新定义这一问题的解法。 这套升级最…...
【12】RUST智能指针
文章目录 智能指针Box<T>Rc<T>Weak<T>方法 Arc<T>RefCell<T>方法Ref<T>和RefMut<T> 常见使用方式 智能指针 Box<T> 数据存储在堆上只能有一个所有者 Rc<T> 单线程,引用计数不可变需要主要避免循环引用 …...
机器学习——ROC曲线、PR曲线
一、ROC曲线简介 1.1 ROC曲线的构成 1.横轴(假正率,FPR): 表示负样本被错误分类为正的比例(越小越好) 2.纵轴(真正率,TPR,即召回率): 表示正样…...
Unity AssetBundle依赖树可视化分析工具开发指南
一、需求分析与技术选型 1.1 需求背景 在Unity项目开发中,AssetBundle依赖关系管理是性能优化的关键。当项目资源量达到GB级别时,依赖树深度可能超过10层,容易导致: 资源重复打包(平均冗余率可达15%-30%)…...
WebStorm中使用live-server插件
文章目录 1. 前提条件1.1 已安装Node1.1.1 淘宝的镜像1.2 安装live-server1.3 安装WebStorm2. Windows配置hosts3. WebStorm配置live-server3.1 WebStorm创建3.2 启动 live-server1. 前提条件 1.1 已安装Node Windows PowerShell 版权所有(C) Microsoft Corporation。保留所…...
RTP Payload Format for H.264 Vide(1)
摘要:: 本备忘录描述了一种用于 ITU-T H.264 视频编码标准(与 ISO/IEC 国际标准 14496-10 技术上相同)的 RTP 负载格式,但不包括可伸缩视频编码(SVC)扩展和多视角视频编码(MVC&#…...
我为女儿开发了一个游戏网站
大家好,我是星河。 自从协助妻子为女儿开发了算数射击游戏后,星河就一直有个想法:为女儿打造一个专属的学习游戏网站。之前的射击游戏虽然有趣,但缺乏难度分级,无法根据女儿的学习进度灵活调整。而且,仅仅…...
【Spring Cloud Netflix】GateWay服务网关
1.基本概述 GateWay用于在微服务架构中提供统一的入口点,对请求进行路由,过滤和处理。它就像是整个微服务系统的大门,所有外部请求都要通过它才能访问到后端的各个微服务。 2.核心概念 2.1路由(Route) 路由是Spring Cloud gateWay中最基本…...
Docker部署Jenkins服务
文章目录 1.下载Jenkins服务2.部署Java21(可选)2.1 安装Java21 3.Maven3.9.9安装4.启动Jenkins5.初始化Jenkins5.1 入门5.2 安装推荐的插件5.3 创建第一个管理员用户5.4 实例配置5.5 Jenkins已就绪5.6 开始使用Jenkins5.7 重启Jenkins 6.配置Jenkins6.1 …...
[ctfshow web入门] web40
信息收集 怎么一下子多这么多过滤啊,我以为跳过了好几题 这又能eval了,但是连$也不能用了 不能用. * ?,所以打不出fla*或者fla?????了 不能用/,构造不出日志注入和伪协议包含 仔细观察,禁的是中文的括号&#x…...
蓝桥杯c ++笔记(含算法 贪心+动态规划+dp+进制转化+便利等)
蓝桥杯 #include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; //常使用的头文件动态规划 小蓝在黑板上连续写下从 11 到 20232023 之间所有的整数,得到了一个数字序列: S12345…...
