STM32 零基础可移植教程 09:串口收一行命令,用 led on 控制 LED

STM32 零基础可移植教程 09:串口收一行命令,用 led on 控制 LED

上一篇我们把 USART RX 中断跑通了。

电脑发一个字符,STM32 能收到,并且能打印:

bash 复制代码
RX: A, hex: 0x41

这一步说明串口接收链路没问题。

但真实项目里,我们很少只收一个字符。更常见的是在串口助手里输入一行命令:

bash 复制代码
led on

led off

led toggle

然后让板子执行对应动作。

这一篇就把单字节接收往前推一步:

bash 复制代码
用 USART RX 中断接收一行命令,然后解析命令控制 LED

本篇还是不讲 DMA、不讲 IDLE、不讲复杂协议。先把"收一行字符串"这件事跑通。

本篇目标

最终现象:

串口助手输入:

bash 复制代码
led on

STM32 返回:

bash 复制代码
> led on

OK: LED on

同时 LED 点亮。

输入:

bash 复制代码
led off

STM32 返回:

bash 复制代码
> led off

OK: LED off

同时 LED 熄灭。

输入:

bash 复制代码
help

STM32 返回命令列表。

本篇跑通标准:

  • Keil 编译通过;

  • 串口能打印启动信息;

  • 串口助手输入 help 后能看到命令列表;

  • 输入 led on 后 LED 点亮;

  • 输入 led off 后 LED 熄灭;

  • 输入 led toggle 后 LED 翻转;

  • 能说清楚一行命令是怎么从一个个字节拼出来的;

  • 能说清楚换 USART、换 LED 引脚时要改哪里。

准备工作

建议从上一篇工程复制一份,改名为:

bash 复制代码
09_usart_line_command

上一篇已经完成:

  • USART 基本配置;

  • USART global interrupt;

  • HAL_UART_Receive_IT() 单字节中断接收;

  • printf() 串口输出;

  • 串口助手接线和参数设置。

这一篇在它的基础上加两个东西:

还需要第 02 篇里的 LED 应用层代码:

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

Core/Src/app_led.c

如果你的工程里还没有这两个文件,本篇后面也会给出。

硬件连接

本篇用到两个硬件功能:

bash 复制代码
USART

LED

USART 接线仍然是:

|

STM32

|

USB 转 TTL

|

| --- | --- |

|

USART_TX

|

RXD

|

|

USART_RX

|

TXD

|

|

GND

|

GND

|

LED 如果是板载 LED,先看原理图确认接到哪个 GPIO。

如果是外接 LED,注意串联限流电阻。

本系列仍然建议把 LED 引脚的 CubeMX User Label 设置为:

bash 复制代码
LED

这样 CubeMX 会在 main.h 里生成:

bash 复制代码
#define LED_Pin ...

#define LED_GPIO_Port ...

应用层代码就能直接用 LED_PinLED_GPIO_Port

先理解一行命令怎么收

上一章我们每次只收 1 个字节。

比如电脑发送:

bash 复制代码
led on

STM32 实际收到的不是"一整句",而是一个个字节:

bash 复制代码
l

e

d

空格

o

n

回车

换行

所以我们要做一件事:

bash 复制代码
每收到 1 个字节,就先放进缓冲区

遇到 \r 或 \n,说明一行结束

然后把这一行交给命令解析函数

led on 这行命令在缓冲区里大概是这样:

bash 复制代码
['l']['e']['d'][' ']['o']['n']['\0']

最后这个 \0 很重要。

C 语言字符串必须以 \0 结束,否则 strcmp()printf("%s") 这些函数不知道字符串到哪里结束。

本篇的处理方式是:

bash 复制代码
app_uart    只负责接收一行字符串

app_command 只负责解析这一行命令

app_led     只负责控制 LED

main.c      只负责把几个模块串起来

不要把所有逻辑都堆在 main.c

这样后面扩展 beepadc readpwm 50 这些命令时,也比较清楚。

CubeMX 配置步骤

1. USART 保持上一篇配置

USART 仍然配置为:

bash 复制代码
Mode: Asynchronous

Baud Rate: 115200

Word Length: 8 Bits

Parity: None

Stop Bits: 1

Hardware Flow Control: None

也就是:

bash 复制代码
115200, 8N1

并且要打开:

bash 复制代码
USARTx global interrupt

