STM32 入门封神之路(四):GPIO 实战 + 寄存器深度拆解 ——LED 控制 + 按键检测全流程(含位操作 + 面试题)

STM32 入门封神之路(四):GPIO 实战 + 寄存器深度拆解 ------LED 控制 + 按键检测全流程(含位操作 + 面试题)

上一篇我们吃透了 GPIO 口的输入模式理论,这一篇就进入核心实战环节!基于 STM32F103C8T6,从 GPIO 输出模式深度解析、推挽 / 开漏区别,到 C 语言位操作、寄存器 / 库函数双版本实战(LED 控制 + 按键检测),再到软件消抖、面试高频题拆解,全程手把手落地,让你不仅 "会用 GPIO",更能 "吃透底层逻辑"!

本文聚焦 GPIO 口的 "实战 + 原理 + 面试" 三重需求,所有代码均可直接编译运行,新手可照搬,进阶者可深挖寄存器底层,兼顾入门与提升!

一、复习回顾:GPIO 口核心理论衔接

在实战前,先梳理核心理论,避免知识断层:

  1. GPIO 口核心定位:通用输入 / 输出口,是 STM32 与外部设备交互的唯一桥梁;
  2. 8 种工作模式:输入 4 种(浮空 / 上拉 / 下拉 / 模拟)、输出 4 种(推挽 / 开漏 / 复用推挽 / 复用开漏);
  3. 配置核心流程:时钟使能→模式配置→状态读取 / 控制(输入读 IDR,输出写 ODR);
  4. 关键前提:STM32 外设默认时钟关闭,GPIO 口配置前必须使能对应端口时钟。

二、GPIO 输出模式深度解析:推挽 vs 开漏(实战核心)

上一篇重点讲输入模式,这一篇聚焦输出模式 ------LED 控制、继电器驱动等场景的核心,需从 "电路结构""工作原理""实战选型" 三个维度彻底掌握。

1. 输出模式总览(4 种模式对比)

表格

输出模式 核心电路结构 核心特点 驱动能力 典型应用
推挽输出(GPIO_Mode_Out_PP) P 沟道 + N 沟道 MOS 管,高低电平主动驱动 高电平输出 3.3V,低电平输出 GND,无需外部电阻 强(单引脚最大 20mA) LED 控制、继电器驱动、普通 IO 输出
开漏输出(GPIO_Mode_Out_OD) 仅 N 沟道 MOS 管,高电平需外部上拉电阻 仅能主动拉低(输出 GND),高电平由外部电阻提供 弱(依赖外部电阻) I2C 总线、电平转换(3.3V→5V)
复用推挽输出(GPIO_Mode_AF_PP) 同推挽输出,引脚复用为外设功能 作为外设输出引脚(如 UART_TX),推挽驱动 UART_TX、SPI_MOSI、TIM_PWM 输出
复用开漏输出(GPIO_Mode_AF_OD) 同开漏输出,引脚复用为外设功能 作为外设输出引脚(如 I2C_SDA),开漏驱动 I2C_SDA/SCL、CAN_TX

2. 推挽输出:LED 控制首选(重点拆解)

推挽输出是最常用的输出模式,核心是 "双向主动驱动",底层电路结构决定了其强驱动能力。

(1)推挽输出底层电路原理

核心由两个互补的 MOS 管(P 沟道 + N 沟道)组成,工作逻辑如下:

  • 输出高电平时:P 沟道 MOS 管导通,N 沟道 MOS 管截止,引脚通过 P 沟道 MOS 管连接 3.3V,输出高电平(3.3V);
  • 输出低电平时:N 沟道 MOS 管导通,P 沟道 MOS 管截止,引脚通过 N 沟道 MOS 管连接 GND,输出低电平(0V);
  • 关键优势:高低电平均为主动驱动,无需外部电阻,驱动能力强,适合直接驱动 LED、继电器等外设。
(2)推挽输出配置流程(库函数 + 寄存器)

