近日某工程师向我反馈,他遇到如下问题:
现象是:系统中存在一个 10 ms 定时器中断,中断服务函数中会翻转 PB5。但在 main 函数中调用了他自己实现的 Ht1621DataUp()函数后,PB5 不再按 10 ms 周期翻转,而是出现无规律的跳变。根据此现象,他认为该函数影响了中断。
为此他还换过MCU芯片,看来是想验证下是不是MCU出了问题。
这个问题其实一看就可以判断出是软件使用问题。拿到他的代码后我首先进行了测试,现象确实如他所说:
这个是PB5正常翻转的波形:
下面是加了函数调用后异常翻转波形:
Ht1621DataUp()函数本身并没有定时器TIM1相关的任何代码,所以TIM1的10ms 定时本身不可能出现问题。发生此问题的原因到底是什么呢?
原因是他在Ht1621DataUp()函数里也有GPIO的相关操作,
看起来是只操作了PB2、PB3、PB4三个引脚,并没有操作PB5,但是实际上会影响到PB5的输出。所以上述现象本质上是PB5的电平被“定时器中断翻转”和“HT1621刷新位操作”交替改写了。
原因出在GPIOB->ODR |= xxxxxx;的使用上,这条语句实际上执行的读-改-写操作。
以上述异常波形中的局部一段为例说明,
刚开始是正常的输出PB5高电平,while(1)里程序里读取的ODR PB5也是高电平,紧接着定时器中断改写PB5为低电平,之后while(1)里继续往下执行后续的改和写操作,基于之前读到的高电平,所以又把PB5改写成了高电平,这就导致 PB5 的低电平被覆盖,从而产生异常波形。在下一个10ms定时器中断到了之后又会执行PB5输出高的操作。所以整体就看到了上述的异常波形。
但是while(1)就基于之前的高电平,把它又改为了高电平。到10ms再之后,中断又弄成了高电平,就看到了上述波形。
Ht1621Wr_Data()并不直接操作 PB5,但由于它与 TIM1 中断都在对同一 GPIO 端口执行“读‑改‑写”操作,因此会间接干扰 PB5 的状态,造成竞态问题。
解决该问题的方法也很简单:把ODR 改成 BSR寄存器,这样每次置位/复位只会影响它自己的脚,不会把其他不相关的引脚一起带着改。
ODR(Output Data Register)寄存器:本身不是原子操作,对其的位操作需要读-改-写三步,可能被中断打断导致竞态。
BSRR(Bit Set Reset Register)寄存器:是原子操作(通过一次写操作完成,无需读-改-写)。
关注我们:扫码加入嵌入式交流群:
149