如果这一项忘了勾,串口打印可能正常,但接收回调不会进。

2. LED 引脚保持 GPIO Output

LED 引脚配置为:

bash 复制代码
GPIO_Output

GPIO 参数仍然建议:

|

配置项

|

推荐值

|

| --- | --- |

|

GPIO output level

|

默认关闭 LED 的电平

|

|

GPIO mode

|

Output Push Pull

|

|

GPIO Pull-up/Pull-down

|

No pull-up and no pull-down

|

|

Maximum output speed

|

Low

|

|

User Label

|

LED

|

LED 是高电平亮还是低电平亮,由 app_led.c 里的宏处理。

3. 生成 Keil 工程

配置完成后点击:

bash 复制代码
GENERATE CODE

打开 Keil 后先编译一次,确认 CubeMX 生成代码没问题。

完整代码

本篇一共有 3 个应用模块:

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

Core/Src/app_uart.c


Core/Inc/app_command.h

Core/Src/app_command.c


Core/Inc/app_led.h

Core/Src/app_led.c

app_uart 负责串口收一行。

app_command 负责解析命令。

app_led 负责控制 LED。

1. Core/Inc/app_uart.h

bash 复制代码
#ifndef APP_UART_H

#define APP_UART_H


#include "main.h"

#include <stdint.h>


#ifndef APP_UART_LINE_MAX

#define APP_UART_LINE_MAX 64u

#endif


void App_UART_Init(void)
;

void App_UART_Print(const char *text)
;

void App_UART_PrintBytes(const uint8_t *data, uint16_t len)
;

HAL_StatusTypeDef App_UART_StartReceiveIT(void)
;

void App_UART_OnRxCplt(UART_HandleTypeDef *huart)
;

uint8_t App_UART_ReadLine(char *line, uint16_t size)
;

uint8_t App_UART_TakeOverflow(void)
;


#endif

这里的:

bash 复制代码
#define APP_UART_LINE_MAX 64u

表示一行命令最多 63 个有效字符,最后 1 个位置留给 \0

新手阶段够用了。

2. Core/Src/app_uart.c

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

#include <stdio.h>

#include <string.h>


#ifndef APP_UART_HANDLE

#define APP_UART_HANDLE huart1

#endif


extern
 UART_HandleTypeDef APP_UART_HANDLE;


static
uint8_t
 s_rx_it_byte = 
0
;

static
char
 s_build_line[APP_UART_LINE_MAX];

static
char
 s_ready_line[APP_UART_LINE_MAX];

static
uint16_t
 s_build_len = 
0
;

static
uint8_t
 s_drop_line = 
0
;

static
volatile
uint8_t
 s_line_ready = 
0
;

static
volatile
uint8_t
 s_line_overflow = 
0
;


static void App_UART_SaveReadyLine(void)
{

    
uint16_t
 i;


    
if
 ((s_build_len == 
0u
) || (s_line_ready != 
0u
))

    {

        s_build_len = 
0
;

        
return
;

    }


    s_build_line[s_build_len] = 
'\0'
;


    
for
 (i = 
0
; i <= s_build_len; i++)

    {

        s_ready_line[i] = s_build_line[i];

    }


    s_line_ready = 
1u
;

    s_build_len = 
0
;

}


void App_UART_Init(void)
{

    s_rx_it_byte = 
0
;

    s_build_len = 
0
;

    s_drop_line = 
0
;

    s_line_ready = 
0
;

    s_line_overflow = 
0
;

    
memset
(s_build_line, 
0
, 
sizeof
(s_build_line));

    
memset
(s_ready_line, 
0
, 
sizeof
(s_ready_line));

}


void App_UART_Print(const char *text)
{

    
if
 (text == 
NULL
)

    {

        
return
;

    }


    HAL_UART_Transmit(&APP_UART_HANDLE,

                      (
uint8_t
 *)text,

                      (
uint16_t
)
strlen
(text),

                      HAL_MAX_DELAY);

}


void App_UART_PrintBytes(const uint8_t *data, uint16_t len)
{

    
if
 ((data == 
NULL
) || (len == 
0u
))

    {

        
return
;

    }


    HAL_UART_Transmit(&APP_UART_HANDLE,

                      (
uint8_t
 *)data,

                      len,

                      HAL_MAX_DELAY);

}


