STM32 (NVIC)中断

1. 什么是中断

OpenHarmony 中断注册服务 ISR ==> Interrupt Service Register。利用 IO 引脚高低电平切变情

况,进行指定中断服务函数/中断处理函数执行。重要参数

  • IO 引脚对应编号
  • 中断触发条件
  1. 下降沿
  2. 上升沿
  3. 高电平转换到低电平,低电平触发
  4. 低电平转换到高电平,高电平触发
  • 中断服务函数/中断处理函数 + 对应参数。

在 STM32 中, 中断 NVIC 分为【内部中断】和【外部中断】,根据根据中断触发状态或者电平切

换进行对应的中断触发和中断处理。主要内容包括

  • 中断服务编号/中断请求编号 IRQn ==> Interrupt ReQuest number
  • 中断服务处理函数 IRQHandler
  • 中断优先级 Priority 占先优先级和次级优先
  • 中断触发标志位 MCU 内部相关的 内核和外设 SR (Status Register)寄存器

2. 中断中的常用概念

  • 断点:在 MCU 中注册的中断触发位置,一般对应中断触发条件,触发寄存器标志位变化。需要通过 CR 寄存器对当前模块中断进行使能 IE ==> Interrupt Enable
  • 中断源:当前中断触发之后,对应的中断处理函数 IRQHandler
  • 压栈:中断触发,此时【中断处理函数 IRQHandler】称为前台函数,中断触发位置函数成为【后台函数】

中断源和中断处理函数

中断压栈行为和前后台概念

3. Cortex-M3 中断分类

Cortex-M3 内核中断分类

  • 内部中断/内核中断:芯片内部/内核中断内容,也可以称之为【Exception 异常】。程序运行正常过程中,出现的非正常情况。例如 RESET 按键,内存溢出,硬件问题。。。内部/内核中断重点关注【系统嘀嗒 SysTick】
  • 外部中断
  1. 片上外设中断,STM32 片上外设大多数都有对应的中断情况,例如 USART 数据发送完成中断,数据接收中断,数据总线空闲中断,数据错误中断。。。大多数中断都是片上外设完成特定任务,指定条件触发... 需要对应外设 CR 和 SR 两个寄存器配合使用。CR用于控制中断是否开启 IE,SR 是判断触发的中断是哪一个。
  2. EXTI 外部中断控制线,MCU 提供给引脚连接的外部设备,例如 SR602,外部压力传感器,烟雾传感器。利用基本的【高低电平切换出现的边沿情况】进行中断触发。扩宽外部中断控制。

4. IRQn 和 IRQHandler 解释

4.1 IRQn 中断请求编号

IRQn ==> Interrupt ReQuest number

  • 在 Cortex-M3 内核原码中,利用【枚举类型】对当前 MCU 执行的所有中断请求进行了编号,编号规则:中断触发设备名称_IRQn,枚举类型是【IRQn_Type】。例如
  1. USART1_IRQn,TIM2_IRQn,SPI1_IRQn,DMA2_Channel2_IRQn
  • 编号是用于在开发中,【注册中断】使用,告知 MCU 当前执行过程中,有哪些中断存在。

在 stm32f10x.h 中声明枚举 IRQn_Type 类型

4.2 IRQHandler 中断处理函数

IRQHandler ==> Interrupt ReQuest Handler

  • IRQHandler 和 IRQn 一一对应,当前在 MCU 中注册了 IRQn 中断请求编号,内核在触发中断之后,会自动调用对应 IRQn 的 IRQHandler 处理函数。例如
  1. 当前 NVIC 注册中断 USART1_IRQn ,程序需要【实现】的函数是 voidUSART1_IRQHandler(void) 函数
  2. 当前 NVIC 注册中断 EXTI0_IRQn ,程序需要【实现】的函数是 void EXTI0_IRQHandler(void) 函数

startup_stm32f10x_hd.s 文件中,告知当前支持的 IRQHandler 中断处理函数

5. EXTI 外部中断控制线开发

5.1 EXTI 框图分析和对应映射分析

5.2 EXTI 相关寄存器配置

分析当前 EXTI 开发所需的必要内容

  • EXTI 外部中断控制线配置对应的 GPIO 引脚。
  • EXTI 边沿检测,上升沿和下降沿控制。
  • EXTI 中断使能

