Когда я оцениваю продуманность интерфейса пользователя специализированных микросхем от STMicroelectronics, меня временами удивляет тот факт, что они вообще способны работать. Но ведь работают же. И не просто работают, а имеют кучу фишек и крайне низкую цену. В результате приходится выбирать их снова и снова...
Очередной день обещал быть простым и приятным,
“Пару часов на прикручивание интерфейса, ещё парочку на конфигурацию регистров и пол дня впереди свободны” — считал я. Можно будет обед провести в лесу, если уж не на шашлыках, то хотя бы за скромным пикничком.
Единственное, что на меня навивало смутное чувство тревоги, это производитель: STMicroelectronics. Но отбросив воспоминания о танцах с бубном вокруг их микросхем для электросчётчика, PLC модема, продвинутого инерционного датчика… я принялся за работу.
Завязываем знакомство
Беглое ознакомление с даташитом и схемой показало что на этот раз ни схемотехник, ни трассировщик не “накосячили” (конечно блокировочный конденсатор рядом с корпусом стоило бы поставить и питание развести чуть по другому, но это повлияет на точность измерений, а не на работоспособность). Интерфейс обмена между микроконтроллером и датчиком по I2C подозрительно был похож на стандартный.
Не буду описывать в подробностях стандартный I2C, расшифрую только основные термины из даташита, которые потребуются чуть позже.
Договоримся о терминах. Для взаимопонимания
ST — старт условие;
SR — операция повторной установки старт условия, она служит для решения коллизии чтения из регистра. Мы не можем произвести чистую операцию чтения — нам сначала необходимо передать прибору номер регистра, который читаем;
SAD — адрес ведомого устройства — нашего датчика (младший бит в нём занимает признак операции -”+ W: или 0 для записи, “+R” или 1 для чтения. Таким образом “SAD+W”=0xBE “SAD+R”=0xBF;
SAC — бит подтверждения “посланный” ведомым прибором, в нашем случае датчиком;
MAK — бит подтверждения от микроконтроллера, который он выставляет в ответ на факт считывания данных NMAK его отсутствие, которым характеризуется последний байт;
SUB — это собственно адрес регистра
DATA — собственно данные
SP — стоп условие
Подтверждение производится путём замыкание шины SDA на землю
Трудно поверить что затейники из STM в этот раз отказались от новаций, но вдруг они меняют свою политику. Ну что ж, начинаем проверять.
Первые шаги. Первые проблемы
Ничего не предвещало сложностей при взгляде на эту картинку. Всего то делов — считать пару 16 битных регистров!
Дорабатываю свой шаблон обмена по I2C и уже через час на попытку записи в регистр микросхема бодро отвечает битами подтверждения. Ну что ж, научить микроконтроллер читать данные из регистров немногим сложнее. Где тут регистры в которых хранятся результаты измерений? Да вот они, но выдают сплошные нули. Что ж, придётся почитать поподробнее про конфигурацию. Пол часа за экраном монитора и чашкой чая показывают что по умолчанию микросхема находится в неактивном состоянии, однако вывести её из ступора можно изменив всего один байт в регистре конфигурации. Заодно подтолкнём ещё парочку, чтобы измерения происходили циклически, благо суровой экономией энергии в этом проекте заниматься не надо.
CTRL_REG1=0x86;
Ура, появились данные в 16 битных регистрах температуры и влажности, но как-то они странно выглядят. И в случае температуры, и влажности оба байта равны друг другу. Это по меньшей мере подозрительно. Надо разбираться. Час уходит на поиски ошибок в протоколе обмена и проверку задержек. Ничего не помогает, приходится снова углубляться в даташит. Ба..., да я не ошибся. Ну не могли разработчики из STМ не приготовить подарка для программиста. Регистров в датчике мало и для их адресации с избытком хватает одного байта. Когда читаешь или пишешь по одному байту всё нормально. Но по спецификации I2C можно как читать, так и писать несколько байт за раз и я активно использую эту возможность в своей программе. Главное, в первом случае после каждого байта посылать бит подтверждения, а во втором проверять вернул ли его датчик. Но инженеры от STM ввели своё ноухау.
Оказывается, для того чтобы после чтения или записи одного байта счётчик увеличивался на единицу и переходил на следующий байт необходимо в байте адреса регистра (SUB) установить старший бит в 1! Иначе будешь до посинения работать взаимодействовать с одним и тем же регистром. Гениально, а главное как непостижимо. Зачем это?
Добавляем костыль к стандартному протоколу.
if (ByteCount>1)
SUB[1]|=0x80;
Без поражений нет побед
Cчитанные данные начинают радовать своим разнообразием, вот только в каких же попугаях они их измеряют? Уж очень странно выглядят считанные значения. Похоже пока не прочитаешь этот даташит от корки до корки, каши с этим датчиком не сваришь.
Вот это засада! Оказывается для того, чтобы получить осязаемый результат в градусах и процентах надо ещё заняться расчётами. Извлечь калибровочные данные из определённых регистров и воспользовавшись кусочно-линейной аппроксимацией вычислить значения. Не то чтобы на Си сложно написать кусок кода, но тщательного изучения даташита явно избежать не получится. Объём кода возрастает и как результат у меня нарисовалась ещё одна проблема — заканчивается память микроконтроллера. Схемотехник изначально заложил камень всего с шестнадцатью килобайтами FLASH, а опрос датчика влажности только маленькая толика задач, на него возложенных. Поскольку микроконтроллер 32 разрядный мы имеем всего всего 4К слов для кода и констант. Даже с учётом того, что я не пользую тяжеловесные HALовские дрова этого чудовищно мало. Мне обещали закупить новую версию микроконтроллеров с памятью в два раза большей. Как там дела? Звоню менеджеру проекта. Новые камни уже пришли, но их не запаяли в платы. Оказывается ждут, когда я отрапортую что в ошибок в плате не обнаружено. Ну что ж, рапортую и получаю ответ — к концу недели может быть сваяем тебе каменный цветок. Конечно плата не сложная, можно было бы спаять самому, хотя чипы очень мелкие и требуют кропотливой пайки феном. Однако в этом проекте есть специально обученные монтажу люди, зачем их оставлять без работы, тем более что мой график очень плотный и ничего, кроме потраченного времени, я с этого не поимею.
В сложившейся ситуации без отладки вперёд не продвинуться. Приходится закоментировать часть уже написанного кода, чтобы освободить место для расчётов.
Задача линейной апроксимации не выглядит слишком сложной,
но зная кудесников из STM лучше поискать готовое решение. 10 минут серфинга приводит меня к Application с описанием методики расчётов и даже кусками кода на Си.
Зачем делать просто то, что можно сделать сложно?
Копирую в свою программу. Вот оно и время обеда. С тоской гляжу на полоску леса на горизонте, похоже не то что шашлыки, но даже пикник на природе на сегодня отменяется. Обед немного поднимает настроение и появляются силы для детального изучения аппликэйшена. Разбираюсь в регистрах с калибровочными значениями. Сначала их организация ставит меня в тупик, потом вызывает смех, почти истерический. Даже для продуктов STM чувствуется перебор. На этом моменте хочется уже подробнее остановиться.
Смотрим на рисунок и пытаемся оценить полёт мысли. В первом столбце таблицы адрес в HEX формате, во второй название, в третьей тип данных, далее побитовое описание.
Со значениями, помеченными как s16 всё понятно — 16 битные знаковые, представленные в стандартном дополнительном формате.
С теми что по адресам 30 и 31 всё веселее. Они восьмибитные. Но для того чтобы использовать их в расчётах необходимо разделить их значение на два. Младший бит незначащий. Неужели трудно было просто значащих 7 бит поместить в регистр, а старший в нуле оставить?
Но это были ещё цветочки. Ягодки пошли с 8 битными значениями по адресам 32 и 33. Оказывается, им не хватает ещё двух битов — девятого и десятого. Они почему то хранятся в регистре с адресом 35. Он содержит 2 старших бита от одного регистра с номером 32 и два от другого с номером 33. Зачем? Не проще ли под каждый из них выделить по два байта и спокойно записать значения, ведь всё равно регистр с адресом 35 пустует!
Но и это ещё не всё. В результате манипуляций мы получаем 10 битные регистры, но оказывается для вычислений нам необходимы их них только старшие семь бит. Поэтому три младших бита надо откинуть
Другими словами, нам нужно только семь бит для расчётов. Зачем спрашивается были все эти манипуляции, когда они легко уместились бы в восьмибитный регистр!
Реально какой то разрыв головного мозга. Информация в регистрах с адресами 30-31 могла выглядеть бы например так:
Для извлечения информации не пришлось бы делать невообразимых кульбитов, а в памяти регистров ещё и лишний бит остаётся.
Путешествие на меркурий
Ладно, проверяю программу, приведённую в апликейшине. Вроде всё очень похоже на описание. Однако радует предпоследняя строчка:
Если относительная влажность больше 1000 процентов принимаем её за 1000 процентов.
Класс.
В результате не проходит и двух часов, как я наконец проверяю результат измерений на практике. Он меня пугает. Датчик показывает что погодные условия в моей комнате очень близки к впадине на дне мнимого меркурианского океана. Температура выше 500 градусов по цельсию, а влажность в районе 1000 процентов. Похоже я совсем заработался и пора включать кондиционер.
Когда выясняется что на показания датчика и это влияет слабо, начинаю проверять код. Я не удержался от доработки грубовато написанного шаблона от ST, вдруг накосячил? Получасовая проверка не даёт результатов. Снова углубляюсь в даташиты чтобы понять откуда взялись этои странные множители “10” в расчётах по методу линейной интерполяции. Пол часа внимательной вычитки дали результат — оказывается это был ход, направленный на повышение точности целочисленного деления. Правда на выходе мы получили результаты уже не в процентах, а в промилле для влажности и в градусах цельсия умноженных на 10 для температуры.
Пробую выкинуть их из кода и внезапно перемещаюсь обратно в среднюю полосу — 29 градусов и 58 процентов влажности. Прижимаю пальцем датчик и очень быстро показания температуры возрастают до 34. Ура, наконец можно закругляться.
Вот только за окном уже смеркается и по тротуарам вереницами тянутся бедолаги, возвращающиеся с работы на московских электричках. Ну что ж, пойду хотя бы до парка прогуляюсь, подготовлюсь мысленно к завтрашнему утреннему разговору с генеральным из Казани. Их фирма решила попытать счастья в импортозамещении на пару с Башнефтью, нужно подумать что они от меня надеятся получить и чем собственно я смогу им помочь.
Сухой остаток
В сухом остатке имеем — ST сделала всё чтобы наш брат разработчик даже для написания простого драйвера прочитал мануал от корки до корки да ещё и с аппликейшином помучился:
- При записи-чтении нескольких байт необходимо изменять адрес регистра;
- Реальные значения измеряемых величин необходимо вычислять с использованием калибровочных значений;
- Сами калибровочные значения расположены в памяти очень странным образом и их необходимо собирать оттуда кусками проводя цирковые манипуляции с битами;
- Текст программы из файла application содержит ошибки;
- Калибровочные и резервные регистры открыты для свободной записи. Случайная запись в них новых значений приведёт к искажению результатов во время подсчёта;
- Относительная влажность легко может зашкаливать за 100 процентов и производителя это совершенно не смущает;
Вышеописанные проблемы с которыми я столкнулся в процессе написания простейшего драйвера отняли у меня больше половины рабочего дня, а новичка их решение могло бы и на несколько дней обеспечить работой.
Что уж тут говорить о создании драйвера для какого нибудь PLC модема, когда к странностям чипа и неточностям описания добавляются ещё схемотехнические ошибки, аппаратные проблемы, нюансы протоколов обмена, качество линий связи. Когда микроконтроллер должен на лету считывать и кодировать/декодировать данные и приходится задействовать на полную катушку механизм прерываний и прямого доступа в память. Причём код должен выполняться в “теневом режиме” и оказывать минимальное воздействие на ход основной программы.
Я уже не говорю о том, что о некоторых не слишком приятных нюансах производители порой сознательно умалчивают, либо предоставляют не слишком достоверную информацию.
В таких условиях без опыта работы, хорошего знания схемотехники и физики работы устройства разработчику приходится очень туго.
Пора отчитываться о результатах
Ну и наконец попробую облегчить жизнь тем, кто решит использовать данный датчик в своих разработках. Публикую ниже код соответствующих программных модулей. Сразу оговорюсь, что он не включает низкоуровневых функций обмена данных по I2C, поскольку он сильно зависит от конкретной реализации — применяемого для опроса микроконтроллера, шины обмена и даже номеров подключаемых портов.
Обмен данными с датчиком осуществляется по I2C интерфейсу. Используется две функции поддерживающие стандартный протокол, их придётся написать самому или поискать в стандартных библиотеках.
Одна для записи данных, согласно протоколу изображённому на рисунке ниже
int WRsubAddr(unsigned char SAD,unsigned char SUB, unsigned char *data,unsigned int ByteCount);
Вторая для чтения данных
int RDsubAddr(unsigned char SAD,unsigned char SUB, unsigned char *data,unsigned int ByteCount);
data — буфер данных
ByteCount — количество считанных или записанных байт
Код программы весьма простой и обильно снабжён комментариями. При инициализации драйвера датчика из него считываются константы для вычислений и помещаются в отдельную структуру. В неё же помещаются считанные и преобразованные в удобочитаемый вид результаты измерений.
Настоящий код программы был модифицирован после бурного двухдневного процесса обсуждения. Я пытался поднять точность преобразований путём использования младших бит регистров H0_rH_x2,H1_rH_x2,H0_T0_OUT,H1_T0_OUT. В случае вычисления температуры это сильно не повлияло на результат, в случае влажности привело к неадекватным результатам. Существенно приблизить показания к реальным удалось применив тип float для вычислений. Для 32 разрядных микроконтроллеров вычисление чисел с плавающей точкой не слишком ресурсоёмкая задача, а большая скорость вычислений при работе с этим датчиком не требуются. Предлагаю вниманию читателей программу именно в этом варианте.
Выражаю благодарность всем пользователям, принявшим участие в обсуждении, особенно хочется отметить mdn-tech, olartamonov и lorc. Кроме этого выражаю признательность команде хабра за то, что в числе нововведение не прикрыли возможность редактирования статей после их написания. Это позволяет менять содержание по результатам обсуждения в комментариях. Я стараюсь пользоваться этой возможностью — исправляю ошибки и иногда добавляю интересную информацию. После нескольких итераций статья становится интересней, полнее отражает тему и главное не содержит ошибок.
Код заголовочного файла
#ifndef HTS221_H
#define HTS221_H
#include "i2c.h"
//определение адресов считанных значений влажности и температуры
#define adr_H_OUT 0x28
#define adr_T_OUT 0x2A
//определение адресов регистров калибровки
#define adr_H0_rH_x2 0x30
#define adr_H1_rH_x2 0x31
#define adr_T0_degC_x8 0x32
#define adr_degC_x8 0x33
#define T1_T0_msb 0x35
#define adr_H0_T0_OUT 0x36
#define adr_H1_T0_OUT 0x3A
#define adr_T0_OUT 0x3C
#define adr_T1_OUT 0x3E
//определение адресов регистров УПРАВЛЕНИЯ
#define adr_WHO_AM_I 0x0F
#define adr_AV_CONF 0x10
#define adr_CTRL_REG1 0x20
#define adr_CTRL_REG2 0x21
#define adr_CTRL_REG3 0x22
#define adr_STATUS_REG 0x27
//адрес микросхемы HTS221 на шине I2C
#define addr_HTS221 0x5F
typedef struct { //структура в которой будут храниться данные калибровки для HTS221
//и самые данные
float H_OUT;//считанное значение влажности
//данные калибровки для влажности
float H0_rH;
float H1_rH;
float H0_T0_OUT;
float H1_T0_OUT;
float T_OUT;//считанное значение температуры
//данные калибровки для температуры
float T0_degC;
float T1_degC;
float T0_OUT;
float T1_OUT;
float Humidity;
float Temperature;
} THTS221str;
THTS221str HTS221str;
unsigned char DevCodeRead(void);
unsigned int InitHTS221CalibrTab(void);
int HTS221_Get_Humidity(void);
int HTS221_Get_Temp(void);
#endif
Код программы на СИ
#include "HTS221.h"
int WRHTS221reg(unsigned char addrREG, unsigned char *data,unsigned char ByteCount)
{//запись count байт информации в регистр
if (ByteCount>1)
addrREG|=0x80;//датчик требует для автоинкремента регистров во время чтения
return WRsubAddr(addr_HTS221,addrREG,data,ByteCount);
}
int RDHTS221regs(unsigned char addrREG, unsigned char *data,unsigned int ByteCount)
{//чтение произвольного количества байт из памяти с начальным адресом addrREG
if (ByteCount>1)
addrREG|=0x80;//датчик требует для автоинкремента регистров во время чтения
return RDsubAddr(addr_HTS221,addrREG,data,ByteCount);
}
unsigned char DevCodeRead(void)
{//считывае название датчика, если там правильное значение то выдаёт 1 иначе ноль
unsigned char Code[2];
if (!RDHTS221regs(0xF,Code,1))
return 0;
if (Code[0]==0xBC)
return 1;
else
return 0;
}
unsigned int InitHTS221CalibrTab(void)
{
unsigned char buffer[4];
short tmpi;
buffer[0]=0x86;//power down OFF, 1 HZ read
if (!WRHTS221reg(adr_CTRL_REG1,buffer,1))
return 0;
if (!DevCodeRead())
return 0;
// 1. Read H0_rH and H1_rH coefficients
if (!RDHTS221regs(adr_H0_rH_x2,buffer,2))
return 0;
HTS221str.H0_rH = (buffer[0]>>2);
HTS221str.H1_rH = (buffer[1]>>2);
//2. Read H0_T0_OUT
if (!RDHTS221regs(adr_H0_T0_OUT,(unsigned char*)(&tmpi),2))
return 0;
HTS221str.H0_T0_OUT=tmpi;
//3. Read H1_T0_OUT
if (!RDHTS221regs(adr_H1_T0_OUT,(unsigned char*)(&tmpi),2))
return 0;
HTS221str.H1_T0_OUT=tmpi;
// 1. Read from 0x32 & 0x33 registers the value of coefficients T0_d egC_x8 and T1_de gC_x8
// 2. Read from 0x35 register the value of the MSB bits of T1_deg C and T0_deg C
if (!RDHTS221regs(adr_T0_degC_x8,buffer,4))
return 0;
HTS221str.T0_degC = (buffer[0]>>3)+(0x60&(buffer[3]<<5));
HTS221str.T1_degC = (buffer[1]>>3)+(0x60&(buffer[3]<<3));
//3. Read from 0x3C & 0x3D registers the value of T0_OUT
if (!RDHTS221regs(adr_T0_OUT,(unsigned char*)(&tmpi),2))
return 0;
HTS221str.T0_OUT=tmpi;
//4. Read from 0x3E & 0x3F registers the value of T1_OUT
if (!RDHTS221regs(adr_T1_OUT,(unsigned char*)(&tmpi),2))
return 0;
HTS221str.T1_OUT=tmpi;
return 1;
}
int HTS221_Get_Humidity(void)
{
short tmp;
//4. Read H_T_OUT
if (!RDHTS221regs(adr_H_OUT,(unsigned char*)(&tmp),2))
return 0;
/*5. Compute the RH [%] value by linea r interpolation */
HTS221str.H_OUT=tmp;
HTS221str.Humidity = ((HTS221str.H_OUT - HTS221str.H0_T0_OUT) * (HTS221str.H1_rH - HTS221str.H0_rH))/(HTS221str.H1_T0_OUT - HTS221str.H0_T0_OUT) + HTS221str.H0_rH;
/* Saturation condition*/
if (HTS221str.Humidity>100)
HTS221str.Humidity =100;
return 1;
}
int HTS221_Get_Temp(void)
{
short tmp;
//4. Read H_T_OUT
if (!RDHTS221regs(adr_T_OUT,(unsigned char*)(&tmp),2))
return 0;
/*5. Compute the RH [%] value by linea r interpolation */
HTS221str.T_OUT=tmp;
HTS221str.Temperature = ((HTS221str.T_OUT - HTS221str.T0_OUT) * (HTS221str.T1_degC - HTS221str.T0_degC))/(HTS221str.T1_OUT - HTS221str.T0_OUT)+ HTS221str.T0_degC;
return 1;
}
Полезные материалы
Слылка на страницу датчика у производителя
Финальный опрос
Производитель заявил что датчик содержит аж цифровой сигнальный процессор для обработки результатов. Тогда почему нельзя было реализовать простейшие вычисления для предоставления данных в удобной для пользователя формы. Ну хорошо, он слишком занят, но почему по крайней мере хотя бы калибровочные регистры не расположить нормальным образом? Почему в простейшем демонстрационном коде содержатся ошибки? И ешё куча почему.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Комментарии (68)
lorc
17.08.2016 19:09+4Я специально полез в даташит, что бы разобраться что к чему. Мне кажется, вы не совсем поняли, для чего это было сделано.
А сделано это для повышения точности рассчетов. Ведь все рассчеты происходят в целых числах.
Если отбросить лишние биты, то вы огрубите все рассчеты. Намного правильнее проводить линейную интерполяцию в х8 и только в самом конце поделить на 8, что бы получить градусы. Иначе вы не получите заявленной точности в 0.5 градуса.
Вы можете считать что значения T0 и T1 — это числа с фиксированной точкой, при чем после точки идут три двоичных разряда.progchip666
17.08.2016 19:37Спасибо за интерес, но в даташите и их формулах, приведённых в апликейшине, почему то требуют производить сдвиги, я не сам это придумал, так что боюсь дело в чём то другом.
lorc
17.08.2016 20:12+1Извините, но в даташите в примере делят на 8 в самый последний момент. Как раз для того, что бы не терять точность. Не поделитесь ссылкой на аппноут? Хочу посмотреть как там.
Akon32
17.08.2016 19:40Но не лучше ли тогда вручную сдвигать вправо на n бит, когда требуется точность?
Иногда такие странные вещи по причине обратной совместимости появляются.progchip666
17.08.2016 19:41Очень похоже на правду. Хотел подобный вариант в опрос включить, но забыл
lorc
17.08.2016 20:12+1Так мы нули получим справа. Вместо значащих бит. Сдвинув влево, автор поста загрубил калибровку в 8 раз.
progchip666
17.08.2016 19:43Если отбросить лишние биты, то вы огрубите все рассчеты. Намного правильнее проводить линейную интерполяцию в х8 и только в самом конце поделить на 8, что бы получить градусы.
Но они то сами предлагают огрублять! Если одну пару значений «огрубить», а вторую нет то все расчёты «съедут» я полагаю.lorc
17.08.2016 20:17Ну в даташите они огрублять не предлагают. Давайте посмотрим ещё аппноут.
progchip666
17.08.2016 21:17The T0 and T1 calibration temperature values are
actually composed of 10 bits (unsigned), where the 2 MSB are in reg 35h, and the 8 LSB are
in regs 32h and 33h, respectively. T0 and T1 are the actual calibration temperature values
multiplied by 8.
А как тогда трактовать последнее предложение. Может прав lorc и можно посчитать с этими дополнительными тремя битами, а потом уже округлить?olartamonov
17.08.2016 22:43Последнее предложение просто неаккуратно сформулировано.
Считать надо со всеми наличествующими битами, округлять — в самом конце.
lorc
17.08.2016 22:47+3Ну я бы именно так делал. Точность рассчетов будет выше на этих три бита.
Я так понимаю, калибровка происходит следующим образом: они охлаждают чип, очень точно замеряют температуру T0_deg и снимают показание АЦП, которое называют T0_OUT, потом подогревают чип, замеряют температуру T1_deg и замеряют показание АЦП которое станет T1_OUT.
Но у них заявленная точность 0.5 градуса, а, чувствительность — вообще 0.0016 градуса. Поэтому они не могут сохранить T0_deg и T1_deg в целых градусах. Поэтому они добавляют ещё три бита точности и таким образом хранят эти значения с точностью 1/8 градуса.
Пока ваши измерения между T0 и T1 — это в принципе не очень страшно. Но судя по примеру из даташита, у них T0 около +10, а T1 — около +20. Итого всего 10 градусов разницы. Поэтому если вы попробуете измерить температуту +40, у вас точность уже будет в два раза хуже, чем на промежутке 10-20. И с каждой десятков градусов точность будет падать ещё в два раза. Вот тут то вас и спасут три лишние бита.
lorc
17.08.2016 19:12+2Кстати, что до влажности 1000% — там скорее всего имелись в виду не проценты, а промилле (тысячные доли).
progchip666
17.08.2016 19:39С одной стороны это вероятное предположение. С другой стороны измерения с помощью этого датчика дают показания в районе 20 — 29 в зависимости от погоды за окном. На улице сплошные дожди и я сильно сомневаюсь, что влажность у меня в квартире от двух до трёх процентов.
lorc
17.08.2016 20:15+1Ну я так понимаю, что показания 20-29 появились после того, как вы убрали умножение на 10:
Снова углубляюсь в даташиты чтобы понять откуда взялись этои странные множители “10” в расчётах по методу линейной интерполяции. Пол часа внимательной вычитки не проясняет причин их появления.
Пробую выкинутьих из кода и внезапно перемещаюсь обратно в среднюю полосу — 29 градусов и 20 процентов влажности.
До того, наверное, было 200 «процентов» влажности, не так ли? И 290 «градусов» (на самом деле 29.0).lorc
17.08.2016 20:21+1Собственно, вот цитаты из того самого аппноута:
@param Pointer to the returned humidity value that must be divided by 10 to get the value in [%]
@param Pointer to the returned temperature value that must be divided by 10 to get the value in ['C]
progchip666
17.08.2016 21:28Странная история. Упоминания об этих множителях есть только в комментариях к программе.
Нигде больше.
И цифры рассчитанные получаются явно не в процентах и градусах если их оставить в коде.lorc
17.08.2016 23:06+1Да там вообще аппноут сделать отвратительно, честно говоря. Такое ощущение, что код писал один человек, а сам документ — другой (при чем, менее понимающий).
progchip666
17.08.2016 23:17собственно и информация калибровки в регистрах тоже отвратительно сгруппирована
vadimr
17.08.2016 20:21Влажность 20% — как-то маловато, если вы не в центре пустыни живёте.
progchip666
17.08.2016 21:07ну утром было 29. У меня есть смутное предположение что кулибины могли залить его слегка сверху лаком. Поскольку он ёмкостный это не должно полностью нарушить работоспособность, но вот как раз уменьшить показания влажности может.
vadimr
17.08.2016 21:43Меньше 60, по идее, при комнатной температуре не должно быть.
progchip666
17.08.2016 21:50ну я вроде бы не в джунглях однако
vadimr
17.08.2016 21:58Если в Москве, то по прогнозу на ближайшие 10 дней – от 50 до 82 процентов на улице.
progchip666
17.08.2016 22:11Около. Москвы.
Боюсь усердные монтажники реально покрыли датчик тонким слоем лака.olartamonov
17.08.2016 22:42Под лаком он показывал бы всё время примерно одно и то же, независимо от реальной влажности. Флюсом скорее заляпали, даже если потом мыли — с датчика всё уже не смоется.
С учётом, что это очень плохо монтирующийся LGA (у них контактные площадки — медные необлуженные), запросто могли бахнуть побольше хорошего, текучего флюса.progchip666
17.08.2016 23:02Да нет влажность он определяет ёмкостным методом. Тонкая плёнка просто ухудшает чувствительность. С ёмкостными датчиками я слава богу повозился прилично за свою карьеру.
Эти датчики конкретно не видел, но все что мне попадались в аналогичных корпусах таки имели облуженные контакты.olartamonov
18.08.2016 08:56Эти датчики конкретно не видел, но все что мне попадались в аналогичных корпусах таки имели облуженные контакты
Мы конкретно с этими тоже не работали, предпочитаем Sensirion. Но всё прочее у ST в таких корпусах, что мы используем — гироакселерометры, барометры — имеет на пузе тоненькую текстолитовую пластинку с необлуженными контактами. Вручную ставится сложнее, чем облуженные, т.к. при нагреве тут же окисляется — так что если монтажник ставил совсем руками, а не трафаретом-пастой-печкой, и думать он обучен не сильно, то он гарантированно в итоге решил флюса положить побольше, и хорошего — EFD, Amtech там какой-нибудь. А они при нагреве становятся очень текучими — и ими легко заляпать мелкий корпус сверху.olartamonov
18.08.2016 09:01+2Впрочем, у нашего монтажника глаза стали квадратными, когда он увидел не это, а TI OPT3001, хехе.
Это не художник так видит. Это оно 2x2 мм размером и в полностью прозрачном корпусе :)
Первыми вопросами были «где тут первая нога?» (надо под 30-кратной лупой рассматривать кристалл, он немного несимметричный), «можно ли это брать пинцетом?» (можно) и «что это вообще такое???».
progchip666
18.08.2016 09:44Спасибо за информацию. Этот проект не я курирую, меня пригласили в качестве программиста встраиваемых систем в него уже когда он пробуксовывать начал. Поэтому монтаж не я курирую, но вроде должны этим опытные люди заниматься. Сегодня постараюсь проверить как там на самом деле обстоит.
Ранее мне приходилось работать только с инерциальными датчиками от ST в подобных корпусах.
Надеюсь что даже если действительно флюсом залили то это не испортило его свойств окончательно.
Кстати, не сталкивались с тем, что у подобного рода датчиков нестандартные требования к монтажу пайке имеются. Ограничения по типам применяемых флюсов, температурному режиму и тому подобное?olartamonov
18.08.2016 10:06+2Опытные люди — это такое понятие, относительно. От нашего монтажника, например, я с удивлением узнал, что есть мало мест, где люди паяют пастой и печкой — с ручным нанесением и ручной расстановкой. Лично он вообще с такими местами не сталкивался. В основном тыкают паяльником. В SMD. И феном в крайних случаях. С потрясающей производительностью и внешним видом пайки.
Ограничения по типам применяемых флюсов, температурному режиму и тому подобное?
Нет, не сталкивался. У датчиков влажности есть только требования по регидратации после пайки — то есть восстановлению датчика. Температуры — феном греть, вообще говоря, нежелательно любые компоненты, но мы по мере возможности всё паяем в печке, там 220 °С максимум. Флюсы по-любому используем неагрессивные, EFD или Amtech.
P.S. У барометров LPS331AP после пайки феном верхняя крышка рыжеет, как ржавая. Но показания выдают корректные.progchip666
18.08.2016 10:31Спасибо.
Датчики должны были паять вроде в Резоните. Достаточно солидная контора чтобы печки иметь. Впрочем, учитывая то что это пока лабораторные образцы в единичном исполнении, а плата очень простая, могли заказать ручную пайку и сэкономить на трафарете.olartamonov
18.08.2016 10:40+1Ну тут есть разные варианты — паяльник, фен вручную, трафарет пластиковый с ручным нанесением, трафарет стальной с ручным нанесением, трафарет стальной на автоматической линии. Мы для мелких прототипов используем трафареты с ручным нанесением, например, причём пластиковые режем сами на бытовом плоттере за 17 тыр — по 0603 включительно он справляется нормально.
Как «Резонит» делает такие партии — не знаю, но монтажник, не оборудованный мозгом, может быть на производстве любого размера. Те же китайцы, например, на мелких партиях лажают просто невероятно и гонят сплошной брак.
Ну или как пример — тот же «Резонит» недавно нам переделывал стальной трафарет, т.к. их инженер сделал с ним необъяснимую хрень, обрезав лист практически по контуру платы.progchip666
18.08.2016 11:57Ну или как пример — тот же «Резонит» недавно нам переделывал стальной трафарет, т.к. их инженер сделал с ним необъяснимую хрень, обрезав лист практически по контуру платы.
Экономить начали? Раньше они делали просто огромных размеров трафареты даже для очень маленьких плат. Вообще у них бывают косяки, но следует признать что в основном на нестандартных заказах.
Вообще написали бы статью о том, как вы устроили своё штучное и мелкосерийное производство. Думаю многим было бы интересно!olartamonov
18.08.2016 12:18Да не думаю, просто человек о чём-то своём думал. Может, день у него был неудачный.
У нас в заказе конфигуратор какой-то гигантский трафарет посчитал, я попросил сразу обрезать до 20x15 см — курьеру везти проще, нам больше не надо. Ну они и обрезали. До 10x7 или около того. По внешнему контуру падов плюс пять миллиметров — в гербере краёв платы даже не было.
Напишу обязательно, когда руки дойдут. Могу лишь сказать, что http://cameo-plotter.ru/shop/machines/curio.php — великая вещь, свою стоимость он окупил уже много раз.
monah_tuk
18.08.2016 06:16+3Джунгли, не джунгли, а во https://ru.wikipedia.org/wiki/%D0%9A%D0%BB%D0%B8%D0%BC%D0%B0%D1%82_%D0%92%D0%BB%D0%B0%D0%B4%D0%B8%D0%B2%D0%BE%D1%81%D1%82%D0%BE%D0%BA%D0%B0 летом влажность в районе 80-90% (временами и выше) — норма. Ну а для комфортного проживания, ЕМНИП, нужна влажность 40-60%, в противном случая начинают пересыхать слизистые оболочки носа, глаз. А так, относительная влажность в Сахаре около 30% :)
progchip666
18.08.2016 08:07А так, относительная влажность в Сахаре около 30%
Честно говоря не ожидал!
x893
18.08.2016 01:20+1На github есть коды к датчику — там не смотрели как люди считают? Есть разница с appNote?
progchip666
18.08.2016 11:30Если есть ссылка, то киньте, с удовольствием посмотрю
x893
18.08.2016 11:56https://github.com/search?utf8=%E2%9C%93&q=HTS221
progchip666
18.08.2016 12:39Трудно найти здравое зерно в этом чулане. Либо обрывки кода, либо слишком глобальные творения — дрова под ОС какие то.
Вот тут насколько я понял используют в расчётах таки 10 битные значения без обрезки. Но странного умножения на 10 я не заметил. В общем надо будет тоже попробовать с 10 битными значениями поработать. Увеличится точность или нет не понятно, но не уменьшится это точно :)
mdn-tech
18.08.2016 14:52Я сейчас тоже сижу разбираюсь с этим датчиком.
В двух словах — измерение влажности у него работает нормально. Датчик честно и достаточно быстро реагирует на дыхание. Но увы, правильные показания получились только после использования в расчёте типов double.progchip666
18.08.2016 14:55Странно. Там вроде достаточно простые вычисления, совершенно непонятно почему float не хватает.
Температура у меня похоже нормально вычисляется. Грею рукой — показывает 34 градуса. Учитывая то что часть тепла уходит в плату — норма. Насчёт влажности есть некоторые сомнения пока. маловаты значения как то.mdn-tech
18.08.2016 15:00Вся проблема в типах, которые используются при расчётах. У них теряется знак.
В примере STM действительно ошибка.
uint32_t tmp; и tmp = ((uint32_t)(H_T_ou t — H0_T0_ou t)) * ((uint32_t)(H1_rh — H0_rh)*10);
Поменяйте на int32_t или на long.progchip666
18.08.2016 15:17Спасибо. Теоретически возможен вариант когда H_T_ou t < H0_T0_ou t и тогда будет беда. Нужно беззнаковое значение использовать. Кстати, причём здесь множитель 10 я так и не понял. У вас с ним нормально считается?
mdn-tech
18.08.2016 15:38Множитель 10 используется для того, чтобы в значение целого типа можно было вписать «десятые» влажности. Т.е. если вы получаете 567 на выходе, то это 56,7 % влажности. Если такая точность не требуется, то тогда множитель можно убрать.
progchip666
19.08.2016 10:26Выражаю благодарность всем пользователям, принявшим участие в обсуждении, особенно хочется отметить mdn-tech, olartamonov и lorc. Исправил начальный вариант программы. Заменил переменные на float, что повысило точность вычислений. Показания влажности у меня после этого стали много более адекватными.
progchip666
19.08.2016 10:29Попытка использовать младшие битики регистров H0_rH_x2,H1_rH_x2,H0_T0_OUT,H1_T0_OUT почему то не привела к повышению точности. В случае влажности она даже существенно упала.
Randl
19.08.2016 11:17Странно, а почему она вообще упасть может? Ведь это буквально аналогично умножению и последующему делению на 8 или 2
progchip666
19.08.2016 11:48Не аналогично.
Для влажности в регистре калибровки H1_rH_x2 расположено совсем малое число. Половина разряда для него — существенная величина. В случае, если младший разряд в регистре на самом деле хранит ерунду, то может прилично снизить точность в такой ситуации. Грубо говоря когда я его учитывал то у меня получалась влажность более ста. Убрал стало выходить 57-58 процентов, что очень похоже на правду. Про дополнительную операцию деления после преобразований в случае лишнего битика я не забыл, сразу говорю.
Мы не знаем методику процесса калибровки. В младших регистрах может содержаться шумовая составляющая, а не полезные данные. Тогда учитывая их мы только снижаем точность вычислений, а не повышаем.
mdn-tech
19.08.2016 13:30Переменные float достаточно много съедают памяти (у меня 8-ми битный микроконтроллер и это заметно), поэтому вот вариант кода с целыми типами:
signed int H0_T0_out, H1_T0_out, H_T_out;
unsigned char H0_rh, H1_rh;
long tmp;
tmp= ((long)(H_T_out — H0_T0_out))*((long)(H1_rh — H0_rh))*10;
temp_humi =(unsigned int) ((tmp/(H1_T0_out — H0_T0_out))+H0_rh*10);
humi_val =(unsigned char) (temp_humi/10); // целое значение влажности
По сравнению с float, разница около 300 слов/байт кода (для PIC16):
float humidity;
humidity= (((float)(H1_rh — H0_rh)*(float)( H_T_out — H0_T0_out ))/(H1_T0_out — H0_T0_out)+H0_rh);
humi_val=(unsigned char) (humidity); // целое значение влажности
Также для двух вариантов нужна проверка на превышение 100%.
if (humi_val>100)
humi_val=100;
Оба варианта выдают одно и тоже значение (проверял на целом).
А вот по точности есть свои особенности. Мне необходмио получать данные достаточно быстро, так как устройство включается (подаёт питание на датчики), измеряет, и дальше в спячку (устройство автономное).
Всё бы хорошо, есть режим ONE_SHOT. Но в этом случае однократно измеренное значение имеет низкую повторяемостью. С включением усреднения x32 и постоянным преобразованием ситуация улучшается, шум в (в целой части) исчезает и данные обретают вменяемый вид. Ещё оказалось что этот датчик не любит прикосновений, значения при этом начинают прыгать.
По поводу абсолютной точности — измеряет правильно, во всяком случае расхождение с одним датчиком на SHT10 не более 1%. В понедельник-вторник на работе проведу сравнительные испытания с 10-20 датчиками на тех же SHT10 с усреднением значения в широком диапазоне влажности. Если будут интересны результаты — могу написать.
Документация у STM — действительно небольшой завал. В описании я не нашёл ни времени готовности датчика после подачи питания, ни длительности первого преобразования (читай сам статусный регистр, будет готово — заберёшь). И ещё больше в недоумение привело описание тока потребления от усреднения (?) при одинаковой скорости выдачи данных.progchip666
19.08.2016 14:11для 8 битного варианта микроконтроллера ваш код безусловно предпочтительней. Для ARMов с их ресурсами и гораздо лучше оптимизацией системы команд для вычислений с плавающей точкой вполне и мой вариант проходит. Умножение на 10 перед делением действительно несколько улучшает точность операции целочисленного деления.
grossws
19.08.2016 21:37ARM далеко не все с hardfp. Если говорить про Cortex-M, там hard fp есть у Cortex-M4F и Cortex-M7F.
progchip666
19.08.2016 21:47Естественно, но даже какой нибудь Cortex-M0
который кстати и стоит у меня в этом проектеи то намного быстрее справляется с такими задачами чем PIC или AVR
У меня там по любому много вычислений с плавающей точкой и экономить лишних сто 50-100 байт нет смысла. Оптимизировать или нет код конкретного проекта под целочисленную арифметику каждый программист решает сам в зависимости от многих факторов.grossws
19.08.2016 22:10В этом смысле да, soft fp на cortex-m существенно приятнее такового на avr или msp430. Поэтому довольно часто используют fixed point.
Я-то зацепился за
системы команд для вычислений с плавающей точкой
Конечно, удачный перенос строки (при отображении) перед этой строкой тоже сыграл свою роль =)
progchip666
19.08.2016 22:37Да, я понял.
Теоретически решение от mdn-tech можно считать оптимальным. Для меня правда на последнем этапе всё равно следует перевести его во float. Появится немного времени я испытаю его со своими калибровочными значениями и если и с ними оно даст хорошую точность применю в своём проекте и исправлю в статье.grossws
19.08.2016 22:41перевести его во float
Во всяких увлажнителях/осушителях это приходится делать, т. к. реально нужна абсолютная влажность, а там уже вычисления приходится делать с плавающей точкой, т. к. диапазон значений слишком большой. У вас аналогичная ситуация, это волюнтаристское решение или что-то иное?
progchip666
19.08.2016 23:08+1У меня всё даже хуже — этот датчик часть газоанализатора.
Кстати, у меня тоже небольшой оффтопик. Почему вы перестали писать на эту площадку. Почитал вашу последнюю статью она очень недурственна и хорошо оформлена кстати.
У меня складывается впечатление что хабр угасает в последнее время как уникальное явление. На него всё меньше пишут независимые авторы, а пустоты заполняются позаимствованными с других сайтов новостями и «переводными статьями» от редакторов. Я вижу что очень многие авторы с хорошими рейтингами уже по два-три года не публикуют своих статей. Интересно почему перестали писать например вы?grossws
19.08.2016 23:43В первую очередь из-за нехватки времени. Вещи связанные с микроконтроллерами — хобби, и времени на него сильно не хватает.
Есть задумки по другим темам, но они, скорее, сначала будут публиковаться в редхэтовских блогах, а потом переводится для хабра. Аналогично некоторые другие статьи тоже хочу сначала публиковать на английском, а позже на хабре, если будет не лень.
Но я не очень представляю, когда смогу найти на это время: есть ещё куча вещей по апачевскому проекту, где я committer & pmc, и по ansible, где я maintainer пачки модулей в core modules.
progchip666
19.08.2016 23:52+1Понятно. Успехов! Впрочем для хабра программирование микроконтроллеров — гадкий утёнок, минимальные права на жизнь которого они признали с большим трудом.
grossws
19.08.2016 23:52+4Ещё один демотивирующий, хотя и слабый, фактор — относительно слабая реакция и невысокие оценки для статей узкой тематики. По сравнению с статьями «про котиков» типа новостей очередного браузера на движке хромиума или статей «как нажать далее-далее-готово». В общем, нормальные статьи теряются среди тонн этого мусора и оказываются недооценены, поэтому стараюсь их находить и вовремя плюсовать.
progchip666
19.08.2016 23:56+2Полностью согласен. Беда в том, что мусора всё больше и больше, а авторских статей всё меньше и меньше. Причём в еженедельные и даже ежедневные топы попадает как правило мусор, во многом лжепереводы или слегка обработанные новости.
mdn-tech
22.08.2016 16:49+1Небольшая корректировка кода, который приводил ранее. Он имеет одно ограничение. Если влажность снижается ниже нуля, то из-за беззнаковой выходной переменной значение перейдёт сразу в 100 (сработает ограничение сверху). В некоторых вычислениях это может быть критично.
Вот исправленный вариант кода:
signed int H0_T0_out, H1_T0_out, H_T_out;
unsigned char H0_rh, H1_rh;
long tmp;
signed int temp_humi;
tmp= ((long)(H_T_out — H0_T0_out))*((long)(H1_rh — H0_rh))*10;
temp_humi =(signed int) ((tmp/(H1_T0_out — H0_T0_out))+H0_rh*10);
if (temp_humi<0)
temp_humi=0;
if (temp_humi>1000) //>100.0 %
temp_humi=1000;
humi_val=(unsigned char)(temp_humi/10);//влажность в значении целого беззнакового типа
Получаемым показаниям с данного расчёта можно верить. Датчик и в широком диапазоне вкладывается в свою погрешность.progchip666
22.08.2016 16:53Спасибо, в ближайшее время поправлю код в статье с учётом ваших изысканий. Сейчас готовлю второй вариант платы к запуску, подправлю свой код, проверю работу, на практике, тогда выложу.
MacIn
Извините, не понял перехода от «младший бит не используется» к «надо всего семь». Откуда еще 2 неиспользуемых взялись?
progchip666
Вы немного запутались, что и немудрено. Там два типа регистров. Те которые за влажность отвечают, они в конце помечены «x2»? что видимо надо понимать как умноженные а два. Они просто делятся на два ну или сдвигаются вправо на байт.
А вот с теми что отвечают за температуру просто отасс.
Они помечены в конце как «x8» их сначало приходится сдвигать вправо на три, тоесть остаётся в них пять значащих. А потом добавлять по два бита из другого регистра в старшие разряды! В результате получаем также значащих 7 бит 8-3+2
Вот кусок кода, который это делает. Он грузит сразу четыре бита и занимается перестановками, формируя два значения калибровки для температуры из трёх исходных регистров
MacIn
А, это разные регистры T/H. Не обратил внимание, спасибо.