以 "PA0 推挽输出控制 LED" 为例,拆解配置逻辑:

① 库函数配置(实战首选)

c

运行

复制代码
// 1. 使能GPIOA时钟(APB2总线,GPIOA时钟使能位为第2位)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// 2. 定义GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStruct;

// 3. 配置结构体参数
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;          // 选择PA0引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;   // 推挽输出模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  // 输出速度50MHz(可选10/20/50MHz)

// 4. 初始化GPIOA
GPIO_Init(GPIOA, &GPIO_InitStruct);

// 5. 控制输出电平(亮灭LED)
GPIO_SetBits(GPIOA, GPIO_Pin_0);    // PA0置高,LED熄灭(根据硬件连接调整)
GPIO_ResetBits(GPIOA, GPIO_Pin_0);  // PA0置低,LED点亮
② 寄存器配置(理解底层)

寄存器配置需直接操作 RCC、GPIOx_CRL、GPIOx_ODR 寄存器,步骤如下:

c

运行

复制代码
// 1. 使能GPIOA时钟(RCC_APB2ENR寄存器,地址0x40021018)
*(volatile uint32_t *)0x40021018 |= (1 << 2);  // 第2位置1,使能GPIOA时钟

// 2. 配置PA0为推挽输出(GPIOA_CRL寄存器,地址0x40010800,PA0对应第0-3位)
*(volatile uint32_t *)0x40010800 &= ~(0x0F << 0);  // 清除PA0的4位配置
*(volatile uint32_t *)0x40010800 |= (0x03 << 0);   // MODE[1:0]=11(输出模式,50MHz),CNF[1:0]=00(推挽输出)

// 3. 控制PA0电平(GPIOA_ODR寄存器,地址0x4001080C)
*(volatile uint32_t *)0x4001080C |= (1 << 0);  // PA0置高(LED熄灭)
*(volatile uint32_t *)0x4001080C &= ~(1 << 0); // PA0置低(LED点亮)

3. 推挽 vs 开漏:核心区别与实战选型

很多新手混淆两种输出模式,用一张表讲清关键差异,避免选型错误:

表格

对比维度 推挽输出 开漏输出
驱动方式 双向主动驱动(高 / 低电平) 单向被动驱动(仅低电平主动,高电平依赖外部电阻)
外部电阻 无需 必须(上拉电阻,4.7KΩ~10KΩ)
电平范围 仅 3.3V(STM32 供电) 可实现电平转换(如外部接 5V 上拉,输出 5V)
线与逻辑 不支持(多个设备同时输出高 / 低会短路) 支持(多个设备共享总线,如 I2C)
驱动能力 强(20mA) 弱(依赖外部电阻电流)
典型场景 LED、继电器、普通 IO 控制 I2C 总线、电平转换、多设备通信

选型原则:无特殊需求(如电平转换、总线通信),优先选推挽输出;需多设备共享总线或电平转换,选开漏输出。

三、C 语言位操作:GPIO 寄存器编程的核心基础

寄存器编程的本质是 "位操作"------ 通过对寄存器的特定位进行置 1、清 0、取反,实现 GPIO 口配置。掌握位操作是理解底层的关键,也是面试高频考点。

1. 位操作核心运算符(4 种基础 + 2 种扩展)

表格

操作目的 运算符 示例(操作寄存器某一位) 核心逻辑
对应位置 1 按位或( =) reg = (1 << n) 保留其他位,第 n 位置 1
对应位清 0 按位与 + 按位非(&= ~) reg &= ~(1 << n) 保留其他位,第 n 位清 0
对应位取反 按位异或(^=) reg ^= (1 << n) 第 n 位取反(0→1,1→0),其他位不变
读取对应位 按位与(&) uint8_t bit = reg & (1 << n) 提取第 n 位的值(0 或非 0)
多位清 0 按位与 + 掩码 reg &= 0xF0 清低 4 位,保留高 4 位(掩码按需定义)
多位置 1 按位或 + 掩码 reg = 0x0F 置高低 4 位,保留其他位