5.2.1 EXTI 外部中断控制线配置对应的 GPIO 引脚

【引脚复用】,在 MCU 中 IO 引脚可以通过 AFIO 寄存器对当前 IO 引脚功能进行【复用配置】。

AFIO 中利用 EXTICR1~ EXTICR4 四个寄存器配置 EXTI0 ~ EXIT15 对应的外部 GPIO 引脚是哪一个

当前采用案例是开发板对应可编程按键,对应引脚是 PA0 PE2 PE3 PE4 ,需要控制的外部中断线对应 EXIT0 EXTI2 EXTI3 EXTI4

5.2.2 EXTI 边沿检测,上升沿和下降沿控制

当前按键

  • KEY_UP ==> PA0 ==> EXTI0 按键按下之后,提供给 MCU 的电平情况是高电平,GPIO 引脚默认是低电平形式,选择【上升沿触发】
  • KEY0 ==> PE4 ==> EXTI4
  • KEY1 ==> PE3 ==> EXTI3
  • KEY2 ==> PE2 ==> EXTI2
  1. 以上按键按下之后提供给 MCU 电平为低电平,GPIO 默认为高电平模式,选择【下降沿触发】

以下两个寄存器配置是控制当前 EXTI 外部中断控制线,选择上升沿还是下降沿触发中断,需要根

据电路原理图进行分析。

5.2.3 EXTI 中断使能

需要开启 EXTI 外部中断线中断触发支持,需要通过两个寄存器配合完成。分别是

  • EXTI_IMR 是否开发当前对应外部中断线的中断
  • EXTI_PR 判断对应外部中断是否触发

5.3 代码实现

按键中断开发分析

key.h 内容补充

