打印
[STM32F1]

STM32中,关于中断函数调用全局变量的问题

[复制链接]
629|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
首先是问题的描述:硬件单片机型号,STM32F103VET6,IDE:keil 5。想通过它实现的一个功能是,通过两个按键控制一个RGB灯的闪烁开始和停止。两个按键通过外部中断上升沿检测,贴一下最开始的代码。

void KEY1_IRQHandler(void)
{undefined
  //确保是否产生了EXTI Line中断
    if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET)
    {        
        run_flag=0x01;
        EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
    }  
}

void KEY2_IRQHandler(void)
{undefined
  //确保是否产生了EXTI Line中断
    if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET)
    {undefined
        run_flag=0;
        EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);     
    }  
}


使用特权

评论回复
沙发
工程师犹饿死|  楼主 | 2022-3-24 15:09 | 只看该作者
定义了一个全局变量run_flag,当有按键按下的时候对run_flag分别赋予不同的值,当然有人会说这是何必多此一举直接调用中断标志不就行了,这也是一个解决的办法。但是解决问题的方式有很多种,尝试用不同的方法解决问题这本身就是一件很有意思的事情。本身从逻辑上来讲没有问题,但是实际烧录程序到单片机之后,出现的情况是按键不起作用,准确来讲是有一个按键不起作用。我一开始怀疑是中断没有起作用,仔细检查了一下,发现并没错误,在中断函数中调用LED()开led函数又是没有问题的,最后我将问题定位到了run_flag上。这个变量在多个函数中都会用到,所以定义了一个全局变量,经过查找资料,找到这样一条表述:“中断中使用的全局变量,在其他函数内要使用时一定要进入临界区(也就是关闭中断)。如果是使用操作系统的话,则在所有使用到与其他任务共享的全局变量的地方都要进临界区”。

使用特权

评论回复
板凳
工程师犹饿死|  楼主 | 2022-3-24 15:10 | 只看该作者
为此增加了如下一段代码
EXTI_InitTypeDef EXTI_InitStructure;
  EXTI_InitStructure.EXTI_LineCmd = DISABLE;
  EXTI_Init(&EXTI_InitStructure);
    run_flag1=run_flag;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);

使用特权

评论回复
地板
工程师犹饿死|  楼主 | 2022-3-24 15:11 | 只看该作者
在读取run_flag的时候,先关毕中断,之后再开中断。下载之后成功解决了这个问题。

使用特权

评论回复
5
工程师犹饿死|  楼主 | 2022-3-24 15:12 | 只看该作者
总结如下(转载)很有用:

1、尽量用局部变量替代全局变量。如果用局部变量能实现功能,最好用局部变量。

使用特权

评论回复
6
工程师犹饿死|  楼主 | 2022-3-24 15:12 | 只看该作者
2、在函数仅仅只是要用到某个全局变量,而无需改动时,就将全局变量通过形参传递进来,变成局部变量使用。并在定义时使用const。这是避免全局变量在函数中被意外的改动。如在判断语句中将'=='误写成'='。这时首先因为在函数内的是局部变量所以即使出错也不会影响全局变量和其他函数或模块。其次因为定义的是const类型,当发生意外改动时,编译器会发出错误信息或警报,这也就是让你的程序自动纠错。这种情况应发生在对模块内的全局变量的调用,而对于其他模块的全局变量应通过模块接口调用。

使用特权

评论回复
7
工程师犹饿死|  楼主 | 2022-3-24 15:13 | 只看该作者
3、中断中使用的全局变量,在其他函数内要使用时一定要进入临界区(也就是关闭中断)。如果是使用操作系统的话,则在所有使用到与其他任务共享的全局变量的地方都要进临界区。

使用特权

评论回复
8
工程师犹饿死|  楼主 | 2022-3-24 15:13 | 只看该作者
4、对于仅在某个函数内使用的全局变量就用静态局部变量替代。

使用特权

评论回复
9
工程师犹饿死|  楼主 | 2022-3-24 15:14 | 只看该作者
5、应尽量避免直接操作或调用其他模块的变量。需要调用或修改其他模块的变量时,可以通过那个模块提供的操作接口函数进行操作。比如某个模块有一个全局的计数值Count,则可以提供函数接口:GetCount()、SetCount()或ReloadCount()。这样一来,其他模块要调用Count时就通过这些指定的函数接口。这样可以降低模块间的耦合程度。另外如果嫌接口用函数实现过于复杂或是影响速度,则可以用带参数的宏定义实现。

使用特权

评论回复
10
工程师犹饿死|  楼主 | 2022-3-24 15:15 | 只看该作者
如:
#define GetCount()  (Count)
#define SetCount(num) (Count=(num))
#define ReloadCount() (Count=0)
这样,使用时与函数调用一样,但没有函数调用时的开销。另外,功能变化是可以将宏定义改成函数,而无需改动调用语句。

使用特权

评论回复
11
工程师犹饿死|  楼主 | 2022-3-24 15:15 | 只看该作者
6、每个模块内的全局变量应该用静态全局变量,一来其他模块无法直接调用这些变量;二来命名空间不会重叠,在实现一个模块时无需考虑某个全局变量的名称是否已被其他模块使用。如在模块A中定义静态变量 static unsigned char Count;在B模块中,仍可以定义静态变量 static unsigned char Count;这两个Count不会相互影响,但是如果定义的是全局变量则不然。

使用特权

评论回复
12
工程师犹饿死|  楼主 | 2022-3-24 15:16 | 只看该作者
7、模块间的耦合性就是指当修改其中某个模块时会影起其他模块运行结果发生不可预料的变化。如果模块间是通过直接调用对方内部的全局变量来实现交互,则两个模块间的耦合性将会变得复杂而且难以控制。而模块间的交互通过一个统一的接口函数来实现,使得模块都无需知道对方模块内部的具体实现(这也就是面向对象里面的封装)。当某个模块内的实现发生改写或升级时,只要确保模块接口不变即可。这就大大降低两模块间的耦合。在规划设计模块时就应认真考虑好模块的接口,以及与其他模块的调用关系。所以要记住编程原则:针对接口编程,而不是针对实现。

使用特权

评论回复
13
工程师犹饿死|  楼主 | 2022-3-24 15:16 | 只看该作者
8、函数的功能要单一,也就是高内聚性。如果某个函数需要调用很多个全局变量,则说明函数的内聚性不够高,可以考虑将函数拆分成几个功能更单一的函数。

使用特权

评论回复
14
工程师犹饿死|  楼主 | 2022-3-24 15:17 | 只看该作者
9、要记住编程是一种平衡的艺术。没有程序可以做到十全十美,省空间、速度快、易扩展、易维护、低耦合、功能强大这些是不可能同时兼得的。如要提高程序的可维护性,就必须在性能方面(空间和速度)做出一点牺牲。至于如何平衡,就要看项目的具体要求了。
这是很有用的,有很多东西也许理解不是很深刻,待更。

使用特权

评论回复
15
工程师犹饿死|  楼主 | 2022-3-24 15:18 | 只看该作者

使用特权

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

本版积分规则

75

主题

886

帖子

1

粉丝