Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
串口DMA接收
volatile
5
volatile在多线程及串口驱动框架中的应用疑问
发布于 2020-11-18 22:35:20 浏览:1242
订阅该版
1. 先贴volatile在iar(v8.32.1)编译器中的说明原文:(也不直接搬网上的说法了,不够正式) ``` By declaring an object volatile, the compiler is informed that the value of the object can change beyond the compiler’s control. The compiler must also assume that any accesses can have side effects—thus all accesses to the volatile object must be preserved. There are three main reasons for declaring an object volatile: ● Shared access; the object is shared between several tasks in a multitasking environment ●Trigger access; as for a memory-mapped SFR where the fact that an access occurs has an effect ● Modified access; where the contents of the object can change in ways not known to the compiler. ``` 2. Iar里规定的3个规则中的第一个 share access我有疑问。大家刚才也给我了不同的答案,我先说我的理解---这条规则建议比如,一个共享变量,在ISR和线程1里都被访问了(抛开互斥保护不谈),这个变量需要加volatile关键字,如果不加,在不同优化等级下,可能会出现异常。 3. 如果我的理解对,那接下来以RT-thread的串口空闲dma方式接受数据来举例,show code: 中断中调用关系如下: ```c dma_isr -> rt_hw_serial_isr -> rt_dma_recv_update_put_index ``` (其中读写了rx_fifo的put_index 或者 get_index. 再来看线程1的读取数据接口: `rt_serial_read –> serial_dma_rx` (接口里面也有读取rx_fifo->get_index的其他共享变量操作) 那么我的问题就是,rx_fifo中的get_index等变量都是非volatile类型,这么写在不同编译器的各种优化等级下是否都能正常? RT的这驱动框架代码肯定是经过不同平台的验证的,我这个问题只是对于IAR中关于volatile的不解。如果目前编译器在不同优化等级下,都能正常应对多线程的共享变量,为何要在帮助手册中单独列出此条?在什么情况下,shard access才会发生问题? 我自己以前也多少了解一点volatile的用法,但是之前程序没出过问题,所以我也就这么用下来了,但是这两天这个问题再次回想起来,我感觉自己不能说服自己。希望有时间的人看完我这篇说明再回复。 谢谢
查看更多
我夏了夏天
认证专家
2020-11-19
Life isn't about finding yourself, life is about creating yourself.
对于你提出的三种规则,我重新整理了一下,将我的看法发给你作为参考: ## Shared access the object is shared between several tasks in a multitasking environment。 当同一**全局变量**在多个线程之间被共享时,有可能会出现同步错误,编译器可能会将访问该全局变量的代码优化为访问某个寄存器,而不会再次访问相应的内存,导致程序运行错误。 测试代码如下: ``` static struct rt_thread v_thread1; static char v_thread1_stack[8192]; static struct rt_thread v_thread2; static char v_thread2_stack[8192]; static int flag; static int count; static void rt_init_thread1_entry(void *parameter) { while(1) { rt_thread_mdelay(300); flag = 1; rt_thread_mdelay(300); flag = 0; if(count++ > 10) { rt_kprintf("thread1 exit.\n"); flag = 1; return; } } } static void rt_init_thread2_entry(void *parameter) { while(1) { while(flag==0); rt_kprintf("thread2 running.\n"); rt_thread_mdelay(100); if(count++ > 10) { rt_kprintf("thread2 exit.\n"); return; } } } int volatile_test() { rt_err_t result = RT_EOK; result = rt_thread_init(&v_thread1, "vth1", rt_init_thread1_entry, RT_NULL, v_thread1_stack, sizeof(v_thread1_stack), RT_THREAD_PRIORITY_MAX / 3 - 1 , 20); if (result == RT_EOK) rt_thread_startup(&v_thread1); result = rt_thread_init(&v_thread2, "vth2", rt_init_thread2_entry, RT_NULL, v_thread2_stack, sizeof(v_thread2_stack), RT_THREAD_PRIORITY_MAX / 3, 20); if (result == RT_EOK) rt_thread_startup(&v_thread2); return 0; } MSH_CMD_EXPORT(volatile_test, run volatile_test); ``` 上面的测试代码在 O0 优化时正常运行,打印结果如下: ``` msh />volatile_test thread2 running. msh />thread2 running. thread2 running. thread2 running. thread2 running. thread2 running. thread2 running. thread2 running. thread2 running. thread2 exit. thread1 exit. ``` 但是如果开启 O3 优化,则打印结果如下: ``` msh />volatile_test thread1 exit. ``` 也就是说 thread2 永远得不到运行,那么原因是什么呢,请看下图的反汇编,语句 ``` while(flag==0); ``` 被优化成了如下汇编: ``` 00108b4c: ldr r3, [r4, #+288] # 第一次读取 flag 的实际值到 r3 00108b50: cmp r3, #0 # 对比 r3 的值是否为 0 00108b54: bne +0 ; # 如果不为 0 则跳转 00108b58: b -8 ; # 再次跳转回 cmp 语句继续循环 ``` 也就是说,整个程序被翻译成,只读取一次 flag 的实际值,后续一直使用 r3 寄存器中的值来进行对比,而第一次读取到的 r3 值为零,因此 while 的条件将永远成立,thread2 永远也得不到执行。 ![1606203190756.png](/uploads/20201124/4773b46140079064a5c59d28ffe31664.png) ## Trigger access as for a memory-mapped SFR(特殊功能寄存器)where the fact that an access occurs has an effect。 当读取类似串口设备的数据寄存器时,一定要加上 volatile,因为该地址寄存器中的数值可能会发生改变,如果不加 volatile,可能会发现读取的数据是错误的。 ## Modified access where the contents of the object can change in ways not known to the compiler. 对象的内容可能会被以编译器不清楚的方式被修改,例如在内核态与用户态的程序在不同的虚拟地址访问同一块物理内存,此时如果不加上 volatile,则外部的修改无法被感知到,造成程序错误。 ## 关于优化错误 如果系统在低优化等级能正常运行,但是在高优化的情况下的无法正常运行,首先怀疑两个方面: - 是否是一些关键操作没有添加 volatile - 是否是有内存写穿(因为不同的优化等级改变了内存排布导致写穿位置发生改变) 如果发现加上了 `printf` 打印,或者调用了某个外部函数,系统就正常运行了,也要怀疑是否出现了变量访问被优化的情况,因为如果加上了**外部函数**(非本文件中的函数或其他库中的函数)调用,则编译器无法确定被引用的变量是否被外部函数所改变,因而会自动从原有地址重新读取该变量的值。 ## 结论 关于 volatile 关键字,最重要的是要认识到一点,即是否在编译器清楚的范围之外,所操作的变量有可能被改变,如果有这种可能性,则一定要添加上 volatile 关键字,以避免这种错误。 归根结底,是要确定代码在真实运行的状态下,当其访问某个变量时,是否真正地从这个变量所在的地址重新读取该变量的值,而不是直接使用上次存储在某个寄存器中的值。 上面的排版可能会有些乱,你可以参考我写的一个小文档: [《volatile 的使用说明》](https://github.com/SummerLife/EmbeddedSystem/blob/master/RTOS/volatile%20%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)来查看。
1
个回答
默认排序
按发布时间排序
撰写答案
登录
注册新账号
关注者
1
被浏览
1.2k
关于作者
gebaba
这家伙很懒,什么也没写!
提问
1
回答
0
被采纳
0
关注TA
发私信
相关问题
1
rt-thread串口DMA接收方式疑问
2
关于rt-thread的串口驱动dma接收的疑问?
3
串口驱动中UART_DMA_RX_IRQHandler函数的疑问
4
bsp中stm32串口dma接收
5
RT-THREAD STUDIO 配置UART5 DMA中断接收 报错
6
串口DMA接收数据,一条数据多次回调
7
stm32l4r5zi lpuart1无法接收数据
8
如果才能提高串口回复速度?
9
stm32h750串口dma可以进中断,但无法接收数据
10
STM32 串口DMA接收问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
2
RT-Thread 发布 EtherKit开源以太网硬件!
3
rt-thread使用cherryusb实现虚拟串口
4
《C++20 图形界面程序:速度与渲染效率的双重优化秘籍》
5
《原子操作:程序世界里的“最小魔法单位”解析》
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
8
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
3
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部