@vicatcu的答案很全面。另外要注意的一点是,在访问I / O(包括程序和数据存储器)时,CPU可能会进入等待状态(停顿的CPU周期)。
例如,我们使用的是TI F28335 DSP。RAM的某些区域处于程序和数据存储器的0等待状态,因此在RAM中执行代码时,每条指令以1个周期运行(那些占用1个以上周期的指令除外)。但是,当您从FLASH存储器(或多或少内置EEPROM)执行代码时,它无法在150MHz的全频率下运行,并且速度慢了好几倍。
关于高速中断代码,您必须学习很多东西。
首先,非常熟悉您的编译器。如果编译器做得很好,那么在大多数情况下,它应该不会比手工编码的汇编程序慢很多。(“慢得多”:我可以接受2倍;不接受10倍)。您需要学习如何(以及何时)使用编译器优化标志,并且不时查看在编译器的输出中查看其工作方式。
您可以让编译器执行其他一些操作以加速代码:
对于小型函数和仅执行一次或两次的函数,请使用内联函数(不记得C是否支持C还是仅是C ++形式)。缺点是内联函数很难调试,尤其是在打开编译器优化的情况下。但是它们为您节省了不必要的调用/返回序列,尤其是在“功能”抽象是出于概念设计目的而不是代码实现的情况下。
查看您的编译器手册,看看它是否具有内在函数-这些是与编译器相关的内置函数,它们直接映射到处理器的汇编指令;一些处理器具有汇编指令,这些指令可以执行诸如最小/最大/位反转之类的有用操作,您可以节省时间。
如果要进行数值计算,请确保没有不必要地调用数学库函数。我们有一种情况,代码类似于y = (y+1) % 4
周期为4的计数器,期望编译器将模4实现为按位与。相反,它称为数学库。因此,我们替换y = (y+1) & 3
为做我们想要的。
熟悉混乱的hacks页面。我保证您会经常使用其中至少一种。
您还应该使用CPU的定时器外设来测量代码执行时间-它们中的大多数都具有可以设置为以CPU时钟频率运行的定时器/计数器。在关键代码的开头和结尾处捕获计数器的副本,您可以看到它花费了多长时间。如果您不能这样做,另一种选择是在代码的开头降低输出引脚,然后在代码的末尾提高输出引脚,然后在示波器上查看该输出以计时执行时间。每种方法都需要权衡:内部计时器/计数器更灵活(您可以为几件事计时),但更难获得信息,而设置/清除输出引脚在示波器上立即可见,并且您可以捕获统计信息,但是很难区分多个事件。
最后,经验带来了一项非常重要的技能-常规的和特定的处理器/编译器组合:知道何时以及何时不进行优化。通常,答案是不要优化。Donald Knuth的报价经常在StackOverflow上发布(通常只是最后一部分):
我们应该忘记效率低下的问题,大约有97%的时间是这样:过早的优化是万恶之源
但是您处在必须知道必须进行某种优化的情况下,因此该该硬着头皮进行优化了(或获得更快的处理器,或两者兼而有之)。千万不要写你的整个ISR组装。这几乎是可以保证的灾难-如果您这样做,那么在几个月甚至几周内,您会忘记所做的部分工作以及原因,并且代码可能非常脆弱且难以更改。有可能是你的代码的部分,但是,是组装好的候选人。
迹象表明您的代码的一部分非常适合于汇编代码:
- 功能齐全,定义明确的小例程,不太可能更改
- 可以利用特定汇编指令的功能(最小/最大/右移/等)
- 被多次调用的函数(获得乘数:如果每次调用节省0.5usec,并且被调用10次,则可以节省5usec,这对您而言很重要)
了解编译器的函数调用约定(例如,将参数放在寄存器中的位置以及将其保存/恢复到哪个寄存器中),以便您可以编写C调用的汇编例程。
在我当前的项目中,我们有一个相当大的代码库,其中包含必须在10kHz中断(100usec –听起来很熟悉?)中运行的关键代码,而且汇编中编写的函数并不多。诸如CRC计算,软件队列,ADC增益/偏移补偿之类的东西。
祝好运!