Keil相关
Keil中常见的几种警告,固然,相对于错误的,警告的程度不及错误的严重性,有时候忽略,会出现意想不到的错误。先看看常见的几种错误,分析出来现的原因。
1、Warning 280:’i’:unreferenced local variable
说明 局部变量 i 在函数中未作任何的存取操作
解决方法 消除函数中 i变量的宣告
2、Warning 206:’Music3’:missing function-prototype
说明 Music3( )函数未作宣告 或未作外部宣告 所以无法给其他函数调用
解决方法 将叙述void Music3(void)写在程序的最前端作宣告 如果是其他文件的函数则要写成 extern void Music3(void),即作外部宣告
3、Compling :C:\8051\MANN.C
Error:318:can’t open file ‘beep.h’
说明 在编译 C:\8051\MANN.C 程序过程中 由于 main.c 用了指令#include “beep.h”,但却找不到所致
解决方法 编写一个 beep.h 的包含档并存入到 c:\8051 的工作目录中
4、Compling:C:\8051\LED.C
Error 237:’LedOn’:function already has a body
说明 LedOn( )函数名称重复定义 即有两个以上一样的函数名称
解决方法 修正其中的一个函数名称 使得函数名称都是独立的
5、***WARNING 16:UNCALLED SEGMENT,IGNORED FOR OVERLAY PROCESS
SEGMENT: ?PR?_DELAYX1MS?DELAY
说明 DelayX1ms( )函数未被其它函数调用 也会占用程序记忆体空间
解决方法 去掉 DelayX1ms( )函数 或利用条件编译#if …..#endif,可保留该函数并不编译
6、***WARNING 6 :XDATA SPACE MEMORY OVERLAP
FROM : 0025H
TO: 0025H
说明 外部资料ROM 的 0025H重复定义地址
解决方法 外部资料 ROM的定义如下
Pdata unsigned char XFR_ADC _at_0x25 其中 XFR_ADC 变量的名称为0x25,请检查是否有其它的变量名称也是定义在 0x25 处并修正它
7、WARNING 206:’DelayX1ms’: missing function-prototype
C:\8051\INPUT.C
Error 267 :’DelayX1ms ‘:requires ANSI-style prototype C:\8051\INPUT.C
说明 程序中有调用DelayX1ms 函数 但该函数没定义 即未编写程序内容或函数已定义但未作宣告
解决方法 编写 DelayX1ms 的内容 编写完后也要作宣告或作外部宣告 可在 delay.h的包含档宣告成外部 以便其它函数调用
8、***WARNING 1:UNRESOLVED EXTERNAL SYMBOL
SYMBOL:MUSIC3
MODULE:C:\8051\MUSIC.OBJ(MUSIC)
***WARNING 2:REFERENCE MADE TO UNRESOLVED EXTERNAL
SYMBOL:MUSIC3
MODULE:C:\8051\MUSIC.OBJ(MUSIC)
ADDRESS:0018H
说明 程序中有调用 MUSIC 函数 但未将该函数的含扩档 C 加入到工程档Prj 作编译和连接
解决方法 设 MUSIC3 函数在 MUSIC C里 将 MUSIC C添加到工程文件中去
9、***ERROR 107:ADDESS SPACE OVERFLOW
SPACE: DATA
SEGMENT: _DATA_GOUP_
LENGTH: 0018H
***ERROR 118: REFERENCE MADE TO ERRONEOUS EXTERNAL
SYMBOL: VOLUME
MODULE: C:\8051\OSDM.OBJ(OSDM)
ADDRESS: 4036H
说明 data 存储空间的地址范围为 0~0x7f,当公用变量数目和函数里的局部变量 如果存储模式设为SMALL 则局部变量先使用工作寄存器R2~R7作暂存 当存储器不够用时则会以 data型别的空间作暂存的个数超过 0x7f 时就会出现地址不够的现象
解决方法 将以 data 型别定义的公共变量修改为 idata型别的定义
10、“*** WARNING L1: UNRESOLVED EXTERNAL SYMBOL”
*** WARNING L2: REFERENCE MADE TO UNRESOLVED EXTERNAL
如果你在用C51编译器出现上面的警告,这个只是初学者和粗心者才会犯的错误:没把C文件添加到项目中!
另外,还有可能是因为存在没有被调用的已经定义的函数,或者相关的已经定义的变量没有使用。
本前言介绍了《RealView 编译工具编译器参考指南》。它分为以下几节:
• 第vi 页的关于本手册
• 第x 页的反馈
本手册提供有关 RealView 编译工具 (RVCT) 的参考信息,并介绍了 ARM 编译器的命令行选项。本手册也提供有关 ARM 如何在编译器中实现 C 和 C++ 的参考材料。有关使用和控制 ARM 编译器的一般信息,请参阅《RVCT 编译器用户指南》。
本手册是为所有使用 RVCT 编写应用程序的开发者编写的。本手册假定您是一位经验丰富的软件开发人员。有关 RVCT 附带的 ARM 开发工具的概述,请参阅《RealView 编译工具要点指南》。
3、标识符
a、变量的命名
方法一:采用匈牙利命名法。命名规则的主要思想是“在变量中加入前缀以增进人们对程序的理解”。
例如平时声明32位整型变量Length对应使用匈牙利命名法为unLength。现在列出经常用到的变量类型。
变量类型 示例
char cLength
unsigned char ucLength
short int sLength
unsigned short int usLength
int nLength
unsigned int unLength
char * szBuf
unsigned char * uszBuf
volatile unsigned char __ucLength
方法二:
Ø 局部变量以小写字母命名;
Ø 全局变量以首字母大写方式命名(骆驼式);
Ø 定义类型和宏定义常数以大写字母命名;
Ø 变量的作用域越大,它的名字所带有的信息就应该越多。
Ø 局部变量: int student_age;
Ø 全局变量: int StudentAge;
Ø 宏定义常数:#define STUDENT_NUM 10
Ø 类型定义: typedef INT16S int;
(我个人喜欢第二种方法)
b、 变量命名要注意缩写而且让人简单易懂,若是特别缩写要详细说明。
经常用到的缩写如:
Count 可缩写为Cnt
Message 可缩写为Msg
Packet 可缩写为Pkt
Temp 可缩写为Tmp
平时不经常用到的缩写,要注释:
SerialCommunication 可缩写为SrlComm //串口通信变量
SerialCommunicationStatus 可缩写为SrlCommStat //串口通信状态变量
c、全局变量和全局函数的命名一定要详细,不惜多用几个单词,例如函数UARTPrintfStringForLCD,
因为它们在整个项目的许多源文件中都会用到,必须让使用者明确这个变量或函数是干什么用的。局部变量和只在一个源文件中调用的内部函数的命名可以。简略一些,但不能太短,不要使用单个字母做变量名,只有一个例外:用i、j 、k 做循环变量是可以的。
d、用于编译开关的文件头,必须加上当前文件名称,防止编译时产生冲突。
例如在UARTInterface.h 头文件中,必须加上以下内容
#ifndef __UARTINTERFACE_H__
#define __UARTINTERFACE_H__
extern void UARTPrintfString(CONST INT8* str);
extern void UARTSendNBytes(UINT8 *ucSendBytes,UINT8 ucLen);
…… //其他外部声明的代码
#endif
e、禁止用汉语拼音作为标识符名称,可读性极差。呵呵。
f、 建议名称间的区别要显而易见。使用标识符名称要注意的一个相关问题是发生在名称之间只有一个字符或少数字符不同的情况,特别是名称比较长时,当名称间的区别很容易被误读时问题就比较显著,比如1(数字1)和l(L 的小写)、0 和O、2 和Z、5 和S,或者n 和h。
4、表达式和基本语句
a、不要编写太复杂的复合表达式;
例如:
i = a >= b && c < d && c + f <= g + h; //复合表达式过于复杂
b、不要有多用途的复合表达式;
例如:
d = (a = b + c) + r ; //应拆分为两个语句:
a = b + c;
d = a + r;
c、如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级。
例如:
if(a | b && a & c) //不良的风格
if((a | b) && (a & c)) //良好的风格
注意:只需记住加减运算的优先级低于乘除运算,其它地方一律加上括号。
d、 if 语句
d.a 布尔变量与零值比较
不可将布尔变量直接与TRUE、FALSE 或者1、0 进行比较。
根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE 定义为1,而Visual Basic 则将TRUE 定义为-1。
例:假设布尔变量名字为flag,它与零值比较的标准if 语句如下:
if (flag) // 表示flag为真时满足条件
if (!flag) // 表示flag为假时满足条件
其它的用法都属于不良风格,例如:
if (flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)
d.b 整型变量与零值比较
应当将整型变量用“==”或“!=”直接与0比较。
例:假设整型变量为value,它与零值比较的标准if 语句如下:
if (value == 0)
if (value != 0)
不可模仿布尔变量的风格而写成
if (value) // 会让人误解 value 是布尔变量
if (!value)
小技巧:想必大家都有过将赋值操作符“=”当作比较相等操作符“==”用过,这个错误比较的隐晦,不易排查,而且编译器从不把这类事情当作是程序员犯下的错。避免的方法有两种,一种是养成良好的编程习惯,在比较数值时小心翼翼的处理;另一种方法见下面给出的代码:
if (NULL = = p)
{
……
}
是不是觉得这种书写方式很古怪?不是程序写错了?
当然不是!
有经验的程序员为了防止将 if (p = = NULL) 误写成 if (p = NULL),而有意把p 和NULL 颠倒。编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为NULL不能被赋值。所以,再次遇到判断整型变量是否与某个数相等时,请这样写吧:
if(2==flag)
{
……
}
d.c 浮点变量与零值比较
不可将浮点变量用“==”或“!=”与任何数字比较。
千万要留意,无论float 还是double 类型变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。
假设浮点变量的名字为x,应当将
if (x == 0.0) // 隐含错误的比较
转化为
if ((x>=-EPSINON) && (x<=EPSINON)) //EPSINON 是精度
5、杂项
a. 一些常量(如圆周率PI)或者常需要在调试时修改的参数最好用#define定义,但要注意宏定义只是简单的替换,因此有些括号不可少。
b. 不要轻易调用某些库函数,因为有些库函数代码很长(我是反对使用printf之类的库函数的,但是是一家之言,并不勉强各位)。
c. 对各运算符的优先级有所了解,记不得没关系,加括号就是,千万不要自作聪明说自己记得很牢。
d. 不管有没有无效分支,switch函数一定要defaut这个分支。一来让阅读者知道程序员并没有遗忘default,并且防止程序运行过程中出现的意外(健壮性)。
e. 函数的参数和返回值没有的话最好使用void。
f. 一些常数和表格之类的应该放到code中去以节省RAM。
g. 程序编完编译看有多少code多少data,注意不要使堆栈为难。
h. 减少函数本身或函数间的递归调用
i. 编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。
j. 在多重循环中,应将最忙的循环放在最内层
k. 避免循环体内含判断语句,应将循环语句置于判断语句的代码块之中。
l. 系统运行之初,要初始化有关变量及运行环境,防止未经初始化的变量被引用。
m. 编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因造成代码丢失。
文章来源: 博客园
最近写一个程序,需要在用户模式下关中断,但ARM 7的体系结构决定了中断必须在特权模式下才可以更改,所以想到使用ARM的软中断来实现关中断和开中断。
使用软中断,首先要有硬件指令的支持,ARM有条指令是SWI。
SWI 指令的格式为:
SWI {条件} 24 位的立即数
SWI 指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在 SWI 的异常处理程序中提供相应的系统服务,指令中 24 位的立即数指定用户程序调用系统例程的类型,相关参数通过通用寄存器传递,当指令中 24 位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器 R0 的内容决定,同时,参数通过其他通用寄存器传递。
指令示例:
SWI 0x02 ;该指令调用操作系统编号位02的系统例程。
在keil MDK中,关键字__svc可以产生硬件SWI指令,使得处理器能响应软件中断。关键字__svc、keil MDK帮助文件中这样描述:
__svc 关键字声明超级用户调用 (SVC) 函数,该函数最多使用四个类似于整数的参数,并通过 value_in_regs 结构最多返回四个结果。
__svc 是一个函数限定符。它影响函数的类型。
语法
__svc(int svc_num) return-type function-name([argument-list]);
其中:
svc_num 是在 SVC 指令中使用的立即值。
它是一个表达式,其计算结果为以下范围内的整数:
• 在 ARM 指令中为 0 到 224–1 (24 位值)
• 在 16 位 Thumb 指令中为 0-255 (8 位值)。
要在keil MDK中使用软件中断,要做好两件事:第一件,更改启动文件,编写软件中断的汇编入口。在这个汇编入口中主要根据软件中断命令号进行相应的函数跳转;第二件,编写相应命令号的C语言服务函数。下面举例怎么样用软件中断实现开中断和关中断:
第一步:更改启动代码
keil MDK自带的启动代码有类似下面的语句:
Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
蓝色语句是程序复位后要执行的第一条指令,即复位异常入口;而红色语句就是执行一个软件中断指令后,要跳转到的软件中断异常入口。通过语句"SWI_Addr DCD SWI_Handler"进行中转,软件中断会跳转到标号为SWI_Handler的语句处,该处即处理软件中断的命令号、源代码如下:
EXPORT SWI_Handler
extern EnableIrqFunc ;使能中断函数名,用C语言实现
extern DisableIrqFunc ;禁止中断函数名,用C语言实现
SWI_Handler
STMFD SP!, {R0,R12,LR} ;入栈
LDR R0, [LR,#-4] ;取指令
BIC R0,R0,#0xFF000000 ;取软件中断命令号
CMP R0,#0 ;和0比较,因为我的使能中断用了软件中断命令0,禁止中断使用了软件中断命令1
BLEQ EnableIrqFunc ;为零调用使能中断函数
BLNE DisableIrqFunc ;不为零调用禁止中断函数
LDMFD SP!,{R0,R12,PC}^ ;出栈
第二部:编写相应命令号的C语言服务函数。
声明软件中断:
__svc(0x00) void EnableIrq(void); //使能中断
__svc(0x01) void DisableIrq(void); //禁止中断
编写服务函数:
void DisableIrqFunc(void)
{
int temp;
__asm
{
MRS temp,SPSR
ORR temp,temp,#0x80
MSR SPSR_c,temp
}
}
void EnableIrqFunc(void)
{
int temp;
__asm
{
MRS temp,SPSR
BIC temp,temp,#0x80
MSR SPSR_c,temp
}
}
到此,使能和禁止中断的软中断就结束了,下面看一下执行过程。
在程序中,如果想关中断,只需使用:DisableIrq();
MDK编译器在执行这句函数时,自动用软中断指令代替,即: SWI 0x01
ARM执行这条软件中断指令后,发生软件中断异常,程序跳转到软件中断异常服务函数处,即汇编代码标号为SWI_Handler处,在这里判断软件中断命令号是0x01,然后执行 BLNE DisableIrqFunc 语句,调用禁止中断函数,实现关中断。
文章来源:博客园