Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Отключение выгрузки регистров в стек (CVAVR)
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
ProMetei
Камень: atmega88pa
Компилятор: CVAVR

Главная часть проекта - 8-канальный аппаратно-программный генератор ШИМа на 6 прерываниях таймеров 0 и 2 (Fclk=2MHz, Fpwm=7,8kHz).
Расчёт значений для ШИМ производится в прерывании от ADC. Сразу после входа в обработчик разрешаются вложенные прерывания (чтоб не глох ШИМ), затем идёт задумчивый код с 32-битной арифметикой и лишь перед выходом из обработчика- запускается новый отсчёт АЦП.

Проблема в том, что компилятор абсолютно верно и предсказуемо сохраняет регистры при входе в обработчик, и лишь затем добирается до SEI, вот упрощенная выдержка из листинга:

CODE

_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 - кощунство.

Попробовал покататься на вот таком костыле- помогает, но не уверен что он правильный.

CODE

#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+

Уважаемые знатоки, подскажите, пожалуйста- есть ли иной вариант, как заставить компилятор запускать вложенные прерывания пораньше?
И если нет - насколько правильно написан мой костыль?

Спасибо!

zombi
Цитата(ProMetei @ Dec 8 2016, 11:46) *
Сразу после входа в обработчик ... идёт задумчивый код с 32-битной арифметикой

Задумчивый код в прерывании!?
Не думаю что это правильно.

Цитата(ProMetei @ Dec 8 2016, 11:46) *
Разбавлять её опросом флага ADSC - кощунство.

либо джиттер либо кощунство
Сергей Борщ
Вы можете вынести меделнную обработку в другое прерывание, скажем, в прерывание по SPM. Тогда ваш основной обработчик будет в начале брать результаты предыдущего вычисления, а конце будет разрешать прерывание SPM. После выхода из основного обработчика будет запускаться обработчик SPM, в котором в самом начале нужно запретить прерывание SPM и разрешить глобальные прерывания. Этот обработчик SPM будет готовить данные для следующего основного прерывания и будет как бы фоновой задачей между остальными прерываниями и основным циклом. Возможно, еще стоит подумать о переходе от плавучки к арифметике с фиксированной точкой.

Добавлено: а, ну да - этот обработчик будет давать точно такую же задержку. Тогда вы можете проверять флаг АЦП и разрешать его прерывание, если флаг взведен, в конце прерывания ШИМ. Тогда ваш обработчик АЦП будег вызван после обработчика ШИМ и мешать ему не будет.
ProMetei
Цитата(Сергей Борщ @ Dec 8 2016, 12:28) *
Добавлено: Тогда вы можете проверять флаг АЦП и разрешать его прерывание, если флаг взведен, в конце прерывания ШИМ. Тогда ваш обработчик АЦП будег вызван после обработчика ШИМ и мешать ему не будет.


Увы, проверять в конце прерывания ШИМ - бессмысленно, следующее ШИМ-прерывание может наступить уже через 8 тактов, а запустившийся до него обработчик АЦП занимает до 3000.

Цитата(Сергей Борщ @ Dec 8 2016, 12:28) *
Возможно, еще стоит подумать о переходе от плавучки к арифметике с фиксированной точкой.


Арифметика вся с фиксированной точкой беззнаковая. Я пробовал ужать до 16-битной арифметики, но набор регистров, которые используется в обработчике остаётся почти таким же большим, а точность генерируемого сигнала падает, вылезая за пределы техзадания.

Весь обработчик писать на ассемблере я не готов.

Господа, а что по поводу костыля - насколько он кривой и опасный?
Сергей Борщ
QUOTE (ProMetei @ Dec 8 2016, 15:22) *
следующее ШИМ-прерывание может наступить уже через 8 тактов
Тогда любое другое прерывание даст вам дрожание. Может стоит уйти от ШИМа к чему-то более другому?
zombi
Цитата(ProMetei @ Dec 8 2016, 16:22) *
насколько он кривой и опасный?

дык вроде ничего ни кривого ни опасного
разрешайте прерывания первой командой обработчика а в конце простой RET.
Zlumd
Цитата(ProMetei @ Dec 8 2016, 15:46) *
Сразу после входа в обработчик разрешаются вложенные прерывания (чтоб не глох ШИМ)
Внутри прерывания ШИМ не глохнет.
ШИМ генерируется таймером аппаратно, независимо от того, чем занимается процессор. Или вы вручную ногами дергаете?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.