嵌入式C语言编程遇到的问题
使用mdk5仿真
新建mdk工程,这里我们选择ARMCM3内核,并在Manage Run-time Environment界面勾选CMSIS的core和Dvice的startup。然后在Source Group1Z中添加main.c文件和debugprint.c文件。
在debugprint.c文件中输入如下代码:
#include <stdio.h>
#define ITM_Port8(n) (*((volatile unsigned char *)(0xE0000000+4*n)))
#define ITM_Port16(n) (*((volatile unsigned short*)(0xE0000000+4*n)))
#define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*n)))
#define DEMCR (*((volatile unsigned long *)(0xE000EDFC)))
#define TRCENA 0x01000000
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;
int fputc(int ch, FILE *f)
{
if (DEMCR & TRCENA)
{
while (ITM_Port32(0) == 0);
ITM_Port8(0) = ch;
}
return(ch);
}
配置工程为仿真模式:点击,选择debug栏选中Use Simulator,选择Target栏下选中
Use MicroLIB。
利用仿真分析实际问题
1.在main.c文件中添加下图中代码,按如下步骤进行仿真
这个程序理论上得到的值是 fun1=2,fun2=3 ;fun2=3,fun1=2 。 但我们可以看到得到的结果跟理论值不同,这是因为我们在两个函数中定义了相同的变量,而printf函数是沿着“右→左”运算的,所以把右边的运算结果被当做变量附给了左边,以第一个函数为例,先执行fun2(),cnt_var=3,再执行fun1(),cnt_var=3+1=4。所以得到如上结果。所以在编辑程序的时候,切忌将函数的结果作为另一个函数的变量。
2.在main.c中输入下图中代码,进行仿真
仿真结果如下:
理论上得到的结果是a=1,b=1;因为在条件执行过程中,前一个条件已经能够判断,导致后面函数没有执行,所以出现如上结果,所以在编程时不要把赋值语句用作条件。
3.在main.c中输入下图代码,进行仿真,仿真结果如图
我们把语句b=*p++改为b=(*p)++,再进行仿真,结果如图
我们发现两次仿真的结果 * p的值是不一样的,这是因为 * 和++的优先级不同导致,前者是按从右向左先运算p++,再运算 * ,相当于*(p++),就是把指针p指向的地址移位; 后者则相反,是把p地址内的值加一,所以,我们在编程的时候要注意运算符优先级顺序,否则可能导致与我们的预期不符,应适当加圆括号,但不可过多使用括号分散代码。
4.另外,在布尔值表达式中不要使用赋值操作,当需要的时候,赋值操作要在布尔值表达式外进行,可避免“=”与“==”混淆。
5.性能优化—减小运算强度的方法
(1)、查表法,对一些需要用到的运算事先建立一个表格,需要用到的时候使用查表的方式替代运算,能够优化代码的运行效率。
(2)、使用位操作,比如取余:a=a%8可改为a=a&7,位操作只需一个指令周期即可完成,而大部分C编辑器的“%”运算均是调用子程序来完成,代码长、执行速度慢。通常只要是求2的n次方的余数,都可使用位操作。此外还有交换a、b的值可用三次位操作实现a^=b ; b^ =a; a^=b。还有类似的乘除运算,平方运算等,如下举例:
y=pow(x,2);
z=y*33;
for(i=0;i<n;i++)
{
h=5*i;
printf(“%d”,h);
}
常改为
y=x*x;
z=(y<<5)+y;
for(i=0,h=0;i<n;i++)
{
h+=5
printf(“%d”,h);
}
下边相对上边的运算效率就会提高很多,所以在编程时要注意适当减小运算强度
(3)、使用增量和减量运算符,在使用加一和减一的操作时尽可能地使用增量和减量运算,回避赋值语句快很多(增、减量运算符的前置和后置运算在重载时是不同的)。
如通常使i用i++或i–,而不是i=i+1或i=i-1。
6. 性能优化—优化编译
虽然编译器已经很智能,但对于高级语言的解释优化能力还远远达不到人脑的等级,所以我们需要用到一些优化。
比如循环的优化,我们先看一个例子:
该程序的作用是将字符串中的大写字母改为小写字母,我们可以看到此程序的第8行有”i<strlen(str)”,该语句是获取字符串长度,由于字符串的长度在运算过程中不变,所以不必要每做一次循环运算一次,我们可以在循环外计算其值,这样就会大大提高运算效率。
7.使用宏定义开关打印
在程序的编写调试过程中,总会使用printf之类的语句,但当程序发布的时候为了某些原因还要删除这些语句,再次编写还要加回去,所以很浪费时间,所以使用宏定义的方式定义调试语句。
代码如下:
#define DEBUG
//#undef DEBUG
#ifdef DEBUG
#define dmsg(format,...) printf(format, ##__VA_ARGS__)
#else
#define dmsg(format,...)
#endif
int main()
{
char str[] = "Hello";
dmsg(" %s",str);
return 0;
}
当我们需要使用输出语句时打开DEBUG开关(#define DEBUG),所有的dmsg会替换成printf语句,仿真后我们可以看到仿真界面打印出了我们要打印的字符串
当我们不需要该功能时,只需将#define DEBUG改为#undef DEBUG就可以关闭DEBUG开关了,此时dmsg会被替换成空,因此不被编译。仿真结果如下:
我们可以看到程序没有执行。