如何写出高效率稳定的单片机代码
如何写出高效率稳定的单片机代码? 由于单片机的性能同电脑的性能是天渊之别的,无论从空间资源上、内存资源、工作频率,都是无法与之比较的。PC 机编程基本上不用考虑空间的占用、内存的占用的问题,最终目的就是实现功能就可以了。对于单片机来说就截然不同了,一般的单片机的Flash 和Ram 的资源是以KB 来衡量的,可想而知,单片机的资源是少得可怜,为此我们必须想法设法榨尽其所有资源,将它的性能发挥到最佳,程序设计时必须遵循以下几点进行优化: 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 时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。 4、适当地使用算法 假如有一道算术题,求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; } 结果很明显,同样的结果不同的计算方法,运行效率会有大大不同,所以我们需要最大限度地通过数学的方法提高程序的执行效率。 5、用指针代替数组 在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。下面的代码作用是相同的,但是效率不一样。 UINT8 szArrayA[64]; UINT8 szArrayB[64]; UINT8 i; UINT8 *p=szArray; for(i=0;i<64;i++)szArrayB[i]=szArrayA[i]; for(i=0;i<64;i++)szArrayB[i]=*p++; 指针方法的优点是,szArrayA 的地址装入指针p 后,在每次循环中只需对p 增量操作。在数组索引方法中,每次循环中都必须进行基于i 值求数组下标的复杂运算。 6、 强制转换 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
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· Windows桌面应用自动更新解决方案SharpUpdater5发布
· 我的家庭实验室服务器集群硬件清单
· C# 13 中的新增功能实操
· Supergateway:MCP服务器的远程调试与集成工具
· Vue3封装支持Base64导出的电子签名组件