Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: DS18B20 + Mega помогите !
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
belenkoff
Доброго времени суток!

Столокнулся с проблемой, при положительной температуре - все нормально, как только ниже нуля - выдает непонятное значение..

void ShowTemp(void)
{
char i;
float temp;
isfloat=1;
temperature=ds18b20_temperature(&rom_code[0][0]);
if (temperature!=-9999)
{
if (temperature>=0) {
temp = temperature;
if (temperature>=1000) {isfloat =0; temp = temperature/10;} //
i=0;
while (temp>=10)
{
temp=temp-10;
i++;
};
if (i>9) i=0; //****

buff1[1]=i;
buff3[1]=i;
i=0;
while (temp>=1)
{
temp=temp-1;
i++;
};
buff1[2]=i;
buff3[2]=i;
i=0;
while (temp>=0.1)
{
temp=temp-0.1;
i++;
};
buff1[3]=i;
buff3[3]=i;
i=0;
while (temp>=0.01)
{
temp=temp-0.01;
i++;
};
buff1[4]=i;
buff3[4]=i;
}
else
{
isfloat=0;
temp = -temperature/10; // /10;
i=0;

while (temp>=10)
{
temp=temp-10;
i++;
};
buff1[1]=i;
buff3[1]=i;
i=0;
while (temp>=1)
{
temp=temp-1;
i++;
};
buff1[2]=i;
buff3[2]=i;
i=0;
while (temp>=0.1)
{
temp=temp-0.1;
i++;
};
buff1[3]=i;
buff3[3]=i;
i=0;
while (temp>=0.01)
{
temp=temp-0.01;
i++;
};
buff1[4]=i;
buff3[4]=i;
}
}
}
Igor26
Посмотрите здесь, может что-то прояснится...
http://electronix.ru/forum/index.php?showt...&hl=ds18b20
zltigo
Цитата(belenkoff @ Nov 7 2006, 12:44) *
....

