Хабрахабр

[Перевод] Двухразрядный термометр

Ему всего два года, и цифры он уже читает, а буквы — нет. Этот двухразрядный светодиодный термометр автор изготовил в качестве подарка на день рождения сыну друга. Датчиком в термометре служит микросхема DS18B20, работающая по протоколу 1-Wire, а микроконтроллер применён типа ATtiny84. Теперь он может узнавать температуру за окном самостоятельно. Автор планирует поместить плату во влагозащищённый корпус и разместить за окном. Плата — квадратная со стороной в 25 мм, по размерам она сравнима с монетой в 50 пенсов. При необходимости в старшем разряде одновременно отображаются минус и единица. Индикация включается кратковременно раз в 24 секунды, и батарейки CR2032 хватает примерно на год.
Термометр работает в диапазоне от -19 до +99 °C. Можно «научить» устройство отображать температуры ниже -19 °C, задействовав в качестве минуса сегмент с точкой. При выходе за пределы диапазона отображаются буквы Lo или Hi.

По такой схеме устройство было предварительно собрано на макетке:

Протитип получился таким: Задействованы все выводы микроконтроллера, использован встроенный тактовый генератор на 8 МГц.

Затем автор разработал плату в Eagle и заказал её в PCBway. В прототипе применены DS18B20 в корпусе TO-92, ATtiny84 в корпусе PDIP и 3,6-дюймовый индикатор 3621AS. Всё, кроме дисплея, впаяно феном Youyue 858D+ при температуре в 250°C. Здесь микроконтроллер уже в корпусе SOIC, датчик — в корпусе µSOP, а резисторы, конденсаторы и дисплей — типоразмера 0805.

Устройство изготовлено в двух вариантах, с индикаторами красного и жёлтого цветов. Как на прототипе, так и на печатной плате применены индикаторы с общим анодом. Красный — на КДПВ, жёлтый — вот:

С обратной стороны впаян держатель для 20-миллиметрового литиевого элемента (любого с обозначением, начинающимся на 20, т.е., 2016, 2025 или 2032):

В реализации интерфейса 1-Wire задействована эта наработка того же автора. Прошивка написана таким образом, чтобы микроконтроллер большую часть времени находился в спящем режиме и просыпался по прерыванию от сторожевого таймера. Времязадающим является 16-битный таймер-счётчик микроконтроллера, работающий на частоте в 1 МГц:

void OneWireSetup () { TCCR1A = 0<<WGM10; // Normal mode TCCR1B = 0<<WGM12 | 2<<CS10; // Normal mode, divide clock by 8
}

Подпрограмма DelayMicros() обеспечивает задержку в заданное число микросекунд, опираясь на регистр сравнения выхода OCR0A:

void DelayMicros (unsigned int micro) { TCNT1 = 0; TIFR1 = 1<<OCF1A; OCR1A = micro; while ((TIFR1 & 1<<OCF1A) == 0);
}

Подпрограмма DisplayTemperature() считывает значение температуры из датчика и отображает его. Поскольку датчик на шине всего один, на серийный номер можно не обращать внимание, и просто подать команду Skip ROM, после чего все последующие команды поступают на любое устройство:

void DisplayTemperature () else { OneWireWrite(SkipROM); OneWireWrite(ConvertT); while (OneWireRead() != 0xFF); OneWireReset(); OneWireWrite(SkipROM); OneWireWrite(ReadScratchpad); OneWireReadBytes(9); sei(); // Interrupts if (OneWireCRC(9) == 0) { int temp = DataWords[0]; Display((temp+8)>>4); // Round to nearest degree } else DisplayError(1); // CRC error }
}

В ответ на запрос датчик возвращает значение температуры в виде 16-битного целого числа со знаком в единицах, равных 1/16 градуса. Число округляется до ближайшего целого градуса и отображается вызовом подпрограммы Display().

Подпрограмма DisplayError() отображает ошибки взаимодействия микроконтроллера с датчиком по шине 1-Wire:

void DisplayError (int no) { Buffer[0] = Error; Buffer[1] = no;
}

E0 — датчик не обнаружен, E1 — ошибка CRC.

Например, чтобы отобразить число 20, надо выполнить: Данные для динамической индикации берутся из массива Buffer[].

Buffer[0]=2; Buffer[1]=0;

Таймер-счётчик 0 генерирует прерывания на частоте в 125 Гц, чего достаточно для устранения мерцания. Вначале таймер сконфигурирован в setup()"

