реклама на сайте
подробности

 
 
> Отключение выгрузки регистров в стек (CVAVR)
ProMetei
сообщение Dec 8 2016, 08:46
Сообщение #1





Группа: Новичок
Сообщений: 2
Регистрация: 11-05-16
Пользователь №: 91 679



Камень: 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+

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

Спасибо!

Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
Сергей Борщ
сообщение Dec 8 2016, 09:28
Сообщение #2


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



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

Добавлено: а, ну да - этот обработчик будет давать точно такую же задержку. Тогда вы можете проверять флаг АЦП и разрешать его прерывание, если флаг взведен, в конце прерывания ШИМ. Тогда ваш обработчик АЦП будег вызван после обработчика ШИМ и мешать ему не будет.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
ProMetei
сообщение Dec 8 2016, 12:22
Сообщение #3





Группа: Новичок
Сообщений: 2
Регистрация: 11-05-16
Пользователь №: 91 679



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


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

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


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

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

Господа, а что по поводу костыля - насколько он кривой и опасный?
Go to the top of the page
 
+Quote Post
zombi
сообщение Dec 8 2016, 14:08
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 076
Регистрация: 10-09-08
Пользователь №: 40 106



Цитата(ProMetei @ Dec 8 2016, 16:22) *
насколько он кривой и опасный?

дык вроде ничего ни кривого ни опасного
разрешайте прерывания первой командой обработчика а в конце простой RET.
Go to the top of the page
 
+Quote Post



Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 20th June 2025 - 17:46
Рейтинг@Mail.ru


Страница сгенерированна за 0.01387 секунд с 7
ELECTRONIX ©2004-2016