stm32常用模块的函数整理
本文最后更新于:2025年1月21日 凌晨
存放一些常用模块的函数实现,.h
和.c
文件就不放啦~
Stm32CubeIDE的一些使用技巧
- 调整代码字体大小
Ctrl+Shift+'+'
放大,Ctrl +'-'
缩小。放大为啥多了个Shift,因为不按Shift,是等号
- 导入已有的工程:参考这篇博客
- 要打开芯片设置页面,直接open工程的
.ioc
文件。
- 要打开芯片设置页面,直接open工程的
- STM32IDE窗口恢复
- 数组统一赋值/清零操作,不能直接赋值,需要用
memset
函数,记得使用前检查是否include <string.h>
1
memset(rx_data, 0, sizeof(rx_data));//清理缓冲区
问题篇
Stm32CubeIDE相关的问题
1.Confirm Perspective Switch(确认视角切换)
- 问题描述:
1
This kind of launch is configured to open the Debug perspective when it suspends.
- 如果不想每次都这么提示,可以点
Remember my decision
。点击switch
就是切换。
2.Command aborted(命令失败)
- 问题描述:
1
Failed to insert all hardware breakpoints
- 一般解决方式:
减少断点/监视点的数量
:硬件调试器通常有硬件断点/监视点的数量限制。你可以尝试删除一些不必要的断点/监视点。使用软件断点
:有些IDE支持软件断点,虽然性能稍逊于硬件断点,但可以避免硬件断点数量限制的问题。检查断点的有效性
:确保所有设置的断点都是在有效的代码位置,而不是在无效或者不适合设置断点的位置。
3.Porblem occurred(发生问题)
- 问题描述:这个问题比较复杂,也遇到很多次,大部分情况,和硬件连接相关,简单说就是硬件没连接好。
1
2
3
4
5
6
7
8
9'Launching xxxx' has encountered a problem.
Error in final launch sequence:
Failed to execute MI command:
load xxxxxx
Error message from debugger back end:
Error finishing flash operation - 解决方式
解决方式不是唯一吧,这里罗列些。检查硬件连接
:确保调试器和目标设备连接正常。如果有物理连接问题,可能导致这种错误。重新启动调试器和设备
:有时重启调试器和设备可以解决问题。检查文件路径和权限
:确保文件或者路径有访问权限,有正确且有访问权限。重新编译项目
:重新编译项目,以确保生成的ELF文件没有问题。更新调试器固件
:确保使用的是最新版本的调试器固件。
4.使用STM32CubeIDE ST-Link下载提示“Target no device found”
复位管脚复位时,利用下载软件(比如STM32 ST-LINK Utility),连接,清除单片机里的程序,然后再试一次烧录。
调试篇
debug模式,打断点+串口输出调试
printf函数输出到串口
keil5-IDE
Keil MDK使用的是ARM编译器,参考这篇博客即可,三种方法。
使用微库(Use MicroLIB)
在 usrat.c 文件中添加如下代码:
1
#include <stdio.h>//放在usrat.h开头
这段代码放在usrat.c文件开头
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
//二选一,功能一样
HAL_UART_Transmit (&huart1 ,(uint8_t *)&ch,1,HAL_MAX_DELAY );
return ch;
// while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
// USART1->DR = (uint8_t) ch;
// return ch;
}- 在任意需要使用printf函数打印的C文件中,都需要引用
#include <stdio.h>
头文件
- 在任意需要使用printf函数打印的C文件中,都需要引用
适合多个串口打印的方法(GCC编译器也可以用)
- 首先在usrat.c 文件中添加如下代码
1
2
3#include <stdarg.h>
#include <string.h>
#include <stdio.h>1
2
3
4
5
6
7
8
9
10
11
12
13void UsartPrintf(UART_HandleTypeDef USARTx, char *fmt,...)
{
unsigned char UsartPrintfBuf[296];
va_list ap;
va_start(ap, fmt);
vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); // 格式化字符串
va_end(ap);
// 发送整个字符串
HAL_UART_Transmit(USARTx, UsartPrintfBuf, strlen((char *)UsartPrintfBuf), HAL_MAX_DELAY);
} - 然后在usrat.h文件中添加如下代码:
1
2
3#define USART_DEBUG huart1 //看硬件选择的是uart几,就在这里写几
void UsartPrintf(UART_HandleTypeDef USARTx, char *fmt,...); - 使用方法:
1
UsartPrintf(USART_DEBUG, "The USART1 is OK!\r\n");
注意:函数参数中
USART_DEBUG
参数为在 usrat.h 中重定义的 huart1 。- 如果同时打开了USART1和USART2,那么在 usrat.h 中还会有一个 huart2 ,像huart1(重新define为USART_DEBUG) 一样重定义 huart2 ,和其它串口区分。
- 首先在usrat.c 文件中添加如下代码
Stm32CubeIDE
- 法一:使用于单个串口调试输出,但是该串口同时使用DMA串口通信时会失效
在usart.h中加入以下代码:
1 |
|
然后直接使用printf打印即可输出到串口。
法二:适合多个串口打印的方法(同上)
法三:在Private includes 中引入:
#include <stdio.h>
- 再在USERCODEBEGIN0添加:然后你就可以在任意地方使用printf语句方便的输出你想要的内容。
1
2
3
4
5int fputc(int ch, FILE *f){
uint8_t temp[1] = {ch};
HAL_UART_Transmit(&huart1, temp, 1, 2);//huart1需要根据你的配置修改
return ch;
}
- 再在USERCODEBEGIN0添加:
串口数据缓冲区每次用完,记得清理
1 |
|
功能模块篇
矩阵键盘
这里是4行3列的键盘
key.h文件
1 |
|
key.c文件
1 |
|
主程序调用
1 |
|
串口通信
工程配置
开启外部晶振
:在Pinout&Configuration -> System Core -> RCC 页面,将 High Speed Clock (HSE) 配置为 Crystal/Ceramic Resonator配置时钟频率
:在Clock Configuration 页面,将PLL Source 选择为 HSE,将System Clock Mux 选择为 PLLCLK,然后在HCLK (MHz) 输入72并回车,将HCLK频率配置为 72 MHz打开串口外设
:Pinout&Configuration -> Connectivity -> USART1/2/3,将Mode选择为Asynchronous(自行选择串口)- Usart3一般是蓝牙串口
添加DMA通道
:在 USARTx -> Configuration ->DMA Settings
标签卡中,点击 Add 按钮,分别添加 USARTx_RX 和 USARTx_TX 的 DMA 通道使能串口中断
:在 USART2 -> Configuration ->NVIC Settings
标签卡中,勾选 USARTx global interrupt 的 Enable
收发主体代码
- 定义全局变量
rx_data
作为串口接收缓冲区,tx_data
作为串口发送缓冲区。- 由于是不定长数据的接收,因此缓冲区大小可以根据实际需求调整,只能大不能小,否则可能会丢失数据
1
2//串口接收缓冲区
uint8_t rx_data[256] = {0};
- 由于是不定长数据的接收,因此缓冲区大小可以根据实际需求调整,只能大不能小,否则可能会丢失数据
- 在 main 函数中,使用
HAL_UARTEx_ReceiveToIdle_DMA
函数开启不定长数据DMA接收- 注意:需要
关闭DMA传输过半中断
,我们只需要接收完成中断。 - 此函数是以空闲中断作为接收完成的标志,而不是接收长度,因此可以接收任意长度的数据。
1
2
3
4// 使用Ex函数,接收不定长数据
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, rx_data, sizeof(rx_data));
// 关闭DMA传输过半中断(HAL库默认开启,但我们只需要接收完成中断)
__HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT);
- 注意:需要
- 在中断函数
HAL_UARTEx_RxEventCallback
中,处理接收到的数据- 记得在回调函数结尾,
重新启动接收,使用Ex函数
,接收不定长数据 - 记得每次接收完和发送完数据之后,都要清理缓冲区
1
2
3
4
5
6
7
8
9
10// 不定长数据接收完成回调函数,这里将接收到的数据又发送出去
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart == &huart1) {
data_ready = 1; // 标记数据已准备好
//在主函数中,检查data_ready,当数据已准备好,进入数据校验和解析过程。
//重新启动接收,使用Ex函数,接收不定长数据
memset(data_send, 0, sizeof(data_send));//清理send缓冲区
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_data, sizeof(rx_data));
}
}
- 记得在回调函数结尾,
校验部分
奇偶校验
1 |
|
和校验
1 |
|
CRC校验
数据包
将信息固定帧格式打包,作为发送端数据包
- 奇偶校验
- 使用串口调试助手生成一个发送数据包的话,可以用电脑计算器的程序员模式异或算出最后一位奇偶校验码,取最后面两位即可。
- 波特率动串口调试助手可以自动计算校验和。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45//全局变量,数据包根据FLAG对发送数据进行打包
int Red_Flag = 0;
int Green_Flag = 0;
int Yello_Flag = 0;
void generateDataPacket(uint8_t* data) {
// 设置初始帧头和长度
data[0] = 0xAA; // 帧头
data[1] = 0x09; // 数据长度(包括帧头、长度、LED控制和校验位)
// 设置LED控制
if (Red_Flag == 1) {
data[2] = 0x01; // 点亮红色LED
data[3] = 0xFF;
} else {
data[2] = 0x01; // 关闭红色LED
data[3] = 0x00;
}
if (Yello_Flag == 1) {
data[4] = 0x02; // 点亮黄色LED
data[5] = 0xFF;
} else {
data[4] = 0x02; // 关闭黄色LED
data[5] = 0x00;
}
if (Green_Flag == 1) {
data[6] = 0x03; // 点亮绿色LED
data[7] = 0xFF;
} else {
data[6] = 0x03; // 关闭绿色LED
data[7] = 0x00;
}
// 计算奇偶校验位
uint8_t parity = 0;
for (int i = 0; i < 8; i++) {
parity ^= data[i]; // XOR计算校验
}
data[8] = parity; // 将计算得出的校验位放入数据包的最后
// 此时数据包已经封装完成,可以通过串口发送
HAL_UART_Transmit(&huart1, data, 9, HAL_MAX_DELAY);
}
- 如果是和校验,则最后一位:
1
2
3
4
5
6// 计算校验和
uint8_t checksum = 0;
for (int i = 0; i < 8; i++) {
checksum += data[i]; // 求和
}
data[8] = checksum;
接收端将数据包固定帧格式解析
1 |
|
蓝牙模块
stm32常用模块的函数整理
http://zoechen04616.github.io/2025/01/19/stm32常用模块的函数整理/