单片机程序设计时必须遵循的优化原则

由于单片机的性能同电脑的性能是天渊之别的,无论从空间资源上、内存资源、工作频率,都是无法与之比较的。pc 机编程基本上不用考虑空间的占用、内存的占用的问题,最终目的就是实现功能。而对于单片机来说就截然不同了,一般的单片机的flash 和ram 的资源是有限的,可想而知,单片机的资源是少得可怜,为此我们必须想法设法榨尽其所有资源,将它的性能发挥到最佳,程序设计时必须遵循以下几点进行优化:
1、使用尽量小的数据类型
能用unsiged就不用signed;
能用char就不用int;
能不用floating就不用;
能用位操作不用算数。
2、使用自加、自减指令
通常使用自加、自减指令和复合赋值表达式(如a-=1 及a+=1 等)都能够生成高质量的程序代码,编译器通常都能够生成inc 和dec 之类的指令,而使用a=a+1 或a=a-1 之类的指令,有很多c 编译器都会生成二到三个字节的指令。
3、减少运算的强度
可以使用运算量小但功能相同的表达式替换原来复杂的的表达式。
(1) 求余运算
n= n %8 可以改为n = n &7
说明:位操作只需一个指令周期即可完成,而大部分的c 编译器的“%”运算均是调用子程序来完成,代码长、执行速度慢。通常,只要求是求2n 方的余数,均可使用位操作的方法来代替。
(2) 平方运算
n=pow(3,2) 可以改为n=3*3
说明:在有内置硬件乘法器的单片机中(如51 系列),乘法运算比求平方运算快得多, 因为浮点数的求平方是通过调用子程序来实现的,乘法运算的子程序比平方运算的子程序代码短,执行速度快。
(3) 用位移代替乘法除法
n=m*8 可以改为n=m《《3
n=m/8 可以改为n=m》》3
说明:通常如果需要乘以或除以2n,都可以用移位的方法代替。如果乘以2n,都可以生成左移的代码,而乘以其它的整数或除以任何数,均调用乘除法子程序。用移位的方法得到代码比调用乘除法子程序生成的代码效率高。实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果。
如n=m*9可以改为n=(m《《3)+m;
(4) 自加自减的区别
例如我们平时使用的延时函数都是通过采用自加的方式来实现。
void delaynms(uint16 t)
{
uint16 i,j;
for(i=0;i
define max(a,b) {(a)》(b)?(a):(b)}
说明:函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,cpu也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些cpu时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。
1.适当地使用算法
假如有一道算术题,求1~100 的和。
作为程序员的我们会毫不犹豫地点击键盘写出以下的计算方法:
uint16 sum(void)
{
uint8 i,s;
for(i=1;i《=100;i++)
{
s+=i;
}
return s;
}
很明显大家都会想到这种方法,但是效率方面并不如意,我们需要动脑筋,就是采用数学算法解决问题,使计算效率提升一个级别。
uint16 sum(void)
{
uint16 s;
s=(100 *(100+1))》》1;
return s;
}
结果很明显,同样的结果不同的计算方法,运行效率会有大大不同,所以我们需要最大限度地通过数学的方法提高程序的执行效率。
2.用指针代替数组
在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。下面的代码作用是相同的,但是效率不一样。
uint8 szarraya[64];
uint8 szarrayb[64];
uint8 i;
uint8 *p=szarray;
for(i=0;i《64;i++)szarrayb=szarraya;
for(i=0;i《64;i++)szarrayb=*p++;
指针方法的优点是,szarraya 的地址装入指针p 后,在每次循环中只需对p 增量操作。在数组索引方法中,每次循环中都必须进行基于i 值求数组下标的复杂运算。
3.强制转换
c 语言精髓第一精髓就是指针的使用,第二精髓就是强制转换的使用,恰当地利用指针和强制转换不但可以提供程序效率,而且使程序更加之简洁,由于强制转换在c 语言编程中占有重要的地位,下面将已五个比较典型的例子作为讲解。
例子1:将带符号字节整型转换为无符号字节整型
uint8 a=0;
int8 b=-3;
a=(uint8)b;
例子2:在大端模式下(8051 系列单片机是大端模式),将数组a[2]转化为无符号16 位整型值。
方法1:采用位移方法。
uint8 a[2]={0x12,0x34};
uint16 b=0;
b=(a[0]《《8)|a[1];
结果:b=0x1234
方法2:强制类型转换。
uint8 a[2]={0x12,0x34};
uint16 b=0;
b= (uint16 )a; //强制转换
结果:b=0x1234
例子3:保存结构体数据内容。
方法1:逐个保存。
typedef struct _st
{
uint8 a;
uint8 b;
uint8 c;
uint8 d;
uint8 e;
}st;
st s;
uint8 a[5]={0};
s.a=1;
s.b=2;
s.c=3;
s.d=4;
s.e=5;
a[0]=s.a;
a[1]=s.b;
a[2]=s.c;
a[3]=s.d;
a[4]=s.e;
结果:数组a 存储的内容是1、2、3、4、5。
方法2:强制类型转换。
typedef struct _st
{
uint8 a;
uint8 b;
uint8 c;
uint8 d;
uint8 e;
}st;
st s;
uint8 a[5]={0};
uint8 p=(uint8 )&s;//强制转换
uint8 i=0;
s.a=1;
s.b=2;
s.c=3;
s.d=4;
s.e=5;
for(i=0;i
define perror(fun) printf(“err:%s %s %d: %s\n”, file, func,line,fun) 类linux的perror函数实现,这里加了出错的文件位置,所在函数,引发出错调用的函数fun。
宏中#和##的用法
define str(s) #s
define cons(a, b) int(a##e##b)
printf(str(vck));//输出vck
printf(“%d\n”, cons(2,3));//2e3 输出2000
这些方法常用,必然会让你的代码更加简洁高效!

消息称小米 2021 年采购 2.4 亿部手机零部件
排水管网流量水位多参数监测系统系统组成
通过工业移动设备从云端对器械进行自动化控制
机器学习法发现了可帮助抗生素杀死细菌的机制
碧野千里微型空气质量监测站介绍
单片机程序设计时必须遵循的优化原则
IoT发展十大趋势:通用汽车/爱立信怎么说?
Hi3519DV500的应用:打造高性能图像处理解决方案
高级负载诊断功能助力缩短 24 VDC 配电的工厂停机时间
CNR插槽,什么是CNR插槽,CNR插槽外形图
华为荣耀Magic2什么时候上市?荣耀Magic2最新消息:荣耀Magic2配置、价格预测,手写笔+麒麟970年底发布
德国进口SycoTec主轴电机对PCB板切割是如何去静电的?
创米小白摄像头怎么样?创米小白N4拆解评测 国产芯片助力无线网络摄像机
实力演绎不凡服务,伊士曼漆面保护膜裁切数据系统焕新面世 创新成就不凡服务体验
CES2023快讯:高通发布车用芯片 Mobileye预计2030年ADAS将实现170亿美元营收
一个简单的立体声平衡指示器电路
CHA6710-FAB X波段中功率放大器UMS
服务器,服务器的作用是什么?
FLIR Fido X2爆炸物痕量探测器的优点
联通混合所有制改造后,移动电信该如何应对?