Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Временные интервалы
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
Страницы: 1, 2
Andruxa-1
Я задействовал таймер1, прерывание по переполнению. Таймер служит для подсчета тиков ну и вычисления системного времени. Внешний кварц на 20 Мгц. Можно ли оптимизировать вычисления.
CODE
#include <avr\io.h>
#include <avr\interrupt.h>

uint32_t ulSystemTime = 0;
uint32_t ulTmp;

void SistemTimerInit(){
TCCR1A = 0;
TIMSK |= (1<<TOIE1);
TCCR1B |= (1<<CS10);
}

uint32_t GetSystemTimeMS(){
cli();

ulTmp = (ulSystemTime<<16) + TCNT1;

sei();

return (ulTmp);
}

SIGNAL(SIG_OVERFLOW1)
{
cli();

ulSystemTime++;

sei();
}
Палыч
По какому критерию Вы бы хотели провести оптимизацию? Можно предложить:
CODE
#include <avr\io.h>
#include <avr\interrupt.h>

uint16_t uSystemTime = 0;

void SistemTimerInit(){
TCCR1A = 0;
TIMSK |= (1<<TOIE1);
TCCR1B |= (1<<CS10);
}

uint32_t GetSystemTimeMS(){
uint32_t ulTmp;
cli();
ulTmp = ((uint32_t)uSystemTime<<16) + TCNT1;
sei();
return (ulTmp);
}

SIGNAL(SIG_OVERFLOW1)
{
cli();
uSystemTime++;
sei();
}


Ещё: в процедуре обработки прерывания команды запрета/разрешения прерываний - излишни - они и так запрещены.
Andruxa-1
этот кусок занимает 22 команды в дизасме ulTmp = ((uint32_t)uSystemTime<<16) + TCNT1;
Поскольку uSystemTime и ulTmp это 4-ч байтовые, думаю можно оптимизировать вычисление, только, честно говря, я не знаю как sad.gif

Я формирую ШИМ с высокой длительностью 1 млс и низкий 10 млс, на фронтах погрешность в 31 мкс. Вот как я формирую ШИМ:
CODE

for(unsigned char i = 0; i < MAX_CHANEL; i++)
{
if(endis & (1<<i)) //Канал вкл/Выкл?
{
tt = GetSystemTimeMS() - ms[i];

if(curate & (1<<i)) //Текущий уровень высокий
{
if(tt > tTiming[1][i])
{
PORTC |= (1<<i);
curate &= ~(1<<i);
ms[i] = GetSystemTimeMS();
}
}

if((curate & (1<<i)) == 0)
{
if(tt > tTiming[0][i])
{
PORTC &= ~(1<<i);
curate |= (1<<i);
ms[i] = GetSystemTimeMS();
}
}
}//if(endis & (1<<i))
}//for(unsigned char i = 0; i < MAX_CHANEL; i++)
MrYuran
Цитата(Andruxa-1 @ Jun 21 2010, 14:22) *
Я формирую ШИМ с высокой длительностью 1 млс и низкий 10 млс, на фронтах погрешность в 31 мкс.

"Погрешность" - это джиттер чтоли? Или постоянная величина?
А что мешает задействовать аппаратный ШИМ?
Andruxa-1
Цитата(MrYuran @ Jun 21 2010, 13:28) *
"Погрешность" - это джиттер чтоли? Или постоянная величина?

Да, джиттер.
Цитата(MrYuran @ Jun 21 2010, 13:28) *
А что мешает задействовать аппаратный ШИМ?

Мне нужно генерировать ШИМ от 0,1 Гц до 2 кГц. И еще на 8 каналов.
Палыч
Цитата(Andruxa-1 @ Jun 21 2010, 14:22) *
думаю можно оптимизировать вычисление, только, честно говря, я не знаю как sad.gif

Сделать ассемблерную вставку или написать GetSystemTimeMS на ассемблере. Процедура возвращает результат на регистрах. Фактически всё, что надо сделать в этой процедуре - положить uSystemTime на регистры (старшие байты - посмотреть в документации - на какие?) и считать в нужные регистры TCNT1 (два младших байта). Плюс команды cli и sei, да ещё команда возврата... Процедуру, короче описанной здесь - не придумать.
MrYuran
Цитата(Andruxa-1 @ Jun 21 2010, 14:32) *
Да, джиттер.
Мне нужно генерировать ШИМ от 0,1 Гц до 2 кГц. И еще на 8 каналов.

