Камень: atmega88pa
Компилятор: CVAVR
Главная часть проекта - 8-канальный аппаратно-программный генератор ШИМа на 6 прерываниях таймеров 0 и 2 (Fclk=2MHz, Fpwm=7,8kHz).
Расчёт значений для ШИМ производится в прерывании от ADC. Сразу после входа в обработчик разрешаются вложенные прерывания (чтоб не глох ШИМ), затем идёт задумчивый код с 32-битной арифметикой и лишь перед выходом из обработчика- запускается новый отсчёт АЦП.
Проблема в том, что компилятор абсолютно верно и предсказуемо сохраняет регистры при входе в обработчик, и лишь затем добирается до SEI, вот упрощенная выдержка из листинга:
_ADC_isr:
ST -Y,R0
ST -Y,R1
ST -Y,R22
ST -Y,R23
ST -Y,R24
ST -Y,R25
ST -Y,R26
ST -Y,R27
ST -Y,R30
ST -Y,R31
IN R30,SREG
ST -Y,R30
sei
; тут живёт медленный код
D R30,Y+
OUT SREG,R30
LD R31,Y+
LD R30,Y+
LD R27,Y+
LD R26,Y+
LD R25,Y+
LD R24,Y+
LD R23,Y+
LD R22,Y+
LD R1,Y+
LD R0,Y+
RETI
Эта пауза в 23/20 (вход/выход) дополнительных тактов даёт джиттер ШИМ до 3 единиц.
Джиттер не особо напрягает, но хотелось бы от него избавиться.
Переносить медленный код в основной цикл - не вариант, там крутится основная логика программы, абстрагированная от железа.
Разбавлять её опросом флага ADSC - кощунство.
Попробовал покататься на вот таком костыле- помогает, но не уверен что он правильный.
#pragma savereg-
interrupt [ADC_INT] void ADC_isr(void) // замер входов АЦП, плавный разгон
{
static unsigned int soft_rpm=0; // внутренняя переменная плавной скорости
static unsigned int addition; // компенсация ограничения
unsigned char temp; // вспомогательная переменная для ускорения расчётов
unsigned long L; // для точных расчётов
#asm
ST -Y,R0 ; выгрузили R0
IN R0,SREG ; в R0 положили SREG (с I-bit=0)
SEI ; разрешили прерывания
ST -Y,R0 ; выгрузили R0 ( реально - там SREG)
ST -Y,R1 ; выгрузили R1
ST -Y,R15 ; выгрузили R15
ST -Y,R22 ; выгрузили R22
ST -Y,R23 ; выгрузили R23
ST -Y,R24 ; выгрузили R24
ST -Y,R25 ; выгрузили R25
ST -Y,R26 ; выгрузили R26
ST -Y,R27 ; выгрузили R27
ST -Y,R30 ; выгрузили R30
ST -Y,R31 ; выгрузили R31
#endasm
{} // тут живёт медленный код
ADCSRA=(1<<ADEN) | (1<<ADSC) |(1<<ADIE) | (1<<ADIF) | 0x06; // перезапуск прерывания
#asm
LD R31,Y+ ; восстановили R31
LD R30,Y+ ; восстановили R30
LD R27,Y+ ; восстановили R27
LD R26,Y+ ; восстановили R26
LD R25,Y+ ; восстановили R25
LD R24,Y+ ; восстановили R24
LD R23,Y+ ; восстановили R23
LD R22,Y+ ; восстановили R22
LD R15,Y+ ; восстановили R15
LD R1,Y+ ; восстановили R1
LD R0,Y+ ; восстановили R0 ( реально - там SREG)
OUT SREG,R0 ; из R0 восстановить SREG (с I-bit=0, запретив прерывания)
LD R0,Y+ ; восстановили R0
#endasm
}
#pragma savereg+
Уважаемые знатоки, подскажите, пожалуйста- есть ли иной вариант, как заставить компилятор запускать вложенные прерывания пораньше?
И если нет - насколько правильно написан мой костыль?
Спасибо!