HAL_StatusTypeDef App_UART_StartReceiveIT(void)
{

    
return
 HAL_UART_Receive_IT(&APP_UART_HANDLE, &s_rx_it_byte, 
1u
);

}


void App_UART_OnRxCplt(UART_HandleTypeDef *huart)
{

    
uint8_t
 ch;


    
if
 (huart == 
NULL
)

    {

        
return
;

    }


    
if
 (huart->Instance != APP_UART_HANDLE.Instance)

    {

        
return
;

    }


    ch = s_rx_it_byte;


    
if
 ((ch == 
'\r'
) || (ch == 
'\n'
))

    {

        s_drop_line = 
0
;

        App_UART_SaveReadyLine();

    }

    
else
if
 (s_drop_line != 
0u
)

    {

        
/* Drop bytes until the next line ending after an overflow. */

    }

    
else
if
 (s_build_len < (APP_UART_LINE_MAX - 
1u
))

    {

        s_build_line[s_build_len] = (
char
)ch;

        s_build_len++;

    }

    
else

    {

        s_build_len = 
0
;

        s_drop_line = 
1u
;

        s_line_overflow = 
1u
;

    }


    (
void
)HAL_UART_Receive_IT(&APP_UART_HANDLE, &s_rx_it_byte, 
1u
);

}


uint8_t App_UART_ReadLine(char *line, uint16_t size)
{

    
uint16_t
 i;


    
if
 ((line == 
NULL
) || (size == 
0u
))

    {

        
return
0u
;

    }


    
if
 (s_line_ready == 
0u
)

    {

        
return
0u
;

    }


    __disable_irq();


    
for
 (i = 
0
; i < (size - 
1u
); i++)

    {

        line[i] = s_ready_line[i];

        
if
 (s_ready_line[i] == 
'\0'
)

        {

            
break
;

        }

    }


    line[size - 
1u
] = 
'\0'
;

    s_line_ready = 
0u
;


    __enable_irq();


    
return
1u
;

}


uint8_t App_UART_TakeOverflow(void)
{

    
uint8_t
 overflow;


    __disable_irq();

    overflow = s_line_overflow;

    s_line_overflow = 
0u
;

    __enable_irq();


    
return
 overflow;

}


int fputc(int ch, FILE *f)
{

    
uint8_t
 data = (
uint8_t
)ch;


    HAL_UART_Transmit(&APP_UART_HANDLE, &data, 
1u
, HAL_MAX_DELAY);


    
return
 ch;

}

这一段里最关键的是:

bash 复制代码
if
 ((ch == 
'\r'
) || (ch == 
'\n'
))

串口助手按下回车,常见会发送 \r\n\r\n

我们遇到回车或换行,就认为一行结束。

还有这句不能少:

bash 复制代码
(
void
)HAL_UART_Receive_IT(&APP_UART_HANDLE, &s_rx_it_byte, 
1u
);

它会重新开启下一次单字节接收。

少了它,还是只能收一次。

3. Core/Inc/app_command.h

bash 复制代码
#ifndef APP_COMMAND_H

#define APP_COMMAND_H


void App_Command_Init(void)
;

void App_Command_ProcessLine(const char *line)
;


#endif

4. Core/Src/app_command.c

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

#include "app_led.h"

#include <stdio.h>

#include <string.h>


static void App_Command_PrintHelp(void)
{

    
printf
(
"Commands:\r\n"
);

    
printf
(
"  help       - show this help\r\n"
);

    
printf
(
"  led on     - turn LED on\r\n"
);

    
printf
(
"  led off    - turn LED off\r\n"
);

    
printf
(
"  led toggle - toggle LED\r\n"
);

}


void App_Command_Init(void)
{

}


void App_Command_ProcessLine(const char *line)
{

    
if
 (line == 
NULL
)

    {

        
return
;

    }


    
if
 (line[
0
] == 
'\0'
)

    {

        
return
;

    }


    
printf
(
"> %s\r\n"
, line);


    
if
 (
strcmp
(line, 
"help"
) == 
0
)

    {

        App_Command_PrintHelp();

    }

    
else
if
 (
strcmp
(line, 
"led on"
) == 
0
)

    {

        App_LED_On();

        
printf
(
"OK: LED on\r\n"
);

    }

    
else
if
 (
strcmp
(line, 
"led off"
) == 
0
)

    {

        App_LED_Off();

        
printf
(
"OK: LED off\r\n"
);

    }

    
else
if
 (
strcmp
(line, 
"led toggle"
) == 
0
)

    {

        App_LED_Toggle();

        
printf
(
"OK: LED toggle\r\n"
);

    }

    
else

    {

        
printf
(
"ERR: unknown command\r\n"
);

        
printf
(
"Type help and press Enter\r\n"
);

    }

}