2. 位操作实战示例(GPIO 配置场景)

以 GPIOA_CRL 寄存器(配置 PA0)为例,拆解位操作的实际应用:

c

运行

复制代码
#define GPIOA_CRL  ((volatile uint32_t *)0x40010800)  // 定义GPIOA_CRL寄存器地址

// 1. 配置PA0为推挽输出(MODE[1:0]=11,CNF[1:0]=00)
*GPIOA_CRL &= ~(0x0F << 0);  // 清PA0的4位配置(掩码0x0F,左移0位)
*GPIOA_CRL |= (0x03 << 0);   // 置MODE[1:0]=11(输出50MHz),CNF[1:0]=00(推挽)

// 2. 配置PA1为上拉输入(MODE[3:2]=00,CNF[3:2]=10)
*GPIOA_CRL &= ~(0x0F << 4);  // 清PA1的4位配置(左移4位,对应PA1)
*GPIOA_CRL |= (0x08 << 4);   // 置CNF[3:2]=10(上拉输入),MODE[3:2]=00(输入模式)

// 3. 翻转PA0电平(GPIOA_ODR寄存器)
#define GPIOA_ODR  ((volatile uint32_t *)0x4001080C)
*GPIOA_ODR ^= (1 << 0);  // PA0电平取反,实现LED闪烁

3. 位操作避坑指南(新手高频错误)

  • 错误 1:移位溢出→如1 << 31(32 位寄存器),导致符号位错误;解决:用(1UL << n)强制转换为无符号长整型,避免溢出;
  • 错误 2:掩码错误→清 0 时掩码未覆盖目标位,导致配置无效;解决:清 0 时掩码需包含目标位的所有位(如 4 位配置用 0x0F 掩码);
  • 错误 3:忘记 volatile 关键字→编译器优化导致寄存器操作失效;解决:定义寄存器时必须加volatile,告诉编译器 "该变量随时可能变化,禁止优化"。

四、实战 1:LED 闪烁(推挽输出 + 位操作)

以 "PA0 控制 LED 闪烁" 为例,分别实现寄存器版本和库函数版本,覆盖不同学习需求。

1. 硬件连接

  • LED 正极 → PA0 引脚(通过 1KΩ 限流电阻);
  • LED 负极 → GND;
  • 核心逻辑:PA0 输出低电平时 LED 点亮,输出高电平时 LED 熄灭(可根据硬件连接调整电平逻辑)。

2. 寄存器版本代码(底层实战)

c

运行

复制代码
#include "stm32f10x.h"

// 延时函数(简单软件延时,单位ms)
void delay_ms(uint32_t ms) {
    uint32_t i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 1000; j++);
    }
}

int main(void) {
    // 1. 使能GPIOA时钟(RCC_APB2ENR,地址0x40021018,第2位)
    *(volatile uint32_t *)0x40021018 |= (1UL << 2);

    // 2. 配置PA0为推挽输出(GPIOA_CRL,第0-3位)
    *(volatile uint32_t *)0x40010800 &= ~(0x0FUL << 0);  // 清4位配置
    *(volatile uint32_t *)0x40010800 |= (0x03UL << 0);   // MODE=11(50MHz),CNF=00(推挽)

    // 3. 循环实现LED闪烁
    while (1) {
        *(volatile uint32_t *)0x4001080C &= ~(1UL << 0);  // PA0置低,LED点亮
        delay_ms(500);
        *(volatile uint32_t *)0x4001080C |= (1UL << 0);   // PA0置高,LED熄灭
        delay_ms(500);
    }
}

3. 库函数版本代码(高效开发)

c

运行

复制代码
#include "stm32f10x.h"

void delay_ms(uint32_t ms) {
    uint32_t i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 1000; j++);
    }
}

