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

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


Знающий
****

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



В общем, сделал так:
Код
#define Kp            (Vref*mm_Hg)/(STEPS_B*GAIN*SPAN)
А перед отображением значения давления на ЖКИ :
Код
diffPressure = ((double)adc_value-STEPS_B)*Kp;

Увеличил стек для процесса measure , и все заработало smile.gif

Цитата(Сергей Борщ @ 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)все работает smile.gif Спасибо, СергейБорщ и rezident. a14.gif

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)
Проверил. Работает smile.gif
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Mar 27 2008, 14:18
Сообщение #17


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
alux
сообщение Mar 27 2008, 15:35
Сообщение #18


Знающий
****

Группа: Свой
Сообщений: 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. Компилятор не выполняет никаких проверок типов в макросе. Поэтому Ваша запись не выдало бы предупреждение.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Mar 27 2008, 16:39
Сообщение #19


Гуру
******

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



Цитата(alux @ Mar 27 2008, 17:35) *
И, соответственно, STEPS_B приводить нужно к типу signed long.
Да, STEPS_B привести к (int32_t), конечно. А adc_value будет приведено из uint32_t к int32_t автоматически, по правилам приведения типов. Или привести к int32_t результат ((adc_value) - (uint32_t)STEPS_B). Переполнение тут не страшно - получите как раз нужное отрицательное число.


--------------------
На любой вопрос даю любой ответ
"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
alux
сообщение Mar 27 2008, 18:22
Сообщение #20


Знающий
****

Группа: Свой
Сообщений: 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 wink.gif


...................................
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)
, что с арифметической точки зрения равнозначно, а на экран вместо цифр выводит ерунду (слеши, точки...). Почему? 05.gif
Go to the top of the page
 
+Quote Post
IgorKossak
сообщение Mar 27 2008, 20:43
Сообщение #21


Шаман
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
alux
сообщение Mar 28 2008, 05:24
Сообщение #22


Знающий
****

Группа: Свой
Сообщений: 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. Почему на ЖКИ выводится на порядок больше, не пойму никак... 05.gif Ф-ции, обеспечивающие вывод на ЖКИ, я приводил в предыдущих постах. Это, должно быть, как-то связано с макросом Vin , потому что тестовый вывод значения АЦП в double типе выводит нормально (0xFFFFFF = 16777215.0).

.......................................
PS. Похоже, снова "фокусы" со стеком. Попробовал полученное значение diffPressure после Vin() домножить на 10000 и вывести на экран с помощью itoa вместо ftoa. Вывело значение 781. Т.е. 781 / 10000 = 0.0781 V . Как и должно быть. Буду переписывать ф-цию ftoa.
Go to the top of the page
 
+Quote Post
alux
сообщение Mar 29 2008, 19:45
Сообщение #23


Знающий
****

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



На этот раз стек здесь ни при чем. Проблема была в ошибке ftoa. Ошибка проявлялась, когда дробная часть была с ведущими нулями. Исправил. Если кому нужны ф-ции itoa, ultoa, ltoa, ftoa, atoi, выкладываю. Может кому-нибудь и пригодятся. Функцию itoa, предоставленную==AVR==, я немного изменил, чтобы число всегда начиналось с начала буфера (без ведущих нулей), добавил '\0' в конце строки и возвращает количество символов строки. Ф-ция atoi любезно предоставлена defunc.
Прикрепленные файлы
Прикрепленный файл  xtoa.rar ( 1.75 килобайт ) Кол-во скачиваний: 63
 
Go to the top of the page
 
+Quote Post
alux
сообщение Apr 8 2008, 10:42
Сообщение #24


Знающий
****

Группа: Свой
Сообщений: 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) происходит во время вызова макроса. Какая разница?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Apr 8 2008, 15:15
Сообщение #25


Гуру
******

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



Цитата(alux @ Apr 8 2008, 13:42) *
Здесь вычитание 0x800000 (STEPS_B) происходит во время вызова макроса. Какая разница?
Здесь - никакой. Как бы вы ни старались, это вычитание будет на этапе выполнения.


--------------------
На любой вопрос даю любой ответ
"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
alux
сообщение Apr 9 2008, 08:18
Сообщение #26


Знающий
****

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



И еще один маленький, но очень большой вопрос.
Как лучше сделать автоматический подбор коэффициента усиления АЦП?
Допустим, стандартная ситуация: АЦП AD7799 работает в режиме непрерывного преобразования и непрерывного чтения с трех каналов биполярных сигналов, которые меняются в широком диапазоне.

Последовательность приблизительно такая:
1. Делаем N измерений с канала AD7799_AIN1_CHAN+mux
2. 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].
Какие будут ваши замечания/предложения по этому поводу?
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.01467 секунд с 7
ELECTRONIX ©2004-2016