Страница 50 из 59
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 17:08:52
levaclaus
Аlex писал(а):В 18B20 младшие 4 бита - дробное значение температуры. И без разницы, какое разрешение установлено. 4 бита - это 16. Отсюда, чтобы получить реальную температуру, значение нужно делить на 16.
вот картинка из датащита
Целое число не нужно делить. Делить нужно только дробную часть. Если эта дробная часть нужна...
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 19:11:57
WiseLord
Я в своих проектах делаю чуть проще - не делю, а, скорее, умножаю.
Как уже сказано, просто забрав два байта из памяти, мы получаем уже готовое для работы значение температуры. Правда, умноженное на 16 (или на 2 в случае DS18S20).
Чтобы получить целое значение достаточно просто его разделить на 16 (2). Это просто сдвиг на 4 (1) позиции вправо. Тут всё понятно
Чтобы получить дробное значение, можно что-то мутить с 4 битами. А можно поступить проще - сначала умножить на 10, затем уже делить на 16 (2). Упростив, сводим это к умножению на 5 и сдвигу на 3 (для DS18B20) или без сдвига (для DS18S20). Результат - целое число, выражающее температуру в десятых долях градуса. Ну а при выводе на экран просто нужно точку не забыть поставить в нужном месте. И никакой возни с float/double и раздувающегося от этого кода.
Код: Выделить всё
int16_t ds18x20GetTemp(uint8_t num)
{
int16_t ret = devs[num].temp * 5;
if (devs[num].id[0] == 0x28) // DS18B20 has 8X better resolution
ret /= 8;
// Return value is in 0.1°C units
return ret;
}
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 19:23:12
ARV
levaclaus писал(а):Целое число не нужно делить.
возьмите калькулятор Windows, включите его в режим программиста и убедитесь, что описанное мною ранее полностью верно.
делить нужно. то, что деление на 16 можно заменить сдвигом на 4, сути не меняет.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 19:58:14
Zhuk72
Я вообще считаю максимальное разрешение В варианта датчика избыточным, учитывая его собственную погрешность и тот факт, что его вроде бы не используют в медицинских целях. Потому и ставлю 9-битное разрешение в его конфиг-регистре. Шага в 0.5 вполне достаточно на мой взгляд.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 20:05:14
levaclaus
ARV писал(а):levaclaus писал(а):Целое число не нужно делить.
возьмите калькулятор Windows, включите его в режим программиста и убедитесь, что описанное мною ранее полностью верно.
делить нужно. то, что деление на 16 можно заменить сдвигом на 4, сути не меняет.
Отличный пример с калькулятором. Ок, откройте калькулятор и введите в него биты с 4 по 11 из таблицы ниже (с 4 по 9 хранится целая часть, откиньте четыре младших бита, я спецом красным выделил) и переведите в 10-тичную систему. Опа, оказывается все сошлось с таблицей. И ненужен не флоат, не интеджер. Достаточно signed char (максимально до 127 градусов).
111 1101 = 125
000 1010 = 10
Теперь, если нужен десятичный знак после запятой, то тогда нужно делить только биты с 0 по 3. В результате получится что-то вроде temp с целым значением и temp10 с дробной частью. Но это же проще и меньше места займет чем гонять float + развивает понимание откуда ноги в ds18_20 растут.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 20:17:35
WiseLord
откройте калькулятор и введите в него биты с 4 по 9 из таблицы ниже
Это-то так (хотя, вернее, с 4 по 11). Но всё равно, чтобы выделить этот байт, без промежуточных вычислений не обойтись. Самое простое - сдвинуть на 4 16-битное число (reg[0] и reg[1] - это байты температуры).
Код: Выделить всё
int16_t temp = *(int16_t*)reg;
temp *= 10;
temp /= 16; // результат в десятых долях градуса
Хотя можно и без 16-битных чисел, чем-то типа такого обойтись, но вряд ли это лучше и компактнее по коду:
Код: Выделить всё
int8_t temp = __builtin_avr_swap((reg[0] & 0x0F) | (reg[1] & 0xF0)); // выделили целую часть из 4..11 битов
uint8_t temp10 = (reg[1] & 0x0F) * 10 / 16; // выделили дробную часть
if (temp < 0)
temp10 = 10 - temp10; // пересчитали для отрицательных температур
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 20:34:09
ARV
levaclaus писал(а):И ненужен не флоат, не интеджер. Достаточно signed char (максимально до 127 градусов)
ок, давайте ваш вариант кода в студию! сравним, будет ли он проще, чем мой
*temp / 16
исходные данные: в массиве buf лежат полученные из датчика 9 байтов, CRC проверили - все верно. теперь пишите ваш код, который из двух char-ов buf[0] и buf[1] извлечет температуру
в целых градусах, особенно, если температура отрицательная.
жду.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 20:39:26
Аlex
levaclaus писал(а):вот картинка из датащита
ds18b20.JPG
Целое число не нужно делить. Делить нужно только дробную часть. Если эта дробная часть нужна...
Как не нужно ? Посмотрите внимательнее на картинку
Делить нужно и целую и дробную части.
Вообще, я всегда храню температуру в int'е с фиксированной точкой после первого разряда (умноженную на 10). Пихаем оба байта в int-переменную, умножаем на 10 и делим на 16. и никаких проблем.
Пример выше у
WiseLord'а.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 20:44:38
WiseLord
ARV: я чуть выше написал, как это предположительно выглядело бы в случае двух char-ов. Смотрится плохо, но вряд ли можно компактнее сделать по коду. С 16-бит числом проще всего, тем более что первые два байта массива фактически и есть это готовое 16-бит число.
В примере я приведение типа указателя использовал. Хотя у себя предпочитаю union и struct:
Код: Выделить всё
#define DS18X20_SCRATCH_LEN 9
#define DS18X20_ID_LEN 8
typedef union {
int16_t temp;
struct {
uint8_t sp[DS18X20_SCRATCH_LEN];
uint8_t id[DS18X20_ID_LEN];
};
} ds18x20Dev;
Тут для датчика все нужные данные легко хранятся - и его ID, и скрэтчпад, и автоматическое приведение первых байтов в in16 температуру.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 20:48:50
levaclaus
в temp10 не может быть отрицательной температуры. В этом то и фишка, и прелесть работы с целой и дробной частью порознь. Дополнительный код распространяется только на целую часть, а дробная в прямом. Все согласно датащиту.
Вот код, рабочий. Главный офигенный + экономия места. Если знак после запятой убрать, места ещё больше окажется. Сейчас прога весит 822 байта. С оригинальной библиотекой более 1,5 кбайта.
Код: Выделить всё
signed char temp=0;
unsigned char temp_10=0;
signed char ds18b20_temperature(void)
{
unsigned char LSB,MSB;
w1_init();
w1_write(0xCC);
w1_write(0xBE);
LSB=w1_read();
MSB=w1_read();
temp_10=LSB;
w1_init();
w1_write(0xCC);
w1_write(0x44);
delay_ms(800); //если 1f то убрать
return ((MSB<<4)&0xf0) | ((LSB>>4)&0x0f);
}
void ds_init(void)
{
w1_init();
w1_write(0x4e);
w1_write(0x64); //100
w1_write(0xD8);
w1_write(0x7f); //1f
delay_ms(15);
}
temp = ds18b20_temperature();
d2=temp/10; // десятки
d3=temp%10; // единицы
d4=(temp_10&0x0f)*10/16; //знак после запятой
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 21:00:52
ARV
WiseLord писал(а):я чуть выше написал, как это предположительно выглядело бы
у меня нет сомнений в вашей квалификации, и свой вопрос я адресовал не вам
levaclaus писал(а):Дополнительный код распространяется только на целую часть, а дробная в прямом.

