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

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Вычисления с помощью макросов, Проблема с выводом значения в реальном времени
alux
сообщение Mar 25 2008, 18:00
Сообщение #1


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Вывожу на ЖКИ 24-битное значение adc_value АЦП с периодом 1 сек. Это значение используется для вычисления дифференциального давления, которое вычисляется по формуле.

Если попытаться выводить сразу в пересчитанное значение дифф. давления, то возникает проблема (виснет программа), хотя по идее макрос заменяется своим определением во время работы препроцессора, поэтому не вызывает затрат времени. И стек не должен потреблять на мой взгляд.
Код
#define Vref          5.0  
#define GAIN         64.0      // Gain
#define STEPS_B    8388608.0 // Full-Scale 2^(24-1) for bipolar operation
#define SPAN          16.7      // Span (mV) of Pressure Sensors = 1 psi
#define mm_Hg       51.714   // мм. рт. ст. (@ 0 гр. C)

#define Vin(adc_value) (((((adc_value)/STEPS_B) - 1.0)*Vref)/GAIN)

diffPressure = (Vin(adc_value)/SPAN)*mm_Hg;
Почему так происходит? Как мне выводить значение давления в реальном времени? Это работает под управлением ОС (scmRTOS).
Go to the top of the page
 
+Quote Post
meister
сообщение Mar 25 2008, 18:49
Сообщение #2


Местный
***

Группа: Участник
Сообщений: 219
Регистрация: 20-11-07
Пользователь №: 32 484



diffPressure = (Vin(0)/SPAN)*mm_Hg;

Тоже виснет?
Go to the top of the page
 
+Quote Post
singlskv
сообщение Mar 25 2008, 18:54
Сообщение #3


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(alux @ Mar 25 2008, 21:00) *
Вывожу на ЖКИ 24-битное значение adc_value АЦП с периодом 1 сек. Это значение используется для вычисления дифференциального давления, которое вычисляется по формуле.

Если попытаться выводить сразу в пересчитанное значение дифф. давления, то возникает проблема (виснет программа), хотя по идее макрос заменяется своим определением во время работы препроцессора, поэтому не вызывает затрат времени. И стек не должен потреблять на мой взгляд.
Код
#define Vref          5.0  
#define GAIN         64.0      // Gain
#define STEPS_B    8388608.0 // Full-Scale 2^(24-1) for bipolar operation
#define SPAN          16.7      // Span (mV) of Pressure Sensors = 1 psi
#define mm_Hg       51.714   // мм. рт. ст. (@ 0 гр. C)

#define Vin(adc_value) (((((adc_value)/STEPS_B) - 1.0)*Vref)/GAIN)

diffPressure = (Vin(adc_value)/SPAN)*mm_Hg;
Почему так происходит? Как мне выводить значение давления в реальном времени? Это работает под управлением ОС (scmRTOS).

для начала, напишите что константы у Вас float, типа:
#define Vref 5.0f

не знаю какого типа у Вас adc_value, но я бы преобразовал тип: (float)adc_value

ну и в формуле возможно ошибка(???), при вычитании 1.0 у Вас всегда будет отрицательное значение
Go to the top of the page
 
+Quote Post
alux
сообщение Mar 25 2008, 19:10
Сообщение #4


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Цитата(singlskv @ Mar 25 2008, 22:54) *
ну и в формуле возможно ошибка(???)
В формуле ошибки нет. Проверял в симуляторе подстановкой крайних значений 0х000000 (отрицательное значение) и 0хffffff(положительное значение). Нулевое значение соответствует значению АЦП 0x800000. adc_value имеет тип unsigned long.
Цитата(singlskv @ Mar 25 2008, 22:54) *
при вычитании 1.0 у Вас всегда будет отрицательное значение
Vin = (2-1)*Vref/Gain - это соответствует крайнему положительному напряжению.
Vin = (0-1)*Vref/Gain - это соответствует крайнему отрицательному напряжению.
Цитата(singlskv @ Mar 25 2008, 22:54) *
для начала, напишите что константы у Вас float, типа:
#define Vref 5.0f
Не помогло.
Цитата(meister @ Mar 25 2008, 22:49) *
diffPressure = (Vin(0)/SPAN)*mm_Hg;
Тоже виснет?
Так не виснет. И что?
Go to the top of the page
 