cs 复制代码
#ifndef _KEY_H
key.c 函数实现
#define _KEY_H
#include "stm32f10x.h"
#include "delay.h"
#include "beep.h"
#include "led.h"
#define AFIO_RCC_APB2_CLOCK_ENABLE (0x01)
#define GPIOA_RCC_APB2_CLOCK_ENABLE (0x01 << 2)
#define GPIOE_RCC_APB2_CLOCK_ENABLE (0x01 << 6)
#define Pull_Up_Or_Down_Input (0x08)
#define EXTI_GPIOAX (0x00)
#define EXTI_GPIOEX (0x04)
// 利用枚举类型描述按键按键标记数据
typedef enum key_value
{
KEY_0_VALUE,
KEY_1_VALUE,
KEY_2_VALUE,
KEY_UP_VALUE,
NO_KEY_PRESSED
} GL_Key_Value;
/**
* @brief 当前开发板中按键初始化函数,配置 PA0 PE2 PE3 PE4
* 所需的 GPIO 工作模式
* PA0 ==> KEY_UP or WK_UP 下拉输入
* PE2 ==> KEY2 上拉输入
* PE3 ==> KEY1 上拉输入
* PE4 ==> KEY0 上拉输入
*/
void Key_Init(void);
/**
* @brief 获取当前开发板中,哪一个按键被按下
*
* @return 返回值是对应按键被按下的标记数据,对应类型为枚举 GL_Key_Value
* 类型
*/
u8 Key_GetValue(void);
/**
* @brief 按键对应外部中断控制线使能配置函数
*/
void Key_EXTI_Interrupt_Enable(void);
#endif
void Key_EXTI_Interrupt_Enable(void)
{
// 1. 因为当前操作设计到 GPIO 复用操作,需要使用使能 AFIO
RCC->APB2ENR |= AFIO_RCC_APB2_CLOCK_ENABLE;
/*
2. AFIO 外部中断控制线 EXTI 对应 GPIO 引脚配置
typedef struct
{
__IO uint32_t EVCR;
__IO uint32_t MAPR;
__IO uint32_t EXTICR[4];
uint32_t RESERVED0;
__IO uint32_t MAPR2;
} AFIO_TypeDef;
AFIO 引脚复用配置结构体类型,底层 EXTICR1 ~ EXTICR4 采用
数组形式进行管理,数组存储数据类型位 uint32_t 对应 32 位寄存器。
如果需要对 EXTICR1 进行配置,实际操作为 AFIO->EXTICR[0] .
需要配置 EXTI0 EXTI2 EXTI3 EXTI4
EXTI0 EXTI2 EXTI3 ==> EXTICR1 寄存器
EXTI4 ==> EXTICR2 寄存器
EXTI0 ==> PA0 ==> 寄存器对应位存储内容为 0000
EXTI2 ==> PE2 ==> 寄存器对应位存储内容为 0100
EXTI3 ==> PE3 ==> 寄存器对应位存储内容为 0100
EXTI4 ==> PE4 ==> 寄存器对应位存储内容为 0100
*/
AFIO->EXTICR[0] |= EXTI_GPIOEX << 12 | EXTI_GPIOEX << 8 | EXTI_GPIOAX;
AFIO->EXTICR[1] |= EXTI_GPIOEX;
/*
3. 配置外部中断控制线 EXTI 对应上升沿,或者下降沿触发中断
EXTI->RTSR |= 0x01; 配置 EXTI0 上升沿触发
EXTI->FTSR |= (0x07 << 2); 配置 EXTI2 3 4 下降沿触发
*/
EXTI->RTSR |= 0x01;
EXTI->FTSR |= (0x07 << 2);
/*
4. 中断屏蔽寄存器配置,开启指定 EXTI 外部中断控制线中断
EXTI->IMR |= 0x1D; 对应开启 EXTI0 2 3 4 外部中断
对应二进制 11101 ==> 0x1D
*/
EXTI->IMR |= 0x1D;
/*
5. 【中断注册】
利用 NVIC 对当前 EXTI 对应的 EXITx_IRQn 进行注册,告知 MCU 内核
当前程序运行过程中,存在 EXTI 外部中断控制线中断内容。
EXTI0_IRQn ==> IRQn_Type 当前内核中断编号
*/
// 当前 EXTIx_IRQn 对应的中断优先级配置。
NVIC_SetPriority(EXTI0_IRQn, 1);
NVIC_SetPriority(EXTI2_IRQn, 1);
NVIC_SetPriority(EXTI3_IRQn, 1);
NVIC_SetPriority(EXTI4_IRQn, 1);
// NVIC 开始/允许指定 IRQ 对应中断触发。
// 同时在内核中,一旦对应中断触发,会自动调用目标 Handler 函数
// 例如 EXTI0 外部中断控制线,触发中断,内核自动调用 EXTI0_IRQHandler
NVIC_EnableIRQ(EXTI0_IRQn);
NVIC_EnableIRQ(EXTI2_IRQn);
NVIC_EnableIRQ(EXTI3_IRQn);
NVIC_EnableIRQ(EXTI4_IRQn);
}
/*
KEY_UP ==> EXTI0_IRQn 中断处理函数实现 EXTI0_IRQHandler
*/
void EXTI0_IRQHandler(void)
{
/*
如果当前 EXTI0 存在中断触发,在 EXTI->PR 寄存器中,对应位 0 应该为 1。
利用 if 判断进行 EXTI0 外部中断确认操作。
*/
if (EXTI->PR & 0x01)
{
/*
if 条件判断通过,表示对应中断存在,
1. 【重点】对应当前中断标志位进行清除,方便下一次中断触发判断
2. 完成必要的中断任务
EXTI->PR 外部中断控制线,中断状态寄存器,寄存器二进制位支持【只读 + W1】
W1 ==> 对目标寄存器位进行清除操作。
*/
EXTI->PR = 0x01;
/*
KEY_UP 控制 BEEP 工作
*/
Beep_Ctrl(1);
}
}
/*
KEY0 ==> EXTI4_IRQn 中断处理函数实现 EXTI4_IRQHandler
*/
void EXTI4_IRQHandler(void)
{
if (EXTI->PR & (0x01 << 4))
{
EXTI->PR = (0x01 << 4);
/*
KEY0 控制 LED1 亮
*/
Led1_Ctrl(1);
}
}
/*
KEY1 ==> EXTI3_IRQn 中断处理函数实现 EXTI3_IRQHandler
*/
void EXTI3_IRQHandler(void)
{
6. USART 接收数据中断优化
6.1 USART 中断优化思想
利用 USART 中断 RXNE 和 IDLE 进行数据接收情况处理
RXNE Read Data Register Not Empty 在 USARTx_SR 寄存器中,RXNE 为 1 表示当前
USARTx_DR 寄存器中,存在数据,需要进行接受处理,从 USARTx_DR 寄存器数据读取之
后,当前寄存器标志位【清除】
IDLE 数据总线空闲中断标志位,可以用于判断当前 USART 数据接收是否结束。如果当前
IDLE 为 1 表示接受数据已完成,【清除中断】利用软件序列完成,先读取 USARTx_SR 寄存
器数据,在读取 USARTx_DR 寄存器数据。
利用 CR 寄存器开始 RXNE 和 IDLE 两个中断,利用 USART1_IRQHandler 函数处理当前中断任
务,根据中断标志位 IDLE 和 RXNE 判断数据读取和截止。
6.2 进行 USART1 代码优化
6.2.1 数据存储结构体优化
if (EXTI->PR & (0x01 << 3))
{
EXTI->PR = (0x01 << 3);
/*
KEY0 控制 LED1 灭
*/
Led1_Ctrl(0);
}
}
/*
KEY2 ==> EXTI2_IRQn 中断处理函数实现 EXTI2_IRQHandler
*/
void EXTI2_IRQHandler(void)
{
if (EXTI->PR & (0x01 << 2))
{
EXTI->PR = (0x01 << 2);
/*
KEY0 控制 Beep 停止工作
*/
Beep_Ctrl(0);
}
}

