STM32 零基础可移植教程 21:1602A 并口 4 位模式,先显示 Hello

STM32 零基础可移植教程 21:1602A 并口 4 位模式,先显示 Hello

前面我们一直在用串口看结果。

串口很好用,但它有一个前提:

bash 复制代码
你旁边得有电脑和串口助手

很多小项目最后还是希望板子自己能显示一点状态,比如:

bash 复制代码
电压:1.65V

温度:28.5C

电机:RUN

计数:1234

所以这一篇开始加一个很经典的小显示器:

bash 复制代码
1602A 字符屏

你手里的是裸 16 针并口屏,不是背面带 I2C 小板的那种。

这一篇就按裸 16 针来做,先不用 I2C。

这一篇只做一个明确目标:

bash 复制代码
用 STM32 GPIO 以 4 位并口模式驱动 1602A,显示 Hello STM32

先不显示中文,不做滚动,不做自定义字符,也不读忙标志。

先把最小显示链路跑通。

本篇目标

最终现象:

bash 复制代码
1602A 第一行显示:Hello STM32

1602A 第二行显示:LCD1602 OK

本篇用到的外设:

bash 复制代码
GPIO Output

本篇跑通标准:

  • Keil 编译通过;

  • 程序能下载到开发板;

  • 1602A 背光能亮;

  • 调节对比度后能看到字符;

  • 第一行能显示 Hello STM32

  • 第二行能显示 LCD1602 OK

  • 能说清楚 RSED4~D7 分别干什么;

  • 能说清楚换引脚时 CubeMX 和代码哪里要改。

准备工作

你需要准备:

|

项目

|

说明

|

| --- | --- |

|

STM32 开发板

|

任意 STM32 开发板

|

|

1602A 裸 16 针字符屏

|

本篇按并口屏写,不按 I2C 小板写

|

|

电位器

|

常见 10k,用来调 VO 对比度

|

|

杜邦线

|

连接 LCD 和 STM32

|

|

下载器

|

ST-LINK/V2 或板载 ST-LINK

|

|

CubeMX 工程

|

可以从任意 GPIO 工程继续

|

1602A 是 16 列 2 行字符屏。

也就是:

bash 复制代码
一行最多显示 16 个字符

一共 2 行

它不是 OLED,也不是 TFT,也不是点阵图形屏。

所以第一版我们就显示英文、数字、符号。

中文显示不在这一篇处理。

1602A 16 个引脚先看懂

裸 16 针 1602A 一般是下面这些引脚。

不同厂家丝印可能略有差异,但大体一致:

|

引脚

|

名称

|

作用

|

| --- | --- | --- |

|

1

|

VSS

|

GND

|

|

2

|

VDD

|

电源,常见 5V

|

|

3

|

VO

|

对比度调节

|

|

4

|

RS

|

选择命令或数据

|

|

5

|

RW

|

读写选择

|

|

6

|

E

|

使能信号

|

|

7

|

D0

|

数据位 0

|

|

8

|

D1

|

数据位 1

|

|

9

|

D2

|

数据位 2

|

|

10

|

D3

|

数据位 3

|

|

11

|

D4

|

数据位 4

|

|

12

|

D5

|

数据位 5

|

|

13

|

D6

|

数据位 6

|

|

14

|

D7

|

数据位 7

|

|

15

|

A / LED+

|

背光正极

|

|

16

|

K / LED-

|

背光负极

|

这一篇用 4 位模式,所以只接:

bash 复制代码
D4, D5, D6, D7

不接:

bash 复制代码
D0, D1, D2, D3

为什么用 4 位模式?

因为它省 GPIO。

8 位模式要 8 根数据线。

4 位模式只要 4 根数据线,再加 RSE,一共 6 个 STM32 GPIO 就能驱动。

RS、RW、E 到底干什么

先把三个控制脚讲清楚。

RS 用来区分你发的是命令还是字符数据:

|

RS

|

含义

|

| --- | --- |

|

0

|

发送命令,比如清屏、设置光标

|

|

1

|

发送数据,比如字符 He1

|

RW 用来区分读还是写:

|

RW

|

含义

|

| --- | --- |

|

0

|

写 LCD

|

|

1

|

读 LCD

|

本篇为了简单,建议:

bash 复制代码
RW 直接接 GND