+Quote Post
singlskv
сообщение Mar 25 2008, 22:14
Сообщение #5


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(alux @ Mar 25 2008, 22:10) *
В формуле ошибки нет. Проверял в симуляторе подстановкой крайних значений 0х000000 (отрицательное значение) и 0хffffff(положительное значение). Нулевое значение соответствует значению АЦП 0x800000. adc_value имеет тип unsigned long.
Vin = (2-1)*Vref/Gain - это соответствует крайнему положительному напряжению.
Vin = (0-1)*Vref/Gain - это соответствует крайнему отрицательному напряжению.
да, это я протормозил, не заметил что Вы делили на 2^(24-1 )
Цитата
Не помогло.
А Вы везде заменили ? и в (((((adc_value)/STEPS_B) - 1.0 )*Vref)/GAIN) тоже ?
перед adc_value (float) ставили ?

покажите минимальный код в котором есть проблема...
Go to the top of the page
 
+Quote Post
rezident
сообщение Mar 25 2008, 22:39
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



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

Код
define Vin(adc_value) (((float)(adc_value)-STEPS_B)/STEPS_B*Vref/GAIN)


Остается одно вычитание константы и одно умножение на константу. Может компилятор не осилил эту оптимизацию при наличии такого количества скобок?
Go to the top of the page
 
+Quote Post
meister
сообщение Mar 26 2008, 04:38
Сообщение #7


Местный
***

Группа: Участник
Сообщений: 219
Регистрация: 20-11-07
Пользователь №: 32 484



Цитата(alux @ Mar 25 2008, 22:10) *
Так не виснет. И что?


Значит, действительно в вычислениях дело. На каком процессоре делаете? Без аппаратной плавающей точки?
Go to the top of the page
 
+Quote Post
alux
сообщение Mar 26 2008, 07:22
Сообщение #8


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Цитата(singlskv @ Mar 26 2008, 02:14) *
А Вы везде заменили ? и в (((((adc_value)/STEPS_B) - 1.0 )*Vref)/GAIN) тоже ?
перед adc_value (float) ставили ?

Поставил в определении макроса
Код
#define Vin(adc_value) ((((((float)adc_value)/STEPS_B) - 1.0f)*Vref)/GAIN)

и при вызове макроса:
Код
diffPressure = (Vin((float)temp_ul)/SPAN)*mm_Hg;
Не помогло. Весь код привести не могу. Ф-ция measure, в которой используется этот макрос, вызывается из процесса под управлением ОС. Т.е. процессор не должен задерживаться надолго в вычислениях. Но это же не ф-ция, здесь все вычисления уже сделал препроцессор. Неужели для препроцессора есть ограничения по количеству скобок? Или я ошибаюсь?
Цитата(rezident @ Mar 26 2008, 02:39) *
alux, у вас оптимизация при компиляции используется?
Да, конечно. Всегда использую максимальную по размеру.
Цитата(rezident @ Mar 26 2008, 02:39) *
Код
define Vin(adc_value) (((float)(adc_value)-STEPS_B)/STEPS_B*Vref/GAIN)

Остается одно вычитание константы и одно умножение на константу. Может компилятор не осилил эту оптимизацию при наличии такого количества скобок?
Не помогло. Та же фигня.
Цитата(meister @ Mar 26 2008, 08:38) *
Значит, действительно в вычислениях дело. На каком процессоре делаете? Без аппаратной плавающей точки?

ATMega324P. Попробовал в симуляторе вставить diffPressure = (Vin((float)temp_ul)/SPAN)*mm_Hg; в начало программы (в main). Вычисление заняло пару тактов.
Go to the top of the page
 
+Quote Post
tag
сообщение Mar 26 2008, 08:46
Сообщение #9


Частый гость
**

Группа: Свой
Сообщений: 151
Регистрация: 21-02-06
Пользователь №: 14 561



