|
Вычисления с помощью макросов, Проблема с выводом значения в реальном времени |
|
|
|
Mar 25 2008, 18:00
|
Знающий
   
Группа: Свой
Сообщений: 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).
|
|
|
|
|
Mar 25 2008, 18:54
|
дятел
    
Группа: Свой
Сообщений: 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 у Вас всегда будет отрицательное значение
|
|
|
|
|
Mar 25 2008, 19:10
|
Знающий
   
Группа: Свой
Сообщений: 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; Тоже виснет? Так не виснет. И что?
|
|
|
|
|
Mar 25 2008, 22:14
|
дятел
    
Группа: Свой
Сообщений: 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) ставили ? покажите минимальный код в котором есть проблема...
|
|
|
|
|
Mar 26 2008, 07:22
|
Знающий
   
Группа: Свой
Сообщений: 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). Вычисление заняло пару тактов.
|
|
|
|
|
Mar 26 2008, 08:46
|
Частый гость
 
Группа: Свой
Сообщений: 151
Регистрация: 21-02-06
Пользователь №: 14 561

|
Цитата(alux @ Mar 25 2008, 21:00)  Вывожу на ЖКИ 24-битное значение adc_value АЦП с периодом 1 сек. Это значение используется для вычисления дифференциального давления, которое вычисляется по формуле. ...а вы для вывода sprintf используете?
|
|
|
|
|
Mar 26 2008, 09:20
|
Знающий
   
Группа: Свой
Сообщений: 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 Мгц. На мой взгляд проблема в чем-то другом.
|
|
|
|
|
Mar 26 2008, 10:50
|
Частый гость
 
Группа: Свой
Сообщений: 151
Регистрация: 21-02-06
Пользователь №: 14 561

|
Цитата(alux @ Mar 26 2008, 12:20)  В симуляторе проверил время выполнения ReDraw одной double переменной на экране. Это заняло 1ms при 8 Мгц. На мой взгляд проблема в чем-то другом. ...а всимуляторе ничего странного не наблюдаете? Код посмотреть можно?
|
|
|
|
|
Mar 26 2008, 13:28
|
Знающий
   
Группа: Свой
Сообщений: 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(); нет: если не вызывать макрос, а просто выводить значение АЦП, то все нормально выводится на экран ЖКИ
|
|
|
|
|
Mar 27 2008, 06:16
|
Частый гость
 
Группа: Свой
Сообщений: 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(); нет: если не вызывать макрос, а просто выводить значение АЦП, то все нормально выводится на экран ЖКИ  ...попробуйте локализовать место где виснет программа. Если adc_value присвоить скажем ноль (постоянно), оставить вычисления с макросом и запустить программу. Зависнет? Размер стека задачи в которой используется подпрограмма преобразования перед выводом каков? И еще как много статических переменных в этой задаче?
|
|
|
|
|
Mar 27 2008, 06:45
|
Знающий
   
Группа: Свой
Сообщений: 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 класс  PS2. Если написать формулу в общем виде: y=(x-a)*b/a, можно ли ее вообще привести к виду y=x*k, где k - выражено через константы a и b?
|
|
|
|
|
Mar 27 2008, 10:28
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|