也就是只写不读。

这样 STM32 不需要读取 LCD 的忙标志,也避免 LCD 的 5V 数据线反向输出到 STM32。

E 是使能脚。

你可以先这样理解:

bash 复制代码
数据线先摆好

给 E 一个脉冲

LCD 在 E 的变化过程中把数据收进去

所以写 LCD 的动作不是"把 GPIO 拉高就完事"。

它是一个小流程:

bash 复制代码
设置 RS

设置 D4~D7

拉高 E

短暂等待

拉低 E

LCD 接收这 4 位数据

硬件连接

本篇示例连接如下。

具体 STM32 引脚你可以自己换,只要 CubeMX 里 User Label 保持一致即可。

|

1602A 引脚

|

连接

|

说明

|

| --- | --- | --- |

|

VSS

|

GND

|

|

|

VDD

|

5V

|

LCD 供电,按你的模块要求

|

|

VO

|

电位器中间脚

|

调对比度

|

|

RS

|

STM32 GPIO,User Label=LCD_RS

|

命令/数据选择

|

|

RW

|

GND

|

只写不读

|

|

E

|

STM32 GPIO,User Label=LCD_E

|

使能脉冲

|

|

D0~D3

|

不接

|

4 位模式不用

|

|

D4

|

STM32 GPIO,User Label=LCD_D4

|

数据线

|

|

D5

|

STM32 GPIO,User Label=LCD_D5

|

数据线

|

|

D6

|

STM32 GPIO,User Label=LCD_D6

|

数据线

|

|

D7

|

STM32 GPIO,User Label=LCD_D7

|

数据线

|

|

A

|

背光正极

|

按模块说明接 5V 或串电阻

|

|

K

|

GND

|

背光负极

|

对比度电位器一般这样接:

bash 复制代码
电位器一端接 5V

电位器另一端接 GND

电位器中间脚接 VO

如果你没有看到字符,但背光亮,不要急着怀疑代码。

先慢慢转对比度电位器。

1602A 的 VO 不对时,很容易出现:

bash 复制代码
只亮背光

完全没字

或者一排黑块

关于 5V LCD 和 3.3V STM32

很多 1602A 模块常用 5V 供电。

STM32 GPIO 输出是 3.3V。

不少 1602A 模块能识别 STM32 的 3.3V 高电平,但这件事最好不要想当然。

本篇为了降低风险,做两个处理:

  1. RW 接 GND,只写不读;

  2. 先用短线连接,确认模块能识别 3.3V 控制信号。

为什么 RW 接 GND 很重要?

因为如果你把 RW 接到 STM32 并读 LCD,LCD 的数据线可能会向 STM32 输出 5V 电平。

对新手来说,第一版没有必要增加这个风险。

CubeMX 配置步骤

1. 配置 6 个 GPIO 输出

在 CubeMX 里选择 6 个普通 GPIO。

例如你可以选:

bash 复制代码
LCD_RS

LCD_E

LCD_D4

LCD_D5

LCD_D6

LCD_D7

每个引脚都配置为:

bash 复制代码
GPIO_Output

然后进入:

bash 复制代码
System Core -> GPIO

User Label 分别改成:

bash 复制代码
LCD_RS

LCD_E

LCD_D4

LCD_D5

LCD_D6

LCD_D7

这一步很关键。

因为后面的 app_lcd1602.c 会直接使用 CubeMX 生成的这些宏:

bash 复制代码
LCD_RS_GPIO_Port

LCD_RS_Pin

LCD_E_GPIO_Port

LCD_E_Pin

LCD_D4_GPIO_Port

LCD_D4_Pin

LCD_D5_GPIO_Port

LCD_D5_Pin

LCD_D6_GPIO_Port

LCD_D6_Pin

LCD_D7_GPIO_Port

LCD_D7_Pin

如果 User Label 不一致,就会编译报错。

2. GPIO 参数建议

6 个 LCD 引脚建议这样配:

|

配置项

|

推荐值

|

说明

|

| --- | --- | --- |

|

GPIO output level

|

Low

|

上电先保持低电平

|

|

GPIO mode

|

Output Push Pull

|

普通推挽输出

|

|

GPIO Pull-up/Pull-down

|

No pull-up and no pull-down

|

输出模式一般不用上下拉

|

|

Maximum output speed

