• volatile关键字的使用


    先来看我不久前遇到的avr studio 6.0中的一个问题:

    我手上有一块atmega128开发板,我想要通过设置定时器1来实现间隔1s控制LED灯呈现不同花样的效果,于是,我写下了下面的代码:

    /***********************************
    描述:利用定时器1进行1s的计数,时间到则led灯变换一种花样
    定时器1可以作为16位加法计数器,最大计数值2^16-1=65535,板上外部晶振提供时钟为8MHz
    可以设置为8分频,所以计数器加1就是1us,计数器初值设为(65535-10000),就是计10ms,
    10ms到,time_count加1,加到100,说明大约是1s,就把PORTA口输出变化。
    ************************************/
    
    
    #include "common.h"
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #define BIT(n)  (1<<n) 
    typedef unsigned char   uchar;
    uchar time_count=1;
    uchar i=0;
    void timer1_init();
    uchar my_led[5]=
    {
    	0x00,0x01,0x02,0x03,0xf3	
    };
    
    int main(void)
    {
    	led_init();  //初始化板上的LED灯模块
    	timer1_init();   //初始化定时器1
    	sei();      //开中断
        while(1)
        {
    		
    		if (time_count>=100)
    		{
    			PORTA = my_led[i++];
    			time_count=1;
    			if (i>4)
    			{
    				i=0;
    			}
    		}	
        }
    }
    //初始化定时器1
    void timer1_init()
    {
    	TCNT1H = (65535-10000)/256;
    	TCNT1L = (65535-10000)%256;
    	TIMSK |= BIT(TOIE1);     //设置中断
    	TCCR1B |= BIT(CS11);	//系统时钟8分频	  
    }
    
    
    //中断服务
    ISR(TIMER1_OVF_vect)
    {
    	cli();
    	TCNT1H = (65535-10000)/256;
    	TCNT1L = (65535-10000)%256;
    	time_count++;
    	sei();
    }
    



    但是下载到开发板后,我发现LED灯根本没有变化····

    但是,如果我把程序改成下面那样呢?

    /***********************************
    描述:利用定时器1进行1s的计数,时间到则led灯变换一种花样
    定时器1可以作为16位加法计数器,最大计数值2^16-1=65535,板上外部晶振提供时钟为8MHz
    可以设置为8分频,所以计数器加1就是1us,计数器初值设为(65535-10000),就是计10ms,
    10ms到,time_count加1,加到100,说明大约是1s,就把PORTA口输出变化。
    ************************************/
    #define F_CPU 800000UL
    #define BIT(n) (1<<n)
    
    #include "common.h"
    #include <avr/io.h>
    #include <avr/interrupt.h>
    typedef unsigned char   uchar;
    uchar time_count=1;
    uchar i=0;
    void timer1_init();
    uchar my_led[5]=
    {
            0x00,0x01,0x02,0x03,0xf3        
    };
    
    int main(void)
    {
            led_init();  //初始化板上的LED灯模块
            timer1_init();   //初始化定时器1
            sei();      //开中断
        while(1)
        {
                    /*这次注释掉
                    if (time_count>=100)
                    {
                            PORTA = my_led[i++];
                            time_count=1;
                            if (i>4)
                            {
                                    i=0;
                            }
                    }
                    */
                    
        }
    }
    //初始化定时器1
    void timer1_init()
    {
            TCNT1H = (65535-10000)/256;
            TCNT1L = (65535-10000)%256;
            TIMSK |= BIT(TOIE1);     //设置中断
            TCCR1B |= BIT(CS11);        //系统时钟8分频          
    }
    
    
    //中断服务
    ISR(TIMER1_OVF_vect)
    {
            cli();
            TCNT1H = (65535-10000)/256;
            TCNT1L = (65535-10000)%256;
            time_count++;
            
                    //这里不一样了
            if (time_count>=100)
            {
                    PORTA = my_led[i++];
                    time_count=1;
                    if (i>4)
                    {
                            i=0;
                    }
            }
            
            sei();
    }


    竟然成功了···只要把对time_count的自增和判断放到一个函数里面,竟然就行了···这是什么原因?

    经过一番搜索,终于找到了问题的原因,那便是volatile关键字。之所以第一个代码没有运行成功,是因为avr studio 6.0的编译器在编译过程中进行了代码优化,为了节省频繁取内存操作的时间,把time_count变量写入了寄存器中,之后虽然中断程序在不断的修改time_count的值,但是可怜的main函数里的while循环却是一直在读取“另外一个”time_count的值,自然就永远到不了所要的100了,所以只会在while循环里不停地运行,却无法进入分支语句进行LED的控制。为了能够让第一个代码段运行成功,当然可以改变编译器的优化选项,但是这毕竟不是长久之计,因为很有可能你的编译器和他人的编译器优化选项是不同的。这个时候就要用到volatile关键字了。

    volatile(易变的)提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象,也就是上面提到的那种问题了。

    一般来收,volatile关键字主要用在以下几个地方:

    1.中断服务程序中修改的供其它程序检测的变量要加上volatile关键字,比如我遇到的这种情况;

    2.多任务环境下各任务间共享的标志变量要加volatile;

    3.存储器映射的硬件寄存器通常也要加volatile。

    要注意的是,这些情况还要考虑到不同进程之间共同修改这个变量带来的问题。在1的情况下,我们可以用开关中断来控制,也就是说,在中断服务程序A修改变量值的时候,暂时关闭中断,防止其他中断发生;2中可以禁止任务调度,总之要确保在某一个时刻,只有一个进程在修改这个值。

    所以要想解决上面的问题,我们只需要在声明time_count的时候将它的类型声明为volatile就可以了~~~

    也就是

    volatile  uchar time_count=1;


     

  • 相关阅读:
    Android 入门到精通 (Index)
    负载平衡与冗余备份方案概述
    Android 程序组件交互分析
    复制时保留文件的目录结构
    notepad++中设置tab缩进的宽度
    scws
    php 将字符(包括汉字) 转换成16进制 (apache access log 中文显示16进制码)
    批量修改完整版本
    根据端口号查进程
    php性能优化
  • 原文地址:https://www.cnblogs.com/xmfbit/p/3872200.html
Copyright © 2020-2023  润新知