При такой организации процесса ничего не поделаешь.
Непредсказуемая задержка возникает, когда в одном или нескольких n-1 предыдущих каналов происходит переключение уровня.
Надо как-то симметрировать алгоритм.
То есть, задержка процедуры обработки одного канала должна быть одинаковой, независимо от внешних условий и ветки алгоритма, по которой в данный момент пошла программа.
Палыч
Цитата(Andruxa-1 @ Jun 21 2010, 14:22) *
Поскольку uSystemTime и ulTmp это 4-ч байтовые...
Отводить четыре байта под uSystemTime - излишество. Достаточно выделить два - всё равно значение сдвигается влево на 16 разрядов...


Цитата(MrYuran @ Jun 21 2010, 14:55) *
Надо как-то симметрировать алгоритм.
Автор вопроса не огласил всех требований - возможно и это не поможет...

P.S. Может быть, подход к решению задачи - в корне неверный. Огласите исходнуя задачу: что генерить, с какими временами и точностью, как задаются времена (период/длительность)...
Andruxa-1
Многоканальный генератор электрических импульсов переменной частоты и скважности на восемь каналов.
Микроконтроллер ATMega8 с внешним кварцем 20Мгц, имеющая восемь выходов на которые и
генерируется частота. Генерация импульсов в диапазоне частот: 0.1 Гц - 2 Кгц. Управление частотой и
скважностью производится посредством установки длительности высокого, и низкого уровней. Уровни задаются квантами времени.
Погрешность не более 1%.
MrYuran
Цитата(Andruxa-1 @ Jun 21 2010, 15:41) *
Генерация импульсов в диапазоне частот: 0.1 Гц - 2 Кгц.
Погрешность не более 1%.

Посчитаем...
2кГц->500мкс, х1% = 5мкс.
Нереально в принципе при софтовой реализации.
Либо загрубляем требования, либо реализуем аппаратно.
Например, внешней ЦПЛД-шкой
Andruxa-1
Цитата(MrYuran @ Jun 21 2010, 14:47) *
Посчитаем...
2кГц->500мкс, х1% = 5мкс.
Нереально в принципе при софтовой реализации.


Аппаратно, на 8 каналов, можно реализовать на этом камне? Подскажите куда копать.
MrYuran
Цитата(Andruxa-1 @ Jun 21 2010, 16:00) *
Аппаратно, на 8 каналов, можно реализовать на этом камне? Подскажите куда копать.

На каком именно?
На АВР по-моему, вообще туговато с железными ШИМами.
Andruxa-1
ATMega8
Палыч
Цитата(Andruxa-1 @ Jun 21 2010, 16:36) *
ATMega8
В AVR не более 2-3 аппаратных ШИМ. Восемь независимых ни на каком AVR c требуемыми Вам параметрами получить не удастся...
MrYuran
Цитата(Палыч @ Jun 21 2010, 16:51) *
Восемь независимых ни на каком AVR c требуемыми Вам параметрами получить не удастся...

А вот на MSP430 - запросто!
Всё, молчу, молчу...
=GM=
Можно вот так оптимизировать
Код
tt=GetSystemTimeMS()-ms;
for(unsigned char i=0;i<MAX_CHANEL;i++)
{
if(imageC & (1<<i))
{
  if(tt > tTiming1[i]) imageС |=(1<<i);
}
else
{
  if(tt > tTiming0[i]) imageС &=~(1<<i);
}
}
PORTC=imageC;
ms=GetSystemTimeMS();
Думаю джиттер существенно сократится, ещё можно цикл развернуть.

Andruxa-1
Цитата(=GM= @ Jun 21 2010, 18:26) *
Можно вот так оптимизировать
Думаю джиттер существенно сократится, ещё можно цикл развернуть.


А как такой цикл можно развернуть?
Палыч
Цитата(Andruxa-1 @ Jun 22 2010, 14:02) *
А как такой цикл можно развернуть?
В Вашем случае - вместо цикла записываете его тело восемь раз, подставляя вместо параметра цикла (i) константы (0,1,2,...7) в соответствующие части. Выполнятся это будет быстрее - константные выражения посчитаются на этапе трансляции.
=GM=
Совершенно верно.