|

Low

|

LCD 很慢,不需要高速

|

|

User Label

|

LCD_RS 等

|

生成可移植宏

|

LCD1602 速度很慢。

这里不需要高频翻转 GPIO。

3. 生成 Keil 工程

配置完成后点击:

bash 复制代码
GENERATE CODE

打开 Keil 后先编译一次,确认 CubeMX 工程本身没问题。

Keil 工程生成和编译

打开 Keil 后,先编译:

bash 复制代码
Build / F7

确认输出里没有错误:

bash 复制代码
0 Error(s)

完整代码

新建两个文件:

bash 复制代码
Core/Inc/app_lcd1602.h

Core/Src/app_lcd1602.c

main.c 只负责调用。

1. Core/Inc/app_lcd1602.h

bash 复制代码
#ifndef APP_LCD1602_H

#define APP_LCD1602_H


#include "main.h"

#include <stdint.h>


void App_LCD1602_Init(void)
;

void App_LCD1602_Clear(void)
;

void App_LCD1602_SetCursor(uint8_t row, uint8_t col)
;

void App_LCD1602_Print(const char *str)
;

void App_LCD1602_PrintLine(uint8_t row, const char *str)
;


#endif

2. Core/Src/app_lcd1602.c

bash 复制代码
#include "app_lcd1602.h"


/* * LCD1602 4-bit parallel mode. * * CubeMX User Labels required: * LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7 * * RW is recommended to connect to GND in this beginner version. */

#ifndef LCD_RS_GPIO_Port

#error "LCD_RS_GPIO_Port is not defined. Set LCD RS pin User Label to LCD_RS in CubeMX."

#endif


#ifndef LCD_RS_Pin

#error "LCD_RS_Pin is not defined. Set LCD RS pin User Label to LCD_RS in CubeMX."

#endif


#ifndef LCD_E_GPIO_Port

#error "LCD_E_GPIO_Port is not defined. Set LCD E pin User Label to LCD_E in CubeMX."

#endif


#ifndef LCD_E_Pin

#error "LCD_E_Pin is not defined. Set LCD E pin User Label to LCD_E in CubeMX."

#endif


#ifndef LCD_D4_GPIO_Port

#error "LCD_D4_GPIO_Port is not defined. Set LCD D4 pin User Label to LCD_D4 in CubeMX."

#endif


#ifndef LCD_D4_Pin

#error "LCD_D4_Pin is not defined. Set LCD D4 pin User Label to LCD_D4 in CubeMX."

#endif


#ifndef LCD_D5_GPIO_Port

#error "LCD_D5_GPIO_Port is not defined. Set LCD D5 pin User Label to LCD_D5 in CubeMX."

#endif


#ifndef LCD_D5_Pin

#error "LCD_D5_Pin is not defined. Set LCD D5 pin User Label to LCD_D5 in CubeMX."

#endif


#ifndef LCD_D6_GPIO_Port

#error "LCD_D6_GPIO_Port is not defined. Set LCD D6 pin User Label to LCD_D6 in CubeMX."

#endif


#ifndef LCD_D6_Pin

#error "LCD_D6_Pin is not defined. Set LCD D6 pin User Label to LCD_D6 in CubeMX."

#endif


#ifndef LCD_D7_GPIO_Port

#error "LCD_D7_GPIO_Port is not defined. Set LCD D7 pin User Label to LCD_D7 in CubeMX."

#endif


#ifndef LCD_D7_Pin

#error "LCD_D7_Pin is not defined. Set LCD D7 pin User Label to LCD_D7 in CubeMX."

#endif


#define LCD1602_CMD_CLEAR_DISPLAY  0x01u

#define LCD1602_CMD_RETURN_HOME    0x02u

#define LCD1602_CMD_ENTRY_MODE     0x06u

#define LCD1602_CMD_DISPLAY_ON     0x0Cu

#define LCD1602_CMD_FUNCTION_SET   0x28u

#define LCD1602_CMD_SET_DDRAM      0x80u


static void App_LCD1602_DelayShort(void)
{

    
volatile
uint32_t
 i;


    
for
 (i = 
0u
; i < 
200u
; i++)

    {

        __NOP();

    }

}