Цитата(alux @ Mar 25 2008, 21:00) *
Вывожу на ЖКИ 24-битное значение adc_value АЦП с периодом 1 сек. Это значение используется для вычисления дифференциального давления, которое вычисляется по формуле.


...а вы для вывода sprintf используете?
Go to the top of the page
 
+Quote Post
alux
сообщение Mar 26 2008, 09:20
Сообщение #10


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Цитата(tag @ Mar 26 2008, 12:46) *
...а вы для вывода sprintf используете?
Нет, от них избавился. Заменил на ftoa
Код
//------------------------------------------------------------------------------
// Вывод результатов измерения на ЖКИ
//------------------------------------------------------------------------------
class TReDraw : public TMsg    
{
public:
//  TReDraw();
  virtual void run()
  {
    TCritSect cs;
        
    ks0108FillRect(0, 11, 80, 9, WHITE);  
    ftoa(diffPressure, lcd_buf, 2, 0);            // Динамическое давление
//    itoa((unsigned int)diffPressure);
    ks0108GotoXY(0, 11);
    ks0108PutStr(lcd_buf);
  ....................
// вывод других переменных на экран
  }
};
На всякий случай приведу еще и код ftoa:
Код
char* ftoa(double v, char *s, unsigned char decimals, bool sign)
{
     unsigned long i, f;

     if(v < 0)
     {
         *s++ = '-';
         v = -v;
     }
     else if(sign)
         *s++ = '+';

     if(v >= 1.0)
     {
         i = (unsigned long)v;
         v = v - (double)i;
         s = ultoa(i, s);
     }
     else
         *s++ = '0';

     if(decimals > 0)
     {
         *s++ = '.';
         for (; decimals > 0; decimals--)
             v = v * 10;
         f = (unsigned long)v;
         s = ultoa(f, s);
     }

     *s = '\0';
     return s;
}


//----------------------------------------------------------------------------
char* ultoa(unsigned long x, char *s)
{
     unsigned long r;
    
       ldiv_t DivRes;

      DivRes = ldiv(x, 10);
      r = '0' + DivRes.rem;
      x = DivRes.quot;

//     r = (x % 10) + '0';
//     x = x / 10;        

     if (x != 0)
         s = ultoa(x, s);

     *s = r;             // Built the str when nesting back
     *(s + 1) = '\0';

     return s + 1;
}
Замена ftoa на itoa не дало положительного результата (в смысле, проблема осталась). Но ведь значения АЦП в doable выводит нормально через ftoa хоть в этой ф-ции используется рекурсия. В симуляторе проверил время выполнения ReDraw одной double переменной на экране. Это заняло 1ms при 8 Мгц. На мой взгляд проблема в чем-то другом.
Go to the top of the page
 
+Quote Post
tag
сообщение Mar 26 2008, 10:50
Сообщение #11


Частый гость
**

Группа: Свой
Сообщений: 151
Регистрация: 21-02-06
Пользователь №: 14 561



Цитата(alux @ Mar 26 2008, 12:20) *
В симуляторе проверил время выполнения ReDraw одной double переменной на экране. Это заняло 1ms при 8 Мгц. На мой взгляд проблема в чем-то другом.


...а всимуляторе ничего странного не наблюдаете? Код посмотреть можно?
Go to the top of the page
 
+Quote Post
alux
сообщение Mar 26 2008, 13:28
Сообщение #12


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Цитата(tag @ Mar 26 2008, 14:50) *
...а всимуляторе ничего странного не наблюдаете? Код посмотреть можно?

