ESP32中断深扒:从 BOOT 按键到 NMI,一顿操作猛如虎

目录

实物图

原理图

ESP32-IDF 示例代码解析

关键知识点

硬件

中断矩阵:ESP32 的“交警系统”

映射方法:给中断“分配房间”

中断状态

软件

枚举类型

函数

使用开发板上的按键,开关灯

实操练习

实现功能

提示


实物图

原理图

找到 KEY 部分

可以看出 boot 按键,按下导通,产生低电平,那可以推断未按下时,应该输入高电平

ESP32-IDF 示例代码解析

  1. 使用 system/nmi_isr 工程 注意这个例子只在 esp32、esp32-s2 和 esp32-s3 适用。

2.分析示例代码:

#include <stdio.h> #include <string.h> #include <inttypes.h> #include "freertos/FreeRTOSConfig.h" #include "freertos/FreeRTOS.h" #include "sdkconfig.h" #include "driver/gpio.h" #include "hal/gpio_ll.h" #include "soc/interrupts.h" #include "example_gpio.h" // NMI中断触发标志位,由汇编中断处理程序设置 extern volatile int nmi_triggered; // NMI中断服务程序的函数声明(定义在asm_funcs.S中) extern void xt_nmi(void*); void app_main(void) { intr_handle_t handle; // 中断句柄 esp_err_t err; // 用于存储函数返回的错误码 printf("example: Start\n"); // 重置GPIO引脚状态 gpio_reset_pin(EXAMPLE_GPIO_IN); /* 确保输入GPIO有下拉电阻,防止噪声干扰(当引脚未连接外部设备时) */ gpio_pulldown_en(EXAMPLE_GPIO_IN); // 将GPIO设置为双向模式(既可以输入也可以输出),用于自测触发中断 gpio_set_direction(EXAMPLE_GPIO_IN, GPIO_MODE_INPUT_OUTPUT); /* 将中断处理程序注册为NMI(不可屏蔽中断)。 * 注意:注册高级别中断时,中断分配器要求传入的处理程序参数必须为NULL, * 因为处理程序由汇编代码直接定义(xt_nmi函数)。 */ err = esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_NMI, NULL, NULL, &handle); if (err != ESP_OK) { printf("Failure: could not install NMI ISR, %d(0x%x)\n", err, err); return; } // 设置GPIO中断类型为高电平触发 gpio_set_intr_type(EXAMPLE_GPIO_IN, GPIO_INTR_HIGH_LEVEL); // 使能GPIO中断 gpio_intr_enable(EXAMPLE_GPIO_IN); // 延时200ms,等待系统稳定 vTaskDelay(200 / portTICK_PERIOD_MS); /* 在CPU端禁用所有常规中断,并验证NMI仍然能够被触发 */ // 保存当前的中断使能掩码,以便后续恢复 const uint32_t mask = esp_cpu_intr_get_enabled_mask(); // 禁用所有CPU中断(0xFFFFFFFF表示所有中断) esp_cpu_intr_disable(0xFFFFFFFF); // 清零NMI触发标志位 nmi_triggered = 0; /* 将EXAMPLE_GPIO_IN设置为高电平,这将触发NMI中断(因为GPIO19配置为高电平触发) */ gpio_set_level(EXAMPLE_GPIO_IN, 1); /* 等待中断发生(轮询nmi_triggered标志位) */ while (nmi_triggered == 0) { /* 注意:由于常规中断已被禁用,不能使用vTaskDelay等需要定时器中断的函数 */ } // 恢复之前保存的中断使能状态 esp_cpu_intr_enable(mask); // 禁用GPIO中断 gpio_intr_disable(EXAMPLE_GPIO_IN); // 释放中断资源 esp_intr_free(handle); // 测试成功输出 printf("example: Success\n"); }

由于使用 nmi,所以需要使用汇编:

