搜索
bottom↓
回复: 12

探讨一个队列中的volatile的问题

[复制链接]

出0入0汤圆

发表于 2013-6-29 13:11:49 | 显示全部楼层 |阅读模式
日前,用IAR在stm32上写了一个串口程序。实现的功能是:把要发送的数据入队列。在串口的发送中断里再出队列。下面我描述问题所在。

/**************************   程序   *****************************************/
#define QueSize 500
static volatile unsigned char QueBuf[QueSize];
static volatile unsigned int head=0;
static volatile unsigned int tail=0;

/**************************************
入队函数
*************************************/
unsigned char EnQueue(void)
{
    if(((head+1)%QueSize)==tail)          //-------(1)
    {
        return 0;
    }
    else
    {
        head++;
        head%=QueSize;
        QueBuf[head]=x;
       return 1;
    }
}

/*************************************
在串口发送空中断里出栈,中断函数这么写的。
*************************************/
void USART2_IRQHandler(void)
{
        if(USART_GetITStatus(USART2, USART_IT_TXE) != RESET)  //发送数据寄存器空
        {        
                if(tail!=head)              //-----(2)
                {        
                        tail++;
                        tail%=QueSize;
                        USART_SendData(USART2,QueBuf[tail] );
                }
                else
                {
                        USART_ITConfig(USART2, USART_IT_TXE, DISABLE);        
                }
        }
}


/*********************************************************************
过程,编译的时候会出警告,分别对应1行和2行
Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
忽略
下载,运行,出错,截图如图1
*********************************************************************/
/*********************************************************************
解决方法1,改写入队函数,编译的时候还是提示行2处有警告,忽略,下载程序,问题解决,结果如图2
*********************************************************************/
/**************************************
入队函数
*************************************/
unsigned char EnQueue(void)
{
   u32 temp=head;
    if(((temp+1)%QueSize)==tail)
    {
        return 0;
    }
    else
    {
        temp++;
        temp%=QueSize;
        QueBuf[temp]=x;
        head=temp;
        return 1;
    }
}

/********************************************************************
解决方法2:不动原来的入队函数,而改写发送中断里的出队语句,编译,行1提示警告,忽略,运行,结果正常,如图2
********************************************************************/
void USART2_IRQHandler(void)
{
        if(USART_GetITStatus(USART2, USART_IT_TXE) != RESET)  //发送数据寄存器空
        {        
               u32 temp=tail;
               if(temp!=head)
              {   
                      temp++;
                      temp%=QueSize;
                      USART_SendData(USART2,QueBuf[temp] );
                      tail=temp;
              }
              else
             {
                     USART_ITConfig(USART2, USART_IT_TXE, DISABLE);   
             }   
        }
}

/*************************************************************
解决方法3,同时改写入队和出队语句,编译没有警告,运行结果正常如图2
**************************************************************//

/**************************************************************
谁能解释这是为啥?
***************************************************************/


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-29 13:16:53 | 显示全部楼层
本帖最后由 yaoyyie2003 于 2013-6-29 13:22 编辑

AR编译的时候,出现警告如下:

Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement.

Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
EW targets: All
EW component: C/C++ compiler
Keywords: C code, keywords, SFR, UART
Last update: January 14, 2009

Background
The warning [Pa082] is issued if 2 (or more) of the variables in a C-statment are volatile. The message text "...order of volatile accesses is undefined..." is the central information, i.e. the compiler (following the ANSII standard) can access the variables in an order that is not defined.

Is this a problem , or not...
...well that depends on your application.
Volatile is (typically) used for variables that are accessed from several threads in the application, and for Special Function Register that are the connection in/out of the chip. So you must study the volatile variables that are present in this C-statement and from the usage of these volatile variables decide if you must make a change or not.

How to change
The change is to break up the C-statement, so that each new C-statement holds only one access to a volatile variable.
In this small example the variables internalChannelSelect and ADC are volatile. If so the following C-source will issue the "Warning[Pa082]".

intAnalogSample[internalChannelSelect] = ADC[internalChannelSelect] ;

Then the following change will make the C-source predictable.

i = internalChannelSelect;
k = ADC;
intAnalogSample = k;



原因是一条语句中出现2个或2个以上的volatile变量。可以利用中间变量将其修改。也可以如下设置



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-29 13:32:27 | 显示全部楼层
/******************************************
另一种尝试,写成下面的形式,编译没有警告,运行,结果如图4
******************************************/
/**************************************
入队函数
*************************************/
unsigned char EnQueue(void)
{
        u32 temp=head;
        if(((temp+1)%QueSize)==tail)
        {
                //队列满
                return 0;//返回错误标志
        }
        else
        {
                head++;
                head%=QueSize;
                QueBuf[head]=x;
                return 1;
        }
}

/***************************************
出队中断
***************************************/
void USART2_IRQHandler(void)
{
        if(USART_GetITStatus(USART2, USART_IT_TXE) != RESET)  //发送数据寄存器空
        {        
                u32 temp=tail;
                if(temp!=head)
                {       
                        tail++;
                        tail%=QueSize;
                        USART_SendData(USART2,QueBuf[tail] );
                }
                else
                {
                        USART_ITConfig(USART2, USART_IT_TXE, DISABLE);       
                }
        }
}