6. USART 接收数据中断优化

6.1 USART 中断优化思想

利用 USART 中断 RXNE 和 IDLE 进行数据接收情况处理

  • RXNE Read Data Register Not Empty 在 USARTx_SR 寄存器中,RXNE 为 1 表示当前USARTx_DR 寄存器中,存在数据,需要进行接受处理,从 USARTx_DR 寄存器数据读取之后,当前寄存器标志位【清除】
  • IDLE 数据总线空闲中断标志位,可以用于判断当前 USART 数据接收是否结束。如果当前IDLE 为 1 表示接受数据已完成,【清除中断】利用软件序列完成,先读取 USARTx_SR 寄存器数据,在读取 USARTx_DR 寄存器数据。

利用 CR 寄存器开始 RXNE 和 IDLE 两个中断,利用 USART1_IRQHandler 函数处理当前中断任

务,根据中断标志位 IDLE 和 RXNE 判断数据读取和截止。

6.2 进行 USART1 代码优化

6.2.1 数据存储结构体优化

cs 复制代码
// USART1_DATA_SIZE 控制当前 USART1 接收数据缓冲区大小。
#define USART1_DATA_SIZE (256)
typedef struct usart1_data
{
// data 是 USART1 接受数据缓冲区数组
u8 data[USART1_DATA_SIZE];
// 当前 USART1 接受数据是否完毕
/*
如果 flag = 0 表示数据尚未接收完毕,
如果触发 IDLE 中断,flag 置为 1,表示数据接收完毕。
6.2.2 USART 中断配置
在 USART1_Init 函数中,添加 USART1 对应的中断使能配置。包括在 NVIC 中注册相关中断和设
定中断优先级。
利用 CR 寄存器开启 RXNEIE 和 IDLEIE
NVIC 注册的中断请求编号为 USART1_IRQn
在 USART1_Init 函数中补充内容 ,需要在 USART1 UE 启动之前
6.2.3 利用 USART1_IRQHandler 处理 USART1 接收数据操作
*/
u8 flag;
// USART1 接收到的数据字节个数
u16 count;
} USART1_Data;

6.2.2 USART 中断配置

USART1_Init 函数中,添加 USART1 对应的中断使能配置。包括在 NVIC 中注册相关中断和设

定中断优先级。

  • 利用 CR 寄存器开启 RXNEIE 和 IDLEIE
  • NVIC 注册的中断请求编号为 USART1_IRQn

USART1_Init函数中补充内容 ,需要在 USART1 UE 启动之前

cs 复制代码
/*
【补充 USART1 RXNE 和 IDLE 中断配置】
*/
// 利用 USART1->CR1 寄存器开始 RXNEIE 和 IDLEIE 中断
USART1->CR1 |= (0x01 << 4) | (0x01 << 5);
// NVIC 配置中断优先级和注册使能相关中断
NVIC_SetPriority(USART1_IRQn, 1);
NVIC_EnableIRQ(USART1_IRQn);

6.2.3 利用 USART1_IRQHandler 处理 USART1 接收数据操作