这篇先用最简单的:

bash 复制代码
strcmp
()

它要求命令完全匹配。

所以:

bash 复制代码
led on

可以识别。

但:

bash 复制代码
LED ON

led  on

led on 

暂时不处理。

这不是不能做,而是这篇先把主线跑通。大小写忽略、去掉多余空格、参数解析,后面可以慢慢加。

5. Core/Inc/app_led.h

bash 复制代码
#ifndef APP_LED_H

#define APP_LED_H


#include "main.h"


void App_LED_Init(void)
;

void App_LED_On(void)
;

void App_LED_Off(void)
;

void App_LED_Toggle(void)
;


#endif

6. Core/Src/app_led.c

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


#ifndef LED_GPIO_Port

#error "LED_GPIO_Port is not defined. Set the LED pin User Label to LED in CubeMX."

#endif


#ifndef LED_Pin

#error "LED_Pin is not defined. Set the LED pin User Label to LED in CubeMX."

#endif


#ifndef APP_LED_ON_LEVEL

#define APP_LED_ON_LEVEL   GPIO_PIN_RESET

#endif


#ifndef APP_LED_OFF_LEVEL

#define APP_LED_OFF_LEVEL  GPIO_PIN_SET

#endif


void App_LED_Init(void)
{

    App_LED_Off();

}


void App_LED_On(void)
{

    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, APP_LED_ON_LEVEL);

}


void App_LED_Off(void)
{

    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, APP_LED_OFF_LEVEL);

}


void App_LED_Toggle(void)
{

    HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);

}

默认这里按"低电平亮"写。

如果你的 LED 是高电平亮,改成:

bash 复制代码
#define APP_LED_ON_LEVEL   GPIO_PIN_SET

#define APP_LED_OFF_LEVEL  GPIO_PIN_RESET

.c 文件加入 Keil 工程

在 Keil 工程树里右键:

bash 复制代码
Application/User/Core

选择:

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

加入:

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

Core/Src/app_command.c

Core/Src/app_led.c

如果忘了添加,常见会报:

bash 复制代码
undefined symbol App_Command_ProcessLine

undefined symbol App_UART_ReadLine

undefined symbol App_LED_On

main.c 调用方式

1. Includes 区域

找到:

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

/* USER CODE END Includes */

改成:

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

#include "app_led.h"

#include "app_uart.h"

#include "app_command.h"

#include <stdio.h>

/* USER CODE END Includes */

2. 初始化区域

确认 CubeMX 初始化函数已经先执行:

bash 复制代码
MX_GPIO_Init();

MX_USART1_UART_Init();

然后在:

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

/* USER CODE END 2 */

里面添加:

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

App_LED_Init();

App_UART_Init();

App_Command_Init();

App_UART_StartReceiveIT();


printf
(
"\r\nUSART command test start\r\n"
);

printf
(
"Type help and press Enter\r\n"
);

/* USER CODE END 2 */

如果你用的是 USART2,CubeMX 初始化函数可能是:

bash 复制代码
MX_USART2_UART_Init();

这没关系,但 app_uart.c 里的 APP_UART_HANDLE 也要改成 huart2

3. while 循环区域

找到:

bash 复制代码
while
 (
1
)

{

  
/* USER CODE END WHILE */


  
/* USER CODE BEGIN 3 */

  
/* USER CODE END 3 */

}

改成:

bash 复制代码
while
 (
1
)

{

/* USER CODE END WHILE */


/* USER CODE BEGIN 3 */

char
 line[APP_UART_LINE_MAX];


if
 (App_UART_TakeOverflow() != 
0u
)

  {

    
printf
(
"ERR: command too long\r\n"
);

  }


if
 (App_UART_ReadLine(line, 
sizeof
(line)) != 
0u
)

  {

    App_Command_ProcessLine(line);

  }


  HAL_Delay(
10
);

/* USER CODE END 3 */

}

主循环做三件事:

  1. 看看命令有没有超长;

  2. 看看有没有收到一整行;

  3. 如果收到,就交给命令解析模块。