int main(void) {
    GPIO_InitTypeDef GPIO_InitStruct;

    // 1. 使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 2. 配置PA0为推挽输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 3. LED闪烁循环
    while (1) {
        GPIO_ResetBits(GPIOA, GPIO_Pin_0);  // 置低点亮
        delay_ms(500);
        GPIO_SetBits(GPIOA, GPIO_Pin_0);    // 置高熄灭
        delay_ms(500);
    }
}

4. 编译运行与验证

  • 编译:两种版本均无错误(0 Error (s), 0 Warning (s)),生成 HEX 文件;
  • 下载:通过 ST-Link 下载到 STM32 最小系统板;
  • 验证:LED 每隔 500ms 亮灭一次,实现预期功能。

五、实战 2:按键检测(上拉输入 + 软件消抖)

按键检测是 GPIO 输入模式的核心实战,需解决 "电平抖动" 问题,否则会导致按键误触发。

1. 硬件连接

  • 按键一端 → PB0 引脚;
  • 按键另一端 → GND;
  • 核心逻辑:PB0 配置为上拉输入(默认高电平),按键按下时 PB0 接地(低电平),释放时恢复高电平。

2. 关键问题:按键抖动与软件消抖

(1)按键抖动现象

机械按键按下 / 释放时,触点会产生高频抖动(约 10ms),导致 GPIO 口电平频繁跳变(0→1→0→1),若直接读取电平,会误判为多次按键。

(2)软件消抖原理

在检测到电平变化后,延时 10~20ms,待抖动稳定后再读取电平,确认是否为真实按键操作。

3. 库函数版本代码(含软件消抖)

c

运行

复制代码
#include "stm32f10x.h"

void delay_ms(uint32_t ms) {
    uint32_t i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 1000; j++);
    }
}

// 按键检测函数:返回1表示按键按下,0表示未按下
uint8_t key_scan(void) {
    // 检测PB0是否为低电平(按键按下)
    if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
        delay_ms(20);  // 软件消抖,延时20ms
        // 再次检测,确认按键真的按下
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
            // 等待按键释放
            while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0);
            return 1;
        }
    }
    return 0;
}

int main(void) {
    GPIO_InitTypeDef GPIO_InitStruct;

    // 1. 使能GPIOB时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // 2. 配置PB0为上拉输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入模式
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    // 3. 配置PA0为推挽输出(控制LED)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 4. 主循环:按键按下时翻转LED状态
    while (1) {
        if (key_scan() == 1) {
            // 翻转PA0电平(LED状态切换)
            GPIO_WriteBit(GPIOA, GPIO_Pin_0, 
                         (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0)));
        }
    }
}

4. 寄存器版本代码(底层消抖)

c

运行

复制代码
#include "stm32f10x.h"

#define GPIOB_CRL  ((volatile uint32_t *)0x40010C00)
#define GPIOB_IDR  ((volatile uint32_t *)0x40010C08)
#define GPIOA_ODR  ((volatile uint32_t *)0x4001080C)
#define RCC_APB2ENR ((volatile uint32_t *)0x40021018)

void delay_ms(uint32_t ms) {
    uint32_t i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 1000; j++);
    }
}

uint8_t key_scan(void) {
    if (!(*GPIOB_IDR & (1UL << 0))) {  // 检测PB0低电平
        delay_ms(20);
        if (!(*GPIOB_IDR & (1UL << 0))) {
            while (!(*GPIOB_IDR & (1UL << 0)));  // 等待释放
            return 1;
        }
    }
    return 0;
}

int main(void) {
    // 1. 使能GPIOB和GPIOA时钟
    *RCC_APB2ENR |= (1UL << 3) | (1UL << 2);  // GPIOB第3位,GPIOA第2位

    // 2. 配置PB0为上拉输入(GPIOB_CRL,第0-3位:CNF=10,MODE=00)
    *GPIOB_CRL &= ~(0x0FUL << 0);
    *GPIOB_CRL |= (0x08UL << 0);

    // 3. 配置PA0为推挽输出
    *(volatile uint32_t *)0x40010800 &= ~(0x0FUL << 0);
    *(volatile uint32_t *)0x40010800 |= (0x03UL << 0);

    // 4. 主循环:按键控制LED翻转
    while (1) {
        if (key_scan() == 1) {
            *GPIOA_ODR ^= (1UL << 0);  // 翻转PA0电平
        }
    }
}

