求助,为什么在死循环里的全局变量赋值在中断里不起作用,附程序
全局变量TEMPERATURE在死循环里赋值了,但是10ms定时中断里的全局变量运算却是始终以所赋初值进行运算,帮帮我吧!#include <avr/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/iom32.h>
// Global variables
unsigned charFLAG = 0x00 , TEMPERATURE = 0x19 , TIME5S = 0x00;
unsigned int COUNT10MS = 0x0000 , COUNT5S = 0x0000;
//10ms定时中断服务程序
SIGNAL ( SIG_OVERFLOW0 )
{
if ( FLAG == 0x00 )
{
switch ( TEMPERATURE / 10 )//显示温度值十位
{
case 0x00:
PORTC |= 0x40;
PORTD = 0x01;
break;
case 0x01:
PORTC |= 0x40;
PORTD = 0x67;
break;
case 0x02:
PORTC |= 0x40;
PORTD = 0x12;
break;
case 0x03:
PORTC |= 0x40;
PORTD = 0x22;
break;
case 0x04:
PORTC |= 0x40;
PORTD = 0x64;
break;
case 0x05:
PORTC |= 0x40;
PORTD = 0x28;
break;
case 0x06:
PORTC |= 0x40;
PORTD = 0x08;
break;
case 0x07:
PORTC |= 0x40;
PORTD = 0x63;
break;
case 0x08:
PORTC |= 0x40;
PORTD = 0x00;
break;
case 0x09:
PORTC |= 0x40;
PORTD = 0x20;
break;
default:
break;
}
FLAG = 0x01;
}
else
{
switch ( TEMPERATURE % 10 )//显示温度值个位
{
case 0x00:
PORTC &= 0xBF;
PORTD = 0x81;
break;
case 0x01:
PORTC &= 0xBF;
PORTD = 0xE7;
break;
case 0x02:
PORTC &= 0xBF;
PORTD = 0x92;
break;
case 0x03:
PORTC &= 0xBF;
PORTD = 0xA2;
break;
case 0x04:
PORTC &= 0xBF;
PORTD = 0xE4;
break;
case 0x05:
PORTC &= 0xBF;
PORTD = 0xA8;
break;
case 0x06:
PORTC &= 0xBF;
PORTD = 0x88;
break;
case 0x07:
PORTC &= 0xBF;
PORTD = 0xE3;
break;
case 0x08:
PORTC &= 0xBF;
PORTD = 0x80;
break;
case 0x09:
PORTC &= 0xBF;
PORTD = 0xA0;
break;
default:
break;
}
FLAG = 0x00;
}
COUNT10MS ++;
if ( COUNT10MS == 0x01F4 )
{
COUNT10MS = 0x0000;
TIME5S ++;
if ( TIME5S == 0x03 )
TIME5S = 0x00;
}
TCNT0 = 0x93;//147,10ms=0.01s=1*(255-147)*1024/11059200
}
//主程序
int main (void)
{
unsigned char num = 0;
cli();
//ADMUX=0xC0;//0b11000000,片内基准电压2.56V,右对齐,单端输入
//ADCSRA=0xcf;//0b11001111,ADC使能,ADC开始转换,中断使能,128分频.
PORTB = 0x11;//0001 0001
DDRB = 0xEF;//1110 1111
PORTC = 0xFF;//1111 1111
DDRC = 0xEA;//1110 1010
PORTD = 0x80;//1000 0000
DDRD = 0xFF;//1111 1111
TCCR0 = 0x00;//T0停止计数
TCNT0 = 0x93;//147,10ms=0.01s=1*(255-147)*1024/11059200
TCCR0 = 0x05;//开始计数,普通模式,1024预分频
TIMSK = 0x01;//允许T0溢出中断
sei();
for(;;)
{
if ((PINB & 0x10) == 0x00)
{
cli();
switch ( num )
{
case 0:
PORTB = 0x1F;
PORTB &= 0xF7;//1111 0111
TEMPERATURE = 98;
num = 1;
break;
case 1:
PORTB = 0x1F;
PORTB &= 0xFB;//1111 1011
TEMPERATURE = 76;
num = 2;
break;
case 2:
PORTB = 0x1F;
PORTB &= 0xFD;//1111 1101
TEMPERATURE = 54;
num = 0;
break;
default:
break;
}
sei();
while ((PINB & 0x10) == 0x00);
}
if ((PINC & 0x10) == 0x00)
{
PORTC &= 0xDF;//1101 1111
PORTB |= 0x20;//0010 0000
while ((PINC & 0x10) == 0x00);
}
if ((PINC & 0x04) == 0x00)
{
PORTC &= 0xF7;//1111 0111
PORTB |= 0x40;//0100 0000
while((PINC&0x04)==0x00);
}
if ((PINC & 0x01) == 0x00)
{
PORTC &= 0xFD;//1111 1101
PORTB |= 0x80;//1000 0000
while ((PINC & 0x01) == 0x00);
}
if (TIME5S == 0x01)
{
//cli();
TEMPERATURE = 32;
//sei();
}
_delay_ms ( 100 );
}
return 0;
} 中断中使用到的变量定义时加volatile前缀 可以看看编译出来的汇编程序 到底是怎么处理TEMPERATURE变量了 楼上正解 谢谢“lanshuitianxia 黑蚂蚁”,果真是缺少了volatile前缀,改成这样就OK了,再一次感谢!
//全局变量定义
volatile unsigned char TEMPERATURE = 0x19 ;
unsigned charFLAG = 0x00 , TIME5S = 0x00 ;
unsigned int COUNT10MS = 0x0000 , COUNT5S = 0x0000 ;
顺带附上刚找的类型定义关键字的详细说明,希望其他同好不要再犯我这样的低级错误。
(1)auto
这个这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。这个关键字不怎么多写,因为所有的变量默认就是auto的。
(2)register
这个关键字命令编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率。
(3)static
常见的两种用途:
1>统计函数被调用的次数;
2>减少局部数组建立和赋值的开销.变量的建立和赋值是需要一定的处理器开销的,特别是数组等含有较多元素的存储类型。在一些含有较多的变量并且被经常调用的函数中,可以将一些数组声明为static类型,以减少建立或者初始化这些变量的开销.
详细说明:
1>、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2>、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
3>当static用来修饰全局变量时,它就改变了全局变量的作用域,使其不能被别的程序extern,限制在了当前文件里,但是没有改变其存放位置,还是在全局静态储存区。
使用注意:
1>若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
2>若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
3>设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题(只要输入数据相同就应产生相同的输出)。
(4)const
被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。它可以修饰函数的参数、返回值,甚至函数的定义体。
作用:
1>修饰输入参数
a.对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
b.对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。
2>用const修饰函数的返回值
a.如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。
如对于: const char * GetString(void);
如下语句将出现编译错误:
char *str = GetString();//cannot convert from 'const char *' to 'char *';
正确的用法是:
const char *str = GetString();
b.如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。 如不要把函数int GetInt(void) 写成const int GetInt(void)。
3>const成员函数的声明中,const关键字只能放在函数声明的尾部,表示该类成员不修改对象.
说明:
const type m; //修饰m为不可改变
示例:
typedef char * pStr; //新的类型pStr;
char string = "abc";
const char *p1 = string;
p1++; //正确,上边修饰的是*p1,p1可变
const pStr p2 = string;
p2++; //错误,上边修饰的是p2,p2不可变,*p2可变
同理,const修饰指针时用此原则判断就不会混淆了。
const int *value; //*value不可变,value可变
int* const value; //value不可变,*value可变
const (int *) value; //(int *)是一种type,value不可变,*value可变
//逻辑上这样理解,编译不能通过,需要tydef int* NewType;
const int* const value;//*value,value都不可变
(5)volatile
表明某个变量的值可能在外部被改变,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。它可以适用于基础类型如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者类的所有成员都会被视为volatile.
该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程。
简单示例:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterpret_cast(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
该线程启动时将intSignal 置为2,然后循环等待直到intSignal 为1 时退出。显然intSignal的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。C 编译器是没有线程概念的,这时候就需要用到volatile。volatile 的本意是指:这个值可能会在当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:
label:
mov ax,signal
if(ax!=1)
goto label
注意:一个参数既可以是const同时是volatile,是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
(6)extern
extern 意为“外来的”···它的作用在于告诉编译器:有这个变量,它可能不存在当前的文件中,但它肯定要存在于工程中的某一个源文件中或者一个Dll的输出中。
下面关于C++的几个关键字是经常和我们打交道的而我们又经常对这些含糊不清的,本文根据自己的学习体会作以总结,以期达到真正理解和活用的目的。
static
l 静态变量作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为0,使用时可改变其值。
l 静态变量或静态函数,即只有本文件内的代码才可访问它,它的名字(变量名或函数名)在其它文件中不可见。
l 在函数体内生成的静态变量它的值也只能维持
int max_so_far( int curr )//求至今(本次调用)为止最大值
{
static int biggest; //该变量保持着每次调用时的最新值,它的有效期等于整个程序的有效期
if( curr > biggest )
biggest = curr;
return biggest;
}
l 在C++类的成员变量被声明为static(称为静态成员变量),意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;而类的静态成员函数也只能访问静态成员(变量或函数)。
l 类的静态成员变量必须在声明它的文件范围内进行初始化才能使用,private类型的也不例外。如,
float SavingsAccount::currentRate = 0.00154;
(注:currentRate是类SavingsAccount的静态成员变量)
register
l 用register声明的变量称着寄存器变量,在可能的情况下会直接存放在机器的寄存器中;但对32位编译器不起作用,当global optimizations(全局优化)开的时候,它会做出选择是否放在自己的寄存器中;不过其它与register关键字有关的其它符号都对32位编译器有效。
auto
l 它是存储类型标识符,表明变量(自动)具有本地范围,块范围的变量声明(如for循环体内的变量声明)默认为auto存储类型。
extern
l 声明变量或函数为外部链接,即该变量或函数名在其它文件中可见。被其修饰的变量(外部变量)是静态分配空间的,即程序开始时分配,结束时释放。用其声明的变量或函数应该在别的文件或同一文件的其它地方定义(实现)。在文件内声明一个变量或函数默认为可被外部使用。
l 在C++中,还可用来指定使用另一语言进行链接,这时需要与特定的转换符一起使用。目前Microsoft C/C++仅支持”C”转换标记,来支持C编译器链接。使用这种情况有两种形式:
u extern “C” 声明语句
u extern “C” { 声明语句块 }
volatile
l 限定一个对象可被外部进程(操作系统、硬件或并发线程等)改变,声明时的语法如下:
int volatile nVint;
这样的声明是不能达到最高效的,因为它们的值随时会改变,系统在需要时会经常读写这个对象的值。 只常用于像中断处理程序之类的异步进程进行内存单元访问。
const
l &n
bsp; const所修饰的对象或变量不能被改变,修饰函数时,该函数不能改变在该函数外面声明的变量也不能调用任何非const函数。在函数的声明与定义时都要加上const,放在函数参数列表的最后一个括号后。
l 在C++中,用const声明一个变量,意味着该变量就是一个带类型的常量,可以代替#define,且比#define多一个类型信息,且它执行内链接,可放在头文件中声明;但在C中,其声明则必须放在源文件(即.C文件)中,在C中const声明一个变量,除了不能改变其值外,它仍是一具变量,如
const int maxarray = 255;
char store_char; //C++中合法,C中不合法
l const修饰指针时要特别注意。例:
char *const aptr = mybuf; // 常量指针
*aptr = 'a'; // Legal
aptr = yourbuf; // Error
const char *bptr = mybuf; // (指针bptr)指向常量数据
*bptr = 'a'; // Error
bptr = yourbuf; // Legal
l const修饰成员函数时不能用于构造和析构函数。 好东东 学习了 解决了 我一直困扰的东西 学习了 解决了 我一直困扰的东西 学习了,volatile主要是为了避免编译器的优化而带来的一些意想不到的问题
页:
[1]