Для сс2530 примеры скудные, по сути одна мелкая библиотека, кривая и не доделанная и все ее слегка под себя модифицируют, но лучше она не становится. По крайней мере чего-то дельного и рабочего я не нашел. Вернее не так: нашел, но работают они плохо.
Приехали ко мне пачка датчиков, я радостно подключил, а показание: 1. По коду 1 - это ошибка инициализации. Путем каких-то манипуляций я сумел получить иные значения, вроде 8500 (это тоже ошибка, что температура еще не сконвертирована). Чувствую, что что-то не так, да и библиотеки странные (не похожие на конечный работающий продукт).
Подключаю датчик к Ардуино и все работает, с первого раза, на любых библиотеках и скетчах. Вот прямо все работает!
Подключаю обратно к сс2530, получаю показание температуры, но всегда одно. Показания температуры не меняются!
Подключил анализатор сигналов/осциллограф. Питание у сс2530 какое-то не такое чистое как у Ардуино. Сигнал одинаковый, но тайминги другие. Полез в библиотеку, подрегулировал тайминги, чтобы было похоже на то что в ардуино, но это ничего не изменило. Проверял сигнал, конечно анализатором, который распознавал 1-wire корректно, как отправку команд, так и получение ответа.
Также я провел ряд тестов:
1. Если сперва отправить команду изменения разрядности датчика, то после этого датчик выдает новое значение корректно.
2. Если, после команды конвертации слишком рано запросить температуру, то будет ошибка, что она еще не сконвертирована.
3. Тайминги, уровень напряжения, фильтрация питания - погоды не делают, т.е. на работу ни как не влияют.
Вообще, тесты показывают, что должно работать, но именно на сс2530 не работает как надо.
Интересно, что в одной из версии библиотек, для сс2530, в функции чтения датчика сперва была команда на изменение разрядности ВСЕГДА! Так у меня и появился этот пункт выше.
ПОДДЕЛКИ!!!
Ну и конечно я нашел много статей, про поддельные чипы. Тут надо по внимательнее.
https://github.com/cpetrich/counterfeit_DS18B20/blob/master/discover-classify_fake_DS18B20.ino
Такой скетч для Ардуино был предложен одним энтузиастом (Спасибо тебе, кто бы ты ни был!!!), в котором проводятся все тесты на предмет определения подделок.
Не трудно догадаться, что рынок просто кишит подделками. Видимо покупка DALLAS компанией Maxim была положительной с точки зрения распространения именно оригинальных чипов и они еще часто попадались даже в китайских магазинах. А вот покупка Analog Device компании Maxim, уже напротив вывела на рынок подделки, т.к. стоимость свежих чипов от AD выше в несколько раз, чем Maxim. Может там есть отличия в качестве, но по описанию отличий не видно.
Запускаю скетч проверки и у меня отклонения от оригинала всего 2 - это первая проверка ROM не пройдена и при установке разрешения 12bit конвертирование происходит за 120микросекунд, хотя оригинал это должен делать за 750 (в максимуме).
Я также купил чип от Maxim, который прошел все проверки, у него время конвертирования 12bit составило 530мкс, что скетч уже не смутило.
С учетом всего этого, моя подделка весьма качественная получается, наверное. Она проходит почти все тесты.
На этом этапе я не пробовал оригинальную микросхему проверить на сс2530 с предложенными библиотеками для ds18b20, но начал писать свою.
Ну как свою... Основой общения с датчиком является протокол 1-wire. Сперва мне показалось, что на Ардуино он аппаратный, но нет, на мк ATmel ничем таким и не пахнет. Библиотека OneWire вполне себе программная. В основе ее лежит функция delay_us(). Вот с нее я и начал.
Берем Анализатор сигнала, начинаем моргать ножкой с задержкой функцией delay_us и смотрим, какие времена имеем в реальности, стараясь добиться максимального попадания.
Есть вот такой вариант:
Код: Выделить всё
static void _delay_us(uint16 microSecs) {
MicroWait(microSecs);
}
Код: Выделить всё
// Wait for specified microseconds
#define MicroWait(t) Onboard_wait(t)
...
void Onboard_wait( uint16 timeout )
{
while (timeout--)
{
asm("NOP");
asm("NOP");
asm("NOP");
}
}
В других вариантах было гораздо больше вызовов "asm("NOP");", от 5 до 8 - это из того, что мне встречались. Когда я начал свои тесты, то у меня их получилось штук 20, не без нюансов.
В тесте я проверял задержки разностью в 5мкс, от 5 до 50 и много раз подряд. Мне важно было получить и 5мкс и 50мкс, достаточно точно и чтобы была повторяемость. С повторяемостью тут конечно сложно совсем, но по наблюдениям компилятор что-то оптимизирует и совсем не в пользу точности. Когда повторяемости не было, не было похоже на вмешательство из вне, вроде прерываний, т.к. тогда неточность имела бы почти случайный характер, а тут разброс в показаниях был слишком стабильный.
5мкс не получался совсем, минимально на что-то рассчитывать можно было только на 8мкс при такой реализации. Это объясняется процедурой вызова функции, плюс мы используем тип данных 16 бит на 8-ми битном мк.
По моим прикидкам, вариант:
Код: Выделить всё
void delay_us( uint16 timeout )
{
timeout-=4;
while (timeout--)
{
asm("NOP");
asm("NOP");
asm("NOP");
...
}
}
Далее подгонка до точности в 1мкс. А на 50мкс у меня уже или перебор на 2.5мкс или недобор в 2.5 мкс при различии всего в одну команду NOP. Половинку команды сделать нельзя - это уже минимальная единица. Делать доп команду каждые 10 мкс сработало бы, но проверка условия на каждую 10-ю команду - это тоже нагрузка, причем слишком большая, чтобы не нарушить вообще всё.
Причем, если задержка чуть меньше работает стабильнее, чем задержка чуть больше. Если сделать вариант чуть с большей задержкой, то чаше появляются расхождения, т.е. там где должно быть 10, может оказаться задержка 20, и так произвольно и часто, а разница всего в одной команде NOP. Не знаю с чем может быть связано такое поведение.
Уже вариант костыля "timeout-=4;" говорил сразу, что надо использовать макрос. Пишем все тоже самое, только вида:
Код: Выделить всё
#define DELAY_MS(t) for(uint16 i = 0; i < t; i++){asm("NOP");asm("NOP");asm("NOP");}
В общем остановился на варианте меньшей длительности, но большей точности. Тут всегда будут доп вмешательства, которые только увеличат задержки, а значит они скорее исправят ситуацию, чем сделают еще хуже.
2-ой ЭТАП!
Пишем функцию "uint8 ds18b20_send_bit(void)".
Не обращаем внимание на название, она всегда одна, название чуть отличается, тут главное суть, что отправляем по одному БИТУ, а не байту. Такая реализация везде.
Код: Выделить всё
// Sends one bit to bus
static void ds18b20_send(uint8 bit) {
TSENS_SBIT = 1;
TSENS_DIR |= TSENS_BV; // output
TSENS_SBIT = 0;
if (bit != 0)
_delay_us(8);
else
_delay_us(80);
TSENS_SBIT = 1;
if (bit != 0)
_delay_us(80);
else
_delay_us(2);
}
Рабочей является библиотека OneWire для Arduino, вот на нее и будем оглядываться.
Код: Выделить всё
void CRIT_TIMING OneWire::write_bit(uint8_t v)
{
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
__attribute__((unused)) volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
if (v & 1) {
noInterrupts();
DIRECT_WRITE_LOW(reg, mask);
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
delayMicroseconds(10);
DIRECT_WRITE_HIGH(reg, mask); // drive output high
interrupts();
delayMicroseconds(55);
} else {
noInterrupts();
DIRECT_WRITE_LOW(reg, mask);
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
delayMicroseconds(65);
DIRECT_WRITE_HIGH(reg, mask); // drive output high
interrupts();
delayMicroseconds(5);
}
}
Код: Выделить всё
static void ds18b20_send_bit(uint8 bit) {
TSENS_SBIT = 1;
TSENS_DIR |= TSENS_BV; // output
if (bit != 0){
TSENS_SBIT = 0;
DELAY_MS(10);
TSENS_SBIT = 1;
DELAY_MS(55);
}else{
TSENS_SBIT = 0;
DELAY_MS(65);
TSENS_SBIT = 1;
DELAY_MS(5);
}
}
Есть еще одна важная функция reset() для 1-wire.
Было:
Код: Выделить всё
// Sends reset pulse
static uint8 ds18b20_Reset(void) {
TSENS_SBIT = 0;
TSENS_DIR |= TSENS_BV; // output
_delay_us(600);
TSENS_DIR &= ~TSENS_BV; // input
_delay_us(70);
uint8 i = TSENS_SBIT;
_delay_us(200);
TSENS_SBIT = 1;
TSENS_DIR |= TSENS_BV; // output
_delay_us(600);
return i;
}
Код: Выделить всё
// Sends reset pulse
static uint8 ds18b20_Reset(void) {
TSENS_DIR |= TSENS_BV; // output
TSENS_SBIT = 0;
DELAY_MS(480);
TSENS_DIR &= ~TSENS_BV; // input
DELAY_MS(70);
uint8 i = TSENS_SBIT;
DELAY_MS(410);
return i;
}
Теперь пробуем функцию readTemperature() из библиотеки для сс2530, но с новыми функциями.
УРА!!! Заработало! Весьма странно, конечно, но факт. Я сравнивал анализатором и сигналы от Ардуино и сс2530 были одинаковыми, но на сс2530 все время выдавалось старое значение, а как поменял функции на то как сделано в Ардуино, данные стали обновляться по запросу. Есть вероятность, что я рано радуюсь и что-то просто не углядел, но предварительно все заработало как надо.
В одной из статей про подделки/клоны было сказано, что чипу при команде на конвертацию не хватает питания, т.к. в этот момент он начинает потреблять существенно больше тока, в то время как оригинал очень экономичен. Токи я не измерял, потому верность этой версии не могу подтвердить применительно к моему клону. Согласно этой версии, измеренная температура сохраняется во внутреннюю память, чип перезагружается и выдает значение из памяти, что объясняет именно старые показания. С учетом, что память не вечна, то время жизни такого чипа будет коротким, если каждый раз все записывать в память. Функция для установки разрешения датчика по сути делает ему принудительный сброс и поэтому происходит принудительное измерение температуры и датчик выдает свежее значение. Судя по одному из вариантов либы для сс2530, кто-то уже с этим сталкивался, но решать проблему глобально не стал, просто вставил в функцию readTemperature первым вызовом установку разрешающей способности, что решило проблему и на этом все.
Удивительно как для сс2530 все проблемы решаются костылями. И в очередной раз восхищаюсь проектом Ардуино, для которого базовые классы, хоть и громоздкие, но отлажены до стабильной работы. Мне кажется Ардуино не заслуженно принижают, что это для новичков и неумех. Довольно проработанная среда. Причем почему-то модные движки для той же java так не критикуются, хотя они очень жирные и делают всю работу за разработчика, работа которого сводится только верхнеуровневому описанию задачи.
Буду далее работать в этом направлении. Необходимо доделать библиотеку, чтобы она умела в нормальную работу с датчиками, плюс надо добавить те возможности, которые используются в скетче для проверки оригинальности, т.к. там есть неплохие методы. Я, наверное, их использовать не буду, но тут уже вопрос принципа, чтобы что-то доделать до хоть немного полного результата, который в последствии приложу.