cs 复制代码
/*
NVIC 中注册中断 USART1_IRQn 对应的处理函数 USART1_IRQHandler
当前 USART1 触发中断,MCU 内核会自动调用 USART1_IRQHandler 函数
进行中断处理
1. RXNE 判断数据是否需要接收
2. IDLE 判断数据是否接收完成
*/
void USART1_IRQHandler(void)
{
u8 tmp = 0;
// 如果 USART1_Data 数据结构体底层 flag 为 1 表示数据已处理
// 如果再次触发中断,当前 USART 需要进程数据接收操作
// 对 USART1_Data 变量进行数据擦除
if (usart1_value.flag)
{
memset(&usart1_value, 0, sizeof(USART1_Data));
}
/*
USART1 中任何一个中断触发,MCU 内核都会执行 USART1_IRQHandler
函数,需要在函数内部,根据 USART1->SR 状态寄存器,判断当前中断
触发内容。
*/
/*
【RXNE 读取数据寄存器非空处理】
RXNE 对应位 5
USART1->SR & (0x01 << 5)
【完成任务】
1. 从 USART1->DR 寄存器将数据移动到 USART1_Data 结构体
底层数据缓冲区
2. 【清除】当前中断进行
特殊情况处理
如果数据缓冲区已满,需要对数据进行特别处理。
*/
if (USART1->SR & (0x01 << 5))
{
/*
usart1_value ==> USART1_Data 结构体变量
data USART1_Data 结构体中用于存储数据的底层 u8 类型数据
count USART1_Data 结构体中接收到的有效字节个数。
【数组数据尾插法】
【清除中断】是读取 USART1->DR 寄存器,当前操作刚刚好进行清除。
*/
usart1_value.data[usart1_value.count++] = USART1->DR;
// 串口数据缓冲区有效字节个数 == 最大支持字节数
if (usart1_value.count == USART1_DATA_SIZE)
{
usart1_value.flag = 1; // 当前数据接收已完成
// 【数据处理】当前操作是将接受到的数据,回显到 PC 端串口调试工具
USART1_SendBuffer(usart1_value.data, usart1_value.count);
}
}
/*
【IDLE 数据总线空闲处理】
IDLE 对应位 4
USART1->SR & (0x01 << 4)
【完成任务】
1. 表示当前接收数据操作已完成,需要对当前接收数据进行处理
2. 数据接收标志位 flag = 1
3. 【清除中断】读取 USART1->SR 寄存器数据,之后读取 USART1->DR 寄存器数据
*/
if (USART1->SR & (0x01 << 4))
{
/*
USART1 数据结构体底层 flag 赋值为 1,表示当前接收数据已完成
*/
usart1_value.flag = 1;
/*
【清除中断标志位】 根据 IDLE 中断清除规则完成
*/
tmp = USART1->SR;
tmp = USART1->DR;
// 【数据处理】当前操作是将接受到的数据,回显到 PC 端串口调试工具
USART1_SendBuffer(usart1_value.data, usart1_value.count);
}
}

7. 中断优先级

7.1 中断优先级概述

用于解决在 MCU 中运行的代码,存在多个中断情况,进行中断触发管理。在 MCU 内核中,优先

级组成是由

  • 占先优先级
  • 次级优先级

同时为了方便进行优先级管理,在 Cortex-M 内核中,针对于优先级进行优先级分组,限制整个程

序中,当前执行的优先级初始化条件。

  • NVIC_PRIORITYGROUP_0
  • NVIC_PRIORITYGROUP_1
  • NVIC_PRIORITYGROUP_2
  • NVIC_PRIORITYGROUP_3
  • NVIC_PRIORITYGROUP_4

7.2 占先优先级和次级优先级

  • 占先优先级在中断优先级中权重最高
  1. 多个中断同时执行,占先优先级高的,首先获取到 MCU 的执行权。
  2. 中断在任务执行过程中,可以被占先更高的中断压栈,进入后台,等待当前更高等级的占先执行完毕。
  • 次级优先级作为中断执行的参考项,有且在占先一致的情况下,才会考虑次级优先级比较。
  1. 多个中断同时触发,在占先优先级一致的情况下,比较次级优先级,次级优先级较高的优先执行。
  2. 中断正在执行过程中,出现与之相同占先优先级中断,同时次级优先级高于当前正在执行的中断,MCU 不会打断正在执行中断。
  • 占先决定谁能执行,次级决定同时出现,谁先执行。