Обалдеть. Это уже словами описать нельзя.
Уже похоже в школах совсем ничему не учат, о программировании вообще уже и речь не идет :-(
Код
#define DS18X20_FRACCONV        625
// meas - то, что с датчика считали (два байта приведенные к signed int)
// dcel   - температура в десятых долях градуса c округлением, естественно
if( meas < 0 )            // Subzero
   dcel = ((meas*DS18X20_FRACCONV)-500)/1000;
else
   dcel = ((meas*DS18X20_FRACCONV)+500)/1000;
printf( "Temp:%+i.%i", dcel / 10, mod(dcel % 10) );
haker_fox
Скачайте этот исходник (под IAR) и проанализируйте его внимательно.
zltigo
Цитата(haker_fox @ Nov 7 2006, 13:18) *
проанализируйте его внимательно.

Наверное имелось ввиду, что нужно читать между строк, поскльку в исходнике начисто отсутствует
преобразование полученного числа в температуру, в чем собственно и была 'проблема'.
haker_fox
Цитата(zltigo @ Nov 7 2006, 19:32) *
Цитата(haker_fox @ Nov 7 2006, 13:18) *

проанализируйте его внимательно.

Наверное имелось ввиду, что нужно читать между строк, поскльку в исходнике начисто отсутствует
преобразование полученного числа в температуру, в чем собственно и была 'проблема'.

Действительно sad.gif прошу прощения, но я не нарочно. Дело было давно и я почему-то был уверен, что в этом коде есть необходимая информация....
belenkoff
Цитата(zltigo @ Nov 7 2006, 14:32) *
Цитата(haker_fox @ Nov 7 2006, 13:18) *

проанализируйте его внимательно.

Наверное имелось ввиду, что нужно читать между строк, поскльку в исходнике начисто отсутствует
преобразование полученного числа в температуру, в чем собственно и была 'проблема'.


Я ползовался стандартной библиотечной функцией

float ds18b20_temperature(unsigned char *addr)
{
unsigned char resolution;
if (ds18b20_read_spd(addr)==0) return -9999;
resolution=(__ds18b20_scratch_pad.conf_register>>5) & 3;
if (ds18b20_select(addr)==0) return -9999;
w1_write(0x44);
delay_ms(conv_delay[resolution]);
if (ds18b20_read_spd(addr)==0) return -9999;
w1_init();
return (*((int *) &__ds18b20_scratch_pad.temp_lsb) & bit_mask[resolution])*0.0625;
}
zltigo
Цитата(belenkoff @ Nov 7 2006, 13:42) *
Я ползовался стандартной библиотечной функцией

1. Стандарнтых (определенных а стандартых С библиотеках) не существуе
2. Можете переадресовать часть комплиментов писателям того, что Вы называете "стандартной",
но ведь и голову на плечах тоже надо иметь, прежде, чем что-то кем-то написанное и 'нахаляву'
выложенное.
belenkoff
Дык и пытаюсь думать, но за первую неделю в Сях как то думается с трудом ... особенно PRINTF() и др.

В данной ситуации как поступить? Перейти на опрос другим способом или както выйти из положения с помощью хитромудрых математических манипуляций ??
freux
Та, та, белый, белый, совсем карячий!

Поступить можно так, - пойти на сайт первоисточника - Atmel в раздел Application Notes, найти там AVR318, - там и описание и код на С для IAR. Можно проявить настойчивость, и сходить на сайт Dallasa, вы не поверите,- там тоже полно информации о том, как работать с 1-Wire интерфейсом.
zltigo
Цитата(belenkoff @ Nov 7 2006, 15:12) *
Перейти на опрос другим способом

О господи! Причем тут 'опрос'! Вы на что 'жаловались' - на бред при минусе, так неудивительно,
ибо в преобразованиии очевидно (раз положительную температуру получаете нормальную) значения
написано такое количество бреда, что и смотреть нечего.
Цитата
или както выйти из положения с помощью хитромудрых математических манипуляций ??

Ну НЕТ совсем НЕТ никаких хитромудрых операций. Пример без хитромудростей, глупостей и плавучки привел, какие проблемы?
Цитата
первую неделю в Сях

Причем тут C, если для начала с элементарной арифметикой проблемы нешуточные.
Возьмите кусок бумаги и напишите как хотите сделать преобразование а уж потом пишите, хоть
на чем. Если нет мысли как сие сделать вообще, то чегоуж тут на языки пенять.
Laksus
А "непонятное значение" это как?
_______________
Непонятно, как температура с DS18B20 может быть больше 1000 градусов?
Вроде ж не больше 125. Поэтому вряд ли это выполнится когда либо:
Код
if (temperature>=1000) {isfloat =0; temp = temperature/10;} //

И при положительных температурах останется всегда не деленным на 10.

А при отрицательной температуре у Вас всегда делится на 10:
Код
if (temperature>=0) {
temp = temperature;
if (temperature>=1000) {isfloat =0; temp = temperature/10;} //
...
}
else
{
isfloat=0;
temp = -temperature/10; // /10;
...

Может не надо делить на 10?
______________
Александр
2006 11 08
Miron
Ну что вы ругаете человека
Просто помогите
на самом деле здесь все просто
Надо объявить переменную
signed short ValueTermo; - двухбайтовое число со знаком
затем побайтно из массиса полученного с датчика
положить в него значение температуры
например так
ValueTermo=Buf_Mlan[1]*256+Buf_Mlan[0];
если нужно только целое значение
то просто делим на 16
если желаем иметь знаки после запятой
просто присваиваем ValueTermo переменной float
и тоже делим на 16
в итоге в С работаем с температурой со знаком

Удачи всем
belenkoff
Спасибо всем за помощь!

void ShowTemp(void)
{
char i;
float temp;
unsigned int tt;
unsigned long TV;
bit Sign;


tt=ds18b20_temperature(&rom_code[0][0])/0.0625;
if (tt!=-9999)
{

if((tt & 0xf800) == 0xf800){
TV=~tt; TV&=0x0000ffff; TV++; Sign=1;}
else {TV=tt; TV&=0x0000ffff; Sign=0;}


if (Sign)
{
isfloat=0;
temperature=(TV-COEF)*0.0625; //COEF - число прогрешности, если вдруг чего
} else
{
isfloat=1;
temperature=(TV+COEF)*0.0625;
}
if (temperature<0) temperature=-temperature;

temp = temperature;
//********************
i=0;
while (temp>=10)
{
temp=temp-10;
i++;
};

buff1[1]=i;
buff3[1]=i;
i=0;
while (temp>=1)
{
temp=temp-1;
i++;
};
buff1[2]=i;
buff3[2]=i;
i=0;
while (temp>=0.1)
{
temp=temp-0.1;
i++;
};
buff1[3]=i;
buff3[3]=i;
i=0;
while (temp>=0.01)
{
temp=temp-0.01;
i++;
};
buff1[4]=i;
buff3[4]=i;
}
}

Жаль, что некоторые "ВЕЛИКИЕ" очень уж любят "чайников"

Может подскажете, как вместо такой ужасной процедуры разложения значения температуры использовать PRINF
zltigo
Цитата(belenkoff @ Nov 12 2006, 22:04) *
Может подскажете, как вместо такой ужасной процедуры разложения значения температуры использовать PRINF

А зачем этот 'шедевр' еще раз постить?
В первом-же посте написал. Код мой реальный. Весь этот ужас действительно заменяется несколькими строчками.
Причем ужас вызван отнюдь не использованием или не использованием printf() ( или sprintf() - это намек) - ....printf() это не более, чем последний необязательный штрих...

Цитата
Жаль, что некоторые "ВЕЛИКИЕ" очень уж любят "чайников"

Если это булыжник в мой огород, то я реально не люблю "ламеров", к "чайникам" отношусь спокойно и стараюсь помогать. Что впрочем не мешает
-называть вещи своими именами.
-не заниматься чрезмерным разжевыванием.
КСПшник
На самом деле здесь все просто. Температура читается из ОЗУ 18B20. Причем сама температура содержится в первых двух байтах (мл. байт по мл. адресу). Так вот, считывать эту температуру необходимо в двухбайтовую переменную (например, unsigned short). Далее, анализируете старший бит этой переменной, он всегда (вне зависимости от разрядности преобразования) говорит о знаке температуры (0 - положительная, 1 - отрицательная). Так вот, если отрицательная, то сама значащая часть (см. даташит), зависящая от заданной разрядности преобразования (по умолчанию - 12 разр.), дается в инверсном коде. Т.е. вам необходимо, при 1 в старшем разряде побитово инвертировать значение температуры, предварительно запомнив "-". Удачи.
freux
Может приведенное ниже не столь красиво, ибо отсутствуют в выражении "всякие" там 1000, -9999 .. и другие десятичные цЫфры, зато давольно-таки оптимально, если глянуть в ассемблерный листинг:
Код
                if (buf[1] > 0x0F)
                    temperature = 0xFF00;
                else
                    temperature = 0;
                temperature = temperature | (buf[0] >> 3) | (buf[1] << 5);
                WriteOutTemp(temperature);

...
...
#pragma optimize=2
void WriteOutTemp(int temperature)
{
unsigned char nHalf = 0;
  temperature = temperature >> 1;
  if (SREG_C == 1)
      nHalf = 5;
  printf("T=%d.%d\r\n", temperature, nHalf);
}

С точностью один знак после запятой работает изумительно. А где printf, там и на индикатор вывести просто
Andy Great
Вопрос по той же конфигурации. Пытаюсь использовать паразитное питание, при частоте опроса до 5сек выдает завышенные показания (85 град вместо 26 комнатных). ЦРЦ в норме. Свыше 5сек в основном выдает часто нормально. Резистор по питанию 3к, паразитное питание на ноге включаю. Как только подключаю питание на 3ю ногу - все ОК. Разрядность пока не менял. ПОЧЕМУ?

Да, использую DS1822, DS18B20 пока не пробовал.

P.S.: Отвечаю сам себе. Разрядность ничего не меняет, надо ногу питания привязать к земле, как в даташите. Похоже, иначе неуверенное определение паразитного питания. При паразитном питании и 9бит преобразовании 100мс недостаточно для завершения оного, 200мс достаточно.
simsim
signed char tt;
//ds18b20_select(0);
//w1_search(0xf0,0);
ds18b20_init(0,-35,35,DS18B20_9BIT_RES);
tt=ds18b20_temperature(0);
=========================
использую библиотеки CodeVision/
tt=-15 всегда...
что не так ?
=GM=
Цитата(simsim @ Mar 4 2007, 15:09) *
signed char tt;
//ds18b20_select(0);
//w1_search(0xf0,0);
ds18b20_init(0,-35,35,DS18B20_9BIT_RES);
tt=ds18b20_temperature(0);
=========================
использую библиотеки CodeVision/
tt=-15 всегда...
что не так ?

9 бит не влезают в char
VXDRV
Решил наконец код привести - пример из Delphi (был под рукой), кому надо тот переведёт и на C и на ассемблер:

var
TH_TEMPERATURE:Byte;
TL_TEMPERATURE:Byte;
INT_TEMPERATURE:WORD;
SIGN:WORD;
TEMPERATURE:REAL;

собственно сама процедура:

TL_TEMPERATURE:=FT_In_Buffer[8];
TH_TEMPERATURE:=FT_In_Buffer[9];

INT_TEMPERATURE:=TH_TEMPERATURE*256+TL_TEMPERATURE;
SIGN:=INT_TEMPERATURE and $8000;
if SIGN=0 then
begin
TEMPERATURE:=INT_TEMPERATURE*0.0625;
end
else
begin
TEMPERATURE:=-(not(INT_TEMPERATURE)+1)*0.0625;
end;
Label1.Caption:=FloatToStrF(TEMPERATURE,ffFixed,6,4)

Принцип:
Определяем старший бит в двух байтах накладывая маску (SIGN:=INT_TEMPERATURE and $8000). Если SIGN равен нулю значит температура положительна - ничего не делаем умножаем и готово.
Иначе SIGN=$8000 (или 0x8000) температура отрицательна - берём инверсию, прибавляем единицу (что-то здесь связано с дополнительным кодом что ли), умножаем, добавляем минус и готово.
У меня работает - удачи!

P.S - В Windows есть очень хорошая программа - Калькулятор (calc.exe) - В инженерном виде позволяет проверить многие вещи.
zltigo
Цитата(VXDRV @ Mar 5 2007, 11:40) *
Решил наконец код привести

Прочитайте наконец пост номер 3.
VXDRV
Цитата(zltigo @ Mar 5 2007, 13:20) *
Цитата(VXDRV @ Mar 5 2007, 11:40) *

Решил наконец код привести

Прочитайте наконец пост номер 3.


У меня просто входная величина не приведена к signed int.
Тем не менее вариант имеет право на существование.
=GM=
Цитата(VXDRV @ Mar 5 2007, 09:40) *
Решил наконец код привести - пример из Delphi (был под рукой), кому надо тот переведёт и на C и на ассемблер:

var
TH_TEMPERATURE:Byte;
TL_TEMPERATURE:Byte;
INT_TEMPERATURE:WORD;
SIGN:WORD;
TEMPERATURE:REAL;

собственно сама процедура:

TL_TEMPERATURE:=FT_In_Buffer[8];
TH_TEMPERATURE:=FT_In_Buffer[9];

INT_TEMPERATURE:=TH_TEMPERATURE*256+TL_TEMPERATURE;
SIGN:=INT_TEMPERATURE and $8000;
if SIGN=0 then
begin
TEMPERATURE:=INT_TEMPERATURE*0.0625;
end
else
begin
TEMPERATURE:=-(not(INT_TEMPERATURE)+1)*0.0625;
end;
Label1.Caption:=FloatToStrF(TEMPERATURE,ffFixed,6,4)

Принцип:
Определяем старший бит в двух байтах накладывая маску (SIGN:=INT_TEMPERATURE and $8000). Если SIGN равен нулю значит температура положительна - ничего не делаем умножаем и готово.
Иначе SIGN=$8000 (или 0x8000) температура отрицательна - берём инверсию, прибавляем единицу (что-то здесь связано с дополнительным кодом что ли), умножаем, добавляем минус и готово.
У меня работает - удачи!

P.S - В Windows есть очень хорошая программа - Калькулятор (calc.exe) - В инженерном виде позволяет проверить многие вещи.

Не пойму, зачем такие сложности? Почему нельзя вот так?
Код
         TL_TEMPERATURE:=FT_In_Buffer[8];
         TH_TEMPERATURE:=FT_In_Buffer[9];
         INT_TEMPERATURE:=TH_TEMPERATURE*256+TL_TEMPERATURE;
         TEMPERATURE:=INT_TEMPERATURE*0.0625;

Ну будет переменная TEMPERATURE отрицательной, ну и что? Принтф или FloatToStrF точно так же её напечатает, как и положительную, только со знаком минус.
Naksojin
Я когда-то вот так делал....

unsigned char Check_Minus (unsigned char in_data)
{
if ((in_data & 0x80) != 0)
{
display.minus = ON;
return (~in_data + 1);
}
else
{
display.minus = OFF;
return in_data;
}
}

Вариантов много.
umup
Для преобразования в формат с фиксированной запятой (1 знак после запятой) я исп. такую ф-ю :
Код
int16_t ds1w_12bit_to_celsius(uint16_t value)
{uint8_t uc1;
uc1 = (value>>8) & 0x80; //запомнить знак числа
if (uc1) value = 0-value; //если нужно, проинвертировать
value = (value>>1) + (value>>3); //скорректировать
if (uc1) value = 0-value; //восстановить знак
return(value);
}
Рома_С
Помогите мне пожалуйста, тоже проблемы с отрицательной температурой правда только пробовал я это в Proteuse 6,9 SP3, пока нет возможности попробовать на реальных термодатчиках в протеусе он мне как бы измеряет отрицательную температуру но показывает вместо -1 число 4095, вместо -2 число 4094, вместо -3 число 4093 и так далие. В чем же дело, все это я сделал из примера в CodeVision для ds18b20, или это Протеус глючит, или это я так глючу smile.gif . Как мне сделать, чтобы минусовая температура отображалась как надо в десятичном виде, и как можно отобразить на дисплее hd44780 температуру в двоичном виде ибо значение температуры в ds18b20 занимает два байта. Как это все сделать в CodeVision.

Заранее спасибо.
=GM=
Цитата(Рома_С @ Mar 10 2007, 20:25) *
Помогите мне пожалуйста, тоже проблемы с отрицательной температурой правда только пробовал я это в Proteuse 6,9 SP3, пока нет возможности попробовать на реальных термодатчиках в протеусе он мне как бы измеряет отрицательную температуру но показывает вместо -1 число 4095, вместо -2 число 4094, вместо -3 число 4093 и так далие. В чем же дело, все это я сделал из примера в CodeVision для ds18b20, или это Протеус глючит, или это я так глючу smile.gif . Как мне сделать, чтобы минусовая температура отображалась как надо в десятичном виде, и как можно отобразить на дисплее hd44780 температуру в двоичном виде ибо значение температуры в ds18b20 занимает два байта. Как это все сделать в CodeVision.

Так и должно быть, никто никого не глючит. Минус единица (-1) представлена в дополнительном коде. Чтобы получить положительное число, надо проинвертировать каждый бит и к результату добавить 1.
Для -1 будет так: 4095=0xFFF, инв(0хFFF)+1=1. Для -2 будет так: 4094=0xFFE, инв(0хFFE)+1=2. И т.д.

Отсюда следует простой алгоритм: проверяете 11 бит числа, если он равен 1, значит число отрицательное. Получаете дополнение, это одна команда NEG Rx.

Преобразование в символьную форму можно осуществить разными путями. Один из них следующий. Поскольку весь диапазон 16-битных чисел со знаком лежит от -32768 до +32767, то надо определить 5 цифр. Старшая цифра, десятков тысяч может быть 0, 1, 2 или 3. Вычитаете из числа значение 10000 столько раз, чтобы число стало меньше 10000. Количество вычитаний вам даст цифру десятков тысяч. Затем из остатка вычитаете 1000 подобным образом, затем 100 и 10. Последний остаток даст вам количество единиц в числе.
Рома_С
А если нужно чтобы температура отображалась сточностю -0,1 или даже -0,05 то как из float сделать стринг?
VXDRV
Дискретность 0,0625 если вы посмотрите DATA_SHEET. Предлагаю вариант вообще без float - (господин GM - может к этому шёл) полученные два байта приводите к INT с корекцией знака (знак запоминаете во флаге каком нибудь) и умножаете на 625 результат - long int
Например 0x01 - типа старший байт 0x40 младший - в общем 0x140 * 625=200000. Ставим запятую после двадцать на самом индикаторе затем рисуем "дробную" часть получаем 20,0000 С. Алгоритм вычисления десятков сотен и т.д. приведён постом выше.
zltigo
Цитата(Рома_С @ Mar 11 2007, 17:17) *
А если нужно чтобы температура отображалась сточностю -0,1 или даже -0,05 то как из float сделать стринг?

Рома! ну почитайте хот ЭТУ ветку с начала!
=GM=
Цитата(Рома_С @ Mar 11 2007, 15:17) *
А если нужно чтобы температура отображалась сточностью -0,1 или даже -0,05 то как из float сделать стринг?

Не путайте представление с фиксированной точкой (0.05) и с плавающей (5*10^-2).
Цитата(VXDRV @ Mar 11 2007, 21:39) *
Дискретность 0,0625 если вы посмотрите DATA_SHEET. Предлагаю вариант вообще без float - (господин GM - может к этому шёл) полученные два байта приводите к INT с корекцией знака (знак запоминаете во флаге каком нибудь) и умножаете на 625 результат - long int

Может и шёл(:-), но больше сосредоточился на преобразовании. Всё правильно вы говорите, могу только развить вашу тему для лучшего понимания. В результате чтения датчика получается двухбайтное число вида YYYYY c ценой одного разряда 0.0625 градуса. Искомые градусы Цельсия получаются умножением YYYYY*0.0625 = YYYYY*625/10000=ZZZZZZZZ/10000. Естественно, деление на 10000 производить не обязательно, достаточно передвинуть запятую на четыре разряда влево. Соответственно, надо преобразовать в символьный вид не два байта, а четыре. Идеологию преобразования, на которой я заострял внимание, автор уже знает.
OlegIvanov
[/quote]
Причем тут C, если для начала с элементарной арифметикой проблемы нешуточные.
Возьмите кусок бумаги и напишите как хотите сделать преобразование а уж потом пишите, хоть
на чем. Если нет мысли как сие сделать вообще, то чегоуж тут на языки пенять.
[/quote]
Рома_С
Свершилось чудо. E меня все получилось как я хотел. Я сделал как написал КСПшник в посте №16. Я просто не знал в что можно поместить 2 байта. С преобразованиями я разобрался. Спасибо всем за помощь Вы мне здорово помогли можно даже сказать заставили biggrin.gif cheers.gif

З.Ы. Все "Великие" были когдато "чайниками". Respect.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.