此文是我很久之前的一个计划,目的是让大家可以在别人的错误中有所收获。
写下我和实验室小伙伴以及一些网友遇到的问题和分析。大家可以遇到问题也可以在博文下留言。当然,本人能力有限,错漏之处请直接提出。希望这篇博文能为所有喜欢嵌入式的朋友答疑解惑!
本文中,如没有特殊说明,描述的都是STM32系列单片机相关问题。
相关博文:【嵌入式开发问题汇总】硬件篇 (持续更新中...)
目录
问题1:定时器PWM——多个定时器的PWM频率不一致
问题2:定时器PWM——高级定时器(TIM1/TIM8)不输出PWM
问题3:定时器PWM——频率和计算值不一致
问题4:定时器中断——中断无法产生
问题5:定时器中断——中断代码执行异常/开启中断后主程序异常/程序死机
问题6:定时器中断——中断代码执行一次之后失效
问题7:正交编码器不工作/数据波动
问题8:代码下载后不运行/重新上电后才执行
问题9:串口连续发送错误
描述:同样的定时器预分频系数(PSC)和重装载值(ARR)配置下的PWM频率不一致(差了一倍)
分析:出现这种问题,一般是没有意识到高级定时器与一般定时器的差别,高级定时器所使用的的时钟和一般定时器不同。在STM32F10xx系列单片机中,TIM1、TIM8是高级定时器(小容量芯片没有TIM8,比如STM32F103Cxxx)。在STM32F4xx系列单片机中,多了TIM9/TIM10/TIM11定时器(这些不是高级定时器但是也使用APB2时钟)。定时器使用外设时钟APB,但是高级定时器(以及F4xx系列中的TIM9/TIM10/TIM11通用定时器)使用APB2时钟,其他的定时器使用APB1时钟。一般来说,APB2时钟频率是APB1的两倍。所以输出PWM时,要根据定时器所使用的时钟的频率计算分频数PSC与重装载值ARR。
描述:按照正常的PWM配置与GPIO配置后,TIM1/TIM8没有输出PWM波形。
分析:高级定时器之所以高级,他有互补输出PWM的功能。这个功能有一个寄存器叫BDTR,高级定时器通过次寄存器完成互补PWM的死区控制。我们在使用普通的PWM输出时,需要关闭死区控制,单打开PWM输出的使能(BDTR寄存器的最高位)即可。所以需要在初始化完成之后加上以下代码(以TIM1为例):
寄存器版本:
TIM1->BDTR |= 0x8000;
库函数版本:
TIM_CtrlPWMOutputs(TIM1, ENABLE);
描述:已知定时器所用的外设时钟APB频率并设置好PWM的预分频PSC和重装载值ARR之后,发现输出的PWM频率与计算所得不一致。
分析:新手长犯的错误,PWM频率越高问题越多。这个就是寄存器的配置问题,我举个例子大家就懂了:时钟频率72MHz,要到的1kHz的PWM,可以这样设置(PSC=72-1;ARR=1000-1),给这俩寄存器的数值是计算值减一,这在手册里说的很清楚。定时器PWM的频率计算公式为:
描述:配置好定时器中断之后,发现程序运行时无法进入中断
分析:只要定时器中断初始化的配置正确,就能进入中断。这里列出几个定时器中断配置时容易忽略的问题:
(1)中断使能未开启,一般情况下,我们使用的中断时间为计数器溢出时间IT,检查初始化中是否有以下代码(库函数):
TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE);//允许更新中断
(2)中断入口函数设置不正确,我曾经犯过这个问题,问题非常隐秘,害我一顿好找:
以上的错误其实会看汇编指令的话很容易发现问题,在触发中断时,代码死在在汇编指令"B ."上,B 是无条件跳转指令,一个点“.”表示没有找到跳转的位置(正常情况下应该跳转到正确的中断入口地址).
描述:定时器中断可以运行,但是一旦打开定时器中断,会发现中断代码为未执行完/主程序出现卡顿或异常/程序运行一段时间后死机。
分析:这种情况有几个可能的原因
(1)在对单片机性能要求不严格的情况下,我也会偏好使用基于Systick时钟的延时函数delay_ms()和delay_us(),但往往就是这个函数引发了众多BUG。为什么会这样呢?主要是Systick时钟只有一个,当然同时也只能执行一个延时函数(如果对其做了基于操作系统的调度等等不在讨论范围内)。如果主函数中和定时器中断同时使用了基于Systick的延时函数,那么第一个触发的延时函数就会出现不可预料的错误,导致中断代码执行异常或主程序异常。最好的建议就是,不要在中断程序中使用基于Systick时钟的延时函数delay_ms()和delay_us(),中断是无法预料的代码段,在里面放置延时函数就是为自己埋雷,如果一定要在中断中使用延时函数,建议使用_NOP空指令或计数器延时。
(2)出现了“套娃错误”:触发中断之后,中断程序还没有执行完,新的中断又来了,如果发生在单个中断函数,只要将中断函数设置成阻塞式即可(在中断程序执行完成之后在开启中断)。如果发生在多个中断之间,则需要合理调度中断,设置合理的中断优先级。
描述:中断执行了一次之后就不在被触发,即使已经满足触发条件。
分析:就我目前的经历看,只有一种可能,第一次退出中断之后没有清除中断标志位,所以再也进不去。这种情况在定时器,串口,SPI,DMA的配置中都会发生。大家写BUG的时候一定小心。
描述:使用正交编码器时,编码器不能正常工作,或者编码器在单向旋转时数据在一个固定数值波动
分析:首先要说的是,支持正交编码模式的定时器要看好了,不是所有的定时器都可以用作正交编码器计数的,在STM32F4xx中,只有TIM1、TIM2、TIM3、TIM4、TIM5、TIM8有正交编码器计数功能,也就是那几个具有硬件PWM功能的定时器,且一定要注意,编码器的AB两相,一定要接到定时器的第1,2通道,3,4通道不行哦!
如果是数据波动,先看数据趋势是否正确,如果只是计数出现毛刺,可以增加滤波(库函数中的初始化结构体):TIM_ICInitStructure.TIM_ICFilter = 5;
如果数据就是在几个值波动,建议检查编码器的电源是否正常供电。有些编码器的霍尔元件需要5-12V的电压在能操作工作。
描述:修改代码之后下载,一定要手动复位或者重新上电之后才会运行修改过后的代码。
分析:调试器一般都有下载完之后自动复位的选择,这里以ST-Link调试器为例:
描述:串口数据在单次发送是正常,连续发送时出现数据帧不完整或发送一部分之后停止发送
分析:这种问题一般出现在串口发送指令放在定时中断中或者循环体中,一般是两个数据帧之间没有足够的时间导致的。比如115200的波特率下,起始位,八位数据加一位停止位,一秒钟最多发送11520个字节,若使放在100Hz的中断中,一个数据帧最多只能发115字节。解决这个问题主要有两种办法,一种是修改发送频率,留够发送时间。另一种是建立消息栈,阻塞式发送,这种方案比较消耗内存,发的太快容易挤爆堆栈。
如果以上的问题与解决方案对你有帮助,请为我在右上角(PC端)/右下角(移动端App)点一下赞,本文持续更新中,可以收藏本文哦,谢谢!
如有问题,可以私信交流或直接在评论区留言!