|
|
  |
К знатокам, Локальные переменные. |
|
|
|
Sep 6 2007, 04:53
|
Бывалый
    
Группа: Свой
Сообщений: 1 584
Регистрация: 7-08-07
Пользователь №: 29 615

|
Цитата(SasaVitebsk @ Sep 6 2007, 04:58)  Пишу достаточно простую прогу. Пытаюсь оптимизировать.
Столкнулся с одной бедой. Попробую описать.
Построено на прерываниях. Между прерываниями разные промежутки. Есть короткие, есть длинные. Как назло именно короткое прерывание сильно загружено. Дабы разгрузить его я пытаюсь часть вычислений вынести в предыдущее не загруженное прерывание. Уже полностью перешёл на указатели, но всё равно шляпа получается.
Проблема в том, что я не могу ввести локальные переменные на два прерывания. Таким образом я ввожу статик. Но тогда во втором прерывании компилятор пытается сохранить значения. А мне это не надо ни капельки. Чтобы этого избежать я ввёл локальные переменные и во втором прерывании выполнил присваивание локальным статик. Код получился значительно компактнее, но всё равно выполняется никому не нужное присваивание. А это 6 указателей!
Теперь сам вопрос. Могу ли я указать компилятору что можно разрушать переменные в данном прерывании. То есть что не надо их хранить. Или как это сделать. Надо типа переобъявить статические переменные локальными. Не знаю как выразится. Надеюсь поняли. Как-то все не очеь ясно, но и из того, что я понял, могу предложить ввести какой-то класс со всеми данными и в нем функции, работы с этими данными. В прерываниях и где-нужно их вызывать и не париться больше со статиками глобальными, локальными и еще чем-то. За Вас все сделает компилятор.
|
|
|
|
|
Sep 6 2007, 05:02
|

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

|
Да, действительно мутновато. Если я правильно понял, то вы можете завести глобальные переменные, в одном прерывании в них сохранять, а во втором заведите локальные переменные, копируйте в них глобальные и работайте с локальными копиями. Если оба прерывания в одном файле - объявите эти глобальные как static, тогда они не будут видны в других файлах. И еще одна мысль - если переменных не много, не знаю как в новых, а в старых версиях можно было залочить несколько регистров и разместить эти переменные в залоченных регистрах.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 6 2007, 07:51
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(SasaVitebsk @ Sep 6 2007, 03:58)  Построено на прерываниях. Между прерываниями разные промежутки. Есть короткие, есть длинные. Как назло именно короткое прерывание сильно загружено. Дабы разгрузить его я пытаюсь часть вычислений вынести в предыдущее не загруженное прерывание. Уже полностью перешёл на указатели, но всё равно шляпа получается. Я тоже не особенно понял суть проблемы, а свои подобные решаю так... Я тоже разбиваю некоторую обработку внутри прерываний на более мелкие части, чтобы сократить время прерывания, но не понимаю, как это связано с классом переменных. Все переменные являются статическими (точнее, определены снаружи), после выполнения части обработки происходит выход из прерывания, а при последующем входе осуществляется другая часть расчета. Выбор - по interrupt_counter. Убогость системы прерываний AVR удручает, но что делать? Чтобы сократить перебор в switch, можно сделать массив функций и выбирать по индексу, но оптимизатор сам это может сделать. Вот мой пример обработки клавиатуры : Код switch (interrupt_counter) // to reduce time of function { case 0: interrupt_counter=1; //refresh statuses kbd_status3=kbd_status2; kbd_status2=kbd_status1; kbd_status1=kbd_status0; kbd_status0=Kbd; interrupt_counter=interrupt_counter; break;
case 1: interrupt_counter=2; //check pressed key every tic pressed_keys=kbd_status0&kbd_status1&kbd_status2; if (pressed_keys&(~kbd_status3)) if (bSound==SoundAlwaysOn) Beep(5,1); found_events|=pressed_keys&(~kbd_status3); // found edge if ((pressed_keys==kUp+kDn)&& // now this two keys ((~kbd_status3)&(kUp+kDn)))//and before it wasn't so {found_events|=kUpDn; // set new bit found_events&=~(kUp+kDn); break; } //reset usual bits if ((pressed_keys==kUp+kSt)&& // now this two keys ((~kbd_status3)&(kUp+kSt)))//and before it wasn't so {found_events|=kUpSt; // set new bit found_events&=~(kUp+kSt); break; }//reset usual bits if ((pressed_keys==kDn+kSt)&& // now this two keys ((~kbd_status3)&(kDn+kSt)))//and before it wasn't so {found_events|=kDnSt; // set new bit found_events&=~(kDn+kSt); break; } //reset usual bits break; case 2: interrupt_counter=3; //check if key the same as before if ((pressed_keys&kUp)||(pressed_keys&kDn)) //key for repeat { if (pressed_keys==old_pressed_keys) // same key? { if (pressed_counter) // yes, time over? {pressed_counter--;} //No else { pressed_counter=NextDelay; //Yes found_events=pressed_keys; } //reset } else // other key {pressed_counter=StartDelay; } }; old_pressed_keys=pressed_keys; break; case 3: interrupt_counter++; break; default: interrupt_counter=0; }
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Sep 6 2007, 09:20
|
Бывалый
    
