|
Вычисления с помощью макросов, Проблема с выводом значения в реальном времени |
|
|
|
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).
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 25)
|
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)
|
|
|
|
|
Mar 27 2008, 10:59
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
В общем, сделал так: Код #define Kp (Vref*mm_Hg)/(STEPS_B*GAIN*SPAN) А перед отображением значения давления на ЖКИ : Код diffPressure = ((double)adc_value-STEPS_B)*Kp; Увеличил стек для процесса measure , и все заработало  Цитата(Сергей Борщ @ Mar 27 2008, 14:28)  Поставив скобки вы ограничили поле деятельности оптимизатору. Поскольку вы делаете вычисления с плавающей точкой, то потери точности в этом выражении не ожидается и поэтому можно убрать лишние скобки, положившись на приоритет операций. Думаю, в этом случае оптимизатор сможет свести ваше выражение к y = kx+b. Вы имеете в виду вместо Код #define Vin(adc_value) ((((((double)adc_value)/STEPS_B) - 1.0f)*Vref)/GAIN) написать так? Код #define Vin(adc_value) (((double)(adc_value)/STEPS_B - 1.0f)*Vref/GAIN) Здесь аргумент заключен в скобки. Нужно ли здесь явное приведение к типу (double) , если при вызове макроса Vin параметр будет явно преобразован к (double)? .................... PS.Проблема была в большом количестве скобок и нехватки стека для процесса measure. Поэтому основные вычисления процессору пришлось выполнять в run time. Если убрать лишние скобки, то препроцессор преобразует вычисление к виду y=(x-a)/b, что собственно я и сделал только по-другому, определив Kp=1/b и во время вычисления diffPressure=(adc_value-a)*Kp. Теперь при вызове макроса Vin(adc_value)все работает  Спасибо, СергейБорщ и rezident. PS2. Для наглядности перевода значения АЦП в давление создал вложенный макрос: Код #define Vin(adc_value) (((adc_value)/STEPS_B - 1.)*Vref/GAIN)
#define SPAN 16.7 // Span (mV) of Pressure Sensors = 1 psi #define mm_Hg 51.714 // мм. рт. ст. (@ 0 гр. C) #define Kp mm_Hg/SPAN
#define PRESSURE(adc_value) (Vin(adc_value)*Kp) Проверил. Работает
|
|
|
|
|
Mar 27 2008, 14:18
|

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

|
Цитата(alux @ Mar 27 2008, 12:59)  Здесь аргумент заключен в скобки. Нужно ли здесь явное приведение к типу (double) , если при вызове макроса Vin параметр будет явно преобразован к (double)? Сушествуют правила неявного приведения типов. Если один из операндов имеет "более старший" тип, то второй преобразуется к этому типу. "Старшинство" выглядит примерно так: char->int->ling->long long->float->double. И параллельно беззнаковый->знаковый. Если STEPS_B у вас имеет тип с плавающей точкой, то adc_value будет приведено к такому же типу. Цитата(alux @ Mar 27 2008, 12:59)  (adc_value)/STEPS_B - 1. здесь можно было бы написать ((adc_value) - (uint32_t)STEPS_B)/STEPS_B - заменяете одно вычитание с плавающей точкой на вычитание с фиксированной => меньше кода, выше скорость.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 27 2008, 15:35
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Сергей Борщ @ Mar 27 2008, 18:18)  здесь можно было бы написать ((adc_value) - (uint32_t)STEPS_B)/STEPS_B - заменяете одно вычитание с плавающей точкой на вычитание с фиксированной => меньше кода, выше скорость. В этом случае adc_value должен быть типа signed long, иначе будет ошибка переполнения кода в случае отрицательных значений АЦП (меньше STEPS_B=0x800000). И, соответственно, STEPS_B приводить нужно к типу signed long. Компилятор не выполняет никаких проверок типов в макросе. Поэтому Ваша запись не выдало бы предупреждение.
|
|
|
|
|
Mar 27 2008, 18:22
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Сергей Борщ @ Mar 27 2008, 18:18)  здесь можно было бы написать ((adc_value) - (int32_t)STEPS_B)/STEPS_B - заменяете одно вычитание с плавающей точкой на вычитание с фиксированной => меньше кода, выше скорость. Не могу отказать себе в удовольствии подловить профессионала на такой элементарной ошибке. Так не будут отображаться отрицательные значения (если adc_value будет иметь тип unsigned long). По Правилам преобразования типов: "Если один из операндов имеет тип unsigned, то другой преобразуется к типу unsigned". Вы здесь ошиблись: Цитата(Сергей Борщ @ Mar 27 2008, 20:39)  А adc_value будет приведено из uint32_t к int32_t автоматически, по правилам приведения типов. adc_value также должен быть типа signed long  ................................... PS. Вконец запутался. Чтобы уменьшить количество скобок изменил Код #define Vin(adc_value) ((((adc_value)-(signed long)STEPS_B)/STEPS_B)*Vref/GAIN) на Код #define Vin(adc_value) (((adc_value)-(signed long)STEPS_B)*Vref/GAIN*STEPS_B) , что с арифметической точки зрения равнозначно, а на экран вместо цифр выводит ерунду (слеши, точки...). Почему?
|
|
|
|
|
Mar 27 2008, 20:43
|