TCCR0A = 2<<WGM00; // CTC mode; count up to OCR0A TCCR0B = 0<<WGM02 | 4<<CS00; // Divide by 256 OCR0A = 250-1; // Compare match at 125Hz TIMSK0 = 0; // Interrupts initially off

Процедура обработки прерывания совпадения при сравнении вызывает подпрограмму DisplayNextDigit() и затем считает в обратном направлении:

ISR(TIM0_COMPA_vect) { DisplayNextDigit(); Ticks--;
}

Подпрограмма DisplayNextDigit() считывает данные из соответствующей ячейки массива Buffer[] и включает нужные сегменты в соответствующем разряде дисплея. Программа использует #define для выбора между индикатором с общим катодом или анодом. Если при подаче питания светятся сразу все сегменты, значит, тип дисплея не соответствует заданному в прошивке. Для общего катода подпрограмму надо заменить на такую:

void DisplayNextDigit () { PORTB = PORTB | 1<<digit; // Turn old digit off digit = digit ^ 1; // Toggle between 0 and 1 char segs = charArray[Buffer[digit]]; PORTA = segs; // Lit segments high PORTB = PORTB & ~(1<<digit); // Turn new digit on
}

Наконец, подпрограмма Display() вырабатывает двухзначное число для записи в массив Buffer[]:

void Display (int n) { int units = n % 10; int tens = n / 10; int temp0 = tens; int temp1 = abs(units); if (tens < -1) {temp0 = Lo; temp1 = Lo+1; } else if (tens > 9) {temp0 = Hi; temp1 = Hi+1; } else if (tens == -1) temp0 = Minus1; else if ((tens == 0) && (units >= 0)) temp0 = Blank; else if ((tens == 0) && (units < 0)) temp0 = Minus; Buffer[0] = temp0; Buffer[1] = temp1;
}

В ней же учтены случаи отображения минуса вместе с единицей в старшем разряде, а также сообщений о выходе температуры за пределы диапазона.

Для максимально возможного энергосбережения отключены АЦП, тактовые генераторы интерфейса USI и АЦП, и разрешён спящий режим PWR_DOWN:

ADCSRA &= ~(1<<ADEN); // Disable ADC to save power PRR = 1<<PRUSI | 1<<PRADC; // Turn off clocks to USI & ADC to save power set_sleep_mode(SLEEP_MODE_PWR_DOWN);

Основная программа отображает температуру в течение десятых долей секунды, затем включает спящий режим. Оказалось, что это минимальная продолжительность индикации, удобная для считывания. За две секунды до отображения температуры кратковременно мигает точка:

void loop () { Buffer[0] = DP; Buffer[1] = Blank; DisplayOn(12); WDDelay(6); // Sleep for 1 second Buffer[0] = Blank; Buffer[1] = DP; DisplayOn(12); WDDelay(6); // Sleep for 1 second DisplayTemperature(); DisplayOn(12); WDDelay(9); // Sleep for 8 seconds WDDelay(9); // Sleep for 16 seconds WDDelay(9); // Sleep for 24 seconds
}

Дисплей остаётся выключенным на 24 секунды за счёт трёх вызовов сторожевого таймера по 8 секунд каждый. При работающем индикаторе потребляемый ток составляет 6,6 мА, в спящем режиме — 4,7 мкА, средний потребляемый ток равен 1/240 * 6,6 мА. Типичная ёмкость элемента CR2032 равна 225 мАч, поэтому хватит его на (225/6.6) x 240 / 24 = 340 дней — чуть меньше года.

Элемент с расширенным температурным диапазоном BR2032 будет работать в диапазоне от -30 до +85 °C. Температурные диапазоны компонентов следующие: микроконтроллера и индикатора — от -40 до +85°C, резисторов и конденсатора — от -55 до +125 °C, батарейки — от -20 до +70 °C.

В IDE надо выбрать пункт ATtiny24/44/84 в разделе ATTinyCore меню Board. Микроконтроллер сделан Arduino-совместимым при помощи этой разработки Spence Konde. Затем надо выставить следующие опции, не обращая внимания на остальные:

Chip: "ATtiny84"
Clock: "8 MHz (internal)"
B.O.D: "B.O.D. Disabled"
Pin Mapping: "Clockwise (like damellis core)"

Программа залита при помощи приспособления Pomona test clip, размещённого поверх микроконтроллера и подключённого к программатору SparkFun Tiny AVR Programmer. Вначале надо выбрать Burn Bootloader, затем — Upload.

Ссылки: полный текст программы, плата и программа на GitHub, плата на OSHpark.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть