Оптимизация этого кода C (AVR)

У меня есть обработчик прерывания, который просто не работает достаточно быстро для того, что я хочу сделать . В основном я использую его для генерации синусоидальных волн путем вывода значения из справочной таблицы в ПОРТ микроконтроллера 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 более эффективным, но я новичок в подобных вещах, поэтому я мог что-то упустить. Я хотел бы получить любую помощь ... Я понимаю, что это довольно сложная и сложная проблема, и обычно я стараюсь не задавать здесь такие вопросы, но я ' Я работал над этим целую вечность и совершенно растерялся, поэтому я действительно воспользуюсь любой помощью, которую смогу получить.

10
задан Bill the Lizard 16 September 2012 в 22:34
поделиться