вот картинка из датащита Целое число не нужно делить. Делить нужно только дробную часть. Если эта дробная часть нужна...Аlex писал(а):В 18B20 младшие 4 бита - дробное значение температуры. И без разницы, какое разрешение установлено. 4 бита - это 16. Отсюда, чтобы получить реальную температуру, значение нужно делить на 16.
Всё по DS18(B/S)20.
- Реклама
Я в своих проектах делаю чуть проще - не делю, а, скорее, умножаю.
Как уже сказано, просто забрав два байта из памяти, мы получаем уже готовое для работы значение температуры. Правда, умноженное на 16 (или на 2 в случае DS18S20).
Чтобы получить целое значение достаточно просто его разделить на 16 (2). Это просто сдвиг на 4 (1) позиции вправо. Тут всё понятно
Чтобы получить дробное значение, можно что-то мутить с 4 битами. А можно поступить проще - сначала умножить на 10, затем уже делить на 16 (2). Упростив, сводим это к умножению на 5 и сдвигу на 3 (для DS18B20) или без сдвига (для DS18S20). Результат - целое число, выражающее температуру в десятых долях градуса. Ну а при выводе на экран просто нужно точку не забыть поставить в нужном месте. И никакой возни с float/double и раздувающегося от этого кода.
Как уже сказано, просто забрав два байта из памяти, мы получаем уже готовое для работы значение температуры. Правда, умноженное на 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;
}возьмите калькулятор Windows, включите его в режим программиста и убедитесь, что описанное мною ранее полностью верно. делить нужно. то, что деление на 16 можно заменить сдвигом на 4, сути не меняет.levaclaus писал(а):Целое число не нужно делить.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
Я вообще считаю максимальное разрешение В варианта датчика избыточным, учитывая его собственную погрешность и тот факт, что его вроде бы не используют в медицинских целях. Потому и ставлю 9-битное разрешение в его конфиг-регистре. Шага в 0.5 вполне достаточно на мой взгляд.
Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
Отличный пример с калькулятором. Ок, откройте калькулятор и введите в него биты с 4 по 11 из таблицы ниже (с 4 по 9 хранится целая часть, откиньте четыре младших бита, я спецом красным выделил) и переведите в 10-тичную систему. Опа, оказывается все сошлось с таблицей. И ненужен не флоат, не интеджер. Достаточно signed char (максимально до 127 градусов).ARV писал(а):возьмите калькулятор Windows, включите его в режим программиста и убедитесь, что описанное мною ранее полностью верно. делить нужно. то, что деление на 16 можно заменить сдвигом на 4, сути не меняет.levaclaus писал(а):Целое число не нужно делить.
111 1101 = 125
000 1010 = 10
Теперь, если нужен десятичный знак после запятой, то тогда нужно делить только биты с 0 по 3. В результате получится что-то вроде temp с целым значением и temp10 с дробной частью. Но это же проще и меньше места займет чем гонять float + развивает понимание откуда ноги в ds18_20 растут.
Последний раз редактировалось levaclaus Сб янв 07, 2017 20:31:15, всего редактировалось 1 раз.
- Реклама
Это-то так (хотя, вернее, с 4 по 11). Но всё равно, чтобы выделить этот байт, без промежуточных вычислений не обойтись. Самое простое - сдвинуть на 4 16-битное число (reg[0] и reg[1] - это байты температуры).откройте калькулятор и введите в него биты с 4 по 9 из таблицы ниже
Код: Выделить всё
int16_t temp = *(int16_t*)reg;
temp *= 10;
temp /= 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 раз.
ок, давайте ваш вариант кода в студию! сравним, будет ли он проще, чем мой *temp / 16levaclaus писал(а):И ненужен не флоат, не интеджер. Достаточно signed char (максимально до 127 градусов)
исходные данные: в массиве buf лежат полученные из датчика 9 байтов, CRC проверили - все верно. теперь пишите ваш код, который из двух char-ов buf[0] и buf[1] извлечет температуру в целых градусах, особенно, если температура отрицательная.
жду.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
Как не нужно ? Посмотрите внимательнее на картинкуlevaclaus писал(а):вот картинка из датащита Целое число не нужно делить. Делить нужно только дробную часть. Если эта дробная часть нужна...
Делить нужно и целую и дробную части.
Вообще, я всегда храню температуру в int'е с фиксированной точкой после первого разряда (умноженную на 10). Пихаем оба байта в int-переменную, умножаем на 10 и делим на 16. и никаких проблем.
Пример выше у WiseLord'а.
ARV: я чуть выше написал, как это предположительно выглядело бы в случае двух char-ов. Смотрится плохо, но вряд ли можно компактнее сделать по коду. С 16-бит числом проще всего, тем более что первые два байта массива фактически и есть это готовое 16-бит число.
В примере я приведение типа указателя использовал. Хотя у себя предпочитаю union и struct:
Тут для датчика все нужные данные легко хранятся - и его ID, и скрэтчпад, и автоматическое приведение первых байтов в in16 температуру.
В примере я приведение типа указателя использовал. Хотя у себя предпочитаю 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;в temp10 не может быть отрицательной температуры. В этом то и фишка, и прелесть работы с целой и дробной частью порознь. Дополнительный код распространяется только на целую часть, а дробная в прямом. Все согласно датащиту.
Вот код, рабочий. Главный офигенный + экономия места. Если знак после запятой убрать, места ещё больше окажется. Сейчас прога весит 822 байта. С оригинальной библиотекой более 1,5 кбайта.
Вот код, рабочий. Главный офигенный + экономия места. Если знак после запятой убрать, места ещё больше окажется. Сейчас прога весит 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; //знак после запятой
у меня нет сомнений в вашей квалификации, и свой вопрос я адресовал не вамWiseLord писал(а):я чуть выше написал, как это предположительно выглядело бы
levaclaus писал(а):Дополнительный код распространяется только на целую часть, а дробная в прямом.
очень далеко не все: как минимум, вы не считываете весь scratchpad и не проверяете CRC принятых данных. одно это полностью противоречит даташиту.levaclaus писал(а):Все согласно датащиту
то есть вы продолжаете настаивать, чтоlevaclaus писал(а):Вот код, рабочий
проще и оптимальнее, чем предложенный мной вариантlevaclaus писал(а):((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
???ARV писал(а):*temp / 16
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
Вы очень сильно ошибаетесь. Дополнительный код распространяется на всё число, и Ваш код в плане temp10 - неверен.levaclaus писал(а):Дополнительный код распространяется только на целую часть, а дробная в прямом. Все согласно датащиту.
Даже не картинке, что Вы выкладывали:
Да, для 0,5 и -0.5 дробная часть одинаковая. Но только для такой дробной части.+10.125 0000 0000 1010 0010
-10.125 1111 1111 0101 1110
для моего варианта нужно одно signed char или дваARV писал(а):
то есть вы продолжаете настаивать, чтопроще и оптимальнее, чем предложенный мной вариантlevaclaus писал(а):((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
для вашего нужен float либо integer и много много памяти для работы с ним.
Да, я наставаю, что мой вариант лучше.
Бли, точно, с дробными ошибся, но делов то.
levaclaus писал(а):либо integer и много много памяти для работы с ним
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
Они одинаковые всего-лишь из-за совпадения. +0.5(значение = +8) и -0.5(значение = -8) имеют одинаковые 4 младших бита.WiseLord писал(а):Да, для 0,5 и -0.5 дробная часть одинаковая. Но только для такой дробной части.
levaclaus ошибается в том, что дробная часть не имеет отрицательного значения. Ещё как имеет. У оцифрованного значения "-1" (это -0.0625 градуса) дробная часть равна 1111.
Добавлено after 10 minutes 7 seconds:
А вот этот код :
Код: Выделить всё
((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)levaclaus, вы вместо выдвиганий своих мыслей за правду, взяли бы лучше бумажку с карандашиком и уделили бы этому вопросу немного времени, разрисовывая битики и байтики. И всё бы встало на свои места. Вопрос то элементарный, на уровне 5-ого класса.
у меня места в контроллере 1кбайт, было бы 2, я бы не взрывал Ваш мозг.Аlex писал(а): А вот этот код :эквивалентен сдвигу обоих байтов вправо на 4 и отбрасыванию старшего байта. Что и есть обычное деление на 16, только с туевой хучей ненужных операцийКод: Выделить всё
((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
levaclaus, вы вместо выдвиганий своих мыслей за правду, взяли бы лучше бумажку с карандашиком и уделили бы этому вопросу немного времени, разрисовывая битики и байтики. И всё бы встало на свои места. Вопрос то элементарный, на уровне 5-ого класса.
В каноничном варианте, коего здесь сторонники собрались, делить на 16 предлагают 16-битное число. С таким подходом программа занимает 1,5 кбайта для варианта с float и 1,3 кбайта для варианта с integer.
Предложенный мной вариант использует два char. И теперь прога мало того что занимает 830 байт, так ещё и работает, зараза.
Сжечь еретика-Коперника, он нам тут воздух портит...
В каноничном варианте, коего здесь сторонники собрались, делить на 16 предлагают 16-битное число. С таким подходом программа занимает 1,5 кбайта для варианта с float и 1,3 кбайта для варианта с integer.
Во-первых, не делить, а сдвигать. Во вторых, даже если делить - компилятор всё равно это сделает сдвигом.
Во-первых, не делить, а сдвигать. Во вторых, даже если делить - компилятор всё равно это сделает сдвигом.
это сильно... такая слава.... такая честь....levaclaus писал(а):Сжечь еретика-Коперника, он нам тут воздух портит
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
Я просто даже представить не могу, что вы там такого понаписали, что сдвиг 16-битной переменной занимает на 500 байт больше, чем вот такое :levaclaus писал(а):и 1,3 кбайта для варианта с integer
((MSB<<4)&0xf0) | ((LSB>>4)&0x0f)
Вы бы слова свои кодом что-ли прикрепляли. В идеале - два куска, которые вы меняете местами, получая такую разницу в использованной памяти.
- Сообщения: 171
- Зарегистрирован: Вс июн 17, 2012 16:32:42
Ну вот,подшаманил я свой градусник. Проблема вывода -9999 исчезла,т.к скинул это значение в другие переменные.Только теперь в случае обрыва шины или выхода из строя датчиков будут выводиться последние значения..Теперь чтобы вывести сигнал аварии(---- на 7-сегментниках) мне уважаемый Аlex подсказал,что нужно проверять CRC. Теперь буду кумекать как прочитать этот CRC..буду рад любой помощи по этому вопросу.. Спасибо..
- Вложения
-
- termo8.rar
- (320.33 КБ) 209 скачиваний


