以下是C代码:
const uint8_t amplitudes60[60] = {127, 140, 153, 166, 176, 191, 202, 212, 221, 230, 237, 243, 248, 251, 253, 254, 253, 251, 248, 243, 237, 230, 221, 212, 202, 191, 179, 166, 153, 140, 127, 114, 101, 88, 75, 63, 52, 42, 33, 24, 17, 11, 6, 3, 1, 0, 1, 3, 6, 11, 17, 24, 33, 42, 52, 63, 75, 88, 101, 114};
const uint8_t amplitudes13[13] = {127, 176, 221, 248, 202, 153, 101, 52, 17, 1, 6, 33, 75};
const uint8_t amplitudes10[10] = {127, 176, 248, 202, 101, 52, 17, 1, 33, 75};
volatile uint8_t numOfAmps = 60;
volatile uint8_t *amplitudes = amplitudes60;
volatile uint8_t amplitudePlace = 0;
ISR(TIMER1_COMPA_vect)
{
PORTD = amplitudes[amplitudePlace];
amplitudePlace++;
if(amplitudePlace == numOfAmps)
{
amplitudePlace = 0;
}
}
amplitudes 和 numOfAmps 都会被另一个比这个更慢的中断例程改变(基本上是为了改变正在播放的频率)。最终我将不使用这些确切的数组,但是设置将非常相似。我可能会有一个包含60个值的数组和另一个只有30个值的数组。这是因为我正在构建频率扫描器,并且在较低的频率下,我可以负担更多的样本,因为我有更多的时钟周期来处理,但在较高的频率下,我非常缺乏时间。
我确实意识到可以使用更低的采样率使其工作,但我不想低于每个周期30个样本。我认为将指向该数组的指针不会使它变得更慢,因为从数组获取值的汇编语言和从数组指针获取值的汇编语言似乎是相同的(这很合理)。
在我必须产生的最高频率下,我被告知应该能够以每个正弦波周期约30个样本的速度运行。目前,在30个样本的情况下,最快运行的速度约为所需最大频率的一半,这意味着我的中断需要运行两次更快。
因此,当模拟该代码时,需要65个周期才能完成。同样,我被告知应该能够将其最佳降至约30个周期。
这是生成的ASM代码,并且我的想法是在每行旁边说明它执行的操作:
ISR(TIMER1_COMPA_vect)
{
push r1
push r0
in r0, 0x3f ; save status reg
push r0
eor r1, r1 ; generates a 0 in r1, used much later
push r24
push r25
push r30
push r31 ; all regs saved
PORTD = amplitudes[amplitudePlace];
lds r24, 0x00C8 ; r24 <- amplitudePlace I’m pretty sure
lds r30, 0x00B4 ; these two lines load in the address of the
lds r31, 0x00B5 ; array which would explain why it’d a 16 bit number
; if the atmega8 uses 16 bit addresses
add r30, r24 ; aha, this must be getting the ADDRESS OF THE element
adc r31, r1 ; at amplitudePlace in the array.
ld r24, Z ; Z low is r30, makes sense. I think this is loading
; the memory located at the address in r30/r31 and
; putting it into r24
out 0x12, r24 ; fairly sure this is putting the amplitude into PORTD
amplitudePlace++;
lds r24, 0x011C ; r24 <- amplitudePlace
subi r24, 0xFF ; subi is subtract imediate.. 0xFF = 255 so I’m
; thinking with an 8 bit value x, x+1 = x - 255;
; I might just trust that the compiler knows what it’s
; doing here rather than try to change it to an ADDI
sts 0x011C, r24 ; puts the new value back to the address of the
; variable
if(amplitudePlace == numOfAmps)
lds r25, 0x00C8 ; r24 <- amplitudePlace
lds r24, 0x00B3 ; r25 <- numOfAmps
cp r24, r24 ; compares them
brne .+4 ; 0xdc <__vector_6+0x54>
{
amplitudePlace = 0;
sts 0x011C, r1 ; oh, this is why r1 was set to 0 earlier
}
}
pop r31 ; restores the registers
pop r30
pop r25
pop r24
pop r19
pop r18
pop r0
out 0x3f, r0 ; 63
pop r0
pop r1
reti
除了在中断中使用更少的寄存器以便减少推/弹操作外,我真的看不出这段汇编代码在哪里效率低下。我唯一的想法是,如果我可以找到一种方法在 C 中获取 n 位整数数据类型,那么可能可以摆脱 if 语句,并且使数字在达到末尾时回绕?我的意思是,我将有 2^n-1 个样本,然后让 amplitudePlace 变量继续计算,直到它达到 2^n 就会溢出并重新设置为零。
我确实尝试过完全删除 if 语句模拟代码,虽然它提高了速度,但只能减少约 10 个周期,因此每次执行大约需要 55 个周期,这仍然不够快,所以我需要进一步优化代码,这很难考虑到没有它只有两行!
我唯一的其他想法是看看是否可以将静态查找表存储在某个需要更少的时钟周期来访问的位置?我认为它使用的用于访问数组的 LDS 指令都需要 2 个周期,因此在那里我可能并不会节省太多时间,但在这个阶段,我愿意尝试任何事情。
我完全不知道从这里开始。我看不出如何使我的 C 代码更加高效,但我在这方面只是一个新手,所以可能会遗漏些什么。我需要任何形式的帮助...我意识到这是一个相当特殊和涉及问题,通常我会尝试避免在这里问这些问题,但我已经在这个问题上工作了很长时间,完全不知所措,所以我真的需要任何可以得到的帮助。
r19
和r18
,而它们没有被推入(或以其他方式使用)?为什么amplitudePlace
有时显然位于地址0x00c8,有时又位于0x011c? - Michael Burr