Группа: Свой
Сообщений: 1 584
Регистрация: 7-08-07
Пользователь №: 29 615

|
Цитата(Dog Pawlowa @ Sep 6 2007, 11:51)  Я тоже разбиваю некоторую обработку внутри прерываний на более мелкие части, чтобы сократить время прерывания, но не понимаю, как это связано с классом переменных. Попробую объяснить. Используем C++. Объявление класса class TCalc { int x,y,z; // промежуточные переменные public: TCalc(void) {x=y=z=0;}; // начальная инициализация void F!(void); // первая функция заносит результат в x void F2(void); // вторая функция заносит результат в y void F3(void); // третья функция заносит результат в z int Read_Z(void) {return z;}; }; Реализация,допустим для F3 void TCalc::F3(void) {z=x+y*y;}; В main.cpp есть строчка TCalc Calc; В других, где нужно extern TCalc Calc; В прерывании 1 Calc.F1(); В прерывании 2 Calc.F2(); В прерывании 3 Calc.F3(); Где нужен результат ....=Calc.Read_Z(); Обратите внимание никаких статиков, инициализаций в main, volatile и т.д. Для любителей указателей можно сделать и на них, но рекомендую прочитать соответствующий пост для понимамания, где они нужны, а где вредны.
|
|
|
|
|
Sep 6 2007, 11:27
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Лучше всех понял проблему Сергей Борщ. Я так и сделал. Но всё равно есть лишние операции. Хотелось бы от них избавится. Приведу код с пояснениями. Код __no_init uint8_t static *uFaz1,*uFaz2,*uFaz3, *uFaz4,*uFaz5,*uFaz6; // Вспомогательный. Указатель на текущую фазу двигателя Это объявление по сути в этих переменных нет необходимости. Чисто чтобы одну операцию разбить на два прерывания. Код // Обработка Шима тех каналов (двигателей) которые отрабатывают угол 45 // вывод уровня 0.707 уровень 2 #pragma vector=TIMER1_COMPB_vect __interrupt static void pvPWWLvl2(void) { // Вывод предварительно расчитанных значений портов PORTE = bOportE[2]; PORTA = bOportA[2]; PORTC = bOportC[2]; // Предварительный расчёт фаз двигателей. Требуется для фазы C uint8_t bFaz; bFaz = (wStateReal[0] & (MAXFAZ-1))>>2; // Выделить фазу одного двигателя порта uFaz1 = &bfPortSh1[0][0]+bFaz; bFaz = (wStateReal[1] & (MAXFAZ-1))>>2; // Выделить фазу одного двигателя порта uFaz2 = &bfPortSh2[0][0]+bFaz; bFaz = (wStateReal[2] & (MAXFAZ-1))>>2; // Выделить фазу одного двигателя порта uFaz3 = &bfPortSh3[0][0]+bFaz; bFaz = (wStateReal[3] & (MAXFAZ-1))>>2; // Выделить фазу одного двигателя порта uFaz4 = &bfPortSh4[0][0]+bFaz; bFaz = (wStateReal[4] & (MAXFAZ-1))>>2; // Выделить фазу одного двигателя порта uFaz5 = &bfPortSh5[0][0]+bFaz; bFaz = (wStateReal[5] & (MAXFAZ-1))>>2; // Выделить фазу одного двигателя порта uFaz6 = &bfPortSh6[0][0]+bFaz; } Это первое прерывание с места где "Предварительный ..." идёт подготовка инфы для следующего прерывания Код // Обработка Шима тех каналов (двигателей) которые отрабатывают угол 67.5. Расчёт предустановленных значений портов. // вывод уровня 0.9239 уровень 3 #pragma vector=TIMER1_COMPC_vect __interrupt static void pvPWWLvl3(void) { // Вывод предварительно расчитанных значений портов PORTE = bOportE[3]; PORTA = bOportA[3]; PORTC = bOportC[3]; // Расчёт предустановленных значений портов. uint8_t i, *faz1,*faz2,*faz3,*faz4,*faz5,*faz6; faz1=uFaz1; faz2=uFaz2; faz3=uFaz3; faz4=uFaz4; faz5=uFaz5; faz6=uFaz6; for(i=0;i<MAXLVLPWW;i++) { bOportE[i] = *faz1++ | *faz2++; // сформировать значение порта bOportA[i] = *faz3++ | *faz4++; // сформировать значение порта bOportC[i] = *faz5++ | *faz6++; // сформировать значение порта }; } Здесь вы видите локальные переменные которые введены чтобы избавится от сохранения uFaz. Но суть в том, что сохранять то мне не надо. В следующем цикле эти значения будут высчитаны заново. Введение локальных переменных Дало самый оптимальный код, но может всё таки кто-нибудь из опыта подскажет другое решение. Важно быстродействие последнего прерывания
|
|
|
|
|
Sep 6 2007, 13:05
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(SasaVitebsk @ Sep 6 2007, 15:27)  Введение локальных переменных Дало самый оптимальный код, но может всё таки кто-нибудь из опыта подскажет другое решение. Важно быстродействие последнего прерывания Быстрее всего можно сделать при помощи ассемблера. Но тогда огласите, какой процессор. Например, если это ARM, то можно в обработчике прерывания, которое подготавливает данные, подготовленные данные сохранить в регистрах процессора R8_fiq, ..., R14_fiq. В быстром прерывании перейти в режим FIQ и использовать эти регистры.
|
|
|
|
|
Sep 6 2007, 19:45
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(scifi @ Sep 6 2007, 16:05)  Быстрее всего можно сделать при помощи ассемблера. Но тогда огласите, какой процессор. Например, если это ARM, то можно в обработчике прерывания, которое подготавливает данные, подготовленные данные сохранить в регистрах процессора R8_fiq, ..., R14_fiq. В быстром прерывании перейти в режим FIQ и использовать эти регистры. Нет процессор at90can128. Применять ASM не хочу. У меня в первый день все двигатели заработали. Ошибки только инициализации и расчётных констант были. Ну и одна ошибка выхода за пределы массива. Что-то я не припомню чтобы на АСМе у меня в первый день что-то заработало. Хочу средствами Си. Нет значит нет. Немного снижу частоту ШИМа. Спасибо за участие
|
|
|
|
|
Sep 8 2007, 10:12
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Цитата(vmp @ Sep 7 2007, 20:42)  Похоже, здесь основная проблема - это ограничения Си. Для передачи между функциями (в частном случае прерываниями) приходится использовать статические переменные. Каждая их модификация на AVR - это загрузка в регистр, модификация, сохранение в память. Локальные же переменные могут храниться в регистрах и доступ к ним заметно быстрее. Действительно, в стандартном С такая проблема есть. Но конкретно в EWAVR есть возможность выделить регистр(ы) для своих целей, и компилятор их использовать не будет. Делается это, насколько помню, с помощью расширенного ключевого слова __regvar. Цитата(vmp @ Sep 7 2007, 20:42)  2. Перешел бы от указателей (слова) к байтовым индексам. По идее они должны работать быстрее, но надо смотреть, что сгенерит компилятор. Там (в EWAVR) можно разместить объект в секции TINY, которая размещается в младшем сегменте - в 8-разрядном адресном пространстве, при обращении к объектам, размещенным в этом адресном пространстве, обращение осуществляется на основе, понятно, 8-битных адресов. Все это тоже, ессно, расширение, объект объявляется с помощью расширенного ключевого слова __tiny.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 8 2007, 10:49
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(SasaVitebsk @ Sep 6 2007, 14:27)  Код // Обработка Шима тех каналов (двигателей) которые отрабатывают угол 67.5. Расчёт предустановленных значений портов. // вывод уровня 0.9239 уровень 3 #pragma vector=TIMER1_COMPC_vect __interrupt static void pvPWWLvl3(void) { // Вывод предварительно расчитанных значений портов PORTE = bOportE[3]; PORTA = bOportA[3]; PORTC = bOportC[3]; // Расчёт предустановленных значений портов. uint8_t i, *faz1,*faz2,*faz3,*faz4,*faz5,*faz6; faz1=uFaz1; faz2=uFaz2; faz3=uFaz3; faz4=uFaz4; faz5=uFaz5; faz6=uFaz6; for(i=0;i<MAXLVLPWW;i++) { bOportE[i] = *faz1++ | *faz2++; // сформировать значение порта bOportA[i] = *faz3++ | *faz4++; // сформировать значение порта bOportC[i] = *faz5++ | *faz6++; // сформировать значение порта }; } Здесь вы видите локальные переменные которые введены чтобы избавится от сохранения uFaz. Но суть в том, что сохранять то мне не надо. В следующем цикле эти значения будут высчитаны заново. Введение локальных переменных Дало самый оптимальный код, но может всё таки кто-нибудь из опыта подскажет другое решение. Важно быстродействие последнего прерывания Тут вообще много кода генерит иар. Так как проц у вас с немалым флешем, я думаю можно особо память программ не экономить. Посему предлагаю такой вариант: Код //Заменяем массивы на просто переменные char bOportE0; char bOportE1; char bOportE2; char bOportE3; char bOportA0; char bOportA1; char bOportA2; char bOportA3; char bOportC0; char bOportC1; char bOportC2; char bOportC3; #pragma vector=TIMER1_COMPC_vect __interrupt static void pvPWWLvl3(void) { PORTE = bOportE3; PORTA = bOportA3; PORTC = bOportC3; uint8_t *faz1,*faz2; faz1=uFaz1; faz2=uFaz2; bOportE0=*faz1++|*faz2++; bOportE1=*faz1++|*faz2++; bOportE2=*faz1++|*faz2++; bOportE3=*faz1++|*faz2++;
faz1=uFaz3; faz2=uFaz4; bOportA0=*faz1++|*faz2++; bOportA1=*faz1++|*faz2++; bOportA2=*faz1++|*faz2++; bOportA3=*faz1++|*faz2++; faz1=uFaz5; faz2=uFaz6; bOportC0=*faz1++|*faz2++; bOportC1=*faz1++|*faz2++; bOportC2=*faz1++|*faz2++; bOportC3=*faz1++|*faz2++;
} и отключаем для этого модуля кластеризацию переменных. Это освободит один индексный регистр и здорово сократит код. А вообще, наверное надо копать не от финальных процедур, а чуть выше, возможно, пересмотрев алгоритм генерации данных, можно будет сильно улучшить производительность... Если есть возможность, расскажите, какие данные вам надо лить в порты, попробуем придумать что-то оригинальное...
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
  |
3 чел. читают эту тему (гостей: 3, скрытых пользователей: 0)
Пользователей: 0
|
|
|