Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Скорость выполнения кода на atmega640
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2
Leonmezon
Просьба помочь с функцией отправки байт по RS232 от atmega640 (сама функция полностью работает, но необходимо ускорить ее выполнения (на ассемблер перейти не могу - его не знаю).
// Функция передачи данных на ЭВМ по RS232 (масивы B1, B2 - создаю в ОЗУ, передаю по переменно взависимости от флагов. перед массив идут 5 0хF0 - заголовок.
Код
void RS232(void)
{
unsigned int i, j;
unsigned char data[4];
signed long *p;
p=(signed long*)data;
flag_BUF=0;
if (flag_B2==1)
{
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  // Преобразуем signed long B1 и в ЭВМ
     for(i=0; i<600; i++)
     {
        *p=B1[i];
         for (j=0; j<4; j++)
           {
              UDR0 = data[j];
              while ( !( UCSR0A & (1<<UDRE0)) ) { };
           }
     }
}
else
{
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
  UDR0 = 0xF0;
  while ( !( UCSR0A & (1<<UDRE0)) ) { };
// Преобразуем signed long B2 и в ЭВМ
    for(i=0; i<600; i++)
     {
        *p=B2[i];
         for (j=0; j<4; j++)
           {
              UDR0 = data[j];
              while ( !( UCSR0A & (1<<UDRE0)) ) { };
           }
     }      
}
}
Непомнящий Евгений
А зачем вам ускорять скорость ее выполнения? Какая скорость rs232? По-идее, вы вполне будете ее выдерживать... Никаких тяжелых вычислений нет.

ps есть полезный тег code
aaarrr
Переведите работу на прерывания - все равно, на чем написана программа, если она тупо ждет установки флага UART'а.
Leonmezon
Цитата(Непомнящий Евгений @ Jul 24 2009, 15:24) *
А зачем вам ускорять скорость ее выполнения? Какая скорость rs232? По-идее, вы вполне будете ее выдерживать... Никаких тяжелых вычислений нет.

ps есть полезный тег code

1. Скорость RS232 - 115200 бит/с.
2. Прерывания - нельзя - эта функция работает постоянно, как готовы новые данные (т.е. заполнен буфер B1 или В2), а по прирыванию - идет заполнение буферов по переменно.
Вот примерно как идет работа: (лишнее убрал)
Код
void main(void)
{
DDRF=0x0F;
DDRC=0x0F;
SpiInit();
uart0_init();
init_devices();
//Инициализация АЦП
// Задание параметров работы АЦП
// Бесконечный цикл с приемом данных и передачей по RS232
while(1)
{
   if(flag_BUF==1)
    {
      RS232();
    }
   NOP();
}
}
zltigo
Цитата(Leonmezon @ Jul 24 2009, 17:17) *
2. Прерывания - нельзя - эта функция работает постоянно...

Вот именно по этой причине и нужны ПРЕРЫВАНИЯ или,как минимум буферизация, иначе эта фигня не "работает постоянно" а тупо жрет время в ожидании передачи байта. Проблема в том, что система постороена неправльно и никакими ASM тут ничем не поможешь.
Цитата
на ассемблер перейти не могу - его не знаю

И Си тоже sad.gif
Непомнящий Евгений
я что-то не пойму. Вы не успеваете отправлять со скоростью 115200?

В функции особо ускорять нечего. Она просто сует байты УАРТу и ждет, пока они отошлются. Если она не успевает их слать - возможно у вас какие-то "долгоиграющие" прерывания работают...

Цитата(zltigo @ Jul 24 2009, 18:27) *
Вот именно по этой причине и нужны ПРЕРЫВАНИЯ или,как минимум буферизация, иначе эта фигня не "работает постоянно" а тупо жрет время в ожидании передачи байта. Проблема в том, что система постороена неправльно и никакими ASM тут ничем не поможешь.


Дык у топикстартера в главном цикле только эта функция и работает... А остальное, как я понял, в прерываниях. Если и эту функциональность перетащить в прерывания - разве получится какой-то выигрыш?
zltigo
Цитата(Непомнящий Евгений @ Jul 24 2009, 17:30) *
Дык у топикстартера в главном цикле только эта функция и работает... А остальное, как я понял, в прерываниях.

Очень сильно сомневаюсь:
Цитата
// Бесконечный цикл с приемом данных и передачей по RS232

нет там прерываний. Совсем.
Moderator:
Тему перенес.
Палыч
Цитата(Leonmezon @ Jul 24 2009, 14:12) *
... но необходимо ускорить ее выполнения ...
Если в посылках данных есть перебои, которые возникают не от того что буфер с данными не готов, то проблема, скорее всего, не в приведённом Вами коде, а в большом времени работы процедур обработки прерываний в которых эти данные готовятся. Если время работы процедуры прерывания превышает время передачи одного байта, то буферный регистр и регистр сдвига USART могут быть опустошены, и передача прекратится до выхода из прерывания...
Leonmezon
Перерыва в передачи байтов нет (данные принимаются на микроЭВМ, но хочу создать запас между посылками: сейчас примерно прием 400 мс, ожидае 600 мс - хочу увеличить время ожидания 800 мс и соотвественно за 200 мс все передовать) - простоя думаю как укорить этот процесс, чтобы был больший запас времени (физически скорость увеличить нельзя - 115200 бит/с - это максимум что может atmega), возможно можно увеличить скорость программно (т.е. уменьшить время между посылками). Весь алгоритм не привожу - так как неделю потрачу на его объяснение (сам струдом понимаю как его умудрился написать и при этом все работает).


Само прерывание (причем таких три - от трех АЦП обрабатываются последовательно - сигнал RDY - обединен и заводиться на INT6):
CODE
/ Функция получения данных от 1-го АЦП
// Запускается только в том случае если прием разрешен,
// и от АЦП пришел сигнал аппаратный о готовности данных - линия RDY
signed long uzmerenie1(void)
{
unsigned int reg;
unsigned long A, B, C;
signed long rez;
signed long K;
double U;
//Чтение регистра ADC Status
PORTF&=~BIT(0); // CS1 установить в 0 - выбор 1 АЦП
delay();
SpiWriteByte(0x04+0x40);
reg=SpiReadByte();
// Выбор канала готовых данных (аппаратно RDY дает сигнал только о готовности данных,без указания канала
switch (reg)
{
case 0x01:
{
// Установка чтение 0 канала данных
//Устанавливаем регистр для чтения - выходные данные 24 бит - 3 байта
SpiWriteByte(0x08+0x40);
K=K11;
}
break;
case 0x04:
{
// Установка чтение 1 канала данных
//Устанавливаем регистр для чтения - выходные данные 24 бит - 3 байта
SpiWriteByte(0x0A+0x40);
K=K12;
}
break;
default: { return (0x00);}
}
//Чтение данных из регистра АЦП
// Читаем и заносим в буфер
SPDR = 0x00;
while (!(SPSR&0x80)); // ожидание готовности порта, прежде чем считывать
A=SPDR; //считать SPDR - 1 байт
SPDR = 0x00;
while (!(SPSR&0x80)); // ожидание готовности порта, прежде чем считывать
B=SPDR; //считать SPDR - 2 байт
SPDR = 0x00;
while (!(SPSR&0x80)); // ожидание готовности порта, прежде чем считывать
C=SPDR; //считать SPDR - 3 байт
// Закрываем АЦП
PORTF|=BIT(0); // CS1 установить в 1
// Получаем длиное целое из полученных байт
// с учетом что первым идет старший байт
rez=(C+(B<<8)+(A<<16));
// Далее преобразуем с типу double и Преобразуем к напряжению
U=(((double)(rez))*20)/Kd; // Получаем напряжение от 0 до 20 В
// Отнимаем 10 В - для сдига в биполярный сигнал - получаем изменение напряжения от -10 до 10В
U=U-10;
U=U*KU;
// Преобразуем в микровольты и возращаем результат
rez=(signed long)(U*1000000);
rez=rez-K;
return (rez);
}
aaarrr
Оптимизацию советую начать отсюда:
Код
U=(((double)(rez))*20)/Kd; // Получаем напряжение от 0 до 20 В

Работа с длинной плавучкой в прерывании, мягко говоря, изрядно все затормозит.

Ускорить void RS232(void) не получится - если он не отрабатывает за 200 с небольшим миллисекунд при скорости UART 115200, значит ему мешают тормозные прерывания.

P.S. Пожалуйста, пользуйтесь тегами [сode][/сode], иначе вашу программу просто никто не станет читать.
MrYuran
Цитата(Leonmezon @ Jul 24 2009, 19:52) *
физически скорость увеличить нельзя - 115200 бит/с - это максимум что может atmega

Ну и кто вам сказал такую ерунду?

Как делитель установите, так и будет передавать.
Leonmezon
Цитата(aaarrr @ Jul 24 2009, 19:59) *
Оптимизацию советую начать отсюда:
Код
U=(((double)(rez))*20)/Kd; // Получаем напряжение от 0 до 20 В

Работа с длинной плавучкой в прерывании, мягко говоря, изрядно все затормозит.

Ускорить void RS232(void) не получится - если он не отрабатывает за 200 с небольшим миллисекунд при скорости UART 115200, значит ему мешают тормозные прерывания.

P.S. Пожалуйста, пользуйтесь тегами [сode][/сode], иначе вашу программу просто никто не станет читать.

А как? Не совсем понятно чем можно заменить (данные на ЭВМ должны идти уже в напряжении).
Значения коээфициентов: (боле понятно будет)
const signed int K11=-831; // программный учет смещения в 1 канале 1 АЦП вычисляеться на ЭВМ за 1 час
const signed int K12=-999; // программный учет смещения в 2 канале 1 АЦП вычисляеться на ЭВМ за 1 час
const double KU=1; // коэффициент передачи сигнала в аналоговой части 1.077
const double Kd=16777215; // длина всего диапазона для +-10В - 0xFFFFFF
aaarrr
Цитата(Leonmezon @ Jul 24 2009, 20:12) *
А как? Не совсем понятно чем можно заменить (данные на ЭВМ должны идти уже в напряжении).

Заменить целочисленными операциями. Как - подумайте, арифметика у вас элементарная и уж точно не требует 64-х битной плавающей запятой.
MrYuran
Цитата(Leonmezon @ Jul 24 2009, 20:12) *
А как?

U=(((double)(rez))*20)/Kd; // Получаем напряжение от 0 до 20 В
rez=(signed long)(U*1000000);

Ну, как минимум, заменить 3 операции умножения / деления одной
Либо двумя, но без плавучки

U=(((long long)rez)*20000000)/Kd;

А вообще, уже писали.
Подход неверный вкорне.

Работа с UARTом должна происходить по прерываниям в фоне, а в это время обсчитывается следующий пакет.
Leonmezon
Цитата(MrYuran @ Jul 24 2009, 20:24) *
U=(((double)(rez))*20)/Kd; // Получаем напряжение от 0 до 20 В
rez=(signed long)(U*1000000);

Ну, как минимум, заменить 3 операции умножения / деления одной
Либо двумя, но без плавучки

U=(((long long)rez)*20000000)/Kd;

А вообще, уже писали.
Подход неверный вкорне.

Работа с UARTом должна происходить по прерываниям в фоне, а в это время обсчитывается следующий пакет.

Просчитал - не получить, я ограничен возможностями компилятора ( максим на тип signed long - 4 байта), а для работы в чисто целочисленном (без использования плавующей точки - (плавующая то же 4 байтная)) - надо 8 байт. Компилятор - менять не буду!
Может конструкция switch {} заменить на if? (здесь можно что то выиграть)
aaarrr
Цитата(Leonmezon @ Jul 24 2009, 21:02) *
Просчитал - не получить, я ограничен возможностями компилятора ( максим на тип signed long - 4 байта), а для работы в чисто целочисленном (без использования плавующей точки - (плавующая то же 4 байтная)) - надо 8 байт. Компилятор - менять не буду!

Вы ограничены не компилятором, а собственным воображением. У компилятора, наверное, есть тип long long.
Но это все равно не поможет, если сначала делить на 16777215, а затем умножать на 1000000. С таким подходом никакой разрядности не хватит,
надо менять образ мышления.

Цитата(Leonmezon @ Jul 24 2009, 21:02) *
Может конструкция switch {} заменить на if? (здесь можно что то выиграть)

По сравнению с остальным это такая мелочь, что не выйграете ровным счетом ничего.
Leonmezon
Цитата(aaarrr @ Jul 24 2009, 21:22) *
Вы ограничены не компилятором, а собственным воображением. У компилятора, наверное, есть тип long long.
Но это все равно не поможет, если сначала делить на 16777215, а затем умножать на 1000000. С таким подходом никакой разрядности не хватит,
надо менять образ мышления.


По сравнению с остальным это такая мелочь, что не выйграете ровным счетом ничего.

Причем здесь мышление не совсем понятно, код в целочисленном виде я могу записать вот так (чтобы не было точно double!!!).
Код
rez=((rez*215540000)/16777215)-107700000;
// где при максимальном rez=0xFFFFFF - получим в первой скобке число 3616160921100000=0xCD8E113271EE0 - 8байт
aaarrr
Да не нужно заниматься ерундой и делить на 16777215! В самом начале Вы получаете 24-х битное число, так и работайте дальше с ним.
Leonmezon
Цитата(aaarrr @ Jul 24 2009, 22:05) *
Да не нужно заниматься ерундой и делить на 16777215! В самом начале Вы получаете 24-х битное число, так и работайте дальше с ним.

Как я могу обойтись без деления, если мне необходимо перевести из коды АЦП в напряжение??? Весь диапазон: 20 В - или 0xFFFFFF в кодах, используя простое правило пропрции - получаем искомое, как можно еще иначе? (причем если значения коэффициентов: масштабного и учет смещения в Вольтах).
aaarrr
Деление на 16777215 и умножение на 20000000 эквивалентно умножению 1.1921. В целых числах делается умножением и сдвигом.
Коэффициенты пересчитайте отдельно.
rezident
Цитата(Leonmezon @ Jul 25 2009, 00:14) *
Как я могу обойтись без деления, если мне необходимо перевести из коды АЦП в напряжение???
Вы огласите сначала, с какой точностью вам нужно проводить измерения?
А то мучаетесь тут с каким-то микровольтами и 24-разрядным результатом измерения АЦП, а требуется, допустим, 0,1% относительная приведенная погрешность. Для этого при диапазоне входных напряжений -10В...+10В достаточно измерять с разрешением (10В-(-10В))/(0,1%*100)<=20мВ скажем 1мВ. Для представления напряжения с точностью 1мВ в указанном диапазоне вполне достаточно 16-и разрядного знакового числа. И соответственно типа long вполне хватит для всех вычислений.
Leonmezon
Цитата(aaarrr @ Jul 24 2009, 22:40) *
Деление на 16777215 и умножение на 20000000 эквивалентно умножению 1.1921. В целых числах делается умножением и сдвигом.
Коэффициенты пересчитайте отдельно.

1. Для меня это сложно! Умножать на 1,1921 со сдвигом. (точнее правильно с масштабом 1,077 умножить надо на К=1,283884124987371265135482855766 чтобы использовать уменьшению формулу:
rez=(rez*К)-107700000;

2. Точность необходима полная - все 24 разряда.
rezident
Цитата(Leonmezon @ Jul 25 2009, 00:58) *
2. Точность необходима полная - все 24 разряда.
Ерунду написали. АЦП измеряет напряжение. Напряжение принято выражать в Вольтах. С какой относительной погрешностью (в процентах) и в каком диапазоне напряжений (в Вольтах) вам нужно проводить измерения?
Leonmezon
Цитата(rezident @ Jul 24 2009, 23:02) *
Ерунду написали. АЦП измеряет напряжение. Напряжение принято выражать в Вольтах. С какой относительной погрешностью (в процентах) и в каком диапазоне напряжений (в Вольтах) вам нужно проводить измерения?

Повторюсь - 24 разряда АЦП или +- 10 В с максимальной точностью что дает АЦП (по заявленным характеристикам без ухищрений +- 150 мкВ).
rezident
Цитата(Leonmezon @ Jul 25 2009, 01:10) *
Повторюсь - 24 разряда АЦП или +- 10 В с максимальной точностью что дает АЦП (по заявленным характеристикам без ухищрений +- 150 мкВ).
150мкВ при опоре 2,5В (или 1,25В?) это уже никак не "все 24 разряда". Плюс потеря точности на делителе/преобразователе, который приводит диапазон -10В...+10В ко входному 2,5В(1,25В). В результате относительная точность измерения входного напряжения скорее всего не лучше 0,01%. Но вы конечно же можете продолжать использовать "все 24-разряда" для вычислений. laughing.gif
Leonmezon
Цитата(rezident @ Jul 24 2009, 23:25) *
150мкВ при опоре 2,5В (или 1,25В?) это уже никак не "все 24 разряда". Плюс потеря точности на делителе/преобразователе, который приводит диапазон -10В...+10В ко входному 2,5В(1,25В). В результате относительная точность измерения входного напряжения скорее всего не лучше 0,01%. Но вы конечно же можете продолжать использовать "все 24-разряда" для вычислений. laughing.gif

Вы не правы! Для начала посмотрите даташит на АЦП (AD7732) где при ИОН 2,5 В за счет внутренних резисторов !!!!!!! (выполненых с точность не менее 0.006 %) и получаеться заявленная мною неустойчивость, так что реально необходимы все разряды АЦП без округления.
Между прочим - я просил код помочь оптимизировать на быстродействие - а не рассказыват о точности АЦП - в любом случае Ваши знания не точнее информации из даташита!
Kuzmi4
2 Leonmezon - откройте секрет laughing.gif - что вы там такое меряете и самое главное НА КАКОМ ЖЕЛЕЗЕ ? А то чтоб 2.5 вольта и чтоб все 24 разряда были точно значащие...

пЫсЫ..
Цитата
компилятор менять не буду!
+ 100 biggrin.gif
mdmitry
На семинаре AD по АЦП специалисты компании утверждали, что получить точность 12 разрядов можно без особых ухищрений, 16 требуются смемотехнические решения, а 24 разряда ОЧЕНЬ сложная задача. А у Вас наводка на плату без экрана не будет больше цены младшего разаряда АЦП? wink.gif
x736C
Цена деления 1,2 мкВ при точности ±150 мкВ.. unsure.gif

Кстати, вы, вероятно, ошиблись. В описании на AD7732 явно указано «% of FSR», а не процент от опорного. Получается около ±6 мВ.

По поводу оптимизации.
Посмотрите в таком направлении, может что и получится:
Думаю, не обязательно перемножать каждый раз то, что можно перемножить заранее.
«Используя простое правило пропорции», никто вас не обязывает брать крайние значения.

Например можно взять половину от hFFFFFF.

rez = (Kd - h7FFFFF) * (10^7*KU) / h7FFFFF
1. Вычитание со знаком;
2. Умножение (6 байт включая знак!).
3. Деление (фактически сдвиг на 7 бит с отбросом двух байт с сохранением знака).
Осталось одно умножение, которое, видимо, можно сделать заблаговременно (10^7 * Ka).
Погрешность у меня получилась ~1,2 мкВ при цене деления 1,2 мкВ.
Вам правильно указали, что надо определиться с точностью.
И тогда, может быть, выбрать еще более приятную всё ту же пропорцию.
Удачи.
Leonmezon
Если вот так записать код, насколько будет быстрее первоначального? (и что еще можно упростить, без применения ухишрений (типа сдвига...). И можно что то сделать с отниманием 1 (или она мало занимает кода).
Код
const signed int K11=-831; // программный учет смещения в 1 канале 1 АЦП
const signed int K12=-999; // программный учет смещения в 2 канале 1 АЦП
const double KU=10770000; // коэффициент передачи сигнала в аналоговой части 1.077 умноженный на 1000000х10
const double Kd=8388608; // половина всего диапазона для +-10В - 0xFFFFFF/2
.
.
// Функция получения данных от 1-го АЦП
signed long uzmerenie1(void)
{
.
.
.
   rez=(C+(B<<8)+(A<<16));
   /* Далее преобразуем с типу double и преобразуем к напряжению с упрощением операций,
   конечный результат: напряжение от -10 В до 10 В в микровольтах*/
   U=((double)(rez))/Kd;
   U=U-1;    // Отнимаем 1  - для сдига в биполярный сигнал
   rez=(signed long)(U*KU);  // Изменяем масштаб и преобразуем в микровольты и возращаем результат
   rez=rez-K; //Учитываем смещение в каждом из каналов АЦП: 1К -  К11; 2К- К12
   return (rez);
}
x736C
Я написал просто деление. Дальше в скобках уточнил, что процедура деления в вашем случае достаточно быстрая.
Не надо рассматривать это, как ухищрение.
Не приводите перед делением rez к типу double. У вас абсолютно целочисленное деление на (2^23 - 1).
Все в signed long.

Напишите, пожалуйста, позже в теме, насколько уменьшилось время выполнения процедуры. Интересно.
На какой частоте работает контроллер?

Цитата(Leonmezon @ Jul 25 2009, 20:24) *
И можно что то сделать с отниманием 1 (или она мало занимает кода).

Думаю, что это не тот случай, чтоб оптимизировать вычитание единицы. Оставьте так smile.gif
Leonmezon
Цитата(x736C @ Jul 25 2009, 20:48) *
Я написал просто деление. Дальше в скобках уточнил, что процедура деления в вашем случае достаточно быстрая.
Не надо рассматривать это, как ухищрение.
Не переводите перед делением rez к типу double. У вас абсолютно целочисленное деление на (2^23 - 1).
Все в signed long.

Напишите, пожалуйста, позже в теме, насколько уменьшилось время выполнения процедуры. Интересно.
На какой частоте работает контроллер?

1. По времени - попробую на работ- напишу. (Хотя тяжело точно отследить - примерно будет только).
2. В целочисленных нельзя (правда я не уверен - как на самом деле будет происходить преобразования типов: сначала результат, потом преобразование или нет): U=rez/Kd - если rez и Kd целочисленные - то результат будет 0 при rez<Kd?
3. Частота 14,... МГц - хотя сейчас думаю перевести на Xmega - с частотой 14,...х2 = 28,.. МГц (в теме Xmega писал уже)
4. В принципе и сейчас все работает (и очень хорошо),но появился датчик (с собственным шумом 300 нВ) и для работы с ним планирую сделать запас времени, чтобы сняв 200 выборок за 1 сек прогнать через простой режекторный цифровой фильтр в микроконтроллере (повысить точность измерений) - для этого и еще как бы выиграть еще время, т.е. перед отправкой буфера (2400 байт) по RS232 успеть 200-300 мс прогнать через фильтр. (Фильтр пишу не я - человек на них собаку съел - но просит запас по времени на работу фильтра)
x736C
1. Как оцениваете время выполнения функции? Симулятор не может посчитать?
2. Результат будет правильным. Типы менять туда-сюда не надо в вашем случае.
3. 28 — огромная частота smile.gif
4.1. Если вы поверите в прерывания, то сможете многое выполнять параллельно. Например, работать с данными и отсылать их одновременно, выполняя что-то третье.
4.2. 200-300 мс — огромное время smile.gif
4.3. 1 сек — cranky.gif

«..200 выборок за 1 сек прогнать через...»
«...успеть 200-300 мс прогнать через...»
Сколько в итоге?

Если не секрет, какого рода данные с датчика? Что-то периодичное? Почему такие точности?


P. S. Неужели ассемблер окончательно отмирает unsure.gif
Leonmezon
Цитата(x736C @ Jul 25 2009, 21:28) *
1. Как оцениваете время выполнения функции? Симулятор не может посчитать?
2. Результат будет правильным. Типы менять туда-сюда не надо в вашем случае.
3. 28 — огромная частота smile.gif
4.1. Если вы поверите в прерывания, то сможете многое выполнять параллельно. Например, работать с данными и отсылать их одновременно, выполняя что-то третье.
4.2. 200-300 мс — огромное время smile.gif
4.3. 1 сек — cranky.gif

«..200 выборок за 1 сек прогнать через...»
«...успеть 200-300 мс прогнать через...»
Сколько в итоге?

Если не секрет, какого рода данные с датчика? Что-то периодичное? Почему такие точности?


P. S. Неужели ассемблер окончательно отмирает unsure.gif

1. Я и на симулятор не могу дома (компилятор не компилирует код - только до 4 кБ - а у меня больше - на работе - лицензия - тогда и проверю).
2. Насчет типов - проверю.
3. 28 - это не много.
4.1. На прерывания - нельзя, ввиду того что на прерываниях сидят 3 АЦП, плюс прерывание синхронизации от GPS.
4.2. Не очень верно написал: по внешнему такту, а запускаю 3 АЦП на оцифровку данных - с частотой 200 Гц - получаю за 1 сек - 600 значений (три канала) и останавливаюсь на 10 мкс, приходит новый такт и процесс повторяться по АЦП, при этом данные принятые за предыдущею секунду передаются по RS232 (надо успеть до нового такта). Т.е. получаем трехканальную систему синхронной оцифровки которая работает с задержкой во времени на 1 сек. (Заказчика устраивает).
4.3. Датчик - сейсмодатчик, последний с динамическим диапазоном 145 дБ (но самый верх можно не мерять - но выше 9 балов по шкале Рихтера можно не мерять...)
5. На ассемблере - это тяжка написать, если только не критические функции...
aaarrr
Цитата(x736C @ Jul 25 2009, 21:28) *
2. Результат будет правильным. Типы менять туда-сюда не надо в вашем случае.

Не будет.

Цитата(x736C @ Jul 25 2009, 21:28) *
P. S. Неужели ассемблер окончательно отмирает unsure.gif

Ну и причем тут ассемблер?

2 Leonmezon:

Во-первых, 24-х значащих бит у Вас не будет никогда, просто примите это как данность. Во-вторых сдвиг является не "ухищрением",
а нормальным методом деления и умножения на степень двойки. И освойте тип long long.

Получите что-то типа:
Код
const signed int K11=-831; // программный учет смещения в 1 канале 1 АЦП
const signed int K12=-999; // программный учет смещения в 2 канале 1 АЦП
const long KU=10770000; // коэффициент передачи сигнала в аналоговой части 1.077 умноженный на 1000000х10
const double Kd=8388608; // половина всего диапазона для +-10В - 0xFFFFFF/2
.
.
// Функция получения данных от 1-го АЦП
signed long uzmerenie1(void)
{
   long long U;
.
.
.
   U=(C+(B<<8)+(A<<16));
   U -= Kd; // Отнимаем 1  - для сдига в биполярный сигнал
   rez = (U * KU) >> 23; // Изменяем масштаб и преобразуем в микровольты и возращаем результат
   rez=rez-K; //Учитываем смещение в каждом из каналов АЦП: 1К -  К11; 2К- К12
   return (rez);

Хотя и это дикий маразм, т.к. я уверен, что и 1.077 и (10000000 / 8388608) можно упихать в один восьмибитный коэффициент,
оставшись при этом в пределах 32-х битной арифметики, безо всякого ущерба для конечного результата.
x736C
Цитата(aaarrr @ Jul 25 2009, 22:23) *
Не будет.

Ну и причем тут ассемблер?


То есть поделить signed long на signed long нельзя? Каким будет результат при отрицательном значении делимого?
Не знал этого, спасибо за замечание.

По поводу ассемблера имел в виду не то, что надо все и вся на нем писать,
а то что иногда надо понимать то, что выполняет Си-комплилятор, понимая процесс чуть глубже.
Опыт использования ассемблера в этом смысле только плюс.
Leonmezon
long long - длиное целое 8 байтовое?
( Если да, то у меня только до 4 байт).
aaarrr
Цитата(x736C @ Jul 25 2009, 22:43) *
То есть поделить signed long на signed long нельзя? Каким будет результат при отрицательном значении делимого?
Не знал этого, спасибо за замечание.

Можно, конечно. Просто нет смысла делить, условно говоря, 12345 на 123456.

Цитата(Leonmezon @ Jul 25 2009, 22:43) *
long long - длиное целое 8 байтовое?
( Если да, то у меня только до 4 байт).

Уверены? И double тоже 32-х битный?
x736C
Вы сначала написали, что результат не будет корректным.
«Нет смысла делить» и «результат не будет правильным» — это разное.

Смысле ваших слов («12345 на 123456») не понял. Лучше бы вы сказали не условно, а по существу.
Leonmezon
Цитата(aaarrr @ Jul 25 2009, 22:48) *
Можно, конечно. Просто нет смысла делить, условно говоря, 12345 на 123456.


Уверены? И double тоже 32-х битный?

Да; long, float, double - 4 байтовые. Компилятор ICC AVR 7.22 STD. Я често не знаю, но только вроде в IAR есть long long 8 байт.

По делению как раз понятно (я еще вчера об этом говорил) 12345/123456= 0 в целочисленном вычеслении.
x736C
Цитата(Leonmezon @ Jul 25 2009, 23:11) *
По делению как раз понятно (я еще вчера об этом говорил) 12345/123456= 0 в целочисленном вычеслении.

Понял, что имел в виду aaarrr.

В целочисленном исчислении нет знака?
-1234567/123456= 0 ?
Все зависит от компилятора, конечно.

Такая («12345/123456») ситуация, как привел aaarrr, когда делимое меньше делителя,
в приведенной мной последовательности вычислений не возникает никогда.
Leonmezon
Цитата(x736C @ Jul 25 2009, 23:24) *
Понял, что имел в виду aaarrr.

В целочисленном исчислении нет знака?
-1234567/123456= 0 ?
Все зависит от компилятора, конечно.

Такая («12345/123456») ситуация, как привел aaarrr, когда делимое меньше делителя,
в приведенной мной последовательности вычислений не возникает никогда.

Нет, может быть: рассуждения относяться к формуле U=((double)(rez))/Kd; - чтоб она правильно работала rez - надо перевести в double.
rez может быть от 0 до 0хFFFFFF, а Kd=0х800000
aaarrr
Цитата(x736C @ Jul 25 2009, 23:24) *
Такая («12345/123456») ситуация, как привел aaarrr, когда делимое меньше делителя,
в приведенной мной последовательности вычислений не возникает никогда.

Дык мы же не про вашу последовательность говорили, а про это:

Цитата(x736C @ Jul 25 2009, 20:54) *
Не приводите перед делением rez к типу double. У вас абсолютно целочисленное деление на (2^23 - 1).
Все в signed long.

Ладно, не важно, если long long все равно нет.

2 Leonmezon:
Тогда оставьте в плавучке одно умножение, а еще лучше поручите все вычисления принимающей стороне.
x736C
Понятно. Запутался слегка.

Из Kd можно убрать лишнюю единичку, на точность не влияет.


Код
Kd = 0x7FFFFF
   U=U-Kd;    // Отнимаем Kd  - для сдвига в биполярный сигнал
   rez=(signed long)(U*KU);  // Изменяем масштаб и преобразуем в микровольты и возвращаем результат
   U=((signed long)(rez))/Kd;

   rez=rez-K; //Учитываем смещение в каждом из каналов АЦП: 1К -  К11; 2К- К12
   return (rez);

А так?
Leonmezon
Цитата(aaarrr @ Jul 25 2009, 23:44) *
Дык мы же не про вашу последовательность говорили, а про это:


Ладно, не важно, если long long все равно нет.

2 Leonmezon:
Тогда оставьте в плавучке одно умножение, а еще лучше поручите все вычисления принимающей стороне.

Принимающей стороне нельзя (там программа должна быть абсолютно одинаковая в 50 комплектах).
Оставлю одно операцию с double.
Возможно только в будущем (зимой) получиться перейти на целочисленную математику, я списывался с ImageCraft - они наняли програмистов под Xmega (пишут генератор кода,примеры..., какие утилиты - возможно и добавять новые типы данных, сейчас есть только double 8 байтный профессиональной версии, но он не решает задачи). (да обязательно требование от заказчика - лицензия, а на IAR перейти - не вариант, дорого).
Спасибо за подсказки.

Цитата(x736C @ Jul 26 2009, 00:02) *
Понятно. Запутался слегка.

Из Kd можно убрать лишнюю единичку, на точность не влияет.


Код
Kd = 0x7FFFFF
   U=U-Kd;    // Отнимаем Kd  - для сдвига в биполярный сигнал
   rez=(signed long)(U*KU);  // Изменяем масштаб и преобразуем в микровольты и возвращаем результат
   U=((signed long)(rez))/Kd;

   rez=rez-K; //Учитываем смещение в каждом из каналов АЦП: 1К -  К11; 2К- К12
   return (rez);

А так?

Не получиться, необходмо 6 байт в rez=(signed long)(U*KU); , а есть только 4! (При максимальном значении 10 и -10 В).
Ладно, как не крути - 1 с плавующей умножени все равно необходимо. (В любом случае лучше чем 4!).
x736C
Понятно. Это как раз то, чего я не мог понять в диалоге между вами и aaarrr.
Ну удачи вам.
Leonmezon
Цитата(x736C @ Jul 26 2009, 00:45) *
Понятно. Это как раз то, чего я не мог понять в диалоге между вами и aaarrr.
Ну удачи вам.


Время исполнения кода (в симуляторе) снизилось на 31%. (Много времени занимает деление - попробую и его убрать, на сдвиг).
aaarrr
Цитата(Leonmezon @ Jul 27 2009, 14:45) *
Время исполнения кода (в симуляторе) снизилось на 31%. (Много времени занимает деление - попробую и его убрать, на сдвиг).

А зачем Вы его (деление) вообще оставили?
Leonmezon
Цитата(aaarrr @ Jul 27 2009, 17:07) *
А зачем Вы его (деление) вообще оставили?

Согласен, деление увеличивает время исполнения в 2 раза:
Начальный вариант: - 2976 циклов;
Мой вариант - 2021 циклов;
Ваш вариант (без деления но в double) - 1000 циклов, впорос в константе KK сколько знаков после запятой оставлять? И второй Kd - может быть тип long - это как то влияет на размер кода (теоретически)?
Вот тестовый файл:
Код
//ICC-AVR application builder : 27.07.2009 20:16:20
// Target : M640
// Crystal: 14.7456Mhz

#include <iom640v.h>
#include <macros.h>

void main(void)
{
   unsigned long A, B, C;
   signed long rez;
   double U, KU, Kd, KK;
   A=128;
   B=25;
   C=55;

// Начальный вариант
   rez=(C+(B<<8)+(A<<16));
   KU=1.077;
   U=(((double)(rez))*20)/0xFFFFFF; // Получаем напряжение от 0 до 20 В
   // Отнимаем 10 В - для сдига в биполярный сигнал - получаем изменение напряжения от -10 до 10В
   U=U-10;
   U=U*KU;
   // Преобразуем в микровольты и возращаем результат
   rez=(signed long)(U*1000000);
    
// Мой вариант
   KU=10770000;
   Kd=8388608;  
    U=(C+(B<<8)+(A<<16));
   // Далее преобразуем с типу double и Преобразуем к напряжению
   U=U/Kd;
   U=U-1;    // Отнимаем 1  - для сдига в биполярный сигнал
   rez=(signed long)(U*KU);  // Изменяем масштаб и преобразуем в микровольты и возращаем результат
  
  
   // Предложенный вариант
  KU=10770000;
  Kd=8388608;
  KK=1.2838840484619140625; // KK=KU/Kd
   U=(C+(B<<8)+(A<<16));
   U -= Kd; // Отнимаем 0x800000  - для сдига в биполярный сигнал
   rez = (signed long)(U*KK); // Изменяем масштаб и преобразуем в микровольты и возращаем результат
  
  while(1)
{;}
  }
rezident
Цитата(Leonmezon @ Jul 27 2009, 22:18) *
Вот тестовый файл:
А вы только кол-во циклов сравнивали или результат тоже? В смысле, что программа правильно (или хотя бы одинаково smile.gif ) считает всеми способами? А то могу подсказать где кроется потенциальная ошибка. Константы отличные (отличающиеся) от типа int (от диапазона переменной типа int на данной архитектуре, в данном компиляторе) следует обозначать явным образом.
типа unsigned int - 32769U
типа long int - 12345678L
типа unsigned long int - 12345678UL
типа long long int - 1234567890LL
типа unsigned long long int - 1234567890ULL
типа float - 1.234567f В противном случае вот здесь константа в формате double может подставляться и вас спасаетвыручает только то, что float и double в вашем компиляторе одинаковой размерности (32 бита).
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.