static void App_LCD1602_Write4Bits(uint8_t value)
{

    HAL_GPIO_WritePin(LCD_D4_GPIO_Port, LCD_D4_Pin, ((value & 
0x01
u) != 
0u
) ? GPIO_PIN_SET : GPIO_PIN_RESET);

    HAL_GPIO_WritePin(LCD_D5_GPIO_Port, LCD_D5_Pin, ((value & 
0x02
u) != 
0u
) ? GPIO_PIN_SET : GPIO_PIN_RESET);

    HAL_GPIO_WritePin(LCD_D6_GPIO_Port, LCD_D6_Pin, ((value & 
0x04
u) != 
0u
) ? GPIO_PIN_SET : GPIO_PIN_RESET);

    HAL_GPIO_WritePin(LCD_D7_GPIO_Port, LCD_D7_Pin, ((value & 
0x08
u) != 
0u
) ? GPIO_PIN_SET : GPIO_PIN_RESET);


    HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_SET);

    App_LCD1602_DelayShort();

    HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_RESET);

    App_LCD1602_DelayShort();

}


static void App_LCD1602_WriteByte(uint8_t rs, uint8_t value)
{

    HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, (rs != 
0u
) ? GPIO_PIN_SET : GPIO_PIN_RESET);


    App_LCD1602_Write4Bits((
uint8_t
)(value >> 
4
));

    App_LCD1602_Write4Bits((
uint8_t
)(value & 
0x0F
u));


    HAL_Delay(
1
);

}


static void App_LCD1602_WriteCommand(uint8_t command)
{

    App_LCD1602_WriteByte(
0u
, command);


    
if
 ((command == LCD1602_CMD_CLEAR_DISPLAY) || (command == LCD1602_CMD_RETURN_HOME))

    {

        HAL_Delay(
2
);

    }

}


static void App_LCD1602_WriteData(uint8_t data)
{

    App_LCD1602_WriteByte(
1u
, data);

}


void App_LCD1602_Init(void)
{

    HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, GPIO_PIN_RESET);

    HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_RESET);

    HAL_GPIO_WritePin(LCD_D4_GPIO_Port, LCD_D4_Pin, GPIO_PIN_RESET);

    HAL_GPIO_WritePin(LCD_D5_GPIO_Port, LCD_D5_Pin, GPIO_PIN_RESET);

    HAL_GPIO_WritePin(LCD_D6_GPIO_Port, LCD_D6_Pin, GPIO_PIN_RESET);

    HAL_GPIO_WritePin(LCD_D7_GPIO_Port, LCD_D7_Pin, GPIO_PIN_RESET);


    HAL_Delay(
40
);


    
/*     * HD44780 compatible 4-bit initialization sequence.     * At this moment LCD is not in 4-bit mode yet, so send nibbles directly.     */

    App_LCD1602_Write4Bits(
0x03
u);

    HAL_Delay(
5
);

    App_LCD1602_Write4Bits(
0x03
u);

    HAL_Delay(
1
);

    App_LCD1602_Write4Bits(
0x03
u);

    HAL_Delay(
1
);

    App_LCD1602_Write4Bits(
0x02
u);

    HAL_Delay(
1
);


    App_LCD1602_WriteCommand(LCD1602_CMD_FUNCTION_SET);

    App_LCD1602_WriteCommand(LCD1602_CMD_DISPLAY_ON);

    App_LCD1602_WriteCommand(LCD1602_CMD_ENTRY_MODE);

    App_LCD1602_Clear();

}


void App_LCD1602_Clear(void)
{

    App_LCD1602_WriteCommand(LCD1602_CMD_CLEAR_DISPLAY);

}


void App_LCD1602_SetCursor(uint8_t row, uint8_t col)
{

    
uint8_t
 address;


    
if
 (row > 
1u
)

    {

        row = 
1u
;

    }


    
if
 (col > 
15u
)

    {

        col = 
15u
;

    }


    address = (row == 
0u
) ? col : (
uint8_t
)(
0x40
u + col);

    App_LCD1602_WriteCommand((
uint8_t
)(LCD1602_CMD_SET_DDRAM | address));

}


void App_LCD1602_Print(const char *str)
{

    
if
 (str == 
0
)

    {

        
return
;

    }


    
while
 (*str != 
'\0'
)

    {

        App_LCD1602_WriteData((
uint8_t
)(*str));

        str++;

    }

}


