本帖最后由 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关键字定义则可能被编译器优化掉。 |