5. 运行效果

  • 按键未按下:LED 保持初始状态(亮或灭);
  • 按键按下一次:LED 状态翻转(亮→灭或灭→亮);
  • 无抖动误触发:软件消抖确保按键稳定识别。

六、GPIO 相关面试高频题(附标准答案)

GPIO 是 STM32 面试的必考点,以下是资料中提到的高频题,结合底层原理给出标准答案,助你面试通关:

1. 问题 1:STM32 GPIO 口有哪些工作模式?推挽和开漏的区别是什么?

标准答案:
  • GPIO 口共 8 种工作模式,分为输入(浮空 / 上拉 / 下拉 / 模拟)和输出(推挽 / 开漏 / 复用推挽 / 复用开漏)两类;
  • 推挽输出:内部有 P/N 沟道 MOS 管,高低电平均主动驱动,无需外部电阻,驱动能力强(20mA),适合 LED、继电器等;
  • 开漏输出:仅 N 沟道 MOS 管,仅能主动拉低,高电平需外部上拉电阻,支持线与逻辑和电平转换,适合 I2C 总线、多设备通信。

2. 问题 2:配置 STM32 GPIO 口时,为什么必须先使能时钟?

标准答案:
  • STM32 为了低功耗设计,所有外设(包括 GPIO 口)默认时钟是关闭的,未使能时钟时,外设寄存器无法被访问,配置无效;
  • 时钟使能通过 RCC 寄存器实现,GPIO 口属于 APB2 总线外设(如 GPIOA/B/C),需操作 RCC_APB2ENR 寄存器对应位。

3. 问题 3:GPIO 口输入模式中,上拉输入和浮空输入的区别是什么?什么时候用上拉输入?

标准答案:
  • 上拉输入:内部接 3.3V 上拉电阻,默认高电平,抗干扰能力强;
  • 浮空输入:无内部上下拉电阻,默认电平不确定,易受干扰;
  • 适用场景:外部无上下拉电阻时(如按键检测),优先选上拉输入,确保默认电平稳定,避免误触发。

4. 问题 4:如何通过寄存器配置 PA0 为推挽输出模式?

标准答案:

步骤如下:

  1. 使能 GPIOA 时钟:RCC_APB2ENR 寄存器第 2 位置 1(RCC_APB2ENR |= (1<<2));
  2. 配置 GPIOA_CRL 寄存器(PA0 属于低 8 位引脚):
    • 清除 PA0 的 4 位配置(GPIOA_CRL &= ~(0x0F<<0));
    • 配置 MODE [1:0]=11(输出 50MHz),CNF [1:0]=00(推挽输出),即GPIOA_CRL |= (0x03<<0)
  3. 控制输出电平:通过 GPIOA_ODR 寄存器第 0 位实现置 1、清 0、取反。

5. 问题 5:按键检测时为什么需要软件消抖?如何实现?

标准答案:
  • 原因:机械按键按下 / 释放时,触点会产生 10~20ms 的高频抖动,导致 GPIO 口电平频繁跳变,若直接读取会误判为多次按键;
  • 实现方式:软件消抖(最常用),检测到电平变化后,延时 10~20ms,待抖动稳定后再次读取电平,确认是否为真实按键操作。