7.3 优先级分组

优先级分组设置通常在 main 函数中设置,主要用于设置整个 MCU 执行过程中的优先级分组限

制,决定当前中断对应占先优先级和次级优先级规则。

  • NVIC_PRIORITYGROUP_0,全部为次级优先级。出现中断同时触发,比较次级优先级高低,保证高优先级中断执行。但是任何一个中断在执行过程中,不会被其他中断断。
  • NVIC_PRIORITYGROUP_1,NVIC_PRIORITYGROUP_2,NVIC_PRIORITYGROUP_3,存在部分占先和次级,中断可以根据占先优先级进行打断其他中断执行,次级可以用于同占用进行顺序排序。可以根据中断的重要程度,进行占先优先级分组,保证高等级中断优先执行。
  • NVIC_PRIORITYGROUP_4,全部为占先优先级,所有的中断执行,完全按照占先规则执行,整个代码内容的中断全部优先保证高优先级的中断执行效率。

7.4 NVIC 中断配置相关函数

NVIC 是中断管理器,相关函数主要用户开启目标中断和配置当前中断优先级。

  • void NVIC_SetPriorityGrouping(uint32_t PriorityGroup); 设置优先级分组
  • void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority); 设置指定中断优先级
  • NVIC_EnableIRQ(IRQn_Type IRQn); 使能/开启目标中断。

main 函数案例

cs 复制代码
#include "stm32f10x.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "led.h"
#include "beep.h"
#include "delay.h"
#include "key.h"
#include "usart1.h"
int main(void)
{
/*
1. void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
设置当前【整个项目】优先级分组,
NVIC_PriorityGroup_2 优先组分组 2 ,2 bit 占先,2 bit 次级
*/
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
/*
【更推荐的方式】
利用 NVIC_InitTypeDef 结构体类型,配置占先和次级优先级内容。
后续库函数讲解
NVIC_InitTypeDef NVIC_Structure;
// 占先优先级
NVIC_Structure.NVIC_IRQChannelPreemptionPriority = 0;
// 次级优先级
NVIC_Structure.NVIC_IRQChannelSubPriority = 1;
*/
Led_Init();
Key_Init();
Beep_Init();
Key_EXTI_Interrupt_Enable();
8. 内核中断/内部中断
编号或者位置在 16 之前是内核中断/内部中断,也称之为异常。
其中复位和硬故障没有对应的 IRQn , 中断请求编号。
内核中断中,使用最多的 SysTick 系统嘀嗒。后续开发中,需要对系统嘀嗒自定义周期时间
在后续的实时操作系统中,部分内核中断内容需要交给实时操作系统管理。
USART1_Init(9600);
while (1)
{
Led0_Ctrl(1);
Delay_ms(2000);
}
}

8. 内核中断/内部中断

编号或者位置在 16 之前是内核中断/内部中断,也称之为异常。

  • 其中复位和硬故障没有对应的 IRQn , 中断请求编号。
  • 内核中断中,使用最多的 SysTick 系统嘀嗒。后续开发中,需要对系统嘀嗒自定义周期时间

在后续的实时操作系统中,部分内核中断内容需要交给实时操作系统管理。

相关推荐
12.=0.1 小时前
【stm32_9.2】FreeRTOS的任务管理:任务策略,调度器启用,任务创建、删除、挂起、恢复
c语言·stm32·单片机·嵌入式硬件
国产电子元器件1 小时前
ACS712国产替代推荐:电流检测芯片选型指南
单片机·嵌入式硬件·物联网
徐怀江2 小时前
ModusToolbox for vscode使用小记
ide·vscode·单片机·mcu·infineon
洋九八3 小时前
STM32 串口(USART)配置
stm32·单片机·嵌入式硬件
华科大胡子3 小时前
单片机IO不够?ULN2003A救急方案
单片机
时空自由民.3 小时前
MCU 串口 printf 耗时优化方案
单片机·嵌入式硬件
金色光环4 小时前
【DSP学习】增强型脉宽调制 EPWM 实验-基于普中DSP开发攻略
单片机·学习·dsp开发
搁浅小泽5 小时前
万用表测试电子元器件
单片机·嵌入式硬件·可靠性工程师
aini_lovee6 小时前
STM32 上实现 SD 卡读取 JPEG 解码 TFT 显示
开发语言·stm32