Цитата(alux @ Mar 26 2008, 11:22) *
Попробовал в симуляторе вставить diffPressure = (Vin((float)temp_ul)/SPAN)*mm_Hg; в начало программы (в main). Вычисление заняло пару тактов.
Вот кусок листинга:
73 int main()
\ main:
74 {double temp_ul = 0x800000;
75 diffPressure = (Vin(temp_ul)/SPAN)*mm_Hg;
\ 00000000 E010 LDI R17, 0
\ 00000002 .... LDI R30, LOW(diffPressure)
\ 00000004 .... LDI R31, (diffPressure) >> 8
\ 00000006 8310 ST Z, R17
\ 00000008 8311 STD Z+1, R17
\ 0000000A 8312 STD Z+2, R17
\ 0000000C 8313 STD Z+3, R17
Из него видно, что по адресу, где находится переменная diffPressure загружается просчитанное значение 0 (для значения АЦП = 0х800000).
А вот листинг для значения АЦП = 0х000000:
Код
     73          int main()
   \                     main:
     74          {double temp_ul = 0x000000;
     75            diffPressure = (Vin(temp_ul)/SPAN)*mm_Hg;
   \   00000000   E503               LDI     R16, 83
   \   00000002   EB1B               LDI     R17, 187
   \   00000004   E727               LDI     R18, 119
   \   00000006   EB3E               LDI     R19, 190
   \   00000008   ....               LDI     R30, LOW(diffPressure)
   \   0000000A   ....               LDI     R31, (diffPressure) >> 8
   \   0000000C   8300               ST      Z, R16
   \   0000000E   8311               STD     Z+1, R17
   \   00000010   8322               STD     Z+2, R18
   \   00000012   8333               STD     Z+3, R19
Вопрос. Почему в этом случае (temp_ul = 0) программа работает?
Реально полностью программу не симулировал. Для этого нужно написать макросы для симуляции прерываний и переферии. Может это как-то связано с работой ОС. Макрос запускается из процесса, который имеет собственный стек. И каким-то образом этот макрос нарушает работу ОС. Это так, мысли вслух...

.................................
PS. Объясните мне, пожалуйста , следующее. Почему, если перед вызовом макроса тупо присвоить аргументу adc_value какое-нибудь значение, то все работает !!! А если adc_value присваивать реальное значение АЦП:
Код
adc_value = ad7799_read_data();
, то не работает (виснет). При этом проблемы с ad7799_read_data(); нет: если не вызывать макрос, а просто выводить значение АЦП, то все нормально выводится на экран ЖКИ 05.gif
Go to the top of the page
 
+Quote Post
tag
сообщение Mar 27 2008, 06:16
Сообщение #13


Частый гость
**

Группа: Свой
Сообщений: 151
Регистрация: 21-02-06
Пользователь №: 14 561



Цитата(alux @ Mar 26 2008, 16:28) *
Из него видно, что по адресу, где находится переменная diffPressure загружается просчитанное значение 0 (для значения АЦП = 0х800000).
А вот листинг для значения АЦП = 0х000000:
Код
     73          int main()
   \                     main:
     74          {double temp_ul = 0x000000;
     75            diffPressure = (Vin(temp_ul)/SPAN)*mm_Hg;
   \   00000000   E503               LDI     R16, 83
   \   00000002   EB1B               LDI     R17, 187
   \   00000004   E727               LDI     R18, 119
   \   00000006   EB3E               LDI     R19, 190
   \   00000008   ....               LDI     R30, LOW(diffPressure)
   \   0000000A   ....               LDI     R31, (diffPressure) >> 8
   \   0000000C   8300               ST      Z, R16
   \   0000000E   8311               STD     Z+1, R17
   \   00000010   8322               STD     Z+2, R18
   \   00000012   8333               STD     Z+3, R19
Вопрос. Почему в этом случае (temp_ul = 0) программа работает?
Реально полностью программу не симулировал. Для этого нужно написать макросы для симуляции прерываний и переферии. Может это как-то связано с работой ОС. Макрос запускается из процесса, который имеет собственный стек. И каким-то образом этот макрос нарушает работу ОС. Это так, мысли вслух...

.................................
PS. Объясните мне, пожалуйста , следующее. Почему, если перед вызовом макроса тупо присвоить аргументу adc_value какое-нибудь значение, то все работает !!! А если adc_value присваивать реальное значение АЦП:
Код
adc_value = ad7799_read_data();
, то не работает (виснет). При этом проблемы с ad7799_read_data(); нет: если не вызывать макрос, а просто выводить значение АЦП, то все нормально выводится на экран ЖКИ 05.gif


...попробуйте локализовать место где виснет программа. Если adc_value присвоить скажем ноль (постоянно), оставить вычисления с макросом и запустить программу. Зависнет? Размер стека задачи в которой используется подпрограмма преобразования перед выводом каков? И еще как много статических переменных в этой задаче?
Go to the top of the page
 
+Quote Post
alux
сообщение Mar 27 2008, 06:45
Сообщение #14


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Цитата(tag @ Mar 27 2008, 10:16) *
Если adc_value присвоить скажем ноль (постоянно), оставить вычисления с макросом и запустить программу. Зависнет?
Я ответил в предыдущем посте. Ответ - очевиден. Если перед вызовом макроса "тупо" присвоить переменной adc_value какое-нибудь значение, то препроцессор "тупо" подставит это значение в макрос еще на стадии компиляции программы. Во время выполнения программы уже будет готовое значение, что и видно было из приведенных листингов. А если значение аргумента макроса присваивать во время выполнения программы, то и вычисления будут выполняться во время выполнения программы. В этом и вся проблема.

Вопрос только в следующем : Во время исполнения программы вычисления будут производится так, как описано в макросе или все что можно вычислит препроцессор, а в run time только умножит/делит аргумент на вычисленное значение?

Мне, по сути, нужно преобразовать значение АЦП в значение измеряемой величины (давление). Т.е. diffPressure=K*adc_value. Думаю облегчить задачу для компилятора и задефайнить K.

И второй вариант : diffPressure=adc_value / K . Но на мой взгляд деление на вещественное число для компилятора тяжелее.

........................
PS. Для униполярного режима все просто: CODE=Vin * K, где K=2^24 *GAIN/Vref
А в моем случае для биполярного режима не все так просто:
CODE=2^23*[(Vin*Gain/Vref) + 1].
Как здесь вычленить K?
Подзабыл уроки алгебры 6 класс smile.gif

PS2. Если написать формулу в общем виде: y=(x-a)*b/a, можно ли ее вообще привести к виду y=x*k, где k - выражено через константы a и b?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Mar 27 2008, 10:28
Сообщение #15


Гуру
******

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



Цитата(alux @ Mar 27 2008, 08:45) *
Если перед вызовом макроса "тупо" присвоить переменной adc_value какое-нибудь значение, то препроцессор "тупо" подставит это значение в макрос еще на стадии компиляции программы.
А если adc_value сделать volatile - то не подставит. Или сделать глобальной и проинициализировать в точке определения.

Предлагаю для начала отделить мух от котлет и выяснить, кто виноват - код или ОС. Я подозреваю нехватку стека, но могу и ошибаться. Для локализации предлагаю обернуть все это вычисление критической секцией и результат вычисления вывести в UART или на экран не используя средства ОС
Цитата(alux @ Mar 27 2008, 08:45) *
Вопрос только в следующем : Во время исполнения программы вычисления будут производится так, как описано в макросе или все что можно вычислит препроцессор, а в run time только умножит/делит аргумент на вычисленное значение?
Поставив скобки вы ограничили поле деятельности оптимизатору. Поскольку вы делаете вычисления с плавающей точкой, то потери точности в этом выражении не ожидается и поэтому можно убрать лишние скобки, положившись на приоритет операций. Думаю, в этом случае оптимизатор сможет свести ваше выражение к y = kx+b.
Цитата(alux @ Mar 27 2008, 08:45) *
PS. Для униполярного режима все просто: CODE=Vin * K, где K=2^24 *GAIN/Vref
А в моем случае для биполярного режима не все так просто:
Не могу понять, что вам мешает перейти от unsigned adc_value к signed просто вычтя из него 0x800000 (точнее значение смещения измеренное при закороченном входе в процессе калибровки)? И дальше те же y=kx+b. Я обычно делаю калибровку по двум точкам. Полчаса медитации над графиком и имеем перевод x в y для любого представления x.

Можно все вычисление свести к фиксированной точке. Не знаю, правда, даст ли это положительный эффект в плане скорости или размера кода - не работаю с плавающей точкой, не с чем сравнить.


--------------------
На любой вопрос даю любой ответ
"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

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

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 00:34
Рейтинг@Mail.ru


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