Шаман
     
Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221

|
Цитата(alux @ Mar 27 2008, 20:22)  PS. Вконец запутался. Чтобы уменьшить количество скобок изменил Код #define Vin(adc_value) ((((adc_value)-(signed long)STEPS_B)/STEPS_B)*Vref/GAIN) на Код #define Vin(adc_value) (((adc_value)-(signed long)STEPS_B)*Vref/GAIN*STEPS_B) , что с арифметической точки зрения равнозначно Равнозначно будет Код #define Vin(adc_value) (((adc_value)-(signed long)STEPS_B)*Vref/GAIN/STEPS_B)
|
|
|
|
|
Mar 28 2008, 05:24
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(IgorKossak @ Mar 28 2008, 00:43)  Равнозначно будет Код #define Vin(adc_value) (((adc_value)-(signed long)STEPS_B)*Vref/GAIN/STEPS_B) Спасибо. Действительно, ведь операции выполняются слева направо. Теперь вывожу на ЖКИ непосредственно значение Vin. Только есть еще одна маленькая проблема. Максимальное значение положительного входного напряжения при GAIN=64 должно быть 0.078125V. А на самом деле на ЖКИ выводит 0.78125 V. Хотя в симуляторе подставлял значение 0xffffff при вызове Vin и получалось 7.8125 E-2. Почему на ЖКИ выводится на порядок больше, не пойму никак...  Ф-ции, обеспечивающие вывод на ЖКИ, я приводил в предыдущих постах. Это, должно быть, как-то связано с макросом Vin , потому что тестовый вывод значения АЦП в double типе выводит нормально (0xFFFFFF = 16777215.0). ....................................... PS. Похоже, снова "фокусы" со стеком. Попробовал полученное значение diffPressure после Vin() домножить на 10000 и вывести на экран с помощью itoa вместо ftoa. Вывело значение 781. Т.е. 781 / 10000 = 0.0781 V . Как и должно быть. Буду переписывать ф-цию ftoa.
|
|
|
|
|
Mar 29 2008, 19:45
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
На этот раз стек здесь ни при чем. Проблема была в ошибке ftoa. Ошибка проявлялась, когда дробная часть была с ведущими нулями. Исправил. Если кому нужны ф-ции itoa, ultoa, ltoa, ftoa, atoi, выкладываю. Может кому-нибудь и пригодятся. Функцию itoa, предоставленную ==AVR==, я немного изменил, чтобы число всегда начиналось с начала буфера (без ведущих нулей), добавил '\0' в конце строки и возвращает количество символов строки. Ф-ция atoi любезно предоставлена defunc.
Прикрепленные файлы
xtoa.rar ( 1.75 килобайт )
Кол-во скачиваний: 63
|
|
|
|
|
Apr 8 2008, 10:42
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Сергей Борщ @ Mar 27 2008, 13:28)  Не могу понять, что вам мешает перейти от unsigned adc_value к signed просто вычтя из него 0x800000 (точнее значение смещения измеренное при закороченном входе в процессе калибровки)? И дальше те же y=kx+b. Я обычно делаю калибровку по двум точкам. Полчаса медитации над графиком и имеем перевод x в y для любого представления x. to СергейБорщ. Вы имеете в виду сделать так? Код signed long ad7799_read_data(void) { unsigned long val = 0;
val = (unsigned long)spiTransferByte(0); val <<= 8; val |= (unsigned long)spiTransferByte(0); val <<= 8; val |= (unsigned long)spiTransferByte(0);
return val - 0x800000; } Не совсем понимаю смысл этого вычитания. Если не использовать вычитание и просто возвращать Код return val; , то по формуле Код #define Vin (((adc_value)-(signed long)STEPS_B)*Vref/GAIN/STEPS_B) получаем знаковое значение напряжения на входе АЦП. Здесь вычитание 0x800000 (STEPS_B) происходит во время вызова макроса. Какая разница?
|
|
|
|
|
Apr 9 2008, 08:18
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
И еще один маленький, но очень большой вопрос. Как лучше сделать автоматический подбор коэффициента усиления АЦП? Допустим, стандартная ситуация: АЦП AD7799 работает в режиме непрерывного преобразования и непрерывного чтения с трех каналов биполярных сигналов, которые меняются в широком диапазоне. Последовательность приблизительно такая: 1. Делаем N измерений с канала AD7799_AIN1_CHAN+mux2. if(++mux > 2) mux=0; 3. ad7799_write_config(AD7799_GAIN, ..., AD7799_AIN1_CHAN+mux);4. Если mux=0, то передаем массив adc_value[3] на преобразование и отображение на ЖКИ. 5. Повторяем с п. 1 На ум приходит только завести статический массив gain[3] для коэффициентов усиления трех каналов. После п. 1 делать проверку значения АЦП: Код if((adc_value == 0xffffff)||(adc_value == 0)) gain[mux]--; else gain[mux]++; И в п. 3 первым параметром передавать AD7799_GAIN+gain[mux]. Какие будут ваши замечания/предложения по этому поводу?
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|