void App_LCD1602_PrintLine(uint8_t row, const char *str)
{

    
uint8_t
 i;


    App_LCD1602_SetCursor(row, 
0u
);


    
for
 (i = 
0u
; i < 
16u
; i++)

    {

        
if
 ((str != 
0
) && (*str != 
'\0'
))

        {

            App_LCD1602_WriteData((
uint8_t
)(*str));

            str++;

        }

        
else

        {

            App_LCD1602_WriteData((
uint8_t
)
' '
);

        }

    }

}

3. 把 app_lcd1602.c 加入 Keil 工程

手动新建 .c 文件后,Keil 不一定会自动编译它。

在 Keil 工程树里右键:

bash 复制代码
Application/User/Core

选择:

bash 复制代码
Add Existing Files to Group 'Application/User/Core'

添加:

bash 复制代码
Core/Src/app_lcd1602.c

main.c 调用方式

1. Includes 区域添加头文件

找到:

bash 复制代码
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

改成:

bash 复制代码
/* USER CODE BEGIN Includes */

#include "app_lcd1602.h"

/* USER CODE END Includes */

2. 初始化区域显示两行文字

确保:

bash 复制代码
MX_GPIO_Init();

已经执行。

然后在 USER CODE BEGIN 2 里添加:

bash 复制代码
/* USER CODE BEGIN 2 */

App_LCD1602_Init();

App_LCD1602_PrintLine(
0
, 
"Hello STM32"
);

App_LCD1602_PrintLine(
1
, 
"LCD1602 OK"
);

/* USER CODE END 2 */

为什么要放在 MX_GPIO_Init() 后面?

因为 LCD 的 6 根控制线和数据线必须先被初始化成 GPIO 输出。

否则你调用 App_LCD1602_Init() 时,GPIO 还没准备好。

3. while 循环先不写

第一版可以先让 LCD 显示固定内容。

while (1) 里不用写 LCD 逻辑。

如果你想显示一个每秒变化的计数,可以这样写:

bash 复制代码
/* USER CODE BEGIN Includes */

#include "app_lcd1602.h"

#include <stdio.h>

/* USER CODE END Includes */

然后在 USER CODE BEGIN 3 里写:

bash 复制代码
/* USER CODE BEGIN 3 */

static
 
uint32_t
 count = 
0
;

char
 line[
17
];


snprintf
(line, 
sizeof
(line), 
"Count:%lu"
, count++);

App_LCD1602_PrintLine(
1
, line);

HAL_Delay(
1000
);

/* USER CODE END 3 */

注意 line[17]

1602A 一行 16 个字符,字符串还需要一个结尾的 \0

所以数组至少要 17。

编译、下载和验证

代码加完后,先编译:

bash 复制代码
Build / F7

没有错误后下载:

bash 复制代码
Download

正常现象是:

bash 复制代码
第一行:Hello STM32

第二行:LCD1602 OK

如果背光亮但没字,先别急着改代码。

第一优先级是:

bash 复制代码
慢慢调 VO 对比度

很多 1602A 第一次不显示,都是对比度没调到合适位置。

移植到其他板子的修改点

这篇移植点主要有 6 个。

|

要改的地方

|

为什么要改

|

在哪里改

|

| --- | --- | --- |

| RS

引脚

|

不同板子可用 GPIO 不同

|

CubeMX Pinout

|

| E

引脚

|

LCD 使能脚必须接对

|

CubeMX Pinout

|

| D4~D7

引脚

|

4 位数据线顺序不能乱

|

CubeMX Pinout 和接线

|

|

User Label

|

代码依赖 LCD_RS_Pin 等宏

|

CubeMX GPIO 页面

|

|

LCD 供电

|

有的 LCD 5V,有的模块可 3.3V

|

看模块说明

|

|

对比度 VO

|

不调对比度可能看不到字

|

电位器

|

换板子的推荐顺序:

  1. 先选 6 个空闲 GPIO;

  2. 在 CubeMX 里配置成 GPIO_Output

  3. User Label 分别填 LCD_RSLCD_ELCD_D4LCD_D5LCD_D6LCD_D7

  4. 按 User Label 接线,不要把 D4~D7 顺序接乱;

  5. RW 先接 GND;

  6. 重新生成代码、编译、下载;

  7. 上电后先调对比度,再判断程序是否正常。

