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

i0jrauutpm6rrsutydyvv7hxb_c.jpeg

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

Термометр работает в диапазоне от -19 до +99 °C. При необходимости в старшем разряде одновременно отображаются минус и единица. При выходе за пределы диапазона отображаются буквы Lo или Hi. Можно «научить» устройство отображать температуры ниже -19 °C, задействовав в качестве минуса сегмент с точкой.

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

1ue7aju6otdv_qjhwdjabjxejf8.gif

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

qf91ndinrn_16xfcxellk_lxzuq.jpeg

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

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

qci9wvs7mdqulra36wldskfg6sc.jpeg

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

3iickx0nahv0rh325lvawiebzdq.jpeg

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

void OneWireSetup () {
  TCCR1A = 0<

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

void DelayMicros (unsigned int micro) {
  TCNT1 = 0; TIFR1 = 1<

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

void DisplayTemperature () {
  cli();                                  // No interrupts
  if (OneWireReset() != 0) {
    sei();
    DisplayError(0);                      // Device not found
  } 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.

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

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

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

TCCR0A = 2<

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

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

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

void DisplayNextDigit () {
  PORTB = PORTB | 1<

Наконец, подпрограмма 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<

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

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 дней — чуть меньше года.

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

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

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.

© Habrahabr.ru