Андрукса, какой у вас джиттер получается с вариантом из поста #16?
Andruxa-1
Цитата(=GM= @ Jun 22 2010, 14:26) *
Совершенно верно.

Андрукса, какой у вас джиттер получаетсся с вариантом из поста #16?


джиттера нет, но получается меандр
demiurg_spb
Цитата(Andruxa-1 @ Jun 22 2010, 16:32) *
джиттера нет, но получается меандр

smile.gif
- Алло! Это баня?
- Нет, это Балера!
=GM=
Цитата(Andruxa-1 @ Jun 22 2010, 11:32) *
джиттера нет, но получается меандр

Похоже, это я лоханулся, переменная MS должна быть для каждого канала своя, т.е. массив MS(i). Возвращайте взад присвоение MS(i) в обоих ветвях цикла.

И лучше не вызывайте GetSystemTime, а просто где требуется вставьте тело подпрограммы.
Andruxa-1
Цитата(=GM= @ Jun 22 2010, 15:58) *
Похоже, это я лоханулся, переменная MS должна быть для каждого канала своя, т.е. массив MS(i). Возвращайте взад присвоение MS(i) в обоих ветвях цикла.

И лучше не вызывайте GetSystemTime, а просто где требуется вставьте тело подпрограммы.


Я пробовал этот вариант, сути не меняет
=GM=
Сути чего?
Andruxa-1
Цитата(=GM= @ Jun 22 2010, 16:32) *
Сути чего?

сори, не правильно выразился, джиттер остается и в этом варианте
=GM=
Ну, при программном подходе джиттер будет всегда, вопрос состоит в том, удовлетворяет величина джиттера или нет?

Чтобы ваш вариант привести в божеский вид, надо работать и работать, например можно вычислять не длительность текущего периода 1 или 0, а сразу время его завершения. Тогда в цикле будет только сравнение, а не вычисление и сравнение. А это ого-го сколько времени для 8 каналов. Вот как-то так:

Код
if(PORTC & _BV(i))                    //if(PORTC & (1<<i))
{
  sysTime=(ulsysTime<<16)+TCNT1;
  if(sysTime>endTime[i])
  {
   PORTC &=~_BV(i);                    //PORTC &=~(1<<i);
   endTime[i]=sysTime+tLow[i];            //новое время завершения 0
  }
}
else
{
  sysTime=(ulsysTime<<16)+TCNT1;
  if(sysTime>endTime[i])
  {
   PORTC |=_BV(i);                    //PORTC |=(1<<i);
   endTime[i]=sysTime+tHigh[i];            //новое время завершения 1
  }
}
xemul
Завести таймер на 5 мкс, в его прерывании делать только
Код
PORTC = tmpC;
Flag_5us = 1;

В основном цикле
Код
uint24_t ch1_cnt, ..., ch8_cnt; // uint24_t придумать, для счётчиков при 0.1 Гц и 5 мкс нужен 21 бит
                                // с uint32_t будут лишние считания
if(Flag_5us)
{
   if(!--ch1_cnt) { tmpC ^= 1; ch1_cnt = (tmpC & 1)? ch1_high_time: ch1_low_time; }
   if(!--ch2_cnt) { tmpC ^= 2; ch2_cnt = (tmpC & 2)? ch2_high_time: ch2_low_time; }
...
   if(!--ch8_cnt) { tmpC ^= 128; ch8_cnt = (tmpC & 128)? ch8_high_time: ch8_low_time; }
   Flag_5us = 0;
}