常见问题排查

1. 背光亮,但没有任何字符

优先检查:

  • VO 对比度有没有调;

  • RS/E/D4~D7 有没有接错;

  • app_lcd1602.c 有没有加入 Keil 工程;

  • App_LCD1602_Init() 有没有放在 MX_GPIO_Init() 后面;

  • User Label 是否和代码一致。

2. 出现一排黑块

一排黑块通常说明 LCD 有供电,对比度也能显示出来,但初始化没成功。

重点检查:

  • 4 位初始化时序是否执行;

  • E 引脚是否接错;

  • D4~D7 顺序是否接反;

  • RW 是否接 GND;

  • STM32 GPIO 是否配置成输出。

3. 显示乱码

常见原因:

  • D4、D5、D6、D7 顺序接错;

  • E 脉冲线接触不好;

  • 供电不稳;

  • 字符串超过一行但没有正确设置光标;

  • 初始化延时太短。

本篇代码延时偏保守,就是为了先保证新手能跑通。

4. 编译报 LCD_RS_GPIO_Port is not defined

说明 CubeMX 没有生成:

bash 复制代码
LCD_RS_GPIO_Port

LCD_RS_Pin

Core/Inc/main.h 看一下。

如果你看到的是:

bash 复制代码
#define RS_Pin ...

#define RS_GPIO_Port ...

那说明 User Label 不是 LCD_RS

解决方法:

  1. 回 CubeMX;

  2. 找到 RS 对应 GPIO;

  3. 把 User Label 改成 LCD_RS

  4. 其他引脚同理;

  5. 重新 Generate Code。

5. 编译报 undefined symbol App_LCD1602_Init

通常是 app_lcd1602.c 没加入 Keil 工程。

解决方法:

  1. 右键 Application/User/Core

  2. Add Existing Files to Group

  3. 添加 Core/Src/app_lcd1602.c

  4. 重新编译。

6. 背光不亮

背光一般和显示控制逻辑分开。

优先检查:

  • A/K 是否接反;

  • 背光是否需要限流电阻;

  • 模块背光是否已经自带电阻;

  • 电源是否正常。

背光不亮不一定代表 LCD 控制代码错。

本篇小结

这一篇我们用 STM32 GPIO 驱动了裸 16 针 1602A。

你现在至少应该知道:

  • 1602A 是 16x2 字符屏,不是图形屏;

  • 裸 16 针屏可以用 4 位并口模式节省 GPIO;

  • RS=0 写命令,RS=1 写字符数据;

  • RW 入门时建议接 GND,只写不读;

  • E 是让 LCD 接收数据的使能脉冲;

  • D4~D7 顺序不能接乱;

  • VO 对比度不调好,代码对了也可能看不到字;

  • 应用层代码放在 app_lcd1602.h/.cmain.c 只调用。

下一篇我们把第 14 篇 ADC 的结果显示到 1602A 上:

STM32 1602A 显示数字:把 ADC 电压显示到屏幕上。

相关推荐
夜月yeyue1 小时前
KCP 与 UDP 可靠传输
linux·网络·单片机·网络协议·udp·php
WIZnet2 小时前
W55RP20-EVB-MKR MicroPython 实战(14):MQTT 协议与 OneNET 平台对接
单片机·网络协议·wiznet
三佛科技-187366133972 小时前
AIP8P005B(SOP14)中微爱芯8位MCU用辉芒微FT60E112A SOP14替代
单片机·嵌入式硬件
西城微科方案开发2 小时前
LED汽车打气泵PCBA方案
单片机·嵌入式硬件
羊羊一洋2 小时前
GCC __attribute__ 完全指南:从入门到实战
c语言·stm32
Purple Coder2 小时前
51单片机(1)
单片机·嵌入式硬件·51单片机
微效电子2 小时前
晶豪代理商-ESMT代理商-ESMT台湾晶豪科技一级代理商
单片机
华普微HOPERF2 小时前
LoRa模块,如何通过卫星通信补齐地面网络的覆盖盲区?
网络·嵌入式硬件·模块·卫星通信
我先去打把游戏先2 小时前
Ubuntu虚拟机(服务器版本)Git卸载完全教程——彻底移除与清理配置
服务器·git·单片机·嵌入式硬件·物联网·ubuntu·51单片机