4. USART 接收完成回调

找到:

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

/* USER CODE END 4 */

添加:

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

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

  App_UART_OnRxCplt(huart);

}

/* USER CODE END 4 */

如果你的工程里已经有这个函数,不要重复写第二个。

把:

bash 复制代码
App_UART_OnRxCplt(huart);

合并到已有函数里即可。

串口助手设置

串口助手参数仍然是:

bash 复制代码
115200

8 data bits

1 stop bit

None parity

None flow control

发送命令时,要让串口助手发送换行。

常见设置叫:

bash 复制代码
发送新行

加回车换行

发送 \r\n

不同串口助手名字不一样。

如果没有这个选项,你也可以手动发送:

bash 复制代码
led on\r\n

本篇代码是看到 \r\n 才认为一行结束。

如果你只输入 led on,但没有发送回车或换行,STM32 会一直等着,不会执行命令。

编译、下载和验证

先编译:

bash 复制代码
Build / F7

确认:

bash 复制代码
0 Error(s)

下载后,复位开发板。

串口助手应该看到:

bash 复制代码
USART command test start

Type help and press Enter

输入:

bash 复制代码
help

正常返回:

bash 复制代码
> help

Commands:

  help       - show this help

  led on     - turn LED on

  led off    - turn LED off

  led toggle - toggle LED

输入:

bash 复制代码
led on

正常返回:

bash 复制代码
> led on

OK: LED on

LED 应该点亮。

输入:

bash 复制代码
led off

正常返回:

bash 复制代码
> led off

OK: LED off

LED 应该熄灭。

输入:

bash 复制代码
led toggle

LED 应该翻转一次。

移植到其他板子的修改点

这篇的移植点主要有 9 个。

|

要改的地方

|

为什么要改

|

在哪里改

|

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

|

USART 实例

|

不同板子可能用 USART1/2/3

|

CubeMX Connectivity

|

|

TX/RX 引脚

|

不同开发板接线不同

|

CubeMX Pinout 和原理图

|

|

USART 中断

|

收命令依赖 RX 中断

|

CubeMX NVIC Settings

|

|

串口句柄

| huart1

huart2 不同

| app_uart.c

APP_UART_HANDLE

|

|

LED 引脚

|

不同板子 LED GPIO 不同

|

CubeMX Pinout

|

|

LED User Label

|

代码依赖 LED_PinLED_GPIO_Port

|

CubeMX GPIO 页面

|

|

LED 有效电平

|

有些高电平亮,有些低电平亮

| app_led.c

|

|

命令长度

|

命令更长时缓冲区要变大

| APP_UART_LINE_MAX |

|

命令内容

|

不同项目命令不同

| app_command.c |

换板子的推荐顺序:

  1. 先确认 USART 接到哪个 USB 串口或外接模块;

  2. CubeMX 配置对应 USART,并打开 global interrupt;

  3. 确认 stm32xx_it.c 里有 HAL_UART_IRQHandler(&huartx)

  4. 修改 APP_UART_HANDLE

  5. 配置 LED GPIO Output,User Label 填 LED

  6. 根据原理图修改 LED 有效电平;

  7. Keil 添加 app_uart.capp_command.capp_led.c

  8. 串口助手发送 help 验证命令入口;

  9. 再发送 led onled off 验证硬件动作。

常见问题排查

1. 串口有启动信息,但输入命令没反应

优先检查:

|

优先检查

|

具体方法

|

| --- | --- |

|

串口助手有没有发送回车换行

|

需要 \r\n 结束一行

|

|

RX 线有没有接

|

USB-TTL TXD 接 STM32 RX

|

|

USART global interrupt 有没有打开

|

CubeMX NVIC Settings

|

|

是否调用 App_UART_StartReceiveIT()

|

放在 USART 初始化后

|

|

回调是否存在

| HAL_UART_RxCpltCallback()

调用 App_UART_OnRxCplt()

|

最常见的其实是第一条:

bash 复制代码
只发送了 led on,没有发送回车或换行

STM32 还在等"一行结束",所以不会执行。

2. 输入 led on,返回 unknown command

先看串口回显:

bash 复制代码
> led on

如果回显不是这个,比如多了空格:

bash 复制代码
> led on 

或者大小写不同:

bash 复制代码
> LED ON

那本篇代码暂时不会识别。

当前命令必须完全匹配:

bash 复制代码
help