本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-6-29 13:39:00 | 显示全部楼层
  1.        u32 temp=head;
  2.         if(((temp+1)%QueSize)==tail)
复制代码
数据出错是正常的,因为中断程序可能会改编tail和head的值,也就是这两句之间如果出中断,自然就出错了。

出0入0汤圆

发表于 2013-6-29 13:11:50 | 显示全部楼层
回到你刚开始有错误的那个程序,然后将全局变量的volatile修饰符去掉,另外有些语句应该放在一条语句里面,比如下面的:
head++;
head%=QueSize;

这两条语句之间可能会被打断,它可能首先会将head加1,然后更新head到它在内存中的区域,然后再取出来执行下面的语句。无论从效率还是安全性来说,都不是最好的。改成下面更好:
head=(head+1)%QueSize;
这条语句首先执行的是head+1,但是并没有将增加后的值更新到head所在的内存,而只是一个局部的变量值,然后用这个局部的变量值对QueSize求余并将结果给head,这样前后head的值只更新一次,而不是有像上面那样前后更新两次而有可能中间被中断的情况存在。
这样试一下,程序应该也会运行正确。

出0入0汤圆

 楼主| 发表于 2013-6-29 13:47:14 | 显示全部楼层
i55x 发表于 2013-6-29 13:39
数据出错是正常的,因为中断程序可能会改编tail和head的值,也就是这两句之间如果出中断,自然就出错了。 ...

楼上正解,分送给你!

出0入0汤圆

 楼主| 发表于 2013-6-29 13:51:47 | 显示全部楼层
本帖最后由 yaoyyie2003 于 2013-6-29 13:57 编辑

2楼和5楼讲的都对。2楼讲的是在入队函数中使用可重入的局部变量替代全局变量,这样就不怕被中断函数打断。5楼的提法是在入队函数中直接禁用发送中断,避免被打断。都可行。谢谢两位

出0入0汤圆

发表于 2013-6-29 13:56:42 | 显示全部楼层
yaoyyie2003 发表于 2013-6-29 13:51
给错了分,随后另外给分5楼,5楼的答案是最佳答案。验证通过。

积分还给你吧,不过找了一下,没发现哪里有归还积分的,不知道这里有没有这个功能?没有的话,我开个帖子赏20分,到时候就可以奖赏的方式还给你了。

出0入0汤圆

 楼主| 发表于 2013-6-29 13:59:19 | 显示全部楼层
Goselff 发表于 2013-6-29 13:56
积分还给你吧,不过找了一下,没发现哪里有归还积分的,不知道这里有没有这个功能?没有的话,我开个帖子 ...

不用,你说的方法我想了,是对的。在入队函数中使用局部变量,可防止被打断。是我没看懂,不好意思哈

出0入0汤圆

发表于 2013-6-29 14:13:54 | 显示全部楼层
yaoyyie2003 发表于 2013-6-29 13:59
不用,你说的方法我想了,是对的。在入队函数中使用局部变量,可防止被打断。是我没看懂,不好意思哈 ...

好吧,就占一次便宜吧。不过真是冲着volatile来的,我发现很多人喜欢用volatile,甚至什么变量都加上,也不管有用没有,需要不需要。我觉得这玩意不需要多用,也不能乱用。用得不恰当,轻者影响效率(虽然很多时候并不明显),重者程序出问题。我写的程序一般在几十K~两三百K(bin)范围,但是印象中从来都没用过volatile,程序也从来没出过问题。在精心规划结构下,到现在还没碰到要用volatile的情况。

出0入0汤圆

 楼主| 发表于 2013-6-29 14:22:19 | 显示全部楼层
Goselff 发表于 2013-6-29 14:13
好吧,就占一次便宜吧。不过真是冲着volatile来的,我发现很多人喜欢用volatile,甚至什么变量都加上,也 ...

有时候我写程序写糊涂了,尽钻牛角,你们一说我就想起来了。软件的架构、封装、接口有很多学问。

出0入0汤圆

发表于 2013-6-29 14:31:29 | 显示全部楼层
跟编译器有关系,楼猪的问题代码我在GCC上面验证都是没问题的(现在这个电脑没装MDK,但是楼猪的警告我在IAR/MDK兼容工程上面确实只有IAR才见过),可能是IAR优化的太狠,具体差异还是要看汇编代码才能验证出来。

出0入0汤圆

发表于 2013-7-6 18:22:35 | 显示全部楼层
改成
head=(head+1)%QueSize;
也不安全,head还是要从内存加载到寄存器进行运算,然后再放回内存中,这期间还是会被中断的,不过效率是比写成两句高一点。
没记错的话,cortex-m3的LOAD命令都可以被中断,为了安全还是老老实实用临界保护吧,安全与效率不可兼得,汗~
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 01:28

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表