Но в 5 мкс (100 тактов на 20 МГц) if(Flag_5us) {...} всё равно не уложится, так что урезайте осетра по разрешению.
singlskv
Цитата(xemul @ Jun 22 2010, 20:18) *
uint24_t ch1_cnt, ..., ch8_cnt; // uint24_t придумать, для счётчиков при 0.1 Гц и 5 мкс нужен 21 бит
это тока АСМ
Цитата
Но в 5 мкс (100 тактов на 20 МГц) if(Flag_5us) {...} всё равно не уложится, так что урезайте осетра по разрешению.
А мне вот кажется что на АСМ такое можно изобразить,
при этом осетра нужно урезать только в части возможности менять времена Ton Toff не на каждом цикле.
Ну и конечно все 8 каналов за 5мкс считать невозможно, нужно буферирование на 16 тиков по 5мкс
и рассчет не более 2х каналов за прерывание/тик.
Andruxa-1
Спасибо за ответы и подсказки, буду пробовать.
singlskv
Кстати, задачка на самом деле очень интересная, и вполне решаемая...
Не забываем что Ton+Toff >= 100 тиков для каждого канал и такта 5мкс,
а всего смен значения порта за 100тиков не более 16
Ну и..., помойму это тот редкий случай когда вложенные прерывания могут быть очень даже к месту...
По моим прикидкам можно получить джиттер на каналах около 1-2мкс
ну и еще останется время на получение новых значений Ton/Toff сверху по уарту на 115200

Как-то так...
=GM=
Цитата(xemul @ Jun 22 2010, 15:18) *
Завести таймер на 5 мкс, в его прерывании делать только
Код
PORTC = tmpC;
Flag_5us = 1;

В основном цикле
Код
uint24_t ch1_cnt, ..., ch8_cnt; // uint24_t придумать, для счётчиков при 0.1 Гц и 5 мкс нужен 21 бит
                                // с uint32_t будут лишние считания
if(Flag_5us)
{
   if(!--ch1_cnt) { tmpC ^= 1; ch1_cnt = (tmpC & 1)? ch1_high_time: ch1_low_time; }
   if(!--ch2_cnt) { tmpC ^= 2; ch2_cnt = (tmpC & 2)? ch2_high_time: ch2_low_time; }
...
   if(!--ch8_cnt) { tmpC ^= 128; ch8_cnt = (tmpC & 128)? ch8_high_time: ch8_low_time; }
   Flag_5us = 0;
}

Но в 5 мкс (100 тактов на 20 МГц) if(Flag_5us) {...} всё равно не уложится, так что урезайте осетра по разрешению.

Тогда уж лучше весь if(Flag_5us) {...} вставить в прерывание, по крайней мере, флаг не придётся проверять. Но прерывание для данной задачи - зло, поэтому лучше делать без прерываний вообще, не будет потерь времени на сохранение контекста в прерывании.

Конструкция if(!--ch1_cnt) ... выглядит компактно, но только на бумаге, здесь 8 раз вычитается то, что в предыдущем решении таймер1 делает аппаратно.

По моим грубым прикидкам, если писать на асме, то можно уложиться в максимальный джиттер 8 мкс.
Палыч
Цитата(singlskv @ Jun 25 2010, 01:04) *
Кстати, задачка на самом деле очень интересная, и вполне решаемая...
Не забываем что Ton+Toff >= 100 тиков для каждого канал и такта 5мкс,
а всего смен значения порта за 100тиков не более 16
Вот и давайте посчитаем: сколько отводиться на смену выходного сигнала? Сто делим на 16 = 6 (четыре такта отдадим на смену Ton/Toff rolleyes.gif ). Не все команды выполняются за один такт. Следовательно, нужно определить прошедшее время удержания сигнала, сравнить с некой константой, при необходимости изменить выходной сигнал и проделать некие действия по новому отсчету времени, и всё это улолжить максимум в шесть команд (точнее в несколько команд, которые выполняются за шесть тактов). Сможите? Пусть даже на ассемблере.
MrYuran
В общем, если отбросить чисто академические изыски, не имеющие практического значения, нужно поставить снаружи ЦПЛД за 30р и упаковать туда 8-канальный таймер-счётчик.
Либо применить готовые программируемые интервальные таймеры

Либо заменить контроллер на более подходящий для таких целей
singlskv
Цитата(Палыч @ Jun 25 2010, 09:57) *
Вот и давайте посчитаем: сколько отводиться на смену выходного сигнала? Сто делим на 16 = 6 (четыре такта отдадим на смену Ton/Toff rolleyes.gif ). Не все команды выполняются за один такт. Следовательно, нужно определить прошедшее время удержания сигнала, сравнить с некой константой, при необходимости изменить выходной сигнал и проделать некие действия по новому отсчету времени, и всё это улолжить максимум в шесть команд (точнее в несколько команд, которые выполняются за шесть тактов). Сможите? Пусть даже на ассемблере.
Вы чего-то совсем не то насчитали, 16смен значения порта будет в самом худшем случае за
100*5мкс=500мкс (если на всех выходах нужно 2КГц и никакие фронты не совпадают)