вы сами-то понимаете, какую ахинею вы несете? вы по своей табличке-то проверьте, которую приводили в начале странички...
levaclaus писал(а):Все согласно датащиту
очень далеко не все: как минимум, вы не считываете весь scratchpad и не проверяете CRC принятых данных. одно это полностью противоречит даташиту.
levaclaus писал(а):Вот код, рабочий
то есть вы продолжаете настаивать, что
levaclaus писал(а):((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
проще и оптимальнее, чем предложенный мной вариант
ARV писал(а):*temp / 16
???

Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 21:02:49
WiseLord
levaclaus писал(а):Дополнительный код распространяется только на целую часть, а дробная в прямом. Все согласно датащиту.
Вы очень сильно ошибаетесь. Дополнительный код распространяется на всё число, и Ваш код в плане temp10 - неверен.
Даже не картинке, что Вы выкладывали:
+10.125 0000 0000 1010 0010
-10.125 1111 1111 0101 1110
Да, для 0,5 и -0.5 дробная часть одинаковая. Но только для такой дробной части.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 21:07:59
levaclaus
ARV писал(а):
то есть вы продолжаете настаивать, что
levaclaus писал(а):((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
проще и оптимальнее, чем предложенный мной вариант
для моего варианта нужно одно signed char или два
для вашего нужен float либо integer и много много памяти для работы с ним.
Да, я наставаю, что мой вариант лучше.
Бли, точно, с дробными ошибся, но делов то.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 21:11:22
ARV
levaclaus писал(а):либо integer и много много памяти для работы с ним

всё, я умолкаю - посрамлен и растоптан

Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 21:28:53
Аlex
WiseLord писал(а):Да, для 0,5 и -0.5 дробная часть одинаковая. Но только для такой дробной части.
Они одинаковые всего-лишь из-за совпадения. +0.5(значение = +8) и -0.5(значение = -8) имеют одинаковые 4 младших бита.
levaclaus ошибается в том, что дробная часть не имеет отрицательного значения. Ещё как имеет. У оцифрованного значения "-1" (это -0.0625 градуса) дробная часть равна
1111.
Добавлено after 10 minutes 7 seconds:
А вот этот код :
эквивалентен сдвигу обоих байтов вправо на 4 и отбрасыванию старшего байта. Что и есть обычное деление на 16, только с туевой хучей ненужных операций
levaclaus, вы вместо выдвиганий своих мыслей за правду, взяли бы лучше бумажку с карандашиком и уделили бы этому вопросу немного времени, разрисовывая битики и байтики. И всё бы встало на свои места. Вопрос то элементарный, на уровне 5-ого класса.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 21:50:07
levaclaus
Аlex писал(а):
А вот этот код :
эквивалентен сдвигу обоих байтов вправо на 4 и отбрасыванию старшего байта. Что и есть обычное деление на 16, только с туевой хучей ненужных операций
levaclaus, вы вместо выдвиганий своих мыслей за правду, взяли бы лучше бумажку с карандашиком и уделили бы этому вопросу немного времени, разрисовывая битики и байтики. И всё бы встало на свои места. Вопрос то элементарный, на уровне 5-ого класса.
у меня места в контроллере 1кбайт, было бы 2, я бы не взрывал Ваш мозг.
В каноничном варианте, коего здесь сторонники собрались, делить на 16 предлагают 16-битное число. С таким подходом программа занимает 1,5 кбайта для варианта с float и 1,3 кбайта для варианта с integer.
Предложенный мной вариант использует два char. И теперь прога мало того что занимает 830 байт, так ещё и работает, зараза.
Сжечь еретика-Коперника, он нам тут воздух портит...

Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 21:55:08
WiseLord
В каноничном варианте, коего здесь сторонники собрались, делить на 16 предлагают 16-битное число. С таким подходом программа занимает 1,5 кбайта для варианта с float и 1,3 кбайта для варианта с integer.
Во-первых, не делить, а сдвигать. Во вторых, даже если делить - компилятор всё равно это сделает сдвигом.
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 22:01:38
ARV
levaclaus писал(а):Сжечь еретика-Коперника, он нам тут воздух портит
это сильно... такая слава.... такая честь....
Re: Всё по DS18(B/S)20.
Добавлено: Сб янв 07, 2017 22:42:55
Аlex
levaclaus писал(а):и 1,3 кбайта для варианта с integer
Я просто даже представить не могу, что вы там такого понаписали, что сдвиг 16-битной переменной занимает на 500 байт больше, чем вот такое :
((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
Вы бы слова свои кодом что-ли прикрепляли. В идеале - два куска, которые вы меняете местами, получая такую разницу в использованной памяти.
Re: Всё по DS18(B/S)20.
Добавлено: Вс янв 08, 2017 00:28:35
eduardo
Ну вот,подшаманил я свой градусник. Проблема вывода -9999 исчезла,т.к скинул это значение в другие переменные.Только теперь в случае обрыва шины или выхода из строя датчиков будут выводиться последние значения..Теперь чтобы вывести сигнал аварии(---- на 7-сегментниках) мне уважаемый Аlex подсказал,что нужно проверять CRC. Теперь буду кумекать как прочитать этот CRC..буду рад любой помощи по этому вопросу.. Спасибо..