#include <xtensa/coreasm.h> #include "soc/gpio_reg.h" #include "example_gpio.h" // 声明nmi_triggered为全局符号,使其可以被C代码访问 .global nmi_triggered // 将nmi_triggered变量放入.bss段(未初始化的全局变量段) .section .bss nmi_triggered: .space 4 // 分配4字节空间(int类型) /** * @brief NMI中断服务程序(必须使用汇编语言编写) * * 这个ISR通过`call0`指令被调用,因此`a0`寄存器(返回地址)被修改了。 * 幸运的是,`a0`的原始值被保存在EXCSAVE寄存器中,需要在返回前恢复。 * * 注意:Xtensa架构要求NMI处理程序必须命名为`xt_nmi` */ .section .iram1, "ax" // 放入iram1段(指令RAM),"ax"表示可分配和可执行 .align 4 // 4字节对齐 .global xt_nmi // 声明为全局符号,可以被链接器识别 .type xt_nmi, @function // 声明为函数类型 xt_nmi: // 分配栈空间:栈指针(sp)向下移动16字节 addi sp, sp, -16 // 将a3寄存器保存到栈中(偏移0位置),用于临时存储 s32i a3, sp, 0 /* ========== 设置中断标志位 ========== */ // 将nmi_triggered变量的地址加载到a0寄存器 movi a0, nmi_triggered // 将立即数1加载到a3寄存器 movi a3, 1 // 将a3的值(1)存储到a0指向的内存地址,即设置nmi_triggered = 1 s32i a3, a0, 0 /* ========== 将GPIO电平拉低,防止再次触发中断 ========== */ // 将GPIO_OUT_W1TC寄存器地址加载到a0(Write One To Clear,写1清除对应的GPIO位) movi a0, GPIO_OUT_W1TC_REG // 将1左移EXAMPLE_GPIO_IN位,构造GPIO掩码(例如GPIO19 -> 1<<19) movi a3, 1 << EXAMPLE_GPIO_IN // 将掩码写入GPIO寄存器,将对应的GPIO引脚设置为低电平 s32i a3, a0, 0 /* ========== 恢复寄存器并返回 ========== */ // 从栈中恢复a3寄存器的原始值 l32i a3, sp, 0 // 释放栈空间:栈指针(sp)向上移动16字节 addi sp, sp, 16 // 从EXCSAVE寄存器中恢复a0寄存器(EXCSAVE + XCHAL_NMILEVEL是NMI级别对应的保存位置) // a0寄存器包含中断返回地址,必须恢复才能正确返回 rsr a0, EXCSAVE + XCHAL_NMILEVEL /* 从NMI中断返回,必须指定中断级别 */ // rfi: Return from Interrupt,从中断返回 // XCHAL_NMILEVEL: NMI的中断级别(通常为最高级别) rfi XCHAL_NMILEVEL

结合配置和代码,可以分析出:

打印nmi_triggered 为1

关键知识点

硬件

中断矩阵:ESP32 的“交警系统”

外设中断源连着 2 个 CPU 核的中断控制器。这控制器支持分开配置——可以把某个中断只给 CPU0,也可以两个 CPU 都伺候着。 支持 99 个外部中断源(实际能用 84 个,剩下 15 个是摆设)。每个 CPU 核只有 32 个中断位,其中 6 个是内部用的,剩下 26 个才给外设用。这就像酒店只有 26 间房,但要接待 84 个客人——得排队等叫号。 中断分两大类:

  • 外部中断:外面来的“不速之客”

    • 电平触发:高电平一直举着手直到 CPU 理你

    • 边沿触发:举手一下 CPU 就得看到

    • NMI:最牛 B 的中断,软件关不掉(除非硬件出手)

  • 内部中断:CPU 自己家里的事

    • 定时器中断:闹钟响了

    • 软件中断:自己写代码触发

    • 解析中断:性能分析用的 优先级有 6 级,NMI 是最高级 VIP,可以插队(中断嵌套)。

映射方法:给中断“分配房间”

在配置寄存器的 0~4 位里设置对应的号数。CPU0 和 CPU1 各管各的,不打架。

具体寄存器地址就不列出来了,见《技术手册》

中断状态

则查看对应位是否被设 1,当然有 99 位,对应 4 个寄存器,cpu0 和 cpu1 各 4 个,一共 8 个寄存器。

另外还有时钟控制和版本控制个 2 个,这里就不展示了。

软件

枚举类型