七、GPIO 实战避坑指南(10 + 高频错误)

  1. 时钟未使能→配置无效

    • 现象:GPIO 口无电平输出 / 输入,寄存器读写无响应;
    • 解决:配置 GPIO 口前,必须使能对应端口时钟(APB2 总线)。
  2. 模式配置错误→功能失效

    • 现象:按键检测不到(浮空输入误配置为推挽输出)、LED 不亮(开漏输出未接外部电阻);
    • 解决:根据功能选择模式(LED 用推挽输出,按键用上拉输入)。
  3. 位操作移位溢出→配置错误

    • 现象:32 位寄存器操作1 << 31,导致符号位错误;
    • 解决:用1UL << n强制转换为无符号长整型。
  4. 忘记 volatile 关键字→寄存器操作失效

    • 现象:编译器优化后,寄存器读写代码被删除,配置无效果;
    • 解决:定义寄存器时添加volatile关键字,禁止编译器优化。
  5. 按键未消抖→误触发

    • 现象:按一次按键,LED 多次翻转;
    • 解决:添加 10~20ms 软件消抖,检测到电平后延时再确认。
  6. 推挽输出接外部上拉电阻→短路风险

    • 现象:输出低电平时,电阻电流过大,芯片发热;
    • 解决:推挽输出无需外部电阻,仅开漏输出需要。
  7. 引脚复用冲突→配置失败

    • 现象:PA2 配置为 GPIO 输入,但始终无法读取正确电平;
    • 解决:PA2 默认复用为 UART1_TX,需先禁用复用功能,再配置 GPIO 模式。

八、总结:GPIO 实战核心要点与进阶方向

1. 核心要点回顾

  • GPIO 口是 STM32 与外部交互的核心,8 种工作模式按需选型(LED 用推挽,按键用上拉,I2C 用开漏);
  • 配置流程:时钟使能→模式配置→状态控制(输入读 IDR,输出写 ODR);
  • 底层核心:位操作是寄存器编程的基础,掌握置 1、清 0、取反即可应对所有配置;
  • 实战关键:软件消抖是按键检测的必备步骤,避免抖动误触发。

2. 进阶学习方向

  • 中断配置:用 GPIO 外部中断替代按键轮询,减少 CPU 占用(下一篇重点);
  • 定时器结合:定时器 PWM 输出(LED 调光)、定时器中断(精准延时);
  • 复用功能:GPIO 口复用为 UART、SPI、I2C 等外设引脚,实现通信功能;
  • 低功耗优化:GPIO 口配置为模拟输入或下拉输入,降低功耗。

GPIO 口的实战是 STM32 入门的关键一步,掌握 LED 控制和按键检测后,你已经具备了与外部设备交互的基础能力。接下来,我们将学习外部中断、定时器等进阶内容,逐步实现更复杂的功能(如电机控制、传感器数据采集)!

相关推荐
撩妹小狗2 小时前
定时器PWM输出功能的使用
单片机·嵌入式硬件
xu_wenming2 小时前
跨文件数据共享模式:通过静态全局变量与访问函数结合
嵌入式硬件·mcu·物联网·设计规范
Saniffer_SH3 小时前
【高清视频】如何针对电动汽车进行通信可靠性测试、故障注入与功率分析?
服务器·驱动开发·测试工具·fpga开发·计算机外设·硬件架构·压力测试
学嵌入式的小杨同学4 小时前
STM32 进阶封神之路(七):中断核心原理 + NVIC 深度解析 —— 从概念到寄存器配置(面试重点)
stm32·单片机·嵌入式硬件·mcu·硬件架构·pcb·嵌入式实时数据库
不吃鱼的羊4 小时前
CodeMeter Runtime Server was not found on this computter问题解决
单片机
蒙塔基的钢蛋儿4 小时前
使用STM32CUEBEIDE/S32DS 开发时,生成compile_commands.json 方便VSCODE智能提示
vscode·stm32·单片机·json
qq_402995754 小时前
RS485通信设计
stm32·单片机·mcu
电子科技圈4 小时前
IAR扩展嵌入式开发平台,推出面向安全关键型应用的长期支持(LTS)服务
嵌入式硬件·安全·设计模式·软件工程·代码规范·设计规范·代码复审
串口哑火达人4 小时前
(七)RT-Thread物联网实战--MQTT-cJSON-OneNET
c语言·单片机·嵌入式硬件·mcu·物联网