Дисплеи, датчики и прочие функциональные узлы, управляемые МК.
Ответить

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 17:08:52

Аlex писал(а):В 18B20 младшие 4 бита - дробное значение температуры. И без разницы, какое разрешение установлено. 4 бита - это 16. Отсюда, чтобы получить реальную температуру, значение нужно делить на 16.

вот картинка из датащита
ds18b20.JPG
(81.71 KiB) Скачиваний: 330


Целое число не нужно делить. Делить нужно только дробную часть. Если эта дробная часть нужна...

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 19:11:57

Я в своих проектах делаю чуть проще - не делю, а, скорее, умножаю.

Как уже сказано, просто забрав два байта из памяти, мы получаем уже готовое для работы значение температуры. Правда, умноженное на 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

levaclaus писал(а):Целое число не нужно делить.
возьмите калькулятор Windows, включите его в режим программиста и убедитесь, что описанное мною ранее полностью верно. делить нужно. то, что деление на 16 можно заменить сдвигом на 4, сути не меняет.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 19:58:14

Я вообще считаю максимальное разрешение В варианта датчика избыточным, учитывая его собственную погрешность и тот факт, что его вроде бы не используют в медицинских целях. Потому и ставлю 9-битное разрешение в его конфиг-регистре. Шага в 0.5 вполне достаточно на мой взгляд.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 20:05:14

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 растут.
Последний раз редактировалось levaclaus Сб янв 07, 2017 20:31:15, всего редактировалось 1 раз.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 20:17:35

откройте калькулятор и введите в него биты с 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; // пересчитали для отрицательных температур
Последний раз редактировалось WiseLord Сб янв 07, 2017 20:37:34, всего редактировалось 1 раз.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 20:34:09

levaclaus писал(а):И ненужен не флоат, не интеджер. Достаточно signed char (максимально до 127 градусов)
ок, давайте ваш вариант кода в студию! сравним, будет ли он проще, чем мой *temp / 16 :)))

исходные данные: в массиве buf лежат полученные из датчика 9 байтов, CRC проверили - все верно. теперь пишите ваш код, который из двух char-ов buf[0] и buf[1] извлечет температуру в целых градусах, особенно, если температура отрицательная.

жду.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 20:39:26

levaclaus писал(а):вот картинка из датащита
ds18b20.JPG


Целое число не нужно делить. Делить нужно только дробную часть. Если эта дробная часть нужна...
Как не нужно ? Посмотрите внимательнее на картинку :facepalm:
Делить нужно и целую и дробную части.

Вообще, я всегда храню температуру в int'е с фиксированной точкой после первого разряда (умноженную на 10). Пихаем оба байта в int-переменную, умножаем на 10 и делим на 16. и никаких проблем.
Пример выше у WiseLord'а.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 20:44:38

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

в 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

WiseLord писал(а):я чуть выше написал, как это предположительно выглядело бы
у меня нет сомнений в вашей квалификации, и свой вопрос я адресовал не вам :beer:

levaclaus писал(а):Дополнительный код распространяется только на целую часть, а дробная в прямом.
:facepalm: вы сами-то понимаете, какую ахинею вы несете? вы по своей табличке-то проверьте, которую приводили в начале странички...

levaclaus писал(а):Все согласно датащиту
очень далеко не все: как минимум, вы не считываете весь scratchpad и не проверяете CRC принятых данных. одно это полностью противоречит даташиту.

