常见问题集

常见问题集

16.烧录不进去

问题描述:jlink 不插板子上的烧录口的时候,电脑还能识别到 jlink, 插上就识别不到了,拔掉过会儿就又能识别到了,
猜测原因:是因为 clk 和 dio 电平被影响了,jlink 和上位机就没法正常通信了
豆包回答:现象根源不是单纯 SWDIO/SWCLK 信号电平干扰调试链路,真实机理:目标板侧漏电 / 短路 / 负载异常,把 J-Link 内部接口电路拉得异常 → 导致 J-Link USB 链路死机、电脑直接丢失 J-Link 设备;断开负载,内部电路恢复,USB 重新枚举识别。

ps:如果仅仅连不上芯片,但电脑始终能看到 J-Link,那就是单纯 SWD 通信问题,就是一开始想的 CLK/DIO 电平、速率、接线问题。

15.HardFault

前提:修改放电逻辑,使用中断函数回调的方式,启动的时候把脉冲的回调函数改成现在的正常放电函数,放电时改成新的,控制放电次数和时间。
问题描述:开机后进Timer5Init函数,执行到NVIC_Init函数的时候进入HardFault死循环。
问题分析:我看了你的修改内容和你的描述就猜到大概会是什么问题了。
①可能是进中断了,中断服务函数没有
②或者回调是空,尝试执行
③还可能栈溢出
分析验证:这里使能后进入脉冲定时器了,然后里面这个中断服务函数里面用到的回调函数是Null
解决办法:使能前先把中断挂起清了

防护进阶:
①回调赋初值,写个空函数。但空函数要执行一次调用,而且麻烦
②回调里执行前先判断回调函数是不是空,有日志的话,如果是空可以在判断里加个错误日志,或者警告日志。判断的时候,直接if(!pFun)也行,或者如图,和空函数比较。

14.CAN通讯异常

问题描述:用周立功的USB-CAN适配器(CAN卡),连接控制板,发现CAN调试工具(CAN上位机)上既没有接收信息也发送信息失败。
分析过程:万用表测了一下,发现CAN_H = 2V,CAN_L = 3V,差分电压 = 2V - 3V = -1V(极性完全反了),正常情况下 CAN_H 应该比 CAN_L 高(显性时 H>L,隐性时接近相等),这里 L 反而比 H 高 1V,这是典型的总线接反 / 芯片损坏 / 终端电阻异常的表现。
解决处理:对调两根线的位置,再测电平,CAN_H = 2.5V,CAN_L = 2.5V

ps:自己只需要写8位数据位,CAN控制器会自动封装解析帧头、帧尾、CRC、位填充、ACK 等,只要帧头、帧尾、CRC、位填充、ACK任何一项不对,CAN 控制器会直接:
丢弃这一帧 → 不交给用户 → 不计入正常接收 → 同时报错误计数。
** 你在代码 / 上位机里能收到的 CAN 数据,100% 是正确无误的。
错误帧、坏帧、残缺帧,CAN 硬件会帮你全部过滤掉。**

13.CAN通讯异常

万用表测了一下,发现CAN_H≈1.5V,CAN_L≈1.5V,电压不正常。
TJA1050和1042的电路设计不一样,焊的1042,通信不了,换成1050就正常了,⑤引脚不一样,1050的⑤引脚是悬空的,1042的⑤引脚需要提供一个参考电压值,接一个3.3V电。

12.ntc开路电阻阻值检测异常修复

问题描述:未连接ntc时,设备本应异常,却偶尔会变正常
分析过程:按理说做了滤波,不应该出现这个问题。下拉电阻且ntc开路的时候,电压为0,采集到的ADC值也为0,此时返回的ntc电阻值也为0(本应该是无穷大的),ntc阻值0再通过查表法,可得温度被定义为最高温度,最高温度设置为150。此时一切正常,但弱下拉,下拉不稳定,有可能共模干扰,或者地之间本来就有电平差,采集到的adc值变为1,此时返回的ntc阻值=10000 *(4095-1)/1 = 40940000. 此时通过查表法,可得温度被定义为最低温度,最低温度设置为-273,线圈未连接的判断是:温度为最高温则判定异常,此时-273就会被判定为正常,导致设备在异常情况下,弱下拉干扰作用下偶尔显示正常。
上拉就不会有这种问题。

