0. 概述
本文主要记录一些STM32使用过程中遇到的问题,不定期更新补充。
有些问题可能并不是技术问题,但是遇到也是一种经历,也记录下来,以供后续参考吧。
点击进入:官网下载keil MDK最新版本、历史版本和芯片pack包。
点击进入:STM32相关手册使用记录。
1. 下载相关
1.1 使用STM32CubeMX生成工程,下载一次后无法继续下载?
提示复位失败。使用镊子短接复位按键–点击下载–立即松开镊子–下载成功。最终查找,是因为STM32CubeMX默认关闭了调试模式。需要根据下载方式,选择对应的选项。
1.2 下载程序过程中擦除失败
试了几次,都是擦除到0x08040000H,也就是256K。果不其然,MCU焊接的小容量,应该焊接512K。。。。
1.3 突然无法下载程序
给板子下载程序时,无法下载。提示如下:
用镊子短接了复位电路的电容,发现没有效果。更换一个j-link试下,就下载成功了。。。
2. Bootloader实现
单独写为一篇:STM32的Bootloader实现和遇到的情况。
3. 仿真调试相关
3.1 定位hardfault问题和遇到的情况
单独写为一篇:Keil环境下STM32定位hardfault位置方法和遇到的情况
3.2 程序无法运行停留在"BKPT 0xAB"
添加部分功能后发现程序没有正常运行,调试看到并没有进入到main函数,完全没有运行:
反复查看了代码,没有发现明显问题。发现这时候连续点击全速运行,又能正常运行起来。最终找到解决办法:Stm32 debug停留在"BKPT 0xAB"或者"SWI 0xAB"的解决办法。
文中提出三种方法,魔术棒勾选micro lib最简单(文中作者并不推荐)。测试可正常运行。
2022-1-26更新
程序中引入了一个开源库,调试中又出现了这个问题,但是已经勾选了微库。程序开始能够运行,在开源相关部分,会出现BKPT 0xAB。经过查找,发现是使用的开源库中使用了:
#define AT_DEBUG(...) printf("[AT]:");printf(__VA_ARGS__) /*do{}while(0)*/
而我的程序中使用jlink进行打印,没有重定向到printf。试着将此处的printf修改为我自己的jlink打印语句,可以正常运行了。
经过这两次可以看到,出现该问题,多半是与printf有关。
4 编译相关
4.1 优化等级导致函数未调用 - 编译生成lib
调试发现程序调用lib库的函数,程序出现异常。使用lib的源文件调试,发现函数调用未生效:
将优化等级从-O3降到-O1,则正常:
为什么会导致函数调用不生效???
最终将文件修改优化等级,重新编译lib文件。
编译方法:Keil 下生成LIB库文件以及如何使用LIB库文件。
4.2 warning: #870-D:invalid multibyte character sequence
搜了一圈,都是说汉字编码原因,这里不是汉字呀,明明是uint8_t
。
不过试了下,解决办法是通用的:让keil屏蔽掉这个类型的warning输出。在报警告的.c源文件中增加宏定义:
#pragma diag_suppress 870
更新:找到原因了,一直关注变量的类型,其实是打印的字符串,冒号使用了中文。修改为英文的冒号,就不必使用#pragma diag_suppress 870
来屏蔽警告了。
4.3 error: #5: cannot open source input file “stm32f10x_lib.h“
使用keil5编译keil4工程报错,单独记为一篇:点击进入。
5. FLASH存储相关
5.1 FLASH擦除返回成功,但读取仍然为原来的值
//2022.2.10
最近编写STM32G0B1芯片的bsp,调试flash时发现擦除异常,虽然HAL_FLASHEx_Erase
返回HAL_OK
,但此时读取仍然为之前写入的值。
驱动在之前的工程上是使用过的,又参考了下rtthread中的drv_flash_g0.c
,也没有明显问题。
网上搜了一下,基本都是擦除函数返回就异常了。STM32L476 HAL_FLASHEx_Erase() 问题出现了同样的问题,表示将程序下载到开发板就成功了。开发板MCU型号不完全一致,但是同一系列。
查看下STM32G0B1的官方demo(STM32 HAL库手册获取和查阅方法以及查看官方例程),才发现我的程序少赋值一个结构体成员:
参考demo,对banks成员赋值,可以正常擦除了。
那么rtthread这里是没有做区分吗?还是我的理解仍然有问题?如果在STM32G0B1上运行,需要针对修改?以后用到了再来补充。
5.2 关于使用__attribute__进行版本号判断、固件防呆attribute
单独写为一篇:STM32定义数组到flash的指定位置。
5.3 STM32是小端模式
在数据传输中经常会遇到大小端问题。STM32是小端模式,也就是数据的高字节保存在内存的高地址中,低字节保存在内存的低地址中。如一个32位无符号数0x12345678
,从低地址到高地址依次储存 78h 56h 34h 12h
(参考链接)。
数据传输时为了方便取数据,常会用到联合体:
typedef union {
uint16_t data_16;
uint8_t data_8[2];
} uni_data_16_t;
在放置数据时就需要注意顺序:
uni_data_16_t data16 = {0};
data16.data_8[0] = node_id >> 8;
data16.data_8[1] = node_id;
reg[0] = data16.data_16;
串口通信在接收端:
node_id = (body[0] << 8) + body[1];
6 延时相关
6.1 HAL_Delay出现死循环 - 未解决
出现HAL_Delay无法退出:
发现是uwTick没有自增:
6.2 使用nop()函数进行延时
// 2022.11.15
MCU:N32G031
在移植一些驱动程序时,经常会见到使用while或者for循环实现的简单delay函数,例如:
void soft_delay_us(uint16_t nus)
{
uint16_t us = 0;
while(nus--)
{
us = 60;
while(us--);
}
}
但是这种方式,除了不够精确之外,还有一个问题:在程序开启较高优化等级之后,可能会被优化。下图是在keil等级优化-O3(左)和-O0(右)下的执行情况:
可以看到,优化等价为-O3的时候,函数体没有执行,直接退出了函数,没有达到期望的演示效果。大概是编译器认为此操作没有实际意义,所以不再执行?(搞清了再来补充)。
这里被优化了,于是改为使用nop函数。
相当于汇编里的nop伪指令,表示的是空操作,可是实现延时,表示执行一条没有什么意义的指令,例如 MOV r0 ,r0。因为是执行“指令”,所以花的时间是一个指令周期,指令周期是以机器周期为单位计算的(有的指令周期是2个或者以上的机器周期,但是nop指令就是花费一个机器周期),所以:1个NOP = 1个机器周期 = 12 * 时钟周期(51而言)= 12 * 1 / f; f 表示的是你的晶振频率,
例如6Mhz的晶振,f = 6000000,带入上式,结果单位为s。(点击进入)
搜索了下,结果使用__NOP();
、__nop();
和__ASM volatile ("nop")
都报错未定义。最终使用如下:
void soft_delay_us(uint16_t nus)
{
while(nus--)
{
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
__asm("nop");
}
}