|
Вычисления с помощью макросов, Проблема с выводом значения в реальном времени |
|
|
|
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]. Какие будут ваши замечания/предложения по этому поводу?
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|