Цитата(=GM= @ Jun 25 2010, 02:35) *
Тогда уж лучше весь if(Flag_5us) {...} вставить в прерывание, по крайней мере, флаг не придётся проверять.
Почти так, только не совсем
Цитата
Но прерывание для данной задачи - зло, поэтому лучше делать без прерываний вообще, не будет потерь времени на сохранение контекста в прерывании.
Прерывания позволят освободить основной цикл проги для приема обновленных значений Ton/Toff
извне, например по уарту или с кнопочек.
Большую часть сохранения контекста можно делать после вывода значения в порт перед вычислениями(это конечно только на АСМ).

Видимо Вы не до конца поняли суть предлагаемого алгоритма.
За 500мкс сменить значение порта нужно не более 16 раз.
Если эти 16смен забуферировать и приделать к ним счетчики сколько времени не менять значение на порту,
то у нас будет куча времени на рассчеты следующих значений.
Вопрос только в правильной организации рассчетов в прерывании и одновременный вывод из буфера уже сохраненных.
Так вот здесь очень красиво можно вписать вложенные прерывания, причем прерывание то будет одно,
только оно может вызываться из уже работающего того же самого обработчика.

MrYuran
Цитата(singlskv @ Jun 25 2010, 11:08) *
Видимо Вы не до конца поняли суть предлагаемого алгоритма.
За 500мкс сменить значение порта нужно не более 16 раз.

Вы видимо, тоже не до конца.
Эти смены не равномерно распределены по интервалу, а могут вплотную друг за другом идти, либо вообще накладываться.
Вот в эти моменты и возникает джиттер. И требование в 5мкс накладывает очень жёсткие требования...
singlskv
Цитата(MrYuran @ Jun 25 2010, 11:18) *
Вы видимо, тоже не до конца.
Эти смены не равномерно распределены по интервалу, а могут вплотную друг за другом идти, либо вообще накладываться.
Вот в эти моменты и возникает джиттер. И требование в 5мкс накладывает очень жёсткие требования...
Как раз для случаев когда они идут с интервалом в 5мкс и вводится буферирование,
и вывод в порт при смене значения всегда из буфера в самом начале прерывания,
а после этого дальнейший рассчет нового значения на освободившееся место в буфере.
=GM=
Цитата(MrYuran @ Jun 25 2010, 05:07) *
-В общем, если отбросить чисто академические изыски, не имеющие практического значения, нужно поставить снаружи ЦПЛД за 30р и упаковать туда 8-канальный таймер-счётчик.
-Либо применить готовые программируемые интервальные таймеры
-Либо заменить контроллер на более подходящий для таких целей

На самом деле в CPLD нужно ставить шестнадцать 24-битных счётчиков ПЛЮС 16 регистров хранения длительностей полупериодов ПЛЮС схема управления ПЛЮС интерфейс связи с МК...придётся осетра за 30 рубчиков урезать :-).

Ещё вариант - поставить четыре 8-ногие тиньки, у которых есть два аппаратных ШИМА...Но можно и чисто программно, те же 6-ногие АТмега10 подойдут. У меня получается где-то 18 тактов на обработку одного канала, т.е. для софтового генератора джиттер не превосходит 1 мкс/канал.
xemul
Цитата(=GM= @ Jun 25 2010, 02:35) *
Тогда уж лучше весь if(Flag_5us) {...} вставить в прерывание, по крайней мере, флаг не придётся проверять. Но прерывание для данной задачи - зло, поэтому лучше делать без прерываний вообще, не будет потерь времени на сохранение контекста в прерывании.

Я затруднюсь сказать, что лучше, не видя остальной части загадки. Потери на общение с волатильными счетчиками могут оказаться больше и непредсказуемей.
Цитата
Конструкция if(!--ch1_cnt) ... выглядит компактно, но только на бумаге, здесь 8 раз вычитается то, что в предыдущем решении таймер1 делает аппаратно.