/** * @brief adc值转换为ntc电阻值__原代码 */staticuint32_tget_ntc_r_val(uint16_tadc_val){adc_val=adc_val>4095?4095:adc_val;uint32_trs=NTC_SERIES_R;returnrs*(4095-adc_val)/adc_val;}
/* -20~100摄氏度对应的电阻阻值 ntc10k 3950 */uint32_tntc10k_neg20to100[]={105385,99109,93252,87783,82674,77898,73432,69253,65341,61678,//-20~-11摄氏度58246,55028,52011,49179,46522,44026,41681,39477,37405,35455,33621,31893,30266,28733,27288,25925,24639,23425,22279,21197,20175,19208,18294,17430,16612,15837,15104,14409,13751,13127,12535,11974,11441,10936,10456,10000,9567,9155,8764,8391,8037,7700,7379,7074,6783,6506,6241,5989,5749,5520,5301,5093,4894,4703,4522,4348,4182,4024,3872,3727,3588,3455,3328,3207,3090,2978,2871,2769,2671,2577,2486,2399,2316,2237,2160,2086,2016,1948,1883,1820,1760,1702,1646,1593,1541,1492,1444,1398,1354,1311,1270,1231,1193,1156,1121,1087,1054,1022,992,962,934,906,880,854,829,805,782,760,738,718,698,};


解决办法:程序改为如下,增加adc_val = adc_val==0 ? 1 : adc_val;使开路时计算的ntc阻值总为无穷大。

/** * @brief adc值转换为ntc电阻值__修改后代码 */staticuint32_tget_ntc_r_val(uint16_tadc_val){adc_val=adc_val>4095?4095:adc_val;adc_val=adc_val==0?1:adc_val;// adduint32_trs=NTC_SERIES_R;returnrs*(4095-adc_val)/adc_val;}

总结分析:需考虑特殊值情况、最大值、最小值。不过这段代码原本也是给上拉电阻的测温写的,所以只写了最大值的判断。后来移植的时候没考虑到这部分。

ps:突然发现我的上拉是这么写的return rs*adc_val/(4095-adc_val+1);直接+1有点绝对但有考虑到分母是0的情况!

11.部分代码无法打断点

如图所示,红框里的代码不能打断点,
众所周知,一共有两种原因:
1.编译器优化:该段代码没有用到,被优化了,该代码没有对应的机器码。
2.代码未被编译:这是刚写好的代码,未编译生成对应的机器码。

最开始以为是没编译,狂按F7,但不行,还是不能打断点。
苦苦思索一中午后,发现if条件有问题,我太习惯等号的顺向思维了,不等号的逆向思维一时没搞清晰,把 || 换成 && ,问题得已解决

10.PB4引脚输出异常,因默认功能非普通GPIO口

PB4连接的阀不能正常工作,换了块儿芯片依旧是这样,问了AI发现是STM32F1系列的经典坑
PB4‌ 是JTAG调试链路的一部分,系统上电后默认启用JTAG调试模式,此时即使正确配置了GPIO模式和时钟,该引脚仍处于调试功能优先状态,无法作为普通GPIO使用。

9.负压风机

负压风机降温,正压不降温

8.上电水泵不工作

因为没检测到线圈的温度传感器,我觉得得上整机测试了,但怎么会有人这么聪明啊,思路打开,格局打开!

7.CAN卡在初始化

问题描述:can通信收发异常。can调试器的CANL接CANL,但CANH不小心接了12V,can芯片会被烧坏吗

会在这里卡很久,但发现这两句话其实在CAN_Init函数里有,写出来完全多余。但为什么没卡在CAN_Init里面啊?(这个很难解释)
先说结论,是CAN芯片(TJA1042 高速can收发器)坏了,换了一个CAN芯片后一切正常,当时就是测了一下CANH、CANL的电压,正常应该都是2.5V,但测到1.2V,所以断定的芯片坏了。

接下来说说问题排查思路:
首先得接12V电源,因为之前只接3.3V电源的时候,程序能跑起来,但没有CAN的收发,而CAN是5V供电,所以只有3.3V是不行的。

接上12V之后就卡在can的初始化了,一开始会觉得可能是因为裸板,没接收发器,接上收发器果然正常了,但下一次重新上电又不正常了,这可真奇怪,跟收发器其实没有关系,就是个随机bug,有时候卡几秒,有时候又会卡几分钟。
后来意识到AI说的外部CAN收发器不是CAN调试器,是指TJA1042芯片,因为是TJA1042连接的TX、RX引脚。因为TJA1042芯片坏了,所以信号不稳定,导致初始化时卡时不卡,偶尔收到信号会退出初始化,收不到就一直卡在初始化,不接TJA1042芯片的话,就一直卡在初始化。应该是这个意思。

终端一般加一个120V的电阻,就是CANH CANL之间加一个120R的电阻,可以增强抗干扰,保证通信稳定性,但是这个终端电阻和能否通讯上没有关系,CAN收发异常时,不需要考虑是否为缺失终端电阻导致的。

6.逻辑分析仪参数

主要参数:带宽、采样率
采样率:单位时间内的采样次数
带宽:能测量并保存显示的最大频率
采样步进是采用速度,采完要处理转化成波形,能测量并保存显示的最大频率就是带宽,采样率决定带宽的上限。
采样步进是硬件层面的采样间隔,频率步进/扫频步进是软件层面的波形显示间隔。

起因:一个50us的输出,测出来实际输出是40us,有时候测出来是60us,40us和60us是一半一半的概率,偏偏就不是50us
分析:不论是单次脉冲还连续脉冲都是这样,单次脉冲用的系统滴答阻塞式延迟50us,连续脉冲是定时器设置50us,完全不同的两种50us设置方式,却有一样的显示结果,怀疑是硬件问题,所以换了一块控制板,但仍有该问题,最后怀疑是逻辑分析仪有问题,因为拿示波器测试就是正常的。
结论:逻辑分析仪带宽50kHz,步进20us,所以无法显示50us

二更:
逻辑分析仪的参数只有采样深度采样率,带宽是示波器的参数,因为逻辑分析仪是数字信号,所以没有带宽。带宽‌是示波器的核心参数,定义为输入正弦波信号幅度衰减至原值的70.7%(-3dB)时的频率
‌采样深度‌(存储深度)表示逻辑分析仪能够保存的采样点数量

5.函数指针

如何让函数在不同场景下表现不同的行为?
1.函数指针
通过定义函数指针变量,在运行时指向不同的函数实现,从而动态改变调用行为
2.参数控制分支逻辑
通过参数决定函数执行路径

4.串口+DMA+循环缓冲区

我的疑问:1.中断里一定要加rt_interrupt_enter和rt_interrupt_leave吗 ?
2.为什么MTCircleBufferWriteMulti之前要失能DMA

答案:1.一定要加,‌RTOS中断上下文保护‌:
在RT-Thread等实时操作系统中,rt_interrupt_enter用于标记中断上下文,防止调度器误判当前处于中断状态而错误调度任务;rt_interrupt_leave则恢复中断状态,确保后续任务调度正常。
‌临界区保护‌:
若中断服务程序(ISR)中调用了RTOS的API(如信号量释放rt_sem_release),必须通过这两函数确保操作原子性,避免竞态条件。
‌例外情况‌:
若ISR仅操作硬件寄存器且不调用RTOS API,可省略(但需确保代码永不扩展至需要RTOS功能)。
2.①在读取DMA接收数据前,需先DMA_Cmd(DISABLE)暂停DMA传输,防止DMA控制器在读取过程中修改接收缓冲区内容(避免数据竞争)。
②通过DMA_GetCurrDataCounter获取已接收数据长度时,DMA必须处于静止状态,否则计数器值可能不准确。
但这个时候我又有疑问了,它一进串口的空闲中断就把DMA失能了,紧接着就把数据放进循环缓冲区了,怎么就能确定这时候DMA已经转运完了呢?万一这帧数据还没转运完呢?失能DMA不就是为了防止数据正在转运导致计数器值不准的嘛。
且在地址递增模式的时候,重新失能使能也可以使得(DMA_SxNDTR)寄存器的值恢复置我们最开始设定的。
查完资料后,发现DMA转运速度很快,只需要几个时钟周期,而串口传输速率很慢,拿115200bps举例,单个字节传输时间约87us,(1s传输115200个bit,一个字节有8个bit的数据位,还要加上1bit的开始位和1bit的停止位,没有校验位的话,那就是10bit,1s传输11520个字节,那么一个字节就是1/11520s ≈ 87us)而空闲中断是1.5个字节没有接收到新数据就触发的,此时DMA通常已经当前帧数据的转运。防止的应该是下个帧数据正在转运导致的计数器不准。
而DMA转运又是怎么触发的呢?是硬件检测触发,一般是串口的RXNE,RXNE不需要软件进行USART_ITConfig的配置。RXNE标志在UART接收到数据时自动置位,触发DMA搬运后由硬件清除

3.git指纹验证

电脑重装系统后,用vscode上传代码会跳git指纹验证的弹窗,点击选项后,弹出来另一个弹窗,不管点击是还是否,都会反复弹



百度一下发现是因为重装系统后,电脑里存储的公钥丢失,需要重新生成并将 SSH 公钥添加到 Gitee 账户中,所以其实参考文章里面的步骤并不需要完全按顺序操作完,执行到第二步就可以了。
参考文章:https://blog.csdn.net/cnzzs/article/details/144573423

2.keil调试无外设

keil调试的时候想看一下GPIO的输出值,但我的peripherals里面的system viewer里面什么也没有,应该是因为重装电脑之后,重新下载keil,导致之前的配置数据丢失,需要重新配置。
办法:Peripherals无外设寄存器:修改Debug选项中的Dialog DLL中的内容为DARMSTM.DLL,修改Parameter中的内容为-pSTM32F103VC。这里因为我建工程时选的MCU是STM32F103VC,所以改成这个。这个参数要和建工程时选的MCU一致。

发现还是不行,查资料说是新版keil的问题,keil更新迭代导致的。还需要在魔法棒的target里添加.SFR文件,框选use custom file,添加.sfr文件。新版是没有自带SFR文件的,需要自己生成,最新发现安装pack的步骤 打开keil—打开pack installer—file—import pack—选择pack离线包貌似可以自动生成sfr文件。就是用keil手动安装离线pack包,不是双击安装,是要到keil里面导入。

tips:参考文章里的原方法中需要右键点击终端中打开去生成sfr文件,但有的电脑并没有终端中打开这个选项,按shift键再点右键,其中打开powershell窗口也是和终端一样的功能。

同时拓展一下,在没有keil的debug调试没有外设寄存器的时候如何调试:可以直接搜寄存器名,放到监控里,看手册找寄存器名,因为标准库都封装好了,直接看对应的结构体里面的值就行了。例如,查看GPIOB的引脚输出,在监控里放GPIOB->ODR即可。

参考文章:https://blog.csdn.net/weixin_71155468/article/details/149575110

1.数组越界导致bug

发送命令使motor0转动,用500ms 的定时器让motor运行500ms后自动停止。motor0开始之后直接卡死,首先想到的就是定时器没初始化成功,遂加上创建成功的输出显示


定时器是创建成功了的,那还能是什么原因呢?
进debug调试,发现是进了断言错误,问题还是在定时器这里


这个时候有点一筹莫展,问了ai

看到错误根源分析,几乎瞬间我就有答案了,数组越界造成的,排除掉1和3,剩下的2就是真相。

在watch里,发现motor的值果然是不正常的

在.map里找了一下,上个值果然是个数组,至此,问题定位完毕,问题出在adc_buf上,adc_buf数组越界导致motor0的定时器地址异常,变为不可访问的地址,执行到motor0的定时器时,就会出现断言错误。

俩宏定义位置互换,问题解决