刚才看到一位很牛的师兄写的一篇日志中提到了Keil C51中using这个关键字的用法,粗心的我本来一直都没有留意它是用来干嘛的(因为我一般看见它都是在中断服务函数的定义开头处,好像没有了它也可以中断呀,所以才没怎么管),然而在日志中有看到这个关键字,所以也考究了一下,突然发现,原来这个东东和我最近在帮一个同学调的一个程序的时候突然遇到一个很怪的问题是有关系的,而且就是因为它才搞得程序莫明奇妙的出错(因为编译通过了,看起来也没什么错误,按C语言的逻辑分析也分析不出个什么所以然来,所以才怪)。
后来调试了好久,甚至到http://www.51hei.com/keil%CF%C2%D4%D8.html 这里下载了好几个版本的keil,反汇编代码看了N次,基本各个版本出来的结果都差不多,不知情的情况下,推断出是Keil C51的编译器问题,以为Keil C51的编译器的变量空间分配出问题了,R7本来是用来作参数传递的,却也被编译器分配用来在主函数中被用来作循环变量,结果就是循环变量的汇编判断指令CJNE指令执行前,R7的值(也就是循环变量)被改变了,导致判断R7总是不能达到循环设置的值,就不断地在循环,也就致使按键扫描去抖动这个延时循环无法完成,就导致延时后的第二次判断无法进行,自然按键下来也就没反应了。后来尝试着关了定时器中断,又可以了,那就可以肯定是在执行CJNE前,R7的值被改变了,就是因为跳到了中断,然后中断函数执行的过程中,R7值被改变了(因为中断函数中还调用了其它的函数,而R7后来被分析反汇编代码时发现是被用作参数传递的),中断执行完后R7的值当然不正确啦!
得出推断后我的解决办法就是:让C51编译好之后手动修改汇编的指令,不要直接使用寄存器来作循环变量,而是使用能实现间接寻地址的R1进行间接寻址(指向0x77)的方式,终于把问题解决了,因为0x77在执行中断函数的过程中没被修改,CJNE语句能正常判断了,循环能完成了,按键也能扫下来了,问题也可以解决了。后来我又想到用_at_关键字直接指定循环变量的分配地址,这样的话就不用手动改汇编指令了,编译器直接就是编译成使用间接寻址的,这也算解决了。但还是有个疑问,为什么编译器会不够聪明地把R7的空间分配错了呢?一直在疑问。。。。。。奇怪,怎么编译器会出这种问题?
直到我刚才考究过了using的用法之后,才发现原来自己的推断是错的,结论的总方向还是对的,只是问题并不在编译器身上,真正的问题根源如下:
1.我的同学写的那个程序,用到了定时器0,它的中断服务函数定义是这样写的:
void t0(void) interrupt 1 using 0
{
........//一大串大代码,其中包括调用了其他函数,用到了R7作参数传递
}
这里就是加了个using 0,而问题就出在加了这个using 0,为什么?且听我的理解:
using关键字的作用就是指定某个函数在执行时切换寄存器组的:
51中有四个寄存器组,每个组有R0-R7这8个寄存器,用于CPU的数据处理,一般主函数main()默认使用第0组,因为PSW寄存器的初始值的第3、4位是00嘛,即默认指定了第0组。using 0就是指定在执行函数时切换为使用第0组,不加using关键字的话,一般都默认使用第0组,但这样的话在调用其他函数(包括中断服务函数)的时候,就会加入一些压栈指令以保护原来的R0-R7寄存器的(这样的话,程序执行会效率会低一点,因为会产生很多个指令是用来压栈出栈的),照这样说,只是加了using 0,使用的也是第0组又不是其它的组,程序就不应该有问题了吧,但是就是因为加了using 0,编译了一次,才发现在定时器中断函数t0()的入口中并没有发现把R0-R7的代码压入栈呀,就是说没有保护好R7呀,那当然就是在执行完之后回来R7不能回复原来的值啦,接下来的事情。。。。我就不说啦,这就是问题的根源,去掉using 0就可以了,编译器就自动帮你将R0-R7压栈,手动加了using 0,就是让编译器以为之前用的并不是第0组,而现在执行这个中断函数时就切换到第0组,而省去了将R0-R7这8个寄存器压入栈的指令了,这样虽然看起来是快了,然而对于这个程序来说却是致命的问题!!!因为根本没有保护好R0-R7,而没有保护R7并不是我预期发生的!!!这不是编译器的问题,是自己没有了解好、用好using的问题啊!还有,其实也可以在编译器选项里有选项来硬性规定编译器统一不直接使用寄存器而是使用间接寻址的办法来改变循环变量分配的地址的,或者使用
#pragma NOAREGS
定义函数
#pragma AREGS
来规定某个函数的是这样子。
当然,这样的规定和使用using本身并不冲突,只是使用了这个关键字后就可能会间接地产生一系列的问题,让人郁闷了这么久,其实本来如果有详细地看整个程序的反汇编代码,或许当时就会发现发现少了那段压栈指令了,而不是说推断为编译器分配空间的问题了。。。。。。。。希望我的这次教训对各位有所帮助!!!
另外,using的用法,其实就是手动指定函数使用的寄存器组,用得不好,如果在中断里还有调用其它函数,用得不好会出现函数传递出错的,不信可以反汇编看看,建议如果对这个关键字用法和C51的结构及汇编不熟的话,请还是让C51编译器帮你好了,不要胡乱使用,因为会比较容易出错的,要切记哦!!!其实用using关键到底对在编译后会造成什么影响,建议自己亲自去查看汇编程序。。。
关键字:Keil C51 using 关键字
引用地址:
关于Keil C51中using关键字的使用心得
推荐阅读最新更新时间:2024-03-16 13:12
课程设计:矩阵键盘(c51,使用扫描法实现)
//可以比较一下和以前发表的一篇用反转法实现的不同(感觉一下那个更简单) //这种方法是用只用P3口的前高四位来比较,看改变了么(由于比较仓促,没有proteus仿真,不过这是个比较经典的方法,网上有很多这个代码,源自郭天祥的视频和书籍) #include reg52.h #define uchar unsigned char #define uint unsigned int sbit wela=P2^7; sbit dula=P2^6; uchar num,num1; unsigned char code table ={0x3f,0x06,0x5b,0x4f,0x66,0x6d, 0x7d,0x07,0
[单片机]
Keil(MDK-ARM)系列教程(四)_工程目标选项配置(Ⅱ)
Ⅱ、C/C++选项 这后面五项中,C/C++选项最为重要,因此部分功能需要重点强调。看选项标题“C/C++”,针对的主要就是C/C++,和后一个选项“Asm”有类似之处。 第1处:预处理(Preprocessor Symbols) 这里主要就是预定义功能,相当于在程序中的#define xxxx。我上面预定义STM32F10X_HD,在stm32f10x.h文件中就不用定义了。 第2处:语言代码生成(Language / Code Generation) Language/code Generation语言代码生成,可以理解成编译、链接到最后生成代码。这部分功能对于代码优化比较重要,初学者可以不用过多理解,对代码大小、运行
[单片机]
[C51代码]LCD1602驱动
/************************************************LCD1602.c*************************************************************************/ #include Atmel/AT89X51.h #include delay.h #include lcd1602.h /*************声明变量****************/ uchar co de table ={0x08,0x0f,0x12,0x0f,0x0a,0x1f,0x02,0x02, //年 0x1f,0x11
[单片机]
C51编程9-数码管(显示)
根据项目需求可以用I/O外部上拉,用来驱动数码管;也可以74HC138(38译码器)+74HC245(8路信号收发器)驱动数码管。本文会以后者为数码管驱动电路,在代码上实现数码管的显示。 驱动电路: 电路讲解: 1)数码管元件采用共阴数码管,如果需要某位数码管被点亮,位选引脚需要被拉成电平; 2)八个数码管有8个位选引脚,8个段选引脚。为了将节省I/O口,使用74HC138将3位(CBA)选择,转换为8位二值代码,例如CBA值为000时,Y0输出低电平,其余为高电平,由于Y0连接到数码1位选引脚(图中的LED1网络标号),数码管1被选中。 3)74HC138控制的是位选引脚,而74HC245控制的是段选引脚
[单片机]
C51单片机中断定义
C51函数声明对ANSI C作了扩展,具体包括: 1. 中断函数声明: 中断声明方法如下: void serial_ISR () interrupt 4 { /* ISR */ } 为提高代码的容错能力,在没用到的中断入口处生成iret语句,定义没用到的中断。 /* define not used interrupt, so generate IRET in their entrance */ void extern0_ISR() interrupt 0{} /* not used */ void timer0_ISR () interrupt 1{} /* not used */ void extern1_ISR()
[单片机]
keil中StartUp.A51的重要性
最近要用STC单片机写个程序,但STC在KEIL的单片机库中没有,就随便找了个代替,但是刚好找到的keil没有添加StartUp.A51文件,刚开始时候写程序调程序也没什么,挺正常。但后来越来越奇怪,经常上电时出错,找了很久才发现有个变量没有初始化为0。忽然发觉是不是keil上电时没有帮我清空内存空间!一看才知道没有StartUp.A51文件。加上后,设置内部空间及外部空间地址,一切正常。足足浪费了我一天的时间。 由于CPU和程序启动代码文件STARTUP.a51的重要性,一些8051派生的CPU产品要求初始化CPU来满足设计中的相应的硬件,因此,有时候用户需要对STARTUP.a51进行修改,所以进行注释一下: ;--------
[单片机]
从0开始学Keil下的S3C2440裸机开发-3使用外部NORFLASH+内部RAM
关于S3C2440启动方式: 程序下载到NORFLASH中后,选择从NORFLASH启动,自动执行,NORFLASH中执行代码,内部RAM分配变量;NORFLASH本身地址映射为0X0000000,内存选用内部RAM。 程序下载到NANDFLASH中后,选择从NANDFLASH启动,会自动搬运4K到RAM中运行。 1、新增工程配置组. 2设置分散加载组 3使用命令生成.bin文件。 4更改S3C2440.S文件: (1)均为: IMPORT ||Image ERROM1ERROM1 RO Length||IMPORT||ImageLength||IMPORT||Image RW_IR
[单片机]
Keil编译后Code RO Data Rw Data ZI的含义
Code为程序代码部分 RO-data 表示 程序定义的常量 const temp; RW-data 表示 已初始化的全局变量 ZI-data 表示 未初始化的全局变量 初始化时RW-data从flash拷贝到RAM 生成的map文件位于list文件夹下 (KEIL) Total RO Size (Code + RO Data) 18568 ( 18.13kB) Total RW Size (RW Data + ZI Data) 4212 ( 4.11kB) Total ROM Size (Code + RO Data + RW Data) 18828 (
[单片机]