Я предложил "рыбу" решения на С - так короче. имхо, исходно было понятно, что отдавать оптимизацию компилятору в этой задаче ... неразумно.
Извините, не понял, какое из предыдущих решений Вы имели в виду.
=GM=
Из поста #26.

1) Ну смотрите, объясняю конструкцию if(!--ch1_cnt) на пальцах. Надо загрузить 4-х байтный счётчик из памяти в регистры, вычесть 1 и сохранить обратно в памяти. И так 8 раз!

2) В прерывании вы выставляете флаг5мкс, а в фоне ждёте его. Если поместить тело if в прерывание, то ждать флага не надо, наступило прерывание - выполняем тело if. Всяко быстрее будет.
xemul
Цитата(=GM= @ Jun 25 2010, 15:30) *
Из поста #26.

1) Ну смотрите, объясняю конструкцию if(!--ch1_cnt) на пальцах. Надо загрузить 4-х байтный счётчик из памяти в регистры, вычесть 1 и сохранить обратно в памяти. И так 8 раз!

За объяснение спасибо. Я об этом смутно догадывался, Вы подтвердили мои опасения.smile.gif
В посте #26 в любой ветке выполняется 3 бинарные (!) операции с uint32_t (две из них - с загрузкой и сохранением). И так 8 раз!
Изначально (мной) было сказано, что достаточно 3 байтов на счётчик, с соответствующими умолчательными предположениями о реализации и оптимизации.
Декремент 3-х (или 4-х) байтового числа с проверкой на 0, имхо, не сложнее if(sysTime>endTime[i]), которая выполняется через вычитание двух uint32_t и проверку знака результата.
Цитата
2) В прерывании вы выставляете флаг5мкс, а в фоне ждёте его. Если поместить тело if в прерывание, то ждать флага не надо, наступило прерывание - выполняем тело if. Всяко быстрее будет.

Вряд ли - только на сохранении/восстановлении регистровой пары в прерывании будет потеряно столько же. В качестве же бесплатного довеска - некоторое количество volatile uint32_t с сопутствующим оверхедом.
Ну и скорость в данном месте как-то сбоку - на джиттер не влияет, а если у контроллера не хватит скорострельности, то абсолютно фиолетово, где не хватит - в прерывании или в фоне.
=GM=
Немного повергло в шок, как компилятор WinAVR транслирует вашу конструкцию
Код
//HEMUL VERSION
  if(!--endTime0)
19c:    8d 81           ldd    r24, Y+5; 0x05
19e:    9e 81           ldd    r25, Y+6; 0x06
1a0:    af 81           ldd    r26, Y+7; 0x07
1a2:    b8 85           ldd    r27, Y+8; 0x08
1a4:    01 97           sbiw    r24, 0x01; 1
1a6:    a1 09           sbc    r26, r1
1a8:    b1 09           sbc    r27, r1
1aa:    8d 83           std    Y+5, r24; 0x05
1ac:    9e 83           std    Y+6, r25; 0x06
1ae:    af 83           std    Y+7, r26; 0x07
1b0:    b8 87           std    Y+8, r27; 0x08
1b2:    8d 81           ldd    r24, Y+5; 0x05
1b4:    9e 81           ldd    r25, Y+6; 0x06
1b6:    af 81           ldd    r26, Y+7; 0x07
1b8:    b8 85           ldd    r27, Y+8; 0x08
1ba:    00 97           sbiw    r24, 0x00; 0
1bc:    a1 05           cpc    r26, r1
1be:    b1 05           cpc    r27, r1
1c0:    81 f4           brne    .+32    ; 0x1e2 <main+0x166>
  {
   PINC =_BV(0);
1c2:    73 bb           out    0x13, r23; 19
   endTime0 =(PORTC & _BV(0))? tLow0 : tHigh0;
1c4:    a8 9b           sbis    0x15, 0; 21
1c6:    05 c0           rjmp    .+10    ; 0x1d2 <main+0x156>
1c8:    8d 85           ldd    r24, Y+13; 0x0d
1ca:    9e 85           ldd    r25, Y+14; 0x0e
1cc:    af 85           ldd    r26, Y+15; 0x0f
1ce:    b8 89           ldd    r27, Y+16; 0x10
1d0:    04 c0           rjmp    .+8     ; 0x1da <main+0x15e>
1d2:    89 89           ldd    r24, Y+17; 0x11
1d4:    9a 89           ldd    r25, Y+18; 0x12
1d6:    ab 89           ldd    r26, Y+19; 0x13
1d8:    bc 89           ldd    r27, Y+20; 0x14
1da:    8d 83           std    Y+5, r24; 0x05
1dc:    9e 83           std    Y+6, r25; 0x06
1de:    af 83           std    Y+7, r26; 0x07
1e0:    b8 87           std    Y+8, r27; 0x08
  }