// 保持 LEVELx 值原样;它们通过 (1<<level) 匹配 #define ESP_INTR_FLAG_LEVEL1 (1<<1) ///< 接受 Level 1 中断向量(最低优先级) #define ESP_INTR_FLAG_LEVEL2 (1<<2) ///< 接受 Level 2 中断向量 #define ESP_INTR_FLAG_LEVEL3 (1<<3) ///< 接受 Level 3 中断向量 #define ESP_INTR_FLAG_LEVEL4 (1<<4) ///< 接受 Level 4 中断向量 #define ESP_INTR_FLAG_LEVEL5 (1<<5) ///< 接受 Level 5 中断向量 #define ESP_INTR_FLAG_LEVEL6 (1<<6) ///< 接受 Level 6 中断向量 #define ESP_INTR_FLAG_NMI (1<<7) ///< 接受 Level 7 中断向量(最高优先级) #define ESP_INTR_FLAG_SHARED (1<<8) ///< 中断可在 ISR 之间共享 #define ESP_INTR_FLAG_EDGE (1<<9) ///< 边沿触发中断 #define ESP_INTR_FLAG_IRAM (1<<10) ///< 当缓存禁用时可以调用 ISR #define ESP_INTR_FLAG_INTRDISABLED (1<<11) ///< 返回时此中断已禁用 #define ESP_INTR_FLAG_LOWMED (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3) ///< 低和中等优先级中断。这些可以用 C 处理。 #define ESP_INTR_FLAG_HIGH (ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6|ESP_INTR_FLAG_NMI) ///< 高级别中断。需要用汇编处理。 /** 所有级别标志的掩码 */ #define ESP_INTR_FLAG_LEVELMASK (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3 | \ ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6 | \ ESP_INTR_FLAG_NMI)

函数

esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);

函数功能:分配并注册中断 输入参数:

参数

类型

说明

source

int

中断源。soc/interrupts.h 中定义的 ETS_*_INTR_SOURCE 中断多路复用源,或此头文件中定义的内部ETS_INTERNAL_*_INTR_SOURCE 源之一

flags

int

ESP_INTR_FLAG_* 定义的按位或掩码。这些限制此例程可以选择的中断。如果此值为 0,则默认分配非共享的级别 1、2 或 3 中断。如果提供了 ESP_INTR_FLAG_SHARED 掩码,将分配给定级别的共享中断(如果未指定则为级别 1)。设置 ESP_INTR_FLAG_INTRDISABLED 将在禁用此中断的情况下返回此函数

handler

intr_handler_t

中断处理程序。当请求级别 >3 的中断时必须为 NULL,因为这些类型的中断不是 C 可调用的

arg

void*

传递给中断处理程序的可选参数

ret_handle

intr_handle_t*

指向 intr_handle_t 的指针,用于存储一个可以稍后请求详细信息或释放中断的句柄。如果不需要句柄可以为 NULL

返回值:esp_err_t 类型,它是枚举类型,定义如下:

返回值

说明

ESP_ERR_INVALID_ARG

参数组合无效

ESP_ERR_NOT_FOUND

未找到具有指定标志的中断

ESP_OK

成功

void esp_cpu_intr_disable(uint32_t intr_mask);

函数功能:禁用当前 CPU 上的特定中断

输入参数:intr_mask:要禁用的中断的位掩码

void esp_cpu_intr_enable(uint32_t intr_mask);

函数功能:禁用当前 CPU 上的特定中断

输入参数:intr_mask:要启用的中断的位掩码

注意:这两个不能控制 NMI

esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type);

函数功能:设置 GPIO 中断触发类型

输入参数

参数

类型

说明

gpio_num

gpio_num_t

GPIO 编号。如果想要设置例如 GPIO16 的中断触发类型,gpio_num 应为 GPIO_NUM_16 (16)

intr_type

gpio_int_type_t

中断类型,从 gpio_int_type_t 中选择

typedef enum { GPIO_INTR_DISABLE = 0, /*!< 禁用 GPIO 中断 */ PIO_INTR_POSEDGE = 1, /*!< GPIO 中断类型:上升沿触发 */ GPIO_INTR_NEGEDGE = 2, /*!< GPIO 中断类型:下降沿触发 */ GPIO_INTR_ANYEDGE = 3, /*!< GPIO 中断类型:上升沿和下降沿触发(双沿触发) */ GPIO_INTR_LOW_LEVEL = 4, /*!< GPIO 中断类型:输入低电平触发 */ GPIO_INTR_HIGH_LEVEL = 5, /*!< GPIO 中断类型:输入高电平触发 */ GPIO_INTR_MAX, } gpio_int_type_t;

