У меня есть обработчик прерывания, который просто не работает достаточно быстро для того, что я хочу сделать . В основном я использую его для генерации синусоидальных волн путем вывода значения из справочной таблицы в ПОРТ микроконтроллера AVR, но, к сожалению, это происходит недостаточно быстро, чтобы я мог получить желаемую частоту волны. Мне сказали, что я должен рассмотреть возможность его реализации в сборке, поскольку сборка, созданная компилятором, может быть немного неэффективной и может быть оптимизирована, но, посмотрев код сборки, я действительно не вижу, что я могу сделать лучше.
Это код на языке 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;
}
}
амплитуды и numOfAmps изменяются другой подпрограммой прерывания, которая работает намного медленнее, чем эта (в основном она выполняется для изменения воспроизводимых частот). В конце концов, я не буду использовать эти точные массивы, но это будет очень похожая настройка. Скорее всего, у меня будет массив с 60 значениями, а другой - всего с 30. Это потому, что я создаю частотный свипер, и на более низких частотах я могу позволить ему давать ему больше выборок, так как у меня больше тактов, с которыми можно поиграть, но на более высоких частотах I ' m очень стеснен во времени.
Я действительно понимаю, что могу заставить его работать с более низкой частотой дискретизации, но я не хочу опускаться ниже 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
Помимо, возможно, использования меньшего количества регистров в прерывании, чтобы у меня было меньше push / pop, я действительно не вижу, где это ассемблерный код неэффективен.
Моя единственная другая мысль: может быть, можно было бы избавиться от оператора if, если бы я мог придумать, как получить тип данных bit int в C, чтобы число обернулось, когда достигнет конца? Под этим я подразумеваю, что у меня было бы 2 ^ n - 1 отсчетов, а затем переменная ampitudePlace просто продолжала считать, так что когда она достигнет 2 ^ n, она переполнится и будет сброшена на ноль.
Я попытался смоделировать код без бита if полностью, и хотя он действительно улучшил скорость, потребовалось всего около 10 циклов, так что на одно выполнение было около 55 циклов, что все еще не работает t достаточно быстро, к сожалению, поэтому мне нужно еще больше оптимизировать код, что сложно, учитывая, что без этого всего 2 строки !!
Моя единственная реальная мысль - посмотреть, могу ли я сохранить статические таблицы поиска где-нибудь, меньше тактов для доступа? Инструкции LDS, которые он использует для доступа к массиву, я думаю, все занимают 2 цикла, поэтому я, вероятно, не стал бы экономить много времени, но на этом этапе я готов попробовать что угодно.
Я совершенно не понимаю Куда пойти отсюда. Я не понимаю, как я могу сделать свой код на C более эффективным, но я новичок в подобных вещах, поэтому я мог что-то упустить. Я хотел бы получить любую помощь ... Я понимаю, что это довольно сложная и сложная проблема, и обычно я стараюсь не задавать здесь такие вопросы, но я ' Я работал над этим целую вечность и совершенно растерялся, поэтому я действительно воспользуюсь любой помощью, которую смогу получить.