Но по зрелому размышлению понял, что лучше и не сделаешь.

Странно, но вот так: if(!endTime0--) получается фрагмент на 4 такта быстрее.
xemul
Цитата(=GM= @ Jun 25 2010, 19:56) *
Но по зрелому размышлению понял, что лучше и не сделаешь.

Вторая загрузка лишняя - достаточно про-OR'ить r24-r27 и сразу brne.
=GM=
А зачем орить? После std сразу brne ... Но это не мне надо объяснять, а компилятору.
=GM=
Вот, немного переделал алгоритм

if(sysTime>endTime0)
{
PINC =_BV(0);
if(PORTC & _BV(0)) endTime0 +=tLow0; //новое время завершения 0 канала
else endTime0 +=tHigh0;
}
. . .
if(sysTime>endTime7)
{
PINC =_BV(7);
if(PORTC & _BV(7)) endTime7 +=tLow7; //новое время завершения 7 канала
else endTime7 +=tHigh7;
}

На асме укладывается в 18 тактов для одного канала при 24-битном времени. Но есть нюанс с системным временем, который пока не преодолел.
demiurg_spb
Цитата(=GM= @ Jun 25 2010, 19:56) *
Немного повергло в шок, как компилятор WinAVR транслирует вашу конструкцию
endTime0 волатильный? Если да то нормально скомпилировал...
=GM=
Цитата(demiurg_spb @ Jun 28 2010, 16:14) *
endTime0 волатильный? Если да то нормально скомпилировал...

Где ж нормально? Сохраняет регистры r24-r27 и тут же загружает ТЕ ЖЕ САМЫЕ РЕГИСТРЫ тем, что только что сохранил, ну не идиотизм? Уж не говорю о том, что операция 4-х байтного вычитания вырабатывает флаг Z, так что после std самое оно оставить только brne.
demiurg_spb
Цитата(=GM= @ Jun 29 2010, 02:08) *
Где ж нормально? Сохраняет регистры r24-r27 и тут же загружает ТЕ ЖЕ САМЫЕ РЕГИСТРЫ тем, что только что сохранил, ну не идиотизм? Уж не говорю о том, что операция 4-х байтного вычитания вырабатывает флаг Z, так что после std самое оно оставить только brne.
А я настаиваю:-), что для volatile фактически так и должно быть.
=GM=
Това-а-рищ Иванов, не надо настаивать на глупости компилятора :-).
demiurg_spb
Для волатильных объектов компилятор фактически не применяет оптимизацию, поэтому
для if(--x) ... мы наблюдаем классику жанра по полной программе (read-modify-store).
Прочитали x декрементировали, сохранили, прочитали вновь, сравнили с нулём.
Естественно, можно было оптимизировать и не производить повторное чтение после записи, но gcc под это видимо не заточен.
Интересно глянуть бы на то как IAR это скушает.

PS: Был очень приятно удивлён Вашему звонку!
Приятно общаться не топча кнопокsmile.gif
Жаль что пивка с Вами не попить - уж больно далеко до Оксфорда.

Кстати как Англия пережила не засчитанный гол на чемпионате мира?
Теперь поди не будут артачится против внедрения видео повтора при вынесении судейских решений...
XVR
Цитата
Естественно, можно было оптимизировать и не производить повторное чтение после записи,
Если компилировалось в режиме С++, то так оптимизировать тоже нельзя - в С++ префиксные операции возвращают референс на модифицируемую переменную. Например можно сделать так:
Код
(++i)*=2;

Цитата
но gcc под это видимо не заточен.
Видимо не мог wink.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.