返回值:esp_err_t 类型,定义如下:

返回值

说明

ESP_OK

成功

ESP_ERR_INVALID_ARG

参数错误

esp_err_t gpio_intr_enable(gpio_num_t gpio_num);

函数功能:启用 GPIO 模块中断信号

输入参数

返回值:esp_err_t 类型,定义如下:

返回值

说明

ESP_OK

成功

ESP_ERR_INVALID_ARG

参数错误

esp_err_t gpio_install_isr_service(int intr_alloc_flags);

函数功能:装 GPIO 驱动的 ETS_GPIO_INTR_SOURCE 中断服务程序(ISR),支持按引脚注册独立的 GPIO 中断处理函数

输入参数:分配中断时使用的标志。一个或多个(按位或)ESP_INTR_FLAG_* 值。 * 详细信息请参阅 esp_intr_alloc.h。

esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void *args);

函数功能:为指定的 GPIO 引脚添加中断服务程序(ISR)处理函数

输入参数

参数名

参数类型

参数说明

gpio_num

gpio_num_t

GPIO 编号,要添加中断处理程序的 GPIO 引脚

isr_handler

gpio_isr_t

中断服务程序处理函数,类型为 gpio_isr_t 的函数指针

args

void*

传递给中断处理程序的参数,可以是任意类型的数据指针

返回值:esp_err_t 类型。

使用注意

  • 必须在调用 gpio_install_isr_service() 之后使用此函数

  • 如果在分配 ISR 时没有传递 ESP_INTR_FLAG_IRAM 标志,则引脚 ISR 处理函数不需要声明为 IRAM_ATTR

  • 此函数将在 ISR 中被调用,因此有堆栈大小限制(可在 menuconfig 中配置为「ISR stack size」),由于额外的间接层级,此限制比全局 GPIO 中断处理程序要小

使用开发板上的按键,开关灯

从原理图上看,我们使用的是 GPIO0,按下为低电平

#include <stdio.h> #include <stdbool.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "driver/gpio.h" #include <inttypes.h> static void IRAM_ATTR gpio_isr_handler(void* arg) { bool *flag = (bool *)arg; if (false == *flag) { gpio_set_level(GPIO_NUM_1, 1); *flag = true; } else { gpio_set_level(GPIO_NUM_1, 0); *flag = false; } } void app_main(void) { gpio_config_t io_conf = {}; bool flag = false; //设置按键 io_conf.intr_type = GPIO_INTR_NEGEDGE; io_conf.pin_bit_mask = 1 << GPIO_NUM_0; io_conf.mode = GPIO_MODE_INPUT; io_conf.pull_up_en = 1; gpio_config(&io_conf); //设置led灯 io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = 1 << GPIO_NUM_1; io_conf.pull_down_en = 0; io_conf.pull_up_en = 0; gpio_config(&io_conf); //关灯 gpio_set_level(GPIO_NUM_1, 1); gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1); gpio_isr_handler_add(GPIO_NUM_0, gpio_isr_handler, (void*) &flag); while(1) { vTaskDelay(1000 / portTICK_PERIOD_MS); } }

实操练习

实现功能

  • 短按:开关 LED 灯

  • 长按(比如按住 2 秒以上):重启系统

提示

  1. 在中断里启动定时器,判断按键时长

  2. 短按就开关灯,长按就调用 esp_restart()

  3. 注意防抖——别按一下触发十几次

答案在 gitee: HTTPS://gitee.com/zhangdong_road/esp32

搜索公众号“小野嵌入式”,一起学习~。

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

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

相关文章

ESP32中断深扒:从 BOOT 按键到 NMI,一顿操作猛如虎

目录 实物图 原理图 ESP32-IDF 示例代码解析 关键知识点 硬件 中断矩阵:ESP32 的“交警系统” 映射方法:给中断“分配房间” 中断状态 软件 枚举类型 函数 使用开发板上的按键&#xff0c;开关灯 实操练习 实现功能 提示 实物图 原理图 找到 KEY 部分 可以看出 b…

