打印
[其他ST产品]

关于STM32的临界变量问题

[复制链接]
1952|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
elife|  楼主 | 2019-9-5 22:07 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
以前大都做8位单片机,用C或汇编,一直都是用8位的全局变量在中断和大循环中共用,++,--,赋值的处理不用考虑临界问题,只有16位以上才考虑。现在用STM32突然想起这个问题,网上一查,这样的方式不行了,bit变量,8位等等都需要考虑这个问题,实际看了KEIL C编译后的汇编,确实一句++,--,汇编是2-3句。是不是STM32所有的中断和大循环通讯都要注意临界状态。可是我看开发板的例程,临界区也没有频繁开关中断。请熟悉这方面的高手指点一下,如果在STM32上用这种方法,要怎样用。或者有更好的方法。是不是如果不按临界处理,都会留下隐患?

使用特权

评论回复
沙发
R2D2| | 2019-9-5 22:14 | 只看该作者
你把RVMDK输出的汇编贴出来看看。

使用特权

评论回复
板凳
elife|  楼主 | 2019-9-5 22:34 | 只看该作者
;;;288            mainTick++;       
00002e  4806              LDR      r0,|L10.72|
000030  7801              LDRB     r1,[r0,#0]  ; mainTick
000032  1c49              ADDS     r1,r1,#1
000034  7001              STRB     r1,[r0,#0]
;;;289      /* USER CODE END SysTick_IRQn 0 */
;;;290      HAL_IncTick();
000036  e8bd4010          POP      {r4,lr}
00003a  f7ffbffe          B.W      HAL_IncTick
;;;291      /* USER CODE BEGIN SysTick_IRQn 1 */
;;;292   
;;;293      /* USER CODE END SysTick_IRQn 1 */

使用特权

评论回复
地板
elife|  楼主 | 2019-9-5 22:39 | 只看该作者
一句mainTick++ 汇编了3句。这样会不会有临界问题?
定义是8位的。volatile uint8_t mainTick=0;

使用特权

评论回复
5
R2D2| | 2019-9-5 23:13 | 只看该作者
R1发生中断的时候被压栈保护起来,退出中断恢复原来的值。你的担心完全是杞人忧天。

使用特权

评论回复
6
elife|  楼主 | 2019-9-5 23:20 | 只看该作者
是变量mainTick,不是寄存器。寄存器一般不会去操作的。

使用特权

评论回复
7
R2D2| | 2019-9-5 23:24 | 只看该作者
mainTick的地址在R0里面,值在R1里面,中断发生的时候都压栈了你还怕什么?

使用特权

评论回复
8
elife|  楼主 | 2019-9-5 23:29 | 只看该作者
正因为值在R1里面,压栈了。在LDRB和STRB之间的需要屏蔽中断,不然中断中更改了mainTick,退中断后,是更改前的值。再STRB就不是中断里改的数据了。

使用特权

评论回复
9
R2D2| | 2019-9-6 00:14 | 只看该作者
既然“不是中断里改的数据了”那就是正确的值,你还怕什么?

天下本无事庸人自扰之。

使用特权

评论回复
10
ayb_ice| | 2019-9-6 13:47 | 只看该作者
这与多少位机没有直接关系

RAM是典型的存储加载机制,也就是读改写的操作,不能直接操作内存,所以++,--,等操作不是原子操作

不过8位机一般可以直接对内存进行操作所以反而是原子操作

使用特权

评论回复
11
ayb_ice| | 2019-9-6 13:49 | 只看该作者
如果只在中断里修改变量,其它地方是只读的话,是没有任何问题的,哪怕不是原子操作

使用特权

评论回复
12
elife|  楼主 | 2019-9-6 19:59 | 只看该作者
楼上说的对。这样只好在执行前关中断,执行后再开中断了,应该也不需要很长时间。

使用特权

评论回复
13
elife|  楼主 | 2019-9-6 20:03 | 只看该作者
因为这样的处理经常用,中断中判断标志,更新标志,主程序也要判断标志,再更新标志。或者共用缓冲区等等。看来为防止错误,也只有开关中断了。

使用特权

评论回复
14
R2D2| | 2019-9-6 20:27 | 只看该作者
STM32就应该用RTOS,中断和任务之间用信号量、邮箱、消息队列传递信息。用全局变量太low了。

使用特权

评论回复
15
zhanzr21| | 2019-9-6 20:57 | 只看该作者
本帖最后由 zhanzr21 于 2019-9-6 21:22 编辑

这个问题虽然简单, 但楼主说的情况不算杞人忧天, 要分情况搞懂其中道理才会设计出正确的程序来.

比如一个变量是局部的, 主程序读写.
uint32_t test_var32 = 1;

test_var32 ++;
在修改的过程中, 确实要先把变量的地址载入寄存器, 修改完再存入该地址. 在载入地址, 修改后还没有存回去的时候发生中断怎么样?
此时硬件会自动push一个stack frame:

这时寄存器会自动保存到Stack上去, 中断退出返回到主程序时, 又会自动恢复现场,所以继续执行没问题的.
这个说的是不涉及到FPU的情况, 如果涉及到FPU情况会复杂点, 但是原理就是会在发生中断时多保存一些寄存器(也可以配置为Lazy Stacking)关于FPU的情况细节不表,不影响这里的讨论.


那么使用的寄存器并非R0,R1, R2, R3, R12中的怎么办, 编译器会在中断入口另外push所用的寄存器, 也不会有问题.

但是如果这个变量是全局的, 中断中也会修改这个变量, 那么就会有冲突. 这时用临界区的处理办法, 或者杜绝使用全局变量.

如果是RTOS, 不仅主程序与中断有冲突可能, task之间也有可能冲突, 解决办法除了临界区和杜绝使用全局变量之外, 还可以加锁, mutex, semaphore都可以.

不当之处请大家指正.

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

394

帖子

4

粉丝