led on

led off

led toggle

后面如果要做得更智能,可以加去空格、转小写、参数解析。

3. 只能执行一次命令,第二次没反应

优先检查 App_UART_OnRxCplt() 里有没有重新开启接收:

bash 复制代码
(
void
)HAL_UART_Receive_IT(&APP_UART_HANDLE, &s_rx_it_byte, 
1u
);

少了这句,就只收一次。

4. 编译报 LED_GPIO_Port is not defined

说明 CubeMX 没有生成:

bash 复制代码
LED_Pin

LED_GPIO_Port

解决方法:

  1. 回 CubeMX;

  2. 找到 LED GPIO;

  3. User Label 改成 LED

  4. 重新 Generate Code。

如果你的标签叫 LED_Red,那生成的是:

bash 复制代码
LED_Red_Pin

LED_Red_GPIO_Port

要么把 CubeMX 标签改成 LED,要么把 app_led.c 改成你实际的宏名。

5. 编译报 undefined symbol App_Command_ProcessLine

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

把这个文件加入:

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

然后重新编译。

6. 输入命令后返回 OK,但 LED 不亮

这说明串口命令链路是通的,问题在 LED 部分。

优先检查:

  • LED 引脚是否选对;

  • LED 的 User Label 是否为 LED

  • LED 有效电平是否写反;

  • LED 是否是板载低电平亮;

  • 外接 LED 是否有串联电阻;

  • App_LED_Init() 是否在初始化区域调用。

可以先在 USER CODE BEGIN 2 里临时写:

bash 复制代码
App_LED_On();

如果这样 LED 也不亮,就先回到第 02 篇排查 LED。

7. 输入很长一串后提示 command too long

本篇一行命令默认最大长度是:

bash 复制代码
#define APP_UART_LINE_MAX 64u

如果超过长度,程序会丢弃这一行,并打印:

bash 复制代码
ERR: command too long

这是为了防止数组越界。

如果你的命令确实需要更长,可以改大:

bash 复制代码
#define APP_UART_LINE_MAX 128u

但不要无限改大,MCU 的 RAM 是有限的。

本篇小结

这一篇我们把 USART 接收从"一个字节"升级成了"一行命令"。

你现在至少应该知道:

  • 串口命令不是一次性自动变成字符串的;

  • STM32 是一个字节一个字节收;

  • 遇到 \r\n 才认为一行结束;

  • C 字符串需要 \0 结尾;

  • app_uart 负责收行,app_command 负责解析,app_led 负责控制硬件;

  • 命令必须和 strcmp() 里的字符串完全一致;

  • 串口助手必须发送回车或换行;

  • 换板子时重点改 USART、LED 引脚、句柄和有效电平。

下一篇我们开始进入时间类外设:

STM32 定时器基础:不用 HAL_Delay 也能 1 秒做一次事。

到这一步,程序就不只是"主循环里延时等待",而是开始用定时器中断做周期任务了。

相关推荐
搁浅小泽2 小时前
8位单片机(8位SCM/MCU)通俗详解
单片机·嵌入式硬件
星夜夏空9911 小时前
STM32单片机学习(32) —— ADC
stm32·单片机·学习
芯岭技术郦12 小时前
批量不到1元,芯岭技术集成 2.4G 射频32 位 MCU 与 USB2.0的超低功耗 SoC XL2417U
单片机·嵌入式硬件·射频工程
少年、潜行15 小时前
【开源】基于STM32的无线姿态检测仪设计
stm32·嵌入式硬件·开源·姿态检测仪
三品吉他手会点灯17 小时前
STM32F103 学习笔记-22-DMA(第1节)-DMA功能框图讲解和DMA初始化结构体讲解
笔记·stm32·单片机·嵌入式硬件·学习
陌上花开缓缓归以18 小时前
定时器和延时函数选型
单片机
华普微HOPERF19 小时前
电视冰箱洗衣机、空调风扇热水器,Matter协议如何塑造全屋智能?
嵌入式硬件·物联网·智能家居·matter协议·全屋智能
ThornArmor19 小时前
【控制篇】斩断无休止空转:4-bit 指令集里的跳转律令与时序状态机
c语言·汇编·c++·单片机·嵌入式硬件
篮子里的玫瑰19 小时前
STM32/MCU【IAP在线升级】全流程深度解析与实战指南
stm32·单片机·嵌入式硬件