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