打印
[新手园地]

《嵌入式系统入门》——学习笔记1——共享资料问题

[复制链接]
1885|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gdmgb520|  楼主 | 2012-6-23 08:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 gdmgb520 于 2012-6-23 08:07 编辑

共享资料问题

记得有次面试,当我介绍完项目中各个模块的功能后,面试官问我我各个模块之间是怎么通信的?当时对模块之间的通信没有什么概念,但是又知道有信号量、邮箱这些名词,虽然面试官也很年轻,但我还是没有瞎忽悠,就说对这个没概念。后来看这本书才明白,其实我们单片机中模块之间通信实际上就是通过全局变量来实现的。

为什么要通信?是因为我们进行模块化编程,各个模块实现一些小的不同的功能,当一个模块完成自己的任务后要告知另一个模块,让它完成它的任务,就向流水线作业。这其中有一种比较特殊的情况,就是中断程序与其他用户程序之间的通信。因为我们不可能把所有事情都放在中断程序中去做,所有中断程序只做一些最紧急的事情,然后告知用户程序,让用户程序处理后续的事情。这里我们常常让中断程序和用户程序共用一个或多个变量来实现他们之间的通信(即中断程序告知用户程序该做些什么样的处理)。

而这种特殊的通信情况就可能产生一个问题——共用资料问题。什么是共用资料问题?

共用资料问题就是因为共用资料而产生的问题。(呵呵,这解释!)到底共用资料会产生怎样的问题呢?最典型的例子是在中断中读取传感器的数据(如加速度的值)放到一个全局变量(如ax,ay,az)中,在主函数中处理这些数据(如,计算|ax|+|ay|+|az|的和是否超过某个阈值THRESHOLD)。考虑一种情况,敲好在主程序处理数据时中断发生,更新了az的值,那么计算的结果就很可能失真。

虽然这个判断语句 if(|ax|+|ay|+|az|>THRESHOLD)是单条语句,但是单条C语句可能代表多条汇编语句,所以该语句并不是“原子的”,也就是说是可能被中断的。而这里if(|ax|+|ay|+|az|>THRESHOLD)要求不能被中断,则这条语句被称为临界代码。

原子的:程序中不能被中断的代码称为“原子的(统一不可分割的)”
临界区:把必须是“原子的”以保证系统正常运转的指令集合定义为临界区。

像上面说的那样,共用资料问题只在偶然的情况下发生(中断恰好在数据处理时产生),所以,共享资料问题造成的问题很难被发现和追踪。因此,应该尽量避免共享资料问题。

解决共享资料问题的方法:
1.在任务程序中禁止中断
        bool        data_processor(bool fIntOn)
        {
                bool bRetVal = False;

                dis_int();        //关闭中断
                if (|ax|+|ay|+|az|>THRESHOLD)
                        bRetVal = TRUE;
                //恢复调用该函数之前的中断状态,如果调用前为开则重新开启中断
                if (fIntOn)
                        enable_int();

                return bRetVal;
        }

2.在代码中判断读取数据是共享资料内容是否发生了改变
        volatile int ax,ay,az;
        bool data_processor(void)
        {
                bool bRetVal = False;
                int a[3],b[3];
                unsigned char i;

                do
                {
                        a[0] = ax;
                        a[1] = ay;
                        a[2] = az;
                        b[0] = ax;
                        b[1] = ay;
                        b[2] = az;
                        if ((a[0] == b[0])&&(a[1] == b[1])&&(a[2] == b[2]))
                                break;
                }while(1);

                if (|ax|+|ay|+|az|>THRESHOLD)
                        bRetVal = TRUE;

                return bRetVal;
        }
        通过上面的方法可以不关闭中断而解决共享资料问题。但是注意第一行语句增加了volatile关键字。因为这种方法是通过判断两次读取的数据是否一致来判断是否有中断发生,而重复读取共享数据时如果没有用volatile关键字定义则可能被编译器优化掉。

相关帖子

沙发
gdmgb520|  楼主 | 2012-6-23 08:06 | 只看该作者
占楼备用

使用特权

评论回复
板凳
john_lee| | 2012-6-24 01:29 | 只看该作者
哈哈,楼主开始写读书心得了,不错不错,学习的态度很端正,鼓励鼓励。

使用特权

评论回复
地板
gdmgb520|  楼主 | 2012-6-24 09:18 | 只看该作者
3# john_lee

谢谢鼓励。
我发现当我把看过的内容总结、写出来时,对这些内容了解得更加清晰。

使用特权

评论回复
5
john_lee| | 2012-6-25 16:57 | 只看该作者
好,鼓励的话说完了,就该讨论了,也可以说是来拍拍砖。
楼主写了两种“避免共享数据冲突”的方法,第1种是最常用的,理论和实践都已验证了这种方法的正确性,缺点是增加了中断潜伏时间。
但这个“第2种”方法,却值得商榷了,我们从第1种方法中的中断关闭时间段得知,共享数据的使用是在 if (ax + ay + az > THRESHOLD) 语句中,而 do { ... } while (1); 位于 if 语句“之前”,实际上是起不到避免冲突的作用。

使用特权

评论回复
6
abin0415| | 2012-6-25 21:26 | 只看该作者
支持LZ

使用特权

评论回复
7
gdmgb520|  楼主 | 2012-6-26 13:25 | 只看该作者
5# john_lee

谢谢Lee老师。

判断语句
  if (|ax|+|ay|+|az|>THRESHOLD)
                         bRetVal = TRUE;
应该改为下面的样子:
if (|a[0]|+|a[1]|+|a[2]|>THRESHOLD)
                         bRetVal = TRUE;

实现的原理是:两次读取ax,ay,az,如果两次读取的值一样,可以认为中间没有发生中断,数据是完整有效,就可以进行下一步的判断了。

使用特权

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

本版积分规则

个人签名:了解新东西才知道自己的不足。 www.elecbench.com

67

主题

452

帖子

1

粉丝