levaclaus писал(а):Вот код, рабочий
то есть вы продолжаете настаивать, что
levaclaus писал(а):((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
проще и оптимальнее, чем предложенный мной вариант
ARV писал(а):*temp / 16
??? :shock:

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 21:02:49

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

ARV писал(а):
то есть вы продолжаете настаивать, что
levaclaus писал(а):((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
проще и оптимальнее, чем предложенный мной вариант

для моего варианта нужно одно signed char или два
для вашего нужен float либо integer и много много памяти для работы с ним.

Да, я наставаю, что мой вариант лучше.

Бли, точно, с дробными ошибся, но делов то.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 21:11:22

levaclaus писал(а):либо integer и много много памяти для работы с ним
:facepalm: всё, я умолкаю - посрамлен и растоптан :)))

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 21:28:53

WiseLord писал(а):Да, для 0,5 и -0.5 дробная часть одинаковая. Но только для такой дробной части.
Они одинаковые всего-лишь из-за совпадения. +0.5(значение = +8) и -0.5(значение = -8) имеют одинаковые 4 младших бита.
levaclaus ошибается в том, что дробная часть не имеет отрицательного значения. Ещё как имеет. У оцифрованного значения "-1" (это -0.0625 градуса) дробная часть равна 1111.

Добавлено after 10 minutes 7 seconds:
А вот этот код :
Код:
((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
эквивалентен сдвигу обоих байтов вправо на 4 и отбрасыванию старшего байта. Что и есть обычное деление на 16, только с туевой хучей ненужных операций :facepalm:

levaclaus, вы вместо выдвиганий своих мыслей за правду, взяли бы лучше бумажку с карандашиком и уделили бы этому вопросу немного времени, разрисовывая битики и байтики. И всё бы встало на свои места. Вопрос то элементарный, на уровне 5-ого класса.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 21:50:07

Аlex писал(а):А вот этот код :
Код:
((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
эквивалентен сдвигу обоих байтов вправо на 4 и отбрасыванию старшего байта. Что и есть обычное деление на 16, только с туевой хучей ненужных операций :facepalm:

levaclaus, вы вместо выдвиганий своих мыслей за правду, взяли бы лучше бумажку с карандашиком и уделили бы этому вопросу немного времени, разрисовывая битики и байтики. И всё бы встало на свои места. Вопрос то элементарный, на уровне 5-ого класса.


у меня места в контроллере 1кбайт, было бы 2, я бы не взрывал Ваш мозг.
В каноничном варианте, коего здесь сторонники собрались, делить на 16 предлагают 16-битное число. С таким подходом программа занимает 1,5 кбайта для варианта с float и 1,3 кбайта для варианта с integer.
Предложенный мной вариант использует два char. И теперь прога мало того что занимает 830 байт, так ещё и работает, зараза.

Сжечь еретика-Коперника, он нам тут воздух портит... :kill:

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 21:55:08

В каноничном варианте, коего здесь сторонники собрались, делить на 16 предлагают 16-битное число. С таким подходом программа занимает 1,5 кбайта для варианта с float и 1,3 кбайта для варианта с integer.

Во-первых, не делить, а сдвигать. Во вторых, даже если делить - компилятор всё равно это сделает сдвигом.

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 22:01:38

levaclaus писал(а):Сжечь еретика-Коперника, он нам тут воздух портит
это сильно... такая слава.... такая честь....

Re: Всё по DS18(B/S)20.

Сб янв 07, 2017 22:42:55

levaclaus писал(а):и 1,3 кбайта для варианта с integer
Я просто даже представить не могу, что вы там такого понаписали, что сдвиг 16-битной переменной занимает на 500 байт больше, чем вот такое :
((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
:shock:
Вы бы слова свои кодом что-ли прикрепляли. В идеале - два куска, которые вы меняете местами, получая такую разницу в использованной памяти.

Re: Всё по DS18(B/S)20.

Вс янв 08, 2017 00:28:35

Ну вот,подшаманил я свой градусник. Проблема вывода -9999 исчезла,т.к скинул это значение в другие переменные.Только теперь в случае обрыва шины или выхода из строя датчиков будут выводиться последние значения..Теперь чтобы вывести сигнал аварии(---- на 7-сегментниках) мне уважаемый Аlex подсказал,что нужно проверять CRC. Теперь буду кумекать как прочитать этот CRC..буду рад любой помощи по этому вопросу.. Спасибо..
Вложения
termo8.rar
(320.33 KiB) Скачиваний: 174
Ответить