低成本信号发生器实现高精度波形输出方法

用一块STM32做出实验室级信号发生器&#xff1f;揭秘低成本高精度波形输出的底层逻辑你有没有遇到过这样的场景&#xff1a;调试一个音频电路&#xff0c;手头却没有信号源&#xff1b;做传感器激励实验&#xff0c;只能靠函数发生器租借&#xff1b;或者在嵌入式项目中想生成一…

为一个杯子开14次会,却在汽车上用手机芯片,这叫高品质?蒙谁呢!

某车企为了强调自己的电车品质高&#xff0c;说为了一个作为汽车周边产品的杯子就开了14次会议&#xff0c;以此来说明它对品质的重视&#xff0c;然而人们却清楚它在汽车上用了手机芯片&#xff0c;重要、涉及安全的汽车芯片却用了存在安全隐患、不耐用的手机芯片&#xff0c;…

UltraScale+ PCIe Gen4在Vivado2025中的实现方案

基于UltraScale的PCIe Gen4设计&#xff1a;Vivado 2025实战指南在当前高性能计算、AI推理加速和高速数据采集系统中&#xff0c;FPGA作为可编程异构计算核心&#xff0c;正越来越多地通过PCIe Gen4与主机CPU/GPU进行低延迟、高带宽互联。Xilinx&#xff08;现AMD&#xff09;的…

UltraScale+ PCIe Gen4在Vivado2025中的实现方案

基于UltraScale的PCIe Gen4设计&#xff1a;Vivado 2025实战指南在当前高性能计算、AI推理加速和高速数据采集系统中&#xff0c;FPGA作为可编程异构计算核心&#xff0c;正越来越多地通过PCIe Gen4与主机CPU/GPU进行低延迟、高带宽互联。Xilinx&#xff08;现AMD&#xff09;的…

基于功耗和散热的续流二极管选型策略系统学习

续流二极管选型的“看不见的敌人”&#xff1a;功耗与散热实战解析在一块小小的电源板上&#xff0c;你可能不会注意到那颗不起眼的贴片二极管——它没有MOSFET那样高频开关的炫技&#xff0c;也不像电感那样体积庞大引人注目。但一旦系统突然宕机、芯片莫名击穿&#xff0c;排…

基于FPGA的数字频率计设计:完整指南

从零构建高性能数字频率计&#xff1a;FPGA实战全解析你有没有遇到过这样的场景&#xff1f;手头有个信号发生器&#xff0c;输出一个神秘的方波&#xff0c;你想知道它的频率到底是多少——是1.234 kHz还是1.235 kHz&#xff1f;普通万用表只能给你个大概&#xff0c;示波器又…

Vivado版本兼容性对ego1开发板大作业的影响说明

Vivado版本问题如何悄悄毁掉你的ego1大作业&#xff1f;你有没有遇到过这种情况&#xff1a;明明代码逻辑没问题&#xff0c;仿真也通过了&#xff0c;XDC约束写得清清楚楚&#xff0c;可下载到ego1开发板上时&#xff0c;LED不亮、数码管乱码&#xff0c;甚至根本烧录失败&…

循迹小车转向机构优化:项目应用解析

从“画龙”到“点睛”&#xff1a;如何让Arduino循迹小车真正“看得准、转得稳”你有没有遇到过这样的场景&#xff1f;花了一整天时间组装好一辆Arduino循迹小车&#xff0c;代码烧录成功&#xff0c;电机嗡嗡作响&#xff0c;信心满满地把它放到赛道上——结果刚出直道就左右…

i.MX硬件加速集成指南:Yocto环境配置

i.MX硬件加速集成实战&#xff1a;从Yocto环境搭建到系统验证你有没有遇到过这样的场景&#xff1f;手头的i.MX8M Plus开发板明明配备了NPU和VPU&#xff0c;但跑起AI模型来速度还不如树莓派&#xff1b;用GStreamer播放4K视频时CPU占用飙到90%以上——这说明&#xff0c;你的硬…

i.MX硬件加速集成指南:Yocto环境配置

i.MX硬件加速集成实战&#xff1a;从Yocto环境搭建到系统验证你有没有遇到过这样的场景&#xff1f;手头的i.MX8M Plus开发板明明配备了NPU和VPU&#xff0c;但跑起AI模型来速度还不如树莓派&#xff1b;用GStreamer播放4K视频时CPU占用飙到90%以上——这说明&#xff0c;你的硬…

有源蜂鸣器和无源区分:频率控制深度剖析

蜂鸣器选型实战&#xff1a;有源与无源的本质区别&#xff0c;不只是“能不能变音”这么简单你有没有遇到过这种情况——项目快量产了&#xff0c;突然发现报警音太单调&#xff0c;想让蜂鸣器“唱个调”&#xff0c;结果一查才发现用的是有源蜂鸣器&#xff0c;压根没法换频率…

大规模工业产线中的Vivado许可证优化使用:实践分享

大规模工业产线中的Vivado许可证优化实践&#xff1a;从“抢不到”到高效复用在一家大型通信设备制造商的FPGA开发中心&#xff0c;每天早上9点刚过&#xff0c;工程师们的工位上几乎同时亮起了Vivado IDE。有人开始修改逻辑设计&#xff0c;有人启动批处理脚本跑回归测试&…

使用hbuilderx开发电商小程序多规格选择完整示例

用HBuilderX开发电商小程序&#xff0c;搞定多规格选择的完整实战你有没有遇到过这种情况&#xff1a;用户在商品详情页点来点去&#xff0c;好不容易选完颜色和尺码&#xff0c;结果一确认——“抱歉&#xff0c;该组合无货”&#xff1f;这种体验简直让人抓狂。而更糟的是&am…

VSCode - 显示EOL字符的插件

VSCode自身没有显示EOL字符的功能&#xff0c;可以通过扩展插件来实现。 在插件市场搜索到&#xff1a; Render Line Endings。 点击安装&#xff0c;Publisher&#xff1a;Josip Medved&#xff0c;选择相信第一次从此publisher安装程序。 This extension renders end of li…

大模型的“牛顿难题”:为什么AI读遍人类所有书籍,仍无法发现万有引力?

来源&#xff1a;今日头条当所有人都在追逐GPT-5的幻想时&#xff0c;一位前谷歌工程师出身的老板揭示了AI发展的真正天花板&#xff1a;大模型永远无法成为牛顿。本文深度剖析了语言局限性与概率系统本质这两大根本缺陷&#xff0c;并提出了下一代AI可能的突破方向——从神经符…

Multisim14.0到NI Ultiboard的无缝导出操作指南

从仿真到制板&#xff1a;手把手教你实现 Multisim14.0 到 NI Ultiboard 的高效协同设计 你有没有过这样的经历&#xff1f;在 Multisim 里把电路图画得清清楚楚&#xff0c;仿真波形也跑通了&#xff0c;信心满满地准备做 PCB 板——结果一导出&#xff0c;飞线乱成一团、封装…

从零实现多层PCB生产流程:实验室级小批量制作方案

实验室里的“芯片工厂”&#xff1a;如何亲手做出一块四层PCB&#xff1f;你有没有过这样的经历&#xff1f;设计好了一块精密的四层板&#xff0c;满怀期待地发给厂家打样&#xff0c;结果等了五天&#xff0c;收货一看——线宽偏差、孔铜太薄、甚至内层错位。更糟的是&#x…

构建轻量级嵌入式OS:Yocto内核裁剪全面讲解

如何用 Yocto 打造极致轻量的嵌入式 Linux 系统&#xff1f;从内核裁剪讲起你有没有遇到过这样的场景&#xff1a;一块 64MB 的 Flash&#xff0c;跑不进一个“最小”Linux系统&#xff1b;设备冷启动要等七八秒&#xff0c;用户还没操作就已经失去耐心&#xff1b;明明只是个数…

联邦学习不再安全?港大TPAMI新作:深挖梯度反转攻击的内幕

来源&#xff1a;机器之心本文第一作者郭鹏鑫&#xff0c;香港大学博士生&#xff0c;研究方向是联邦学习、大模型微调等。本文共同第一作者王润熙&#xff0c;香港大学硕士生&#xff0c;研究方法是联邦学习、隐私保护等。本文通讯作者